/*!
Parts of this script are from the TurboWarp Packager <https://packager.turbowarp.org/>, licensed as follows:
Copyright (C) 2021-2022 Thomas Weber

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Parts of this script are from Scratch <https://scratch.mit.edu/>, licensed as follows:
Copyright (c) 2016, Massachusetts Institute of Technology
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

/******/ (function(modules) { // webpackBootstrap
/******/ 	// install a JSONP callback for chunk loading
/******/ 	function webpackJsonpCallback(data) {
/******/ 		var chunkIds = data[0];
/******/ 		var moreModules = data[1];
/******/
/******/
/******/ 		// add "moreModules" to the modules object,
/******/ 		// then flag all "chunkIds" as loaded and fire callback
/******/ 		var moduleId, chunkId, i = 0, resolves = [];
/******/ 		for(;i < chunkIds.length; i++) {
/******/ 			chunkId = chunkIds[i];
/******/ 			if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
/******/ 				resolves.push(installedChunks[chunkId][0]);
/******/ 			}
/******/ 			installedChunks[chunkId] = 0;
/******/ 		}
/******/ 		for(moduleId in moreModules) {
/******/ 			if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/ 				modules[moduleId] = moreModules[moduleId];
/******/ 			}
/******/ 		}
/******/ 		if(parentJsonpFunction) parentJsonpFunction(data);
/******/
/******/ 		while(resolves.length) {
/******/ 			resolves.shift()();
/******/ 		}
/******/
/******/ 	};
/******/
/******/
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// object to store loaded and loading chunks
/******/ 	// undefined = chunk not loaded, null = chunk preloaded/prefetched
/******/ 	// Promise = chunk loading, 0 = chunk loaded
/******/ 	var installedChunks = {
/******/ 		"scaffolding-min": 0
/******/ 	};
/******/
/******/
/******/
/******/ 	// script path function
/******/ 	function jsonpScriptSrc(chunkId) {
/******/ 		return __webpack_require__.p + "scaffolding/" + ({"vendors~iframe-extension-worker":"vendors~iframe-extension-worker"}[chunkId]||chunkId) + ".js"
/******/ 	}
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/ 	// This file contains only the entry chunk.
/******/ 	// The chunk loading function for additional chunks
/******/ 	__webpack_require__.e = function requireEnsure(chunkId) {
/******/ 		var promises = [];
/******/
/******/
/******/ 		// JSONP chunk loading for javascript
/******/
/******/ 		var installedChunkData = installedChunks[chunkId];
/******/ 		if(installedChunkData !== 0) { // 0 means "already installed".
/******/
/******/ 			// a Promise means "currently loading".
/******/ 			if(installedChunkData) {
/******/ 				promises.push(installedChunkData[2]);
/******/ 			} else {
/******/ 				// setup Promise in chunk cache
/******/ 				var promise = new Promise(function(resolve, reject) {
/******/ 					installedChunkData = installedChunks[chunkId] = [resolve, reject];
/******/ 				});
/******/ 				promises.push(installedChunkData[2] = promise);
/******/
/******/ 				// start chunk loading
/******/ 				var script = document.createElement('script');
/******/ 				var onScriptComplete;
/******/
/******/ 				script.charset = 'utf-8';
/******/ 				script.timeout = 120;
/******/ 				if (__webpack_require__.nc) {
/******/ 					script.setAttribute("nonce", __webpack_require__.nc);
/******/ 				}
/******/ 				script.src = jsonpScriptSrc(chunkId);
/******/
/******/ 				// create error before stack unwound to get useful stacktrace later
/******/ 				var error = new Error();
/******/ 				onScriptComplete = function (event) {
/******/ 					// avoid mem leaks in IE.
/******/ 					script.onerror = script.onload = null;
/******/ 					clearTimeout(timeout);
/******/ 					var chunk = installedChunks[chunkId];
/******/ 					if(chunk !== 0) {
/******/ 						if(chunk) {
/******/ 							var errorType = event && (event.type === 'load' ? 'missing' : event.type);
/******/ 							var realSrc = event && event.target && event.target.src;
/******/ 							error.message = 'Loading chunk ' + chunkId + ' failed.\n(' + errorType + ': ' + realSrc + ')';
/******/ 							error.name = 'ChunkLoadError';
/******/ 							error.type = errorType;
/******/ 							error.request = realSrc;
/******/ 							chunk[1](error);
/******/ 						}
/******/ 						installedChunks[chunkId] = undefined;
/******/ 					}
/******/ 				};
/******/ 				var timeout = setTimeout(function(){
/******/ 					onScriptComplete({ type: 'timeout', target: script });
/******/ 				}, 120000);
/******/ 				script.onerror = script.onload = onScriptComplete;
/******/ 				document.head.appendChild(script);
/******/ 			}
/******/ 		}
/******/ 		return Promise.all(promises);
/******/ 	};
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ 		}
/******/ 	};
/******/
/******/ 	// define __esModule on exports
/******/ 	__webpack_require__.r = function(exports) {
/******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 		}
/******/ 		Object.defineProperty(exports, '__esModule', { value: true });
/******/ 	};
/******/
/******/ 	// create a fake namespace object
/******/ 	// mode & 1: value is a module id, require it
/******/ 	// mode & 2: merge all properties of value into the ns
/******/ 	// mode & 4: return value when already ns object
/******/ 	// mode & 8|1: behave like require
/******/ 	__webpack_require__.t = function(value, mode) {
/******/ 		if(mode & 1) value = __webpack_require__(value);
/******/ 		if(mode & 8) return value;
/******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ 		var ns = Object.create(null);
/******/ 		__webpack_require__.r(ns);
/******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ 		return ns;
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// on error function for async loading
/******/ 	__webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/
/******/ 	var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
/******/ 	var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
/******/ 	jsonpArray.push = webpackJsonpCallback;
/******/ 	jsonpArray = jsonpArray.slice();
/******/ 	for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
/******/ 	var parentJsonpFunction = oldJsonpFunction;
/******/
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = "./src/scaffolding/export.js");
/******/ })
/************************************************************************/
/******/ ({

/***/ "./node_modules/@turbowarp/json/src/index.js":
/*!***************************************************!*\
  !*** ./node_modules/@turbowarp/json/src/index.js ***!
  \***************************************************/
/*! exports provided: _parse, parse, stringify */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_parse", function() { return _parse; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "parse", function() { return parse; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "stringify", function() { return stringify; });
/**
 * @param {string} source
 * @returns {unknown}
 */
const _parse = (source) => {
  let index = 0;

  const lineInfo = () => {
    let line = 0;
    let column = 0;
    for (let i = 0; i < index; i++) {
      if (source[i] === '\n') {
        line++;
        column = 0;
      } else {
        column++;
      }
    }
    return { line: line + 1, column: column + 1 };
  };

  const error = (message) => {
    const { line, column } = lineInfo();
    throw new SyntaxError(
      `${message} (Line ${line} Column ${column})`
    );
  };

  const charAt = (index) => {
    if (index >= source.length) {
      error('Unexpected end of input');
    }
    return source[index];
  };

  const currentChar = () => charAt(index);

  const next = () => {
    index++;
  };

  const expect = (char) => {
    if (currentChar() !== char) {
      error(`Expected '${char}' but found '${currentChar()}'`);
    }
    next();
  };

  const peek = (length = 1, offset = 1) => {
    if (index + offset + length > source.length) {
      return '';
    }
    if (length === 1) {
      return charAt(index + offset);
    }
    let result = '';
    for (let i = 0; i < length; i++) {
      result += charAt(index + offset + i);
    }
    return result;
  };

  const skipWhitespaceAndComments = () => {
    while (true) {
      if (/\s/.test(currentChar())) {
        next();
      } else {
        const next2 = peek(2, 0);
        if (next2 === '//') {
          next(); // consume /
          next(); // consume /
          while (currentChar() !== '\n') {
            next();
          }
        } else if (next2 === '/*') {
          next(); // consume /
          next(); // consume *
          while (peek(2, 0) !== '*/') {
            next();
          }
          next(); // consume *
          next(); // consume /
        } else {
          break;
        }
      }
    }
  };

  const parseValue = () => {
    skipWhitespaceAndComments();
    const char = currentChar();
    switch (char) {
      case '"':
        return parseString();
      case '{':
        return parseObject();
      case '[':
        return parseList();
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
      case '-':
        return parseNumber();
      default:
        return parseWord();
    }
  };

  const parseWord = () => {
    if (peek(4, 0) === 'null') {
      for (let i = 0; i < 4; i++) {
        next();
      }
      return null;
    }
    if (peek(4, 0) === 'true') {
      for (let i = 0; i < 4; i++) {
        next();
      }
      return true;
    }
    if (peek(5, 0) === 'false') {
      for (let i = 0; i < 5; i++) {
        next();
      }
      return false;
    }
    // Non-standard extensions
    if (peek(8, 0) === 'Infinity') {
      for (let i = 0; i < 8; i++) {
        next();
      }
      return Infinity;
    }
    if (peek(3, 0) === 'NaN') {
      for (let i = 0; i < 3; i++) {
        next();
      }
      return NaN;
    }
    error(`Unexpected character '${currentChar()}'`);
  };

  const parseNumber = () => {
    // Non-standard extension
    if (peek(9, 0) === '-Infinity') {
      for (let i = 0; i < 9; i++) {
        next();
      }
      return -Infinity;
    }
    let number = '';
    while (true) {
      number += currentChar();
      if (/[\d.e+-]/i.test(peek())) {
        next();
      } else {
        break;
      }
    }
    next();
    const value = +number;
    if (Number.isNaN(value)) {
      error(`Not a number: ${number}`);
    }
    return value;    
  };

  const parseString = () => {
    expect('"');
    let result = '';
    while (true) {
      const char = currentChar();
      if (char === '"') {
        break;
      } else if (char === '\\') {
        next();
        switch (currentChar()) {
          case '"':
            result += '"';
            break;
          case '/':
            result += '/';
            break;
          case '\\':
            result += '\\';
            break;
          case 'b':
            result += '\b';
            break;
          case 'f':
            result += '\f';
            break;
          case 'n':
            result += '\n';
            break;
          case 'r':
            result += '\r';
            break;
          case 't':
            result += '\t';
            break;
          case 'u': {
            let hexString = '';
            for (let i = 0; i < 4; i++) {
              next();
              const nextChar = currentChar();
              if (!/[0-9a-f]/i.test(nextChar)) {
                error(`Invalid hex code: ${nextChar}`);
              }
              hexString += nextChar;
            }
            const hexNumber = Number.parseInt(hexString, 16);
            const letter = String.fromCharCode(hexNumber);
            result += letter;
            break;
          }
          default:
            error(`Invalid escape code: \\${currentChar()}`);
        }
      } else {
        result += char;
      }
      next();
    }
    expect('"');
    return result;
  };

  const parseList = () => {
    expect('[');
    skipWhitespaceAndComments();
    const result = [];
    while (true) {
      skipWhitespaceAndComments();
      if (currentChar() === ']') {
        break;
      }
      const value = parseValue();
      result.push(value);
      skipWhitespaceAndComments();
      if (currentChar() === ',') {
        next();
      } else {
        break;
      }
    }
    expect(']');
    return result;
  };

  const parseObject = () => {
    expect('{');
    skipWhitespaceAndComments();
    const result = {};
    while (true) {
      skipWhitespaceAndComments();
      if (currentChar() === '}') {
        break;
      }
      const key = parseString();
      skipWhitespaceAndComments();
      expect(':');
      const value = parseValue();
      result[key] = value;
      skipWhitespaceAndComments();
      if (currentChar() === ',') {
        next();
      } else {
        break;
      }
    }
    expect('}');
    return result;
  };

  return parseValue();
};

/**
 * @param {string} source
 * @returns {unknown}
 */
const parse = (source) => {
  try {
    return JSON.parse(source);
  } catch (e1) {
    try {
      return _parse(source);
    } catch (e2) {
      // The error from JSON.parse is probably more useful.
      throw e1;
    }
  }
};

/**
 * @param {unknown} object
 * @returns {string}
 */
const stringify = (object) => {
  if (typeof object === 'string') {
    return JSON.stringify(object);
  }
  if (typeof object === 'number' || typeof object === 'boolean') {
    // Difference from regular JSON: [-]Infinity and NaN will be sanitized as-is
    return object.toString();
  }
  if (object === null || typeof object === 'undefined' || typeof object === 'symbol') {
    return 'null';
  }
  if (Array.isArray(object)) {
    return `[${object.map((i) => stringify(i)).join(',')}]`;
  }
  if (typeof object === 'object') {
    let result = '{';
    let isFirstItem = true;
    const keys = Object.keys(object);
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      const value = object[key];
      if (typeof value !== 'undefined') {
        if (!isFirstItem) {
          result += ',';
        }
        result += `${JSON.stringify(key)}:${stringify(value)}`;
        isFirstItem = false;
      }
    }
    result += '}';
    return result;
  }
  if (typeof object === 'bigint') {
    throw new TypeError('Can not stringify bigint');
  }
  throw new TypeError(`Can not stringify: ${object}`);
};


/***/ }),

/***/ "./node_modules/@vernier/godirect/dist/godirect.min.umd.js":
/*!*****************************************************************!*\
  !*** ./node_modules/@vernier/godirect/dist/godirect.min.umd.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * Copyright (c) 2019 Vernier Software. All rights reserved.
 * This code may only be used under the BSD 3-Clause license found at
 * https://raw.githubusercontent.com/VernierST/godirect-js/master/LICENSE
 */

!function(e,t){ true?module.exports=t():undefined}(this,function(){"use strict";function e(e,t,n,r,i,a,o){try{var s=e[a](o),u=s.value}catch(e){return void n(e)}s.done?t(u):Promise.resolve(u).then(r,i)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(i,a){var o=t.apply(n,r);function s(t){e(o,i,a,s,u,"next",t)}function u(t){e(o,i,a,s,u,"throw",t)}s(void 0)})}}function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function r(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}function i(e,t,n){return t&&r(e.prototype,t),n&&r(e,n),e}function a(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function");e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,writable:!0,configurable:!0}}),t&&s(e,t)}function o(e){return(o=Object.setPrototypeOf?Object.getPrototypeOf:function(e){return e.__proto__||Object.getPrototypeOf(e)})(e)}function s(e,t){return(s=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e})(e,t)}function u(e,t){return!t||"object"!=typeof t&&"function"!=typeof t?function(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}(e):t}!function(e){var t,n=Object.prototype,r=n.hasOwnProperty,i="function"==typeof Symbol?Symbol:{},a=i.iterator||"@@iterator",o=i.asyncIterator||"@@asyncIterator",s=i.toStringTag||"@@toStringTag",u="object"==typeof module,c=e.regeneratorRuntime;if(c)u&&(module.exports=c);else{(c=e.regeneratorRuntime=u?module.exports:{}).wrap=b;var f="suspendedStart",h="suspendedYield",l="executing",p="completed",d={},m={};m[a]=function(){return this};var v=Object.getPrototypeOf,g=v&&v(v(C([])));g&&g!==n&&r.call(g,a)&&(m=g);var y=x.prototype=_.prototype=Object.create(m);k.prototype=y.constructor=x,x.constructor=k,x[s]=k.displayName="GeneratorFunction",c.isGeneratorFunction=function(e){var t="function"==typeof e&&e.constructor;return!!t&&(t===k||"GeneratorFunction"===(t.displayName||t.name))},c.mark=function(e){return Object.setPrototypeOf?Object.setPrototypeOf(e,x):(e.__proto__=x,s in e||(e[s]="GeneratorFunction")),e.prototype=Object.create(y),e},c.awrap=function(e){return{__await:e}},E(S.prototype),S.prototype[o]=function(){return this},c.AsyncIterator=S,c.async=function(e,t,n,r){var i=new S(b(e,t,n,r));return c.isGeneratorFunction(t)?i:i.next().then(function(e){return e.done?e.value:i.next()})},E(y),y[s]="Generator",y[a]=function(){return this},y.toString=function(){return"[object Generator]"},c.keys=function(e){var t=[];for(var n in e)t.push(n);return t.reverse(),function n(){for(;t.length;){var r=t.pop();if(r in e)return n.value=r,n.done=!1,n}return n.done=!0,n}},c.values=C,A.prototype={constructor:A,reset:function(e){if(this.prev=0,this.next=0,this.sent=this._sent=t,this.done=!1,this.delegate=null,this.method="next",this.arg=t,this.tryEntries.forEach(M),!e)for(var n in this)"t"===n.charAt(0)&&r.call(this,n)&&!isNaN(+n.slice(1))&&(this[n]=t)},stop:function(){this.done=!0;var e=this.tryEntries[0].completion;if("throw"===e.type)throw e.arg;return this.rval},dispatchException:function(e){if(this.done)throw e;var n=this;function i(r,i){return s.type="throw",s.arg=e,n.next=r,i&&(n.method="next",n.arg=t),!!i}for(var a=this.tryEntries.length-1;a>=0;--a){var o=this.tryEntries[a],s=o.completion;if("root"===o.tryLoc)return i("end");if(o.tryLoc<=this.prev){var u=r.call(o,"catchLoc"),c=r.call(o,"finallyLoc");if(u&&c){if(this.prev<o.catchLoc)return i(o.catchLoc,!0);if(this.prev<o.finallyLoc)return i(o.finallyLoc)}else if(u){if(this.prev<o.catchLoc)return i(o.catchLoc,!0)}else{if(!c)throw new Error("try statement without catch or finally");if(this.prev<o.finallyLoc)return i(o.finallyLoc)}}}},abrupt:function(e,t){for(var n=this.tryEntries.length-1;n>=0;--n){var i=this.tryEntries[n];if(i.tryLoc<=this.prev&&r.call(i,"finallyLoc")&&this.prev<i.finallyLoc){var a=i;break}}a&&("break"===e||"continue"===e)&&a.tryLoc<=t&&t<=a.finallyLoc&&(a=null);var o=a?a.completion:{};return o.type=e,o.arg=t,a?(this.method="next",this.next=a.finallyLoc,d):this.complete(o)},complete:function(e,t){if("throw"===e.type)throw e.arg;return"break"===e.type||"continue"===e.type?this.next=e.arg:"return"===e.type?(this.rval=this.arg=e.arg,this.method="return",this.next="end"):"normal"===e.type&&t&&(this.next=t),d},finish:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.finallyLoc===e)return this.complete(n.completion,n.afterLoc),M(n),d}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var n=this.tryEntries[t];if(n.tryLoc===e){var r=n.completion;if("throw"===r.type){var i=r.arg;M(n)}return i}}throw new Error("illegal catch attempt")},delegateYield:function(e,n,r){return this.delegate={iterator:C(e),resultName:n,nextLoc:r},"next"===this.method&&(this.arg=t),d}}}function b(e,t,n,r){var i=t&&t.prototype instanceof _?t:_,a=Object.create(i.prototype),o=new A(r||[]);return a._invoke=function(e,t,n){var r=f;return function(i,a){if(r===l)throw new Error("Generator is already running");if(r===p){if("throw"===i)throw a;return L()}for(n.method=i,n.arg=a;;){var o=n.delegate;if(o){var s=R(o,n);if(s){if(s===d)continue;return s}}if("next"===n.method)n.sent=n._sent=n.arg;else if("throw"===n.method){if(r===f)throw r=p,n.arg;n.dispatchException(n.arg)}else"return"===n.method&&n.abrupt("return",n.arg);r=l;var u=w(e,t,n);if("normal"===u.type){if(r=n.done?p:h,u.arg===d)continue;return{value:u.arg,done:n.done}}"throw"===u.type&&(r=p,n.method="throw",n.arg=u.arg)}}}(e,n,o),a}function w(e,t,n){try{return{type:"normal",arg:e.call(t,n)}}catch(e){return{type:"throw",arg:e}}}function _(){}function k(){}function x(){}function E(e){["next","throw","return"].forEach(function(t){e[t]=function(e){return this._invoke(t,e)}})}function S(e){var t;this._invoke=function(n,i){function a(){return new Promise(function(t,a){!function t(n,i,a,o){var s=w(e[n],e,i);if("throw"!==s.type){var u=s.arg,c=u.value;return c&&"object"==typeof c&&r.call(c,"__await")?Promise.resolve(c.__await).then(function(e){t("next",e,a,o)},function(e){t("throw",e,a,o)}):Promise.resolve(c).then(function(e){u.value=e,a(u)},function(e){return t("throw",e,a,o)})}o(s.arg)}(n,i,t,a)})}return t=t?t.then(a,a):a()}}function R(e,n){var r=e.iterator[n.method];if(r===t){if(n.delegate=null,"throw"===n.method){if(e.iterator.return&&(n.method="return",n.arg=t,R(e,n),"throw"===n.method))return d;n.method="throw",n.arg=new TypeError("The iterator does not provide a 'throw' method")}return d}var i=w(r,e.iterator,n.arg);if("throw"===i.type)return n.method="throw",n.arg=i.arg,n.delegate=null,d;var a=i.arg;return a?a.done?(n[e.resultName]=a.value,n.next=e.nextLoc,"return"!==n.method&&(n.method="next",n.arg=t),n.delegate=null,d):a:(n.method="throw",n.arg=new TypeError("iterator result is not an object"),n.delegate=null,d)}function U(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function M(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function A(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(U,this),this.reset(!0)}function C(e){if(e){var n=e[a];if(n)return n.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var i=-1,o=function n(){for(;++i<e.length;)if(r.call(e,i))return n.value=e[i],n.done=!1,n;return n.value=t,n.done=!0,n};return o.next=o}}return{next:L}}function L(){return{value:t,done:!0}}}(function(){return this||"object"==typeof self&&self}()||Function("return this")());var c=new Uint8Array([88,0,0,0]),f=new Uint8Array([26,165,74,6,73,7,72,8,71,9,70,10,69,11,68,12,67,13,66,14,65]),h=new Uint8Array([24,255,1,0,0,0,0,0,0,0,0,0,0,0,0]),l=new Uint8Array([25,255,0,255,255,255,255]),p=new Uint8Array([27,255,0,0,0,0,0,0,0,0,0]),d={HEADER:c,INIT:f,DISCONNECT:new Uint8Array([84]),START_MEASUREMENTS:h,STOP_MEASUREMENTS:l,SET_MEASUREMENT_PERIOD:p,GET_INFO:new Uint8Array([85]),GET_STATUS:new Uint8Array([16]),GET_SENSOR_IDS:new Uint8Array([81]),GET_SENSOR_INFO:new Uint8Array([80,0]),GET_DEFAULT_SENSORS_MASK:new Uint8Array([86])},m=6,v=7,g=10,y=8,b=9,w=11,_=12,k=13,x=14,E=32,S=function(e){return 0!==e},R=function(){function e(){n(this,e),this._listenerMap=new Map}return i(e,[{key:"on",value:function(e,t){this._listenerMap.has(e)||this._listenerMap.set(e,[]),this._listenerMap.get(e).push(t)}},{key:"off",value:function(e,t){var n=this._listenerMap.get(e);if(n&&n.length){var r=n.reduce(function(e,n,r){return"function"==typeof n&&n===t?e=r:e},-1);if(r>-1)return n.splice(r,1),this._listenerMap.set(e,n),!0}return!1}},{key:"unbind",value:function(){this._listenerMap.clear()}},{key:"emit",value:function(e){for(var t=arguments.length,n=new Array(t>1?t-1:0),r=1;r<t;r++)n[r-1]=arguments[r];var i=this._listenerMap.get(e);return!(!i||!i.length)&&(i.forEach(function(e){e.apply(void 0,n)}),!0)}}]),e}(),U=function(){},M=function(){};function A(e){return Array.from(new Uint8Array(e)).map(function(e){return e.toString(16).padStart(2,"0")}).join(" ")}var C,L=function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};n(this,e),this.type=t.type,this.mode=t.mode,this.minValue=t.minValue,this.maxValue=t.maxValue,this.uncertainty=t.uncertainty,this.minPeriod=t.minPeriod,this.maxPeriod=t.maxPeriod,this.typicalPeriod=t.typicalPeriod,this.granularity=t.granularity},P=function e(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};n(this,e),this.number=t.number,this.name=t.name,this.unit=t.unit,this.id=t.id,this.mutalExclusionMask=t.mutalExclusionMask,this.measurementInfo=t.measurementInfo},T=function(e){function t(e){var r;return n(this,t),(r=u(this,o(t).call(this))).number=e.number,r.name=e.name,r.unit=e.unit,r.specs=e,r.enabled=!1,r.values=[],r.value=null,r}return a(t,R),i(t,[{key:"clear",value:function(){this.value=null,this.values=[]}},{key:"setValue",value:function(e,t){this.value=e,t&&this.values.push(this.value),this.emit("value-changed",this)}},{key:"setEnabled",value:function(e){this.enabled!==e&&(this.enabled=e,this.emit("state-changed",this))}}]),t}(),N=function(e){function r(e){var t;if(n(this,r),t=u(this,o(r).call(this)),"undefined"==typeof TextDecoder){var i=__webpack_require__(/*! text-encoding */ "./src/scaffolding/text-encoding/index.js");C=i.TextDecoder}else C=TextDecoder;return t.device=e,t.sensors=[],t.opened=!1,t.rollingCounter=0,t.collecting=!1,t.measurementPeriod=10,t.response=null,t.remainingResponseLength=0,t.defaultSensorsMask=0,t.keepValues=!0,t.minMeasurementPeriod=10,t.serialNumber="",t.orderCode="",t.name="",t}return a(r,R),i(r,[{key:"getBatteryLevel",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this._getStatus();case 2:return t=e.sent,e.abrupt("return",t.battery);case 4:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"getChargingState",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this._getStatus();case 2:return t=e.sent,e.abrupt("return",t.chargingStatus);case 4:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"open",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t,n=arguments;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return t=n.length>0&&void 0!==n[0]&&n[0],e.prev=1,e.next=4,this._connect();case 4:return e.next=6,this._init();case 6:return e.next=8,this._getStatus();case 8:return e.next=10,this._getDeviceInfo();case 10:return e.next=12,this._getDefaultSensorsMask();case 12:return e.next=14,this._getAvailableSensors();case 14:this._onOpened(),t&&this.start(),e.next=21;break;case 18:e.prev=18,e.t0=e.catch(1),console.error(e.t0);case 21:case"end":return e.stop()}},e,this,[[1,18]])}));return function(){return e.apply(this,arguments)}}()},{key:"close",value:function(){var e=t(regeneratorRuntime.mark(function e(){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this._stopMeasurements();case 2:return e.next=4,this._sendCommand(d.DISCONNECT);case 4:return e.abrupt("return",this._disconnect());case 5:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"enableDefaultSensors",value:function(){for(var e=1,t=0;t<32;++t){if((this.defaultSensorsMask&e)===e){var n=this.getSensor(t);n&&n.setEnabled(!0)}e<<=1}}},{key:"start",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=this.sensors.filter(function(e){return e.enabled});0===t.length&&(this.enableDefaultSensors(),t=this.sensors.filter(function(e){return e.enabled})),t.forEach(function(e){return e.clear()}),e&&(this.measurementPeriod=e),this._startMeasurements()}},{key:"stop",value:function(){this._stopMeasurements()}},{key:"getSensor",value:function(e){return this.sensors.find(function(t){return t.number===e})}},{key:"_connect",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t=this;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.device.setup({onClosed:function(){return t._onClosed()},onResponse:function(e){return t._handleResponse(e)}}));case 1:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"_disconnect",value:function(){var e=t(regeneratorRuntime.mark(function e(){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.device.close());case 1:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"_init",value:function(){return this.collecting=!1,this.rollingCounter=255,this.writeQueue=[],this._sendCommand(d.INIT)}},{key:"_handleResponse",value:function(e){if(U("command notified: ".concat(A(e.buffer))),this.remainingResponseLegnth>0){if(this.remainingResponseLegnth-=e.buffer.byteLength,this.response=new DataView((t=this.response.buffer,n=e.buffer.slice(0),(r=new Uint8Array(t.byteLength+n.byteLength)).set(new Uint8Array(t),0),r.set(new Uint8Array(n),t.byteLength),r.buffer)),this.remainingResponseLegnth>0)return}else this.response=e;var t,n,r,i=this.response.getUint8(1);if(i>this.response.buffer.byteLength)this.remainingResponseLegnth=i-this.response.buffer.byteLength;else switch(U("handle command: ".concat(A(this.response.buffer))),this.response.getUint8(0)){case E:this._processMeasurements(this.response);break;default:var a=this.response.getUint8(4),o=this.response.getUint8(5),s=new DataView(this.response.buffer,6);this._resolveWriteCommand(a,o,s),this.remainingResponseLegnth=0,this.response=null}}},{key:"_getSensorsWithMask",value:function(e){for(var t=[],n=1,r=0;r<32;++r){if((e&n)===n){var i=this.getSensor(r);i&&(t.push(i),U("available: [".concat(e,"] ").concat(t[t.length-1].number)))}n<<=1}return t}},{key:"_processMeasurements",value:function(e){var t=[],n=!0,r=0,i=0,a=e.getUint8(4);switch(a){case m:t=this._getSensorsWithMask(e.getUint16(5,!0)),r=e.getUint8(7,!0),i=9;break;case v:t=this._getSensorsWithMask(e.getUint32(5,!0)),r=e.getUint8(9,!0),i=11;break;case g:case y:t[0]=this.getSensor(e.getUint8(6)),r=e.getUint8(7,!0),i=8;break;case w:case b:t[0]=this.getSensor(e.getUint8(6)),r=e.getUint8(7,!0),i=8,n=!1;break;case _:case k:case x:U("Purposely Ignoring packet type: ".concat(a));break;default:U("Unknown packet type: ".concat(a))}for(var o=0;o<r;++o)for(var s=0;s<t.length;++s)n?t[s].setValue(e.getFloat32(i,!0),this.keepValues):t[s].setValue(e.getInt32(i,!0),this.keepValues),i+=4}},{key:"_resolveWriteCommand",value:function(e,t,n){var r=this.writeQueue.find(function(n){return n.command===e&&n.rollingCounter===t});r&&(r.resolve(n),this.writeQueue=this.writeQueue.filter(function(e){return e!==r}))}},{key:"_onOpened",value:function(){U("opened"),this.opened=!0,this.emit("device-opened")}},{key:"_onClosed",value:function(){U("closed"),this.opened=!1,this.emit("device-closed")}},{key:"_decRollingCounter",value:function(){return this.rollingCounter-=1,this.rollingCounter}},{key:"_calculateChecksum",value:function(e){for(var t=e[1],n=-1*e[3],r=0;r<t;++r)n+=e[r],n&=255;return n<0||n>255?(U("Checksum failed!"),0):n}},{key:"_sendCommand",value:function(e){var t=new Uint8Array(d.HEADER.byteLength+e.byteLength);return t.set(new Uint8Array(d.HEADER),0),t.set(new Uint8Array(e),d.HEADER.byteLength),t[1]=t.length,t[2]=this._decRollingCounter(),t[3]=this._calculateChecksum(t),this._queueWriteCommand(t,0,t.length)}},{key:"_writeCommand",value:function(){var e=t(regeneratorRuntime.mark(function e(t,n,r){var i;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!(r>0)){e.next=12;break}return e.prev=1,r>20?(i=t.subarray(n,n+20),r-=20,n+=20):(i=t.subarray(n,n+r),r=0),e.next=5,this.device.writeCommand(i);case 5:e.next=10;break;case 7:e.prev=7,e.t0=e.catch(1),U("Write Failure: ".concat(e.t0));case 10:e.next=0;break;case 12:case"end":return e.stop()}},e,this,[[1,7]])}));return function(t,n,r){return e.apply(this,arguments)}}()},{key:"_queueWriteCommand",value:function(e,t,n){var r=this;U("command queued: ".concat(A(e)));var i=new Promise(function(t,n){r.writeQueue.push({command:e[4],rollingCounter:e[2],resolve:t,reject:n}),setTimeout(function(){r.writeQueue=r.writeQueue.filter(function(t){return t.command===e[4]&&t.rollingCounter!==e[2]}),n(new Error("write command timed out after 5s. Command: ".concat(e[4].toString(16)," Rolling Counter: ").concat(e[2].toString(16))))},1e4)});return this._writeCommand(e,t,n),i}},{key:"_getStatus",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t,n;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this._sendCommand(d.GET_STATUS);case 2:return t=e.sent,n={masterFirmwareVersion:"".concat(t.getUint8(2),".").concat(t.getUint8(3)),bleFirmwareVersion:"".concat(t.getUint8(6),".").concat(t.getUint8(9)),battery:t.getUint8(10),chargingStatus:"".concat(t.getUint8(11))},e.abrupt("return",n);case 5:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"_getAvailableSensors",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t,n,r=this;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=2,this._sendCommand(d.GET_SENSOR_IDS).then(function(e){r.availableSensors=e.getUint32(0,!0),U("Get Available Sensors Returned ".concat(r.availableSensors))});case 2:t=1,n=0;case 4:if(!(n<31)){e.next=12;break}if((this.availableSensors&t)!==t){e.next=8;break}return e.next=8,this._getSensorInfo(n);case 8:t<<=1;case 9:++n,e.next=4;break;case 12:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"_getDefaultSensorsMask",value:function(){var e=this;return this._sendCommand(d.GET_DEFAULT_SENSORS_MASK).then(function(t){e.defaultSensorsMask=t.getUint32(0,!0),U("Default Sensors:"),M(e)})}},{key:"_getDeviceInfo",value:function(){var e=this;return this._sendCommand(d.GET_INFO).then(function(t){var n=new C("utf-8");e.orderCode=n.decode(new Uint8Array(t.buffer,6,16).filter(S)),e.serialNumber=n.decode(new Uint8Array(t.buffer,22,16).filter(S)),e.name=n.decode(new Uint8Array(t.buffer,38,32).filter(S)),U("Device Info:"),M(e)})}},{key:"_getSensorInfo",value:function(){var e=t(regeneratorRuntime.mark(function e(t){var n,r=this;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return(n=new Uint8Array(d.GET_SENSOR_INFO))[1]=t,e.abrupt("return",this._sendCommand(n).then(function(e){var t=e.getUint32(2,!0);if(t>0){var n=new C("utf-8"),i=new L({type:e.getUint8(6),mode:e.getUint8(7),minValue:e.getFloat64(108,!0),maxValue:e.getFloat64(116,!0),uncertainty:e.getFloat64(100,!0),minPeriod:e.getUint32(124,!0)/1e3,maxPeriod:((e.getUint32(132,!0)<<32)+e.getUint32(128,!0))/1e3,typicalPeriod:e.getUint32(136,!0)/1e3,granularity:e.getUint32(140,!0)/1e3}),a=new P({number:e.getUint8(0),name:n.decode(new Uint8Array(e.buffer,14,60).filter(S)),unit:n.decode(new Uint8Array(e.buffer,74,32).filter(S)),mutalExclusiveMask:e.getUint32(144,!0),measurementInfo:i,sensorId:t}),o=new T(a);U("Get Sensor Info Returned"),M(o),r.sensors.push(o),o.on("state-changed",function(){U("Sensor Restart: ".concat(o.number)),o.enabled&&(r.measurementPeriod=o.specs.measurementInfo.typicalPeriod,r.sensors.forEach(function(e){if(o.number!==e.number&&e.enabled){var t=1<<e.number;(t&o.specs.mutalExclusiveMask)===t?e.enabled=!1:e.specs.measurementInfo.typicalPeriod>r.measurementPeriod&&(r.measurementPeriod=e.specs.measurementInfo.typicalPeriod)}})),r._restartMeasurements()})}}));case 3:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}()},{key:"_restartMeasurements",value:function(){var e=t(regeneratorRuntime.mark(function e(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(t=this.collecting,!this.collecting){e.next=10;break}return e.prev=2,e.next=5,this._stopMeasurements();case 5:e.next=10;break;case 7:e.prev=7,e.t0=e.catch(2),console.error(e.t0);case 10:if(this.collecting||!t){e.next=19;break}return e.prev=11,e.next=14,this._startMeasurements();case 14:e.next=19;break;case 16:e.prev=16,e.t1=e.catch(11),console.error(e.t1);case 19:case"end":return e.stop()}},e,this,[[2,7],[11,16]])}));return function(){return e.apply(this,arguments)}}()},{key:"_setMeasurementPeriod",value:function(e){var t=new Uint8Array(d.SET_MEASUREMENT_PERIOD),n=1e3*this.minMeasurementPeriod;return e<n&&(e=n),U("MeasurementPeriod: ".concat(e)),t[3]=e>>0&255,t[4]=e>>8&255,t[5]=e>>16&255,t[6]=e>>24&255,this._sendCommand(t)}},{key:"_getEnabledChannelMask",value:function(){var e=0;return this.sensors.filter(function(e){return e.enabled}).forEach(function(t){e+=1<<t.number}),e}},{key:"_startMeasurements",value:function(){var e=this;return this._setMeasurementPeriod(1e3*this.measurementPeriod).then(function(){var t=e._getEnabledChannelMask();U("ChannelMask: ".concat(t));var n=new Uint8Array(d.START_MEASUREMENTS);return n[3]=t>>0&255,n[4]=t>>8&255,n[5]=t>>16&255,n[6]=t>>24&255,e._sendCommand(n).then(function(t){0===t.getUint8(0)&&(e.collecting=!0,e.emit("measurements-started"))})})}},{key:"_stopMeasurements",value:function(){var e=this;return this._sendCommand(d.STOP_MEASUREMENTS).then(function(t){0===t.getUint8(0)&&(e.collecting=!1,e.emit("measurements-stopped"))})}}]),r}(),O=function(){function e(t){n(this,e),this.webBluetoothNativeDevice=t,this.deviceCommand=null,this.deviceResponse=null}return i(e,[{key:"writeCommand",value:function(){var e=t(regeneratorRuntime.mark(function e(t){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.deviceCommand.writeValue(t));case 1:case"end":return e.stop()}},e,this)}));return function(t){return e.apply(this,arguments)}}()},{key:"setup",value:function(){var e=t(regeneratorRuntime.mark(function e(t){var n,r,i,a,o=this;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return n=t.onClosed,r=t.onResponse,this.webBluetoothNativeDevice.addEventListener("gattserverdisconnected",n),e.prev=2,e.next=5,this.webBluetoothNativeDevice.gatt.connect();case 5:return i=e.sent,e.next=8,i.getPrimaryService("d91714ef-28b9-4f91-ba16-f0d9a604f112");case 8:return a=e.sent,e.next=11,a.getCharacteristics();case 11:e.sent.forEach(function(e){switch(e.uuid){case"f4bf14a6-c7d5-4b6d-8aa8-df1a7c83adcb":o.deviceCommand=e;break;case"b41e6675-a329-40e0-aa01-44d2f444babe":o.deviceResponse=e,o.deviceResponse.addEventListener("characteristicvaluechanged",function(e){var t=e.target.value;r(t)}),o.deviceResponse.startNotifications();break;default:U("No case found for ".concat(e.uuid))}}),e.next=18;break;case 15:e.prev=15,e.t0=e.catch(2),console.error(e.t0);case 18:if(this.deviceCommand&&this.deviceResponse){e.next=20;break}throw new Error("Expected command and response characteristics not found.");case 20:case"end":return e.stop()}},e,this,[[2,15]])}));return function(t){return e.apply(this,arguments)}}()},{key:"close",value:function(){var e=t(regeneratorRuntime.mark(function e(){return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",this.webBluetoothNativeDevice.gatt.disconnect());case 1:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()},{key:"godirectAdapter",get:function(){return!0}}]),e}(),D={createDevice:function(){var e=t(regeneratorRuntime.mark(function e(t){var n,r,i,a,o,s,u,c=arguments;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(n=c.length>1&&void 0!==c[1]?c[1]:{},r=n.open,i=void 0===r||r,a=n.startMeasurements,o=void 0===a||a,(s=t).godirectAdapter||(s=new O(t)),u=new N(s),!i){e.next=14;break}return e.prev=5,e.next=8,u.open(o);case 8:e.next=14;break;case 10:throw e.prev=10,e.t0=e.catch(5),console.error(e.t0),e.t0;case 14:return e.abrupt("return",u);case 15:case"end":return e.stop()}},e,this,[[5,10]])}));return function(t){return e.apply(this,arguments)}}(),selectDevice:function(){var e=t(regeneratorRuntime.mark(function e(){var t;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(navigator.bluetooth){e.next=2;break}return e.abrupt("return",Promise.reject(new Error("No Web Bluetooth support.")));case 2:return e.next=4,navigator.bluetooth.requestDevice({filters:[{namePrefix:"GDX"}],optionalServices:["d91714ef-28b9-4f91-ba16-f0d9a604f112"]});case 4:return t=e.sent,e.abrupt("return",D.createDevice(t));case 6:case"end":return e.stop()}},e,this)}));return function(){return e.apply(this,arguments)}}()};return D});


/***/ }),

/***/ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultBitmap.png":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/arraybuffer-loader!./node_modules/scratch-storage/src/builtins/defaultBitmap.png ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(/*! ../../../arraybuffer-loader/lib/to-array-buffer.js */ "./node_modules/arraybuffer-loader/lib/to-array-buffer.js")("iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAAAAADmVT4XAAADZElEQVR42u3a70vbQBgH8O/7vOibvsmbFUEEWUGGUhQ25nDuhSKIUFDxlYi0SAd94atRmFiHCH2x4W8iiNtwowXRgd1QipJYY3N/1F4kTQ6XLKS9yyHcvWyO8MnT5y53Tw5EcIMESIAESIAESIAESIAEPFVAq/6l8G4oAQCp4aniwbUVJ6BVzafwqKXy1VZMgNb+CHzbyK4ZB+Bi0nnk0VyhsDiSoAmvvnMHmGUVAF6UG+0ffsxRhsRHky/AyCsAUpumX0wAAIs6T4CxDADDtcdZsal6gjmdH8AsAEC6+s8Fa48SFExeAKusAEju+V367AmUCi9ANQ0AU3pwcOw2cMEH0FwAAKz5X228pRLR5AI4TgIAtIDL24oLUE94AB6W7LsH/cONN14Ilh44AOqD9s2LQR1KHiB9zgGgOSGeuA3ocPrME1Q4ANaceyePAzrcjHmAZYs9IBeW4/fzHiAwTCwAgTle9ACZK44ALDT9e6x7gL4aT0BQCHb4AqgA5/1TTOMLoJ7vZSMM0H/GHnCeDnu+CrU8vGYPcN5FADBYD0vCaYPDy+i0p337GSMsS1Z4vA3t9QgAZTt0IuIxFRPSKicAQHlvhk3FXF5GhBByMa8qo/sBO6BarwuYbYrYnFKDYEvE7phKgcxvEQBqnvhgiQCUOgoAO4C3JOS3L/hv23UXxVldBECfcueAqpAa0WGy/QeULREAY6aTXRFDgBuA13+ElOncAERMAGaA9hBQD4gQQHsIRE1AZoD2xrgQS5nOZyHgFKmWDSIG4GRAVidiAE4GRB6AzAB2AKIPQFaA5myHA5AV4ESNugZhC7BLR50lIBPAebrzBGQCKAFQNSIMcDvRTQIwAHxNApM3RBygGK0uyhxwOwGsPAgEnPWj5ycRCNiJtBHlAChGKQVwANzPY+CXSMDNWISiLA9AfRA5IhJQ68UnoQAt+AtOPIBKhJosF8B6UM0yLkAxQk2WCyDX9SiUh1gkQAIkoCuAvjGuquMbuijAtwG7MPL8yBICOHSPk3W8Ne8KcEUdmBi6FADYos+QleIH0N/ogOxd7AD6uAS6WRU8WcBdlgZMG/EnYUlwEpLLoQ4PjzGbiA5UsRMRsbQ+5ziDJmYqJuR6NaMomdXuFuZyPSABEiABEiABEiABEiAc8Bd6VyvCEKGqcQAAAABJRU5ErkJggg==")

/***/ }),

/***/ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultSound.wav":
/*!******************************************************************************************************!*\
  !*** ./node_modules/arraybuffer-loader!./node_modules/scratch-storage/src/builtins/defaultSound.wav ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(/*! ../../../arraybuffer-loader/lib/to-array-buffer.js */ "./node_modules/arraybuffer-loader/lib/to-array-buffer.js")("UklGRiYAAABXQVZFZm10IBAAAAABAAEAIlYAAESsAAACABAAZGF0YQIAAAAAAA==")

/***/ }),

/***/ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultVector.svg":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/arraybuffer-loader!./node_modules/scratch-storage/src/builtins/defaultVector.svg ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(/*! ../../../arraybuffer-loader/lib/to-array-buffer.js */ "./node_modules/arraybuffer-loader/lib/to-array-buffer.js")("PD94bWwgdmVyc2lvbj0iMS4wIj8+Cjxzdmcgd2lkdGg9IjEyOCIgaGVpZ2h0PSIxMjgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiA8Zz4KICA8cmVjdCBmaWxsPSIjQ0NDIiBoZWlnaHQ9IjEyOCIgd2lkdGg9IjEyOCIvPgogIDx0ZXh0IGZpbGw9ImJsYWNrIiB5PSIxMDciIHg9IjM1LjUiIGZvbnQtc2l6ZT0iMTI4Ij4/PC90ZXh0PgogPC9nPgo8L3N2Zz4K")

/***/ }),

/***/ "./node_modules/arraybuffer-loader/lib/to-array-buffer.js":
/*!****************************************************************!*\
  !*** ./node_modules/arraybuffer-loader/lib/to-array-buffer.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(Buffer) {

module.exports = function (base64Data) {
  var isBrowser = typeof window !== 'undefined' && typeof window.atob === 'function'
  var binary = isBrowser ? window.atob(base64Data) : Buffer.from(base64Data, 'base64').toString('binary')
  var bytes = new Uint8Array(binary.length)

  for (var i = 0; i < binary.length; ++i) {
    bytes[i] = binary.charCodeAt(i)
  }

  return bytes.buffer
}

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

/***/ }),

/***/ "./node_modules/atob/browser-atob.js":
/*!*******************************************!*\
  !*** ./node_modules/atob/browser-atob.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer, module) {(function (w) {
  "use strict";

  function findBest(atobNative) {
    // normal window
    if ('function' === typeof atobNative) { return atobNative; }


    // browserify (web worker)
    if ('function' === typeof Buffer) {
      return function atobBrowserify(a) {
        //!! Deliberately using an API that's deprecated in node.js because
        //!! this file is for browsers and we expect them to cope with it.
        //!! Discussion: github.com/node-browser-compat/atob/pull/9
        return new Buffer(a, 'base64').toString('binary');
      };
    }

    // ios web worker with base64js
    if ('object' === typeof w.base64js) {
      // bufferToBinaryString
      // https://git.coolaj86.com/coolaj86/unibabel.js/blob/master/index.js#L50
      return function atobWebWorker_iOS(a) {
        var buf = w.base64js.b64ToByteArray(a);
        return Array.prototype.map.call(buf, function (ch) {
          return String.fromCharCode(ch);
        }).join('');
      };
    }

		return function () {
			// ios web worker without base64js
			throw new Error("You're probably in an old browser or an iOS webworker." +
				" It might help to include beatgammit's base64-js.");
    };
  }

  var atobBest = findBest(w.atob);
  w.atob = atobBest;

  if (( true) && module && module.exports) {
    module.exports = atobBest;
  }
}(window));

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../buffer/index.js */ "./node_modules/buffer/index.js").Buffer, __webpack_require__(/*! ./../webpack/buildin/module.js */ "./node_modules/webpack/buildin/module.js")(module)))

/***/ }),

/***/ "./node_modules/audio-context/index.js":
/*!*********************************************!*\
  !*** ./node_modules/audio-context/index.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var window = __webpack_require__(/*! global/window */ "./node_modules/global/window.js")

var OfflineContext = window.OfflineAudioContext || window.webkitOfflineAudioContext
var Context = window.AudioContext || window.webkitAudioContext

var cache = {}

module.exports = function getContext (options) {
	if (!Context) return null

	if (typeof options === 'number') {
		options = {sampleRate: options}
	}

	var sampleRate = options && options.sampleRate


	if (options && options.offline) {
		if (!OfflineContext) return null

		return new OfflineContext(options.channels || 2, options.length, sampleRate || 44100)
	}


	//cache by sampleRate, rather strong guess
	var ctx = cache[sampleRate]

	if (ctx) return ctx

	//several versions of firefox have issues with the
	//constructor argument
	//see: https://bugzilla.mozilla.org/show_bug.cgi?id=1361475
	try {
		ctx = new Context(options)
	}
	catch (err) {
		ctx = new Context()
	}
	cache[ctx.sampleRate] = cache[sampleRate] = ctx

	return ctx
}


/***/ }),

/***/ "./node_modules/base64-js/lib/b64.js":
/*!*******************************************!*\
  !*** ./node_modules/base64-js/lib/b64.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';

;(function (exports) {
	'use strict';

  var Arr = (typeof Uint8Array !== 'undefined')
    ? Uint8Array
    : Array

	var PLUS   = '+'.charCodeAt(0)
	var SLASH  = '/'.charCodeAt(0)
	var NUMBER = '0'.charCodeAt(0)
	var LOWER  = 'a'.charCodeAt(0)
	var UPPER  = 'A'.charCodeAt(0)
	var PLUS_URL_SAFE = '-'.charCodeAt(0)
	var SLASH_URL_SAFE = '_'.charCodeAt(0)

	function decode (elt) {
		var code = elt.charCodeAt(0)
		if (code === PLUS ||
		    code === PLUS_URL_SAFE)
			return 62 // '+'
		if (code === SLASH ||
		    code === SLASH_URL_SAFE)
			return 63 // '/'
		if (code < NUMBER)
			return -1 //no match
		if (code < NUMBER + 10)
			return code - NUMBER + 26 + 26
		if (code < UPPER + 26)
			return code - UPPER
		if (code < LOWER + 26)
			return code - LOWER + 26
	}

	function b64ToByteArray (b64) {
		var i, j, l, tmp, placeHolders, arr

		if (b64.length % 4 > 0) {
			throw new Error('Invalid string. Length must be a multiple of 4')
		}

		// the number of equal signs (place holders)
		// if there are two placeholders, than the two characters before it
		// represent one byte
		// if there is only one, then the three characters before it represent 2 bytes
		// this is just a cheap hack to not do indexOf twice
		var len = b64.length
		placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0

		// base64 is 4/3 + up to two characters of the original data
		arr = new Arr(b64.length * 3 / 4 - placeHolders)

		// if there are placeholders, only get up to the last complete 4 chars
		l = placeHolders > 0 ? b64.length - 4 : b64.length

		var L = 0

		function push (v) {
			arr[L++] = v
		}

		for (i = 0, j = 0; i < l; i += 4, j += 3) {
			tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
			push((tmp & 0xFF0000) >> 16)
			push((tmp & 0xFF00) >> 8)
			push(tmp & 0xFF)
		}

		if (placeHolders === 2) {
			tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
			push(tmp & 0xFF)
		} else if (placeHolders === 1) {
			tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
			push((tmp >> 8) & 0xFF)
			push(tmp & 0xFF)
		}

		return arr
	}

	function uint8ToBase64 (uint8) {
		var i,
			extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
			output = "",
			temp, length

		function encode (num) {
			return lookup.charAt(num)
		}

		function tripletToBase64 (num) {
			return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
		}

		// go through the array every three bytes, we'll deal with trailing stuff later
		for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
			temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
			output += tripletToBase64(temp)
		}

		// pad the end with zeros, but make sure to not forget the extra bytes
		switch (extraBytes) {
			case 1:
				temp = uint8[uint8.length - 1]
				output += encode(temp >> 2)
				output += encode((temp << 4) & 0x3F)
				output += '=='
				break
			case 2:
				temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
				output += encode(temp >> 10)
				output += encode((temp >> 4) & 0x3F)
				output += encode((temp << 2) & 0x3F)
				output += '='
				break
		}

		return output
	}

	exports.toByteArray = b64ToByteArray
	exports.fromByteArray = uint8ToBase64
}( false ? (undefined) : exports))


/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Grand9K-Pixel.ttf":
/*!**********************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/Grand9K-Pixel.ttf ***!
  \**********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Griffy-Regular.ttf":
/*!***********************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/Griffy-Regular.ttf ***!
  \***********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Knewave.ttf":
/*!****************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/Knewave.ttf ***!
  \****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/NotoSans-Medium.ttf":
/*!************************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/NotoSans-Medium.ttf ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Scratch.ttf":
/*!****************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/Scratch.ttf ***!
  \****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/SourceSerifPro-Regular.otf":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/SourceSerifPro-Regular.otf ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/handlee-regular.ttf":
/*!************************************************************************************************!*\
  !*** ./node_modules/base64-loader!./node_modules/scratch-render-fonts/src/handlee-regular.ttf ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = " "

/***/ }),

/***/ "./node_modules/btoa/index.js":
/*!************************************!*\
  !*** ./node_modules/btoa/index.js ***!
  \************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer) {(function () {
  "use strict";

  function btoa(str) {
    var buffer;

    if (str instanceof Buffer) {
      buffer = str;
    } else {
      buffer = Buffer.from(str.toString(), 'binary');
    }

    return buffer.toString('base64');
  }

  module.exports = btoa;
}());

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

/***/ }),

/***/ "./node_modules/buffer/index.js":
/*!**************************************!*\
  !*** ./node_modules/buffer/index.js ***!
  \**************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {/*!
 * The buffer module from node.js, for the browser.
 *
 * @author   Feross Aboukhadijeh <http://feross.org>
 * @license  MIT
 */
/* eslint-disable no-proto */



var base64 = __webpack_require__(/*! base64-js */ "./node_modules/buffer/node_modules/base64-js/index.js")
var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js")
var isArray = __webpack_require__(/*! isarray */ "./node_modules/isarray/index.js")

exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
exports.INSPECT_MAX_BYTES = 50

/**
 * If `Buffer.TYPED_ARRAY_SUPPORT`:
 *   === true    Use Uint8Array implementation (fastest)
 *   === false   Use Object implementation (most compatible, even IE6)
 *
 * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
 * Opera 11.6+, iOS 4.2+.
 *
 * Due to various browser bugs, sometimes the Object implementation will be used even
 * when the browser supports typed arrays.
 *
 * Note:
 *
 *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
 *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
 *
 *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
 *
 *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
 *     incorrect length in some situations.

 * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
 * get the Object implementation, which is slower but behaves correctly.
 */
Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
  ? global.TYPED_ARRAY_SUPPORT
  : typedArraySupport()

/*
 * Export kMaxLength after typed array support is determined.
 */
exports.kMaxLength = kMaxLength()

function typedArraySupport () {
  try {
    var arr = new Uint8Array(1)
    arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
    return arr.foo() === 42 && // typed array instances can be augmented
        typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
        arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
  } catch (e) {
    return false
  }
}

function kMaxLength () {
  return Buffer.TYPED_ARRAY_SUPPORT
    ? 0x7fffffff
    : 0x3fffffff
}

function createBuffer (that, length) {
  if (kMaxLength() < length) {
    throw new RangeError('Invalid typed array length')
  }
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    // Return an augmented `Uint8Array` instance, for best performance
    that = new Uint8Array(length)
    that.__proto__ = Buffer.prototype
  } else {
    // Fallback: Return an object instance of the Buffer class
    if (that === null) {
      that = new Buffer(length)
    }
    that.length = length
  }

  return that
}

/**
 * The Buffer constructor returns instances of `Uint8Array` that have their
 * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
 * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
 * and the `Uint8Array` methods. Square bracket notation works as expected -- it
 * returns a single octet.
 *
 * The `Uint8Array` prototype remains unmodified.
 */

function Buffer (arg, encodingOrOffset, length) {
  if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
    return new Buffer(arg, encodingOrOffset, length)
  }

  // Common case.
  if (typeof arg === 'number') {
    if (typeof encodingOrOffset === 'string') {
      throw new Error(
        'If encoding is specified then the first argument must be a string'
      )
    }
    return allocUnsafe(this, arg)
  }
  return from(this, arg, encodingOrOffset, length)
}

Buffer.poolSize = 8192 // not used by this implementation

// TODO: Legacy, not needed anymore. Remove in next major version.
Buffer._augment = function (arr) {
  arr.__proto__ = Buffer.prototype
  return arr
}

function from (that, value, encodingOrOffset, length) {
  if (typeof value === 'number') {
    throw new TypeError('"value" argument must not be a number')
  }

  if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
    return fromArrayBuffer(that, value, encodingOrOffset, length)
  }

  if (typeof value === 'string') {
    return fromString(that, value, encodingOrOffset)
  }

  return fromObject(that, value)
}

/**
 * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
 * if value is a number.
 * Buffer.from(str[, encoding])
 * Buffer.from(array)
 * Buffer.from(buffer)
 * Buffer.from(arrayBuffer[, byteOffset[, length]])
 **/
Buffer.from = function (value, encodingOrOffset, length) {
  return from(null, value, encodingOrOffset, length)
}

if (Buffer.TYPED_ARRAY_SUPPORT) {
  Buffer.prototype.__proto__ = Uint8Array.prototype
  Buffer.__proto__ = Uint8Array
  if (typeof Symbol !== 'undefined' && Symbol.species &&
      Buffer[Symbol.species] === Buffer) {
    // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
    Object.defineProperty(Buffer, Symbol.species, {
      value: null,
      configurable: true
    })
  }
}

function assertSize (size) {
  if (typeof size !== 'number') {
    throw new TypeError('"size" argument must be a number')
  } else if (size < 0) {
    throw new RangeError('"size" argument must not be negative')
  }
}

function alloc (that, size, fill, encoding) {
  assertSize(size)
  if (size <= 0) {
    return createBuffer(that, size)
  }
  if (fill !== undefined) {
    // Only pay attention to encoding if it's a string. This
    // prevents accidentally sending in a number that would
    // be interpretted as a start offset.
    return typeof encoding === 'string'
      ? createBuffer(that, size).fill(fill, encoding)
      : createBuffer(that, size).fill(fill)
  }
  return createBuffer(that, size)
}

/**
 * Creates a new filled Buffer instance.
 * alloc(size[, fill[, encoding]])
 **/
Buffer.alloc = function (size, fill, encoding) {
  return alloc(null, size, fill, encoding)
}

function allocUnsafe (that, size) {
  assertSize(size)
  that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
  if (!Buffer.TYPED_ARRAY_SUPPORT) {
    for (var i = 0; i < size; ++i) {
      that[i] = 0
    }
  }
  return that
}

/**
 * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
 * */
Buffer.allocUnsafe = function (size) {
  return allocUnsafe(null, size)
}
/**
 * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
 */
Buffer.allocUnsafeSlow = function (size) {
  return allocUnsafe(null, size)
}

function fromString (that, string, encoding) {
  if (typeof encoding !== 'string' || encoding === '') {
    encoding = 'utf8'
  }

  if (!Buffer.isEncoding(encoding)) {
    throw new TypeError('"encoding" must be a valid string encoding')
  }

  var length = byteLength(string, encoding) | 0
  that = createBuffer(that, length)

  var actual = that.write(string, encoding)

  if (actual !== length) {
    // Writing a hex string, for example, that contains invalid characters will
    // cause everything after the first invalid character to be ignored. (e.g.
    // 'abxxcd' will be treated as 'ab')
    that = that.slice(0, actual)
  }

  return that
}

function fromArrayLike (that, array) {
  var length = array.length < 0 ? 0 : checked(array.length) | 0
  that = createBuffer(that, length)
  for (var i = 0; i < length; i += 1) {
    that[i] = array[i] & 255
  }
  return that
}

function fromArrayBuffer (that, array, byteOffset, length) {
  array.byteLength // this throws if `array` is not a valid ArrayBuffer

  if (byteOffset < 0 || array.byteLength < byteOffset) {
    throw new RangeError('\'offset\' is out of bounds')
  }

  if (array.byteLength < byteOffset + (length || 0)) {
    throw new RangeError('\'length\' is out of bounds')
  }

  if (byteOffset === undefined && length === undefined) {
    array = new Uint8Array(array)
  } else if (length === undefined) {
    array = new Uint8Array(array, byteOffset)
  } else {
    array = new Uint8Array(array, byteOffset, length)
  }

  if (Buffer.TYPED_ARRAY_SUPPORT) {
    // Return an augmented `Uint8Array` instance, for best performance
    that = array
    that.__proto__ = Buffer.prototype
  } else {
    // Fallback: Return an object instance of the Buffer class
    that = fromArrayLike(that, array)
  }
  return that
}

function fromObject (that, obj) {
  if (Buffer.isBuffer(obj)) {
    var len = checked(obj.length) | 0
    that = createBuffer(that, len)

    if (that.length === 0) {
      return that
    }

    obj.copy(that, 0, 0, len)
    return that
  }

  if (obj) {
    if ((typeof ArrayBuffer !== 'undefined' &&
        obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
      if (typeof obj.length !== 'number' || isnan(obj.length)) {
        return createBuffer(that, 0)
      }
      return fromArrayLike(that, obj)
    }

    if (obj.type === 'Buffer' && isArray(obj.data)) {
      return fromArrayLike(that, obj.data)
    }
  }

  throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
}

function checked (length) {
  // Note: cannot use `length < kMaxLength()` here because that fails when
  // length is NaN (which is otherwise coerced to zero.)
  if (length >= kMaxLength()) {
    throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                         'size: 0x' + kMaxLength().toString(16) + ' bytes')
  }
  return length | 0
}

function SlowBuffer (length) {
  if (+length != length) { // eslint-disable-line eqeqeq
    length = 0
  }
  return Buffer.alloc(+length)
}

Buffer.isBuffer = function isBuffer (b) {
  return !!(b != null && b._isBuffer)
}

Buffer.compare = function compare (a, b) {
  if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
    throw new TypeError('Arguments must be Buffers')
  }

  if (a === b) return 0

  var x = a.length
  var y = b.length

  for (var i = 0, len = Math.min(x, y); i < len; ++i) {
    if (a[i] !== b[i]) {
      x = a[i]
      y = b[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

Buffer.isEncoding = function isEncoding (encoding) {
  switch (String(encoding).toLowerCase()) {
    case 'hex':
    case 'utf8':
    case 'utf-8':
    case 'ascii':
    case 'latin1':
    case 'binary':
    case 'base64':
    case 'ucs2':
    case 'ucs-2':
    case 'utf16le':
    case 'utf-16le':
      return true
    default:
      return false
  }
}

Buffer.concat = function concat (list, length) {
  if (!isArray(list)) {
    throw new TypeError('"list" argument must be an Array of Buffers')
  }

  if (list.length === 0) {
    return Buffer.alloc(0)
  }

  var i
  if (length === undefined) {
    length = 0
    for (i = 0; i < list.length; ++i) {
      length += list[i].length
    }
  }

  var buffer = Buffer.allocUnsafe(length)
  var pos = 0
  for (i = 0; i < list.length; ++i) {
    var buf = list[i]
    if (!Buffer.isBuffer(buf)) {
      throw new TypeError('"list" argument must be an Array of Buffers')
    }
    buf.copy(buffer, pos)
    pos += buf.length
  }
  return buffer
}

function byteLength (string, encoding) {
  if (Buffer.isBuffer(string)) {
    return string.length
  }
  if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
      (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
    return string.byteLength
  }
  if (typeof string !== 'string') {
    string = '' + string
  }

  var len = string.length
  if (len === 0) return 0

  // Use a for loop to avoid recursion
  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'ascii':
      case 'latin1':
      case 'binary':
        return len
      case 'utf8':
      case 'utf-8':
      case undefined:
        return utf8ToBytes(string).length
      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return len * 2
      case 'hex':
        return len >>> 1
      case 'base64':
        return base64ToBytes(string).length
      default:
        if (loweredCase) return utf8ToBytes(string).length // assume utf8
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}
Buffer.byteLength = byteLength

function slowToString (encoding, start, end) {
  var loweredCase = false

  // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
  // property of a typed array.

  // This behaves neither like String nor Uint8Array in that we set start/end
  // to their upper/lower bounds if the value passed is out of range.
  // undefined is handled specially as per ECMA-262 6th Edition,
  // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
  if (start === undefined || start < 0) {
    start = 0
  }
  // Return early if start > this.length. Done here to prevent potential uint32
  // coercion fail below.
  if (start > this.length) {
    return ''
  }

  if (end === undefined || end > this.length) {
    end = this.length
  }

  if (end <= 0) {
    return ''
  }

  // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
  end >>>= 0
  start >>>= 0

  if (end <= start) {
    return ''
  }

  if (!encoding) encoding = 'utf8'

  while (true) {
    switch (encoding) {
      case 'hex':
        return hexSlice(this, start, end)

      case 'utf8':
      case 'utf-8':
        return utf8Slice(this, start, end)

      case 'ascii':
        return asciiSlice(this, start, end)

      case 'latin1':
      case 'binary':
        return latin1Slice(this, start, end)

      case 'base64':
        return base64Slice(this, start, end)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return utf16leSlice(this, start, end)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = (encoding + '').toLowerCase()
        loweredCase = true
    }
  }
}

// The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
// Buffer instances.
Buffer.prototype._isBuffer = true

function swap (b, n, m) {
  var i = b[n]
  b[n] = b[m]
  b[m] = i
}

Buffer.prototype.swap16 = function swap16 () {
  var len = this.length
  if (len % 2 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 16-bits')
  }
  for (var i = 0; i < len; i += 2) {
    swap(this, i, i + 1)
  }
  return this
}

Buffer.prototype.swap32 = function swap32 () {
  var len = this.length
  if (len % 4 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 32-bits')
  }
  for (var i = 0; i < len; i += 4) {
    swap(this, i, i + 3)
    swap(this, i + 1, i + 2)
  }
  return this
}

Buffer.prototype.swap64 = function swap64 () {
  var len = this.length
  if (len % 8 !== 0) {
    throw new RangeError('Buffer size must be a multiple of 64-bits')
  }
  for (var i = 0; i < len; i += 8) {
    swap(this, i, i + 7)
    swap(this, i + 1, i + 6)
    swap(this, i + 2, i + 5)
    swap(this, i + 3, i + 4)
  }
  return this
}

Buffer.prototype.toString = function toString () {
  var length = this.length | 0
  if (length === 0) return ''
  if (arguments.length === 0) return utf8Slice(this, 0, length)
  return slowToString.apply(this, arguments)
}

Buffer.prototype.equals = function equals (b) {
  if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
  if (this === b) return true
  return Buffer.compare(this, b) === 0
}

Buffer.prototype.inspect = function inspect () {
  var str = ''
  var max = exports.INSPECT_MAX_BYTES
  if (this.length > 0) {
    str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
    if (this.length > max) str += ' ... '
  }
  return '<Buffer ' + str + '>'
}

Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
  if (!Buffer.isBuffer(target)) {
    throw new TypeError('Argument must be a Buffer')
  }

  if (start === undefined) {
    start = 0
  }
  if (end === undefined) {
    end = target ? target.length : 0
  }
  if (thisStart === undefined) {
    thisStart = 0
  }
  if (thisEnd === undefined) {
    thisEnd = this.length
  }

  if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
    throw new RangeError('out of range index')
  }

  if (thisStart >= thisEnd && start >= end) {
    return 0
  }
  if (thisStart >= thisEnd) {
    return -1
  }
  if (start >= end) {
    return 1
  }

  start >>>= 0
  end >>>= 0
  thisStart >>>= 0
  thisEnd >>>= 0

  if (this === target) return 0

  var x = thisEnd - thisStart
  var y = end - start
  var len = Math.min(x, y)

  var thisCopy = this.slice(thisStart, thisEnd)
  var targetCopy = target.slice(start, end)

  for (var i = 0; i < len; ++i) {
    if (thisCopy[i] !== targetCopy[i]) {
      x = thisCopy[i]
      y = targetCopy[i]
      break
    }
  }

  if (x < y) return -1
  if (y < x) return 1
  return 0
}

// Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
// OR the last index of `val` in `buffer` at offset <= `byteOffset`.
//
// Arguments:
// - buffer - a Buffer to search
// - val - a string, Buffer, or number
// - byteOffset - an index into `buffer`; will be clamped to an int32
// - encoding - an optional encoding, relevant is val is a string
// - dir - true for indexOf, false for lastIndexOf
function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
  // Empty buffer means no match
  if (buffer.length === 0) return -1

  // Normalize byteOffset
  if (typeof byteOffset === 'string') {
    encoding = byteOffset
    byteOffset = 0
  } else if (byteOffset > 0x7fffffff) {
    byteOffset = 0x7fffffff
  } else if (byteOffset < -0x80000000) {
    byteOffset = -0x80000000
  }
  byteOffset = +byteOffset  // Coerce to Number.
  if (isNaN(byteOffset)) {
    // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
    byteOffset = dir ? 0 : (buffer.length - 1)
  }

  // Normalize byteOffset: negative offsets start from the end of the buffer
  if (byteOffset < 0) byteOffset = buffer.length + byteOffset
  if (byteOffset >= buffer.length) {
    if (dir) return -1
    else byteOffset = buffer.length - 1
  } else if (byteOffset < 0) {
    if (dir) byteOffset = 0
    else return -1
  }

  // Normalize val
  if (typeof val === 'string') {
    val = Buffer.from(val, encoding)
  }

  // Finally, search either indexOf (if dir is true) or lastIndexOf
  if (Buffer.isBuffer(val)) {
    // Special case: looking for empty string/buffer always fails
    if (val.length === 0) {
      return -1
    }
    return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
  } else if (typeof val === 'number') {
    val = val & 0xFF // Search for a byte value [0-255]
    if (Buffer.TYPED_ARRAY_SUPPORT &&
        typeof Uint8Array.prototype.indexOf === 'function') {
      if (dir) {
        return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
      } else {
        return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
      }
    }
    return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
  }

  throw new TypeError('val must be string, number or Buffer')
}

function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
  var indexSize = 1
  var arrLength = arr.length
  var valLength = val.length

  if (encoding !== undefined) {
    encoding = String(encoding).toLowerCase()
    if (encoding === 'ucs2' || encoding === 'ucs-2' ||
        encoding === 'utf16le' || encoding === 'utf-16le') {
      if (arr.length < 2 || val.length < 2) {
        return -1
      }
      indexSize = 2
      arrLength /= 2
      valLength /= 2
      byteOffset /= 2
    }
  }

  function read (buf, i) {
    if (indexSize === 1) {
      return buf[i]
    } else {
      return buf.readUInt16BE(i * indexSize)
    }
  }

  var i
  if (dir) {
    var foundIndex = -1
    for (i = byteOffset; i < arrLength; i++) {
      if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
        if (foundIndex === -1) foundIndex = i
        if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
      } else {
        if (foundIndex !== -1) i -= i - foundIndex
        foundIndex = -1
      }
    }
  } else {
    if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
    for (i = byteOffset; i >= 0; i--) {
      var found = true
      for (var j = 0; j < valLength; j++) {
        if (read(arr, i + j) !== read(val, j)) {
          found = false
          break
        }
      }
      if (found) return i
    }
  }

  return -1
}

Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
  return this.indexOf(val, byteOffset, encoding) !== -1
}

Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
}

Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
  return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
}

function hexWrite (buf, string, offset, length) {
  offset = Number(offset) || 0
  var remaining = buf.length - offset
  if (!length) {
    length = remaining
  } else {
    length = Number(length)
    if (length > remaining) {
      length = remaining
    }
  }

  // must be an even number of digits
  var strLen = string.length
  if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')

  if (length > strLen / 2) {
    length = strLen / 2
  }
  for (var i = 0; i < length; ++i) {
    var parsed = parseInt(string.substr(i * 2, 2), 16)
    if (isNaN(parsed)) return i
    buf[offset + i] = parsed
  }
  return i
}

function utf8Write (buf, string, offset, length) {
  return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
}

function asciiWrite (buf, string, offset, length) {
  return blitBuffer(asciiToBytes(string), buf, offset, length)
}

function latin1Write (buf, string, offset, length) {
  return asciiWrite(buf, string, offset, length)
}

function base64Write (buf, string, offset, length) {
  return blitBuffer(base64ToBytes(string), buf, offset, length)
}

function ucs2Write (buf, string, offset, length) {
  return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
}

Buffer.prototype.write = function write (string, offset, length, encoding) {
  // Buffer#write(string)
  if (offset === undefined) {
    encoding = 'utf8'
    length = this.length
    offset = 0
  // Buffer#write(string, encoding)
  } else if (length === undefined && typeof offset === 'string') {
    encoding = offset
    length = this.length
    offset = 0
  // Buffer#write(string, offset[, length][, encoding])
  } else if (isFinite(offset)) {
    offset = offset | 0
    if (isFinite(length)) {
      length = length | 0
      if (encoding === undefined) encoding = 'utf8'
    } else {
      encoding = length
      length = undefined
    }
  // legacy write(string, encoding, offset, length) - remove in v0.13
  } else {
    throw new Error(
      'Buffer.write(string, encoding, offset[, length]) is no longer supported'
    )
  }

  var remaining = this.length - offset
  if (length === undefined || length > remaining) length = remaining

  if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
    throw new RangeError('Attempt to write outside buffer bounds')
  }

  if (!encoding) encoding = 'utf8'

  var loweredCase = false
  for (;;) {
    switch (encoding) {
      case 'hex':
        return hexWrite(this, string, offset, length)

      case 'utf8':
      case 'utf-8':
        return utf8Write(this, string, offset, length)

      case 'ascii':
        return asciiWrite(this, string, offset, length)

      case 'latin1':
      case 'binary':
        return latin1Write(this, string, offset, length)

      case 'base64':
        // Warning: maxLength not taken into account in base64Write
        return base64Write(this, string, offset, length)

      case 'ucs2':
      case 'ucs-2':
      case 'utf16le':
      case 'utf-16le':
        return ucs2Write(this, string, offset, length)

      default:
        if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
        encoding = ('' + encoding).toLowerCase()
        loweredCase = true
    }
  }
}

Buffer.prototype.toJSON = function toJSON () {
  return {
    type: 'Buffer',
    data: Array.prototype.slice.call(this._arr || this, 0)
  }
}

function base64Slice (buf, start, end) {
  if (start === 0 && end === buf.length) {
    return base64.fromByteArray(buf)
  } else {
    return base64.fromByteArray(buf.slice(start, end))
  }
}

function utf8Slice (buf, start, end) {
  end = Math.min(buf.length, end)
  var res = []

  var i = start
  while (i < end) {
    var firstByte = buf[i]
    var codePoint = null
    var bytesPerSequence = (firstByte > 0xEF) ? 4
      : (firstByte > 0xDF) ? 3
      : (firstByte > 0xBF) ? 2
      : 1

    if (i + bytesPerSequence <= end) {
      var secondByte, thirdByte, fourthByte, tempCodePoint

      switch (bytesPerSequence) {
        case 1:
          if (firstByte < 0x80) {
            codePoint = firstByte
          }
          break
        case 2:
          secondByte = buf[i + 1]
          if ((secondByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
            if (tempCodePoint > 0x7F) {
              codePoint = tempCodePoint
            }
          }
          break
        case 3:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
            if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
              codePoint = tempCodePoint
            }
          }
          break
        case 4:
          secondByte = buf[i + 1]
          thirdByte = buf[i + 2]
          fourthByte = buf[i + 3]
          if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
            tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
            if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
              codePoint = tempCodePoint
            }
          }
      }
    }

    if (codePoint === null) {
      // we did not generate a valid codePoint so insert a
      // replacement char (U+FFFD) and advance only 1 byte
      codePoint = 0xFFFD
      bytesPerSequence = 1
    } else if (codePoint > 0xFFFF) {
      // encode to utf16 (surrogate pair dance)
      codePoint -= 0x10000
      res.push(codePoint >>> 10 & 0x3FF | 0xD800)
      codePoint = 0xDC00 | codePoint & 0x3FF
    }

    res.push(codePoint)
    i += bytesPerSequence
  }

  return decodeCodePointsArray(res)
}

// Based on http://stackoverflow.com/a/22747272/680742, the browser with
// the lowest limit is Chrome, with 0x10000 args.
// We go 1 magnitude less, for safety
var MAX_ARGUMENTS_LENGTH = 0x1000

function decodeCodePointsArray (codePoints) {
  var len = codePoints.length
  if (len <= MAX_ARGUMENTS_LENGTH) {
    return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
  }

  // Decode in chunks to avoid "call stack size exceeded".
  var res = ''
  var i = 0
  while (i < len) {
    res += String.fromCharCode.apply(
      String,
      codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
    )
  }
  return res
}

function asciiSlice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i] & 0x7F)
  }
  return ret
}

function latin1Slice (buf, start, end) {
  var ret = ''
  end = Math.min(buf.length, end)

  for (var i = start; i < end; ++i) {
    ret += String.fromCharCode(buf[i])
  }
  return ret
}

function hexSlice (buf, start, end) {
  var len = buf.length

  if (!start || start < 0) start = 0
  if (!end || end < 0 || end > len) end = len

  var out = ''
  for (var i = start; i < end; ++i) {
    out += toHex(buf[i])
  }
  return out
}

function utf16leSlice (buf, start, end) {
  var bytes = buf.slice(start, end)
  var res = ''
  for (var i = 0; i < bytes.length; i += 2) {
    res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
  }
  return res
}

Buffer.prototype.slice = function slice (start, end) {
  var len = this.length
  start = ~~start
  end = end === undefined ? len : ~~end

  if (start < 0) {
    start += len
    if (start < 0) start = 0
  } else if (start > len) {
    start = len
  }

  if (end < 0) {
    end += len
    if (end < 0) end = 0
  } else if (end > len) {
    end = len
  }

  if (end < start) end = start

  var newBuf
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    newBuf = this.subarray(start, end)
    newBuf.__proto__ = Buffer.prototype
  } else {
    var sliceLen = end - start
    newBuf = new Buffer(sliceLen, undefined)
    for (var i = 0; i < sliceLen; ++i) {
      newBuf[i] = this[i + start]
    }
  }

  return newBuf
}

/*
 * Need to make sure that buffer isn't trying to write out of bounds.
 */
function checkOffset (offset, ext, length) {
  if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
  if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
}

Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }

  return val
}

Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    checkOffset(offset, byteLength, this.length)
  }

  var val = this[offset + --byteLength]
  var mul = 1
  while (byteLength > 0 && (mul *= 0x100)) {
    val += this[offset + --byteLength] * mul
  }

  return val
}

Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 1, this.length)
  return this[offset]
}

Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  return this[offset] | (this[offset + 1] << 8)
}

Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  return (this[offset] << 8) | this[offset + 1]
}

Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return ((this[offset]) |
      (this[offset + 1] << 8) |
      (this[offset + 2] << 16)) +
      (this[offset + 3] * 0x1000000)
}

Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] * 0x1000000) +
    ((this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    this[offset + 3])
}

Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var val = this[offset]
  var mul = 1
  var i = 0
  while (++i < byteLength && (mul *= 0x100)) {
    val += this[offset + i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) checkOffset(offset, byteLength, this.length)

  var i = byteLength
  var mul = 1
  var val = this[offset + --i]
  while (i > 0 && (mul *= 0x100)) {
    val += this[offset + --i] * mul
  }
  mul *= 0x80

  if (val >= mul) val -= Math.pow(2, 8 * byteLength)

  return val
}

Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 1, this.length)
  if (!(this[offset] & 0x80)) return (this[offset])
  return ((0xff - this[offset] + 1) * -1)
}

Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset] | (this[offset + 1] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 2, this.length)
  var val = this[offset + 1] | (this[offset] << 8)
  return (val & 0x8000) ? val | 0xFFFF0000 : val
}

Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset]) |
    (this[offset + 1] << 8) |
    (this[offset + 2] << 16) |
    (this[offset + 3] << 24)
}

Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)

  return (this[offset] << 24) |
    (this[offset + 1] << 16) |
    (this[offset + 2] << 8) |
    (this[offset + 3])
}

Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, true, 23, 4)
}

Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 4, this.length)
  return ieee754.read(this, offset, false, 23, 4)
}

Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, true, 52, 8)
}

Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
  if (!noAssert) checkOffset(offset, 8, this.length)
  return ieee754.read(this, offset, false, 52, 8)
}

function checkInt (buf, value, offset, ext, max, min) {
  if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
  if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
}

Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var mul = 1
  var i = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  byteLength = byteLength | 0
  if (!noAssert) {
    var maxBytes = Math.pow(2, 8 * byteLength) - 1
    checkInt(this, value, offset, byteLength, maxBytes, 0)
  }

  var i = byteLength - 1
  var mul = 1
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    this[offset + i] = (value / mul) & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  this[offset] = (value & 0xff)
  return offset + 1
}

function objectWriteUInt16 (buf, value, offset, littleEndian) {
  if (value < 0) value = 0xffff + value + 1
  for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
    buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
      (littleEndian ? i : 1 - i) * 8
  }
}

Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
  } else {
    objectWriteUInt16(this, value, offset, true)
  }
  return offset + 2
}

Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 8)
    this[offset + 1] = (value & 0xff)
  } else {
    objectWriteUInt16(this, value, offset, false)
  }
  return offset + 2
}

function objectWriteUInt32 (buf, value, offset, littleEndian) {
  if (value < 0) value = 0xffffffff + value + 1
  for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
    buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
  }
}

Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset + 3] = (value >>> 24)
    this[offset + 2] = (value >>> 16)
    this[offset + 1] = (value >>> 8)
    this[offset] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, true)
  }
  return offset + 4
}

Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 24)
    this[offset + 1] = (value >>> 16)
    this[offset + 2] = (value >>> 8)
    this[offset + 3] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, false)
  }
  return offset + 4
}

Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) {
    var limit = Math.pow(2, 8 * byteLength - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = 0
  var mul = 1
  var sub = 0
  this[offset] = value & 0xFF
  while (++i < byteLength && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) {
    var limit = Math.pow(2, 8 * byteLength - 1)

    checkInt(this, value, offset, byteLength, limit - 1, -limit)
  }

  var i = byteLength - 1
  var mul = 1
  var sub = 0
  this[offset + i] = value & 0xFF
  while (--i >= 0 && (mul *= 0x100)) {
    if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
      sub = 1
    }
    this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
  }

  return offset + byteLength
}

Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
  if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
  if (value < 0) value = 0xff + value + 1
  this[offset] = (value & 0xff)
  return offset + 1
}

Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
  } else {
    objectWriteUInt16(this, value, offset, true)
  }
  return offset + 2
}

Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 8)
    this[offset + 1] = (value & 0xff)
  } else {
    objectWriteUInt16(this, value, offset, false)
  }
  return offset + 2
}

Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value & 0xff)
    this[offset + 1] = (value >>> 8)
    this[offset + 2] = (value >>> 16)
    this[offset + 3] = (value >>> 24)
  } else {
    objectWriteUInt32(this, value, offset, true)
  }
  return offset + 4
}

Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
  value = +value
  offset = offset | 0
  if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
  if (value < 0) value = 0xffffffff + value + 1
  if (Buffer.TYPED_ARRAY_SUPPORT) {
    this[offset] = (value >>> 24)
    this[offset + 1] = (value >>> 16)
    this[offset + 2] = (value >>> 8)
    this[offset + 3] = (value & 0xff)
  } else {
    objectWriteUInt32(this, value, offset, false)
  }
  return offset + 4
}

function checkIEEE754 (buf, value, offset, ext, max, min) {
  if (offset + ext > buf.length) throw new RangeError('Index out of range')
  if (offset < 0) throw new RangeError('Index out of range')
}

function writeFloat (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
  }
  ieee754.write(buf, value, offset, littleEndian, 23, 4)
  return offset + 4
}

Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
  return writeFloat(this, value, offset, true, noAssert)
}

Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
  return writeFloat(this, value, offset, false, noAssert)
}

function writeDouble (buf, value, offset, littleEndian, noAssert) {
  if (!noAssert) {
    checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
  }
  ieee754.write(buf, value, offset, littleEndian, 52, 8)
  return offset + 8
}

Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
  return writeDouble(this, value, offset, true, noAssert)
}

Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
  return writeDouble(this, value, offset, false, noAssert)
}

// copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
Buffer.prototype.copy = function copy (target, targetStart, start, end) {
  if (!start) start = 0
  if (!end && end !== 0) end = this.length
  if (targetStart >= target.length) targetStart = target.length
  if (!targetStart) targetStart = 0
  if (end > 0 && end < start) end = start

  // Copy 0 bytes; we're done
  if (end === start) return 0
  if (target.length === 0 || this.length === 0) return 0

  // Fatal error conditions
  if (targetStart < 0) {
    throw new RangeError('targetStart out of bounds')
  }
  if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
  if (end < 0) throw new RangeError('sourceEnd out of bounds')

  // Are we oob?
  if (end > this.length) end = this.length
  if (target.length - targetStart < end - start) {
    end = target.length - targetStart + start
  }

  var len = end - start
  var i

  if (this === target && start < targetStart && targetStart < end) {
    // descending copy from end
    for (i = len - 1; i >= 0; --i) {
      target[i + targetStart] = this[i + start]
    }
  } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
    // ascending copy from start
    for (i = 0; i < len; ++i) {
      target[i + targetStart] = this[i + start]
    }
  } else {
    Uint8Array.prototype.set.call(
      target,
      this.subarray(start, start + len),
      targetStart
    )
  }

  return len
}

// Usage:
//    buffer.fill(number[, offset[, end]])
//    buffer.fill(buffer[, offset[, end]])
//    buffer.fill(string[, offset[, end]][, encoding])
Buffer.prototype.fill = function fill (val, start, end, encoding) {
  // Handle string cases:
  if (typeof val === 'string') {
    if (typeof start === 'string') {
      encoding = start
      start = 0
      end = this.length
    } else if (typeof end === 'string') {
      encoding = end
      end = this.length
    }
    if (val.length === 1) {
      var code = val.charCodeAt(0)
      if (code < 256) {
        val = code
      }
    }
    if (encoding !== undefined && typeof encoding !== 'string') {
      throw new TypeError('encoding must be a string')
    }
    if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
      throw new TypeError('Unknown encoding: ' + encoding)
    }
  } else if (typeof val === 'number') {
    val = val & 255
  }

  // Invalid ranges are not set to a default, so can range check early.
  if (start < 0 || this.length < start || this.length < end) {
    throw new RangeError('Out of range index')
  }

  if (end <= start) {
    return this
  }

  start = start >>> 0
  end = end === undefined ? this.length : end >>> 0

  if (!val) val = 0

  var i
  if (typeof val === 'number') {
    for (i = start; i < end; ++i) {
      this[i] = val
    }
  } else {
    var bytes = Buffer.isBuffer(val)
      ? val
      : utf8ToBytes(new Buffer(val, encoding).toString())
    var len = bytes.length
    for (i = 0; i < end - start; ++i) {
      this[i + start] = bytes[i % len]
    }
  }

  return this
}

// HELPER FUNCTIONS
// ================

var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g

function base64clean (str) {
  // Node strips out invalid characters like \n and \t from the string, base64-js does not
  str = stringtrim(str).replace(INVALID_BASE64_RE, '')
  // Node converts strings with length < 2 to ''
  if (str.length < 2) return ''
  // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
  while (str.length % 4 !== 0) {
    str = str + '='
  }
  return str
}

function stringtrim (str) {
  if (str.trim) return str.trim()
  return str.replace(/^\s+|\s+$/g, '')
}

function toHex (n) {
  if (n < 16) return '0' + n.toString(16)
  return n.toString(16)
}

function utf8ToBytes (string, units) {
  units = units || Infinity
  var codePoint
  var length = string.length
  var leadSurrogate = null
  var bytes = []

  for (var i = 0; i < length; ++i) {
    codePoint = string.charCodeAt(i)

    // is surrogate component
    if (codePoint > 0xD7FF && codePoint < 0xE000) {
      // last char was a lead
      if (!leadSurrogate) {
        // no lead yet
        if (codePoint > 0xDBFF) {
          // unexpected trail
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        } else if (i + 1 === length) {
          // unpaired lead
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
          continue
        }

        // valid lead
        leadSurrogate = codePoint

        continue
      }

      // 2 leads in a row
      if (codePoint < 0xDC00) {
        if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
        leadSurrogate = codePoint
        continue
      }

      // valid surrogate pair
      codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
    } else if (leadSurrogate) {
      // valid bmp char, but last char was a lead
      if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
    }

    leadSurrogate = null

    // encode utf8
    if (codePoint < 0x80) {
      if ((units -= 1) < 0) break
      bytes.push(codePoint)
    } else if (codePoint < 0x800) {
      if ((units -= 2) < 0) break
      bytes.push(
        codePoint >> 0x6 | 0xC0,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x10000) {
      if ((units -= 3) < 0) break
      bytes.push(
        codePoint >> 0xC | 0xE0,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else if (codePoint < 0x110000) {
      if ((units -= 4) < 0) break
      bytes.push(
        codePoint >> 0x12 | 0xF0,
        codePoint >> 0xC & 0x3F | 0x80,
        codePoint >> 0x6 & 0x3F | 0x80,
        codePoint & 0x3F | 0x80
      )
    } else {
      throw new Error('Invalid code point')
    }
  }

  return bytes
}

function asciiToBytes (str) {
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    // Node's code seems to be doing this and not & 0x7F..
    byteArray.push(str.charCodeAt(i) & 0xFF)
  }
  return byteArray
}

function utf16leToBytes (str, units) {
  var c, hi, lo
  var byteArray = []
  for (var i = 0; i < str.length; ++i) {
    if ((units -= 2) < 0) break

    c = str.charCodeAt(i)
    hi = c >> 8
    lo = c % 256
    byteArray.push(lo)
    byteArray.push(hi)
  }

  return byteArray
}

function base64ToBytes (str) {
  return base64.toByteArray(base64clean(str))
}

function blitBuffer (src, dst, offset, length) {
  for (var i = 0; i < length; ++i) {
    if ((i + offset >= dst.length) || (i >= src.length)) break
    dst[i + offset] = src[i]
  }
  return i
}

function isnan (val) {
  return val !== val // eslint-disable-line no-self-compare
}

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/buffer/node_modules/base64-js/index.js":
/*!*************************************************************!*\
  !*** ./node_modules/buffer/node_modules/base64-js/index.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function getLens (b64) {
  var len = b64.length

  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // Trim off extra bytes after placeholder bytes are found
  // See: https://github.com/beatgammit/base64-js/issues/42
  var validLen = b64.indexOf('=')
  if (validLen === -1) validLen = len

  var placeHoldersLen = validLen === len
    ? 0
    : 4 - (validLen % 4)

  return [validLen, placeHoldersLen]
}

// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function _byteLength (b64, validLen, placeHoldersLen) {
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function toByteArray (b64) {
  var tmp
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]

  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

  var curByte = 0

  // if there are placeholders, only get up to the last complete 4 chars
  var len = placeHoldersLen > 0
    ? validLen - 4
    : validLen

  var i
  for (i = 0; i < len; i += 4) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 18) |
      (revLookup[b64.charCodeAt(i + 1)] << 12) |
      (revLookup[b64.charCodeAt(i + 2)] << 6) |
      revLookup[b64.charCodeAt(i + 3)]
    arr[curByte++] = (tmp >> 16) & 0xFF
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 2) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 2) |
      (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 1) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 10) |
      (revLookup[b64.charCodeAt(i + 1)] << 4) |
      (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] +
    lookup[num >> 12 & 0x3F] +
    lookup[num >> 6 & 0x3F] +
    lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp =
      ((uint8[i] << 16) & 0xFF0000) +
      ((uint8[i + 1] << 8) & 0xFF00) +
      (uint8[i + 2] & 0xFF)
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    parts.push(
      lookup[tmp >> 2] +
      lookup[(tmp << 4) & 0x3F] +
      '=='
    )
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + uint8[len - 1]
    parts.push(
      lookup[tmp >> 10] +
      lookup[(tmp >> 4) & 0x3F] +
      lookup[(tmp << 2) & 0x3F] +
      '='
    )
  }

  return parts.join('')
}


/***/ }),

/***/ "./node_modules/canvas-toBlob/canvas-toBlob.js":
/*!*****************************************************!*\
  !*** ./node_modules/canvas-toBlob/canvas-toBlob.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* canvas-toBlob.js
 * A canvas.toBlob() implementation.
 * 2016-05-26
 * 
 * By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
 * License: MIT
 *   See https://github.com/eligrey/canvas-toBlob.js/blob/master/LICENSE.md
 */

/*global self */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
  plusplus: true */

/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */

(function(view) {
"use strict";
var
	  Uint8Array = view.Uint8Array
	, HTMLCanvasElement = view.HTMLCanvasElement
	, canvas_proto = HTMLCanvasElement && HTMLCanvasElement.prototype
	, is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i
	, to_data_url = "toDataURL"
	, base64_ranks
	, decode_base64 = function(base64) {
		var
			  len = base64.length
			, buffer = new Uint8Array(len / 4 * 3 | 0)
			, i = 0
			, outptr = 0
			, last = [0, 0]
			, state = 0
			, save = 0
			, rank
			, code
			, undef
		;
		while (len--) {
			code = base64.charCodeAt(i++);
			rank = base64_ranks[code-43];
			if (rank !== 255 && rank !== undef) {
				last[1] = last[0];
				last[0] = code;
				save = (save << 6) | rank;
				state++;
				if (state === 4) {
					buffer[outptr++] = save >>> 16;
					if (last[1] !== 61 /* padding character */) {
						buffer[outptr++] = save >>> 8;
					}
					if (last[0] !== 61 /* padding character */) {
						buffer[outptr++] = save;
					}
					state = 0;
				}
			}
		}
		// 2/3 chance there's going to be some null bytes at the end, but that
		// doesn't really matter with most image formats.
		// If it somehow matters for you, truncate the buffer up outptr.
		return buffer;
	}
;
if (Uint8Array) {
	base64_ranks = new Uint8Array([
		  62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
		, -1, -1,  0, -1, -1, -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9
		, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
		, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
		, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
	]);
}
if (HTMLCanvasElement && (!canvas_proto.toBlob || !canvas_proto.toBlobHD)) {
	if (!canvas_proto.toBlob)
	canvas_proto.toBlob = function(callback, type /*, ...args*/) {
		  if (!type) {
			type = "image/png";
		} if (this.mozGetAsFile) {
			callback(this.mozGetAsFile("canvas", type));
			return;
		} if (this.msToBlob && /^\s*image\/png\s*(?:$|;)/i.test(type)) {
			callback(this.msToBlob());
			return;
		}

		var
			  args = Array.prototype.slice.call(arguments, 1)
			, dataURI = this[to_data_url].apply(this, args)
			, header_end = dataURI.indexOf(",")
			, data = dataURI.substring(header_end + 1)
			, is_base64 = is_base64_regex.test(dataURI.substring(0, header_end))
			, blob
		;
		if (Blob.fake) {
			// no reason to decode a data: URI that's just going to become a data URI again
			blob = new Blob
			if (is_base64) {
				blob.encoding = "base64";
			} else {
				blob.encoding = "URI";
			}
			blob.data = data;
			blob.size = data.length;
		} else if (Uint8Array) {
			if (is_base64) {
				blob = new Blob([decode_base64(data)], {type: type});
			} else {
				blob = new Blob([decodeURIComponent(data)], {type: type});
			}
		}
		callback(blob);
	};

	if (!canvas_proto.toBlobHD && canvas_proto.toDataURLHD) {
		canvas_proto.toBlobHD = function() {
			to_data_url = "toDataURLHD";
			var blob = this.toBlob();
			to_data_url = "toDataURL";
			return blob;
		}
	} else {
		canvas_proto.toBlobHD = canvas_proto.toBlob;
	}
}
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));


/***/ }),

/***/ "./node_modules/css-loader/dist/cjs.js?!./src/scaffolding/style.css":
/*!***********************************************************************************!*\
  !*** ./node_modules/css-loader/dist/cjs.js??ref--7-1!./src/scaffolding/style.css ***!
  \***********************************************************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/cssWithMappingToString.js */ "./node_modules/css-loader/dist/runtime/cssWithMappingToString.js");
/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ "./node_modules/css-loader/dist/runtime/api.js");
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/getUrl.js */ "./node_modules/css-loader/dist/runtime/getUrl.js");
/* harmony import */ var _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _check_svg__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./check.svg */ "./src/scaffolding/check.svg");
// Imports




var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default.a);
var ___CSS_LOADER_URL_REPLACEMENT_0___ = _node_modules_css_loader_dist_runtime_getUrl_js__WEBPACK_IMPORTED_MODULE_2___default()(_check_svg__WEBPACK_IMPORTED_MODULE_3__["default"]);
// Module
___CSS_LOADER_EXPORT___.push([module.i, ".sc-root {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n  --sc-accent-color: #ff4c4c;\n  --sc-accent-color-transparent: #ff4c4c59;\n}\n.sc-layers {\n  position: relative;\n}\n.sc-layers > * {\n  position: absolute;\n}\n.sc-scaled-overlays-outer {\n  overflow: hidden;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n}\n.sc-scaled-overlays-inner {\n  transform-origin: top left;\n}\n.sc-root * {\n  box-sizing: border-box;\n}\n.sc-canvas {\n  width: 100%;\n  height: 100%;\n}\n\n.sc-question-root {\n  bottom: 0;\n  width: 100%;\n  color: black;\n  pointer-events: all;\n}\n.sc-question-inner {\n  margin: 0.5rem;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  border-radius: 0.5rem;\n  border-width: 2px;\n  padding: 1rem;\n  background: white;\n}\n.sc-question-text {\n  font-size: 12px;\n  padding-bottom: 8px;\n  font-weight: bold;\n}\n.sc-question-input-outer {\n  position: relative;\n}\n.sc-question-input {\n  width: 100%;\n  height: 2rem;\n  padding: 0 12px;\n  font-size: 10px;\n  border-width: 1px;\n  border-style: solid;\n  border-color: hsla(0, 0%, 0%, 0.15);\n  border-radius: 2rem;\n  outline: none;\n  transition: 0.25s ease-out;\n  box-shadow: none;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  color: inherit;\n}\n.sc-question-input:focus {\n  border-color: var(--sc-accent-color);\n  box-shadow: 0px 0px 0px 3px var(--sc-accent-color-transparent);\n}\n.sc-question-input:hover {\n  border-color: var(--sc-accent-color);\n}\n.sc-question-submit-button {\n  top: 4px;\n  right: 4px;\n  width: 24px;\n  height: 24px;\n  position: absolute;\n  outline: none;\n  border: none;\n  padding: 2px;\n  margin: 0;\n  border-radius: 100%;\n  background: none;\n  cursor: pointer;\n  color: white;\n  background-color: var(--sc-accent-color);\n  background-image: url(" + ___CSS_LOADER_URL_REPLACEMENT_0___ + ");\n  overflow: hidden;\n}\n.sc-question-submit-button:focus {\n  outline: 2px solid black;\n}\n.sc-question-submit-button-image {\n  width: 100%;\n  height: 100%;\n}\n\n.sc-monitor-overlay {\n\n}\n.sc-monitor-root {\n  position: absolute;\n  top: 0;\n  left: 0;\n  background: hsla(215, 100%, 95%, 1);\n  color: #575e75;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  border-radius: 4px;\n  font-size: 12px;\n  overflow: hidden;\n  user-select: none;\n  -webkit-user-select: none;\n  display: flex;\n  flex-direction: column;\n  pointer-events: all;\n}\n.sc-monitor-inner {\n  display: flex;\n  flex-direction: column;\n  padding: 3px;\n}\n.sc-monitor-row {\n  display: flex;\n  flex-direction: row;\n}\n.sc-monitor-label {\n  font-weight: bold;\n  text-align: center;\n  margin: 0 5px;\n  white-space: nowrap;\n}\n.sc-monitor-slider {\n  margin: 0;\n  width: 100%;\n}\n.sc-monitor-value {\n  min-width: 40px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  text-align: center;\n  color: white;\n  margin: 0 5px;\n  border-radius: 4px;\n  padding: 0 2px;\n  white-space: pre-wrap;\n  background-color: #0fbd8c;\n}\n.sc-monitor-root[opcode^=\"motion_\"] .sc-monitor-value-color {\n  background-color: #4c97ff;\n}\n.sc-monitor-root[opcode^=\"sensing_\"] .sc-monitor-value-color {\n  background-color: #5cb1d6;\n}\n.sc-monitor-root[opcode^=\"data_\"] .sc-monitor-value-color {\n  background-color: #ff8c1a;\n}\n.sc-monitor-root[opcode^=\"looks_\"] .sc-monitor-value-color {\n  background-color: #9966ff;\n}\n.sc-monitor-root[opcode^=\"sound_\"] .sc-monitor-value-color {\n  background-color: #cf63cf;\n}\n.sc-monitor-large-value {\n  min-height: 1.4rem;\n  min-width: 3rem;\n  padding: 0.1rem 0.25rem;\n  text-align: center;\n  color: white;\n  font-size: 1rem;\n  white-space: pre-wrap;\n}\n\n.sc-monitor-list-label {\n  background-color: white;\n  text-align: center;\n  font-weight: bold;\n  border-bottom: 1px solid hsla(0, 0%, 0%, 0.15);\n  padding: 3px;\n}\n.sc-monitor-list-footer {\n  display: flex;\n  background-color: white;\n  text-align: center;\n  font-weight: bold;\n  padding: 3px;\n}\n.sc-monitor-list-footer-text {\n  text-align: center;\n  flex-grow: 1;\n}\n.sc-monitor-row-delete, .sc-monitor-list-add {\n  font-weight: bold;\n  background: none;\n  border: none;\n  outline: none;\n  font-family: inherit;\n  color: inherit;\n  cursor: pointer;\n  margin: 0;\n  padding: 0;\n}\n.sc-monitor-rows-outer {\n  flex-grow: 1;\n}\n.sc-monitor-rows-inner {\n  height: 100%;\n  overflow-y: auto;\n  overflow-x: hidden;\n  position: relative;\n}\n.sc-monitor-rows-endpoint {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 1px;\n  height: 1px;\n  pointer-events: none;\n}\n\n.sc-monitor-row-root {\n  position: absolute;\n  top: 0;\n  left: 0;\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n  padding: 2px;\n  width: 100%;\n}\n.sc-monitor-row-index {\n  font-weight: bold;\n  color: hsla(225, 15%, 40%, 1);\n  margin: 0 3px;\n}\n.sc-monitor-row-value-outer {\n  display: flex;\n  align-items: center;\n  min-width: 40px;\n  height: 22px;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  background-color: #fc662c;\n  color: white;\n  margin: 0 3px;\n  border-radius: calc(0.5rem / 2);\n  flex-grow: 1;\n}\n.sc-monitor-row-value-editing .sc-monitor-row-value-outer {\n  background-color: #e25c28;\n}\n.sc-monitor-row-value-inner {\n  padding: 3px 5px;\n  width: 100%;\n  color: inherit;\n  background: none;\n  border: none;\n  font: inherit;\n  outline: none;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  user-select: text;\n  -webkit-user-select: text;\n  white-space: pre;\n}\n.sc-monitor-row-value-editing .sc-monitor-row-value-inner {\n  padding-right: 0;\n}\n.sc-monitor-row-delete {\n  display: none;\n  font-size: 150%;\n  padding: 0 2px;\n}\n.sc-monitor-row-value-editing .sc-monitor-row-delete {\n  display: block;\n}\n.sc-monitor-empty {\n  text-align: center;\n  padding: 5px;\n}\n\n.sc-controls-bar {\n  transform: translateY(-100%);\n  display: flex;\n  width: 100%;\n  justify-content: space-between;\n  flex-wrap: nowrap;\n}\n.sc-controls-bar > * {\n  display: flex;\n  align-items: center;\n  flex-wrap: nowrap;\n}\n\n.sc-context-menu {\n  position: absolute;\n  color: black;\n  box-shadow: 0px 0px 5px 1px hsla(0, 0%, 0%, 0.25);\n  min-width: 130px;\n  opacity: 0;\n  transition: .2s opacity;\n}\n.sc-context-menu-item {\n  display: block;\n  width: 100%;\n  background-color: white;\n  border: none;\n  border-radius: 0;\n  cursor: pointer;\n  text-align: left;\n  font-family: inherit;\n  font-size: 0.85em;\n  padding: 8px 12px;\n  margin: 0;\n  transition: .1s background-color, .1s color;\n}\n.sc-context-menu-item:focus {\n  outline: 2px solid black;\n}\n.sc-context-menu-item:hover,\n.sc-context-menu-item:active {\n  color: white;\n  background-color: var(--sc-accent-color);\n}\n.sc-context-menu-item:first-child {\n  border-radius: 4px 4px 0 0;\n}\n.sc-context-menu-item:last-child {\n  border-radius: 0 0 4px 4px;\n}\n\n.sc-dropping {\n  background-color: var(--sc-accent-color-transparent);\n}\n", "",{"version":3,"sources":["webpack://./src/scaffolding/style.css"],"names":[],"mappings":"AAAA;EACE,2DAA2D;EAC3D,WAAW;EACX,YAAY;EACZ,aAAa;EACb,mBAAmB;EACnB,uBAAuB;EACvB,kBAAkB;EAClB,0BAA0B;EAC1B,wCAAwC;AAC1C;AACA;EACE,kBAAkB;AACpB;AACA;EACE,kBAAkB;AACpB;AACA;EACE,gBAAgB;EAChB,WAAW;EACX,YAAY;EACZ,oBAAoB;AACtB;AACA;EACE,0BAA0B;AAC5B;AACA;EACE,sBAAsB;AACxB;AACA;EACE,WAAW;EACX,YAAY;AACd;;AAEA;EACE,SAAS;EACT,WAAW;EACX,YAAY;EACZ,mBAAmB;AACrB;AACA;EACE,cAAc;EACd,uCAAuC;EACvC,qBAAqB;EACrB,iBAAiB;EACjB,aAAa;EACb,iBAAiB;AACnB;AACA;EACE,eAAe;EACf,mBAAmB;EACnB,iBAAiB;AACnB;AACA;EACE,kBAAkB;AACpB;AACA;EACE,WAAW;EACX,YAAY;EACZ,eAAe;EACf,eAAe;EACf,iBAAiB;EACjB,mBAAmB;EACnB,mCAAmC;EACnC,mBAAmB;EACnB,aAAa;EACb,0BAA0B;EAC1B,gBAAgB;EAChB,gBAAgB;EAChB,uBAAuB;EACvB,cAAc;AAChB;AACA;EACE,oCAAoC;EACpC,8DAA8D;AAChE;AACA;EACE,oCAAoC;AACtC;AACA;EACE,QAAQ;EACR,UAAU;EACV,WAAW;EACX,YAAY;EACZ,kBAAkB;EAClB,aAAa;EACb,YAAY;EACZ,YAAY;EACZ,SAAS;EACT,mBAAmB;EACnB,gBAAgB;EAChB,eAAe;EACf,YAAY;EACZ,wCAAwC;EACxC,yDAAgC;EAChC,gBAAgB;AAClB;AACA;EACE,wBAAwB;AAC1B;AACA;EACE,WAAW;EACX,YAAY;AACd;;AAEA;;AAEA;AACA;EACE,kBAAkB;EAClB,MAAM;EACN,OAAO;EACP,mCAAmC;EACnC,cAAc;EACd,uCAAuC;EACvC,kBAAkB;EAClB,eAAe;EACf,gBAAgB;EAChB,iBAAiB;EACjB,yBAAyB;EACzB,aAAa;EACb,sBAAsB;EACtB,mBAAmB;AACrB;AACA;EACE,aAAa;EACb,sBAAsB;EACtB,YAAY;AACd;AACA;EACE,aAAa;EACb,mBAAmB;AACrB;AACA;EACE,iBAAiB;EACjB,kBAAkB;EAClB,aAAa;EACb,mBAAmB;AACrB;AACA;EACE,SAAS;EACT,WAAW;AACb;AACA;EACE,eAAe;EACf,aAAa;EACb,uBAAuB;EACvB,mBAAmB;EACnB,kBAAkB;EAClB,YAAY;EACZ,aAAa;EACb,kBAAkB;EAClB,cAAc;EACd,qBAAqB;EACrB,yBAAyB;AAC3B;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,kBAAkB;EAClB,eAAe;EACf,uBAAuB;EACvB,kBAAkB;EAClB,YAAY;EACZ,eAAe;EACf,qBAAqB;AACvB;;AAEA;EACE,uBAAuB;EACvB,kBAAkB;EAClB,iBAAiB;EACjB,8CAA8C;EAC9C,YAAY;AACd;AACA;EACE,aAAa;EACb,uBAAuB;EACvB,kBAAkB;EAClB,iBAAiB;EACjB,YAAY;AACd;AACA;EACE,kBAAkB;EAClB,YAAY;AACd;AACA;EACE,iBAAiB;EACjB,gBAAgB;EAChB,YAAY;EACZ,aAAa;EACb,oBAAoB;EACpB,cAAc;EACd,eAAe;EACf,SAAS;EACT,UAAU;AACZ;AACA;EACE,YAAY;AACd;AACA;EACE,YAAY;EACZ,gBAAgB;EAChB,kBAAkB;EAClB,kBAAkB;AACpB;AACA;EACE,kBAAkB;EAClB,MAAM;EACN,OAAO;EACP,UAAU;EACV,WAAW;EACX,oBAAoB;AACtB;;AAEA;EACE,kBAAkB;EAClB,MAAM;EACN,OAAO;EACP,aAAa;EACb,6BAA6B;EAC7B,mBAAmB;EACnB,YAAY;EACZ,WAAW;AACb;AACA;EACE,iBAAiB;EACjB,6BAA6B;EAC7B,aAAa;AACf;AACA;EACE,aAAa;EACb,mBAAmB;EACnB,eAAe;EACf,YAAY;EACZ,uCAAuC;EACvC,yBAAyB;EACzB,YAAY;EACZ,aAAa;EACb,+BAA+B;EAC/B,YAAY;AACd;AACA;EACE,yBAAyB;AAC3B;AACA;EACE,gBAAgB;EAChB,WAAW;EACX,cAAc;EACd,gBAAgB;EAChB,YAAY;EACZ,aAAa;EACb,aAAa;EACb,gBAAgB;EAChB,uBAAuB;EACvB,iBAAiB;EACjB,yBAAyB;EACzB,gBAAgB;AAClB;AACA;EACE,gBAAgB;AAClB;AACA;EACE,aAAa;EACb,eAAe;EACf,cAAc;AAChB;AACA;EACE,cAAc;AAChB;AACA;EACE,kBAAkB;EAClB,YAAY;AACd;;AAEA;EACE,4BAA4B;EAC5B,aAAa;EACb,WAAW;EACX,8BAA8B;EAC9B,iBAAiB;AACnB;AACA;EACE,aAAa;EACb,mBAAmB;EACnB,iBAAiB;AACnB;;AAEA;EACE,kBAAkB;EAClB,YAAY;EACZ,iDAAiD;EACjD,gBAAgB;EAChB,UAAU;EACV,uBAAuB;AACzB;AACA;EACE,cAAc;EACd,WAAW;EACX,uBAAuB;EACvB,YAAY;EACZ,gBAAgB;EAChB,eAAe;EACf,gBAAgB;EAChB,oBAAoB;EACpB,iBAAiB;EACjB,iBAAiB;EACjB,SAAS;EACT,2CAA2C;AAC7C;AACA;EACE,wBAAwB;AAC1B;AACA;;EAEE,YAAY;EACZ,wCAAwC;AAC1C;AACA;EACE,0BAA0B;AAC5B;AACA;EACE,0BAA0B;AAC5B;;AAEA;EACE,oDAAoD;AACtD","sourcesContent":[".root {\n  font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n  width: 100%;\n  height: 100%;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  position: relative;\n  --sc-accent-color: #ff4c4c;\n  --sc-accent-color-transparent: #ff4c4c59;\n}\n.layers {\n  position: relative;\n}\n.layers > * {\n  position: absolute;\n}\n.scaled-overlays-outer {\n  overflow: hidden;\n  width: 100%;\n  height: 100%;\n  pointer-events: none;\n}\n.scaled-overlays-inner {\n  transform-origin: top left;\n}\n.root * {\n  box-sizing: border-box;\n}\n.canvas {\n  width: 100%;\n  height: 100%;\n}\n\n.question-root {\n  bottom: 0;\n  width: 100%;\n  color: black;\n  pointer-events: all;\n}\n.question-inner {\n  margin: 0.5rem;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  border-radius: 0.5rem;\n  border-width: 2px;\n  padding: 1rem;\n  background: white;\n}\n.question-text {\n  font-size: 12px;\n  padding-bottom: 8px;\n  font-weight: bold;\n}\n.question-input-outer {\n  position: relative;\n}\n.question-input {\n  width: 100%;\n  height: 2rem;\n  padding: 0 12px;\n  font-size: 10px;\n  border-width: 1px;\n  border-style: solid;\n  border-color: hsla(0, 0%, 0%, 0.15);\n  border-radius: 2rem;\n  outline: none;\n  transition: 0.25s ease-out;\n  box-shadow: none;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  color: inherit;\n}\n.question-input:focus {\n  border-color: var(--sc-accent-color);\n  box-shadow: 0px 0px 0px 3px var(--sc-accent-color-transparent);\n}\n.question-input:hover {\n  border-color: var(--sc-accent-color);\n}\n.question-submit-button {\n  top: 4px;\n  right: 4px;\n  width: 24px;\n  height: 24px;\n  position: absolute;\n  outline: none;\n  border: none;\n  padding: 2px;\n  margin: 0;\n  border-radius: 100%;\n  background: none;\n  cursor: pointer;\n  color: white;\n  background-color: var(--sc-accent-color);\n  background-image: url(check.svg);\n  overflow: hidden;\n}\n.question-submit-button:focus {\n  outline: 2px solid black;\n}\n.question-submit-button-image {\n  width: 100%;\n  height: 100%;\n}\n\n.monitor-overlay {\n\n}\n.monitor-root {\n  position: absolute;\n  top: 0;\n  left: 0;\n  background: hsla(215, 100%, 95%, 1);\n  color: #575e75;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  border-radius: 4px;\n  font-size: 12px;\n  overflow: hidden;\n  user-select: none;\n  -webkit-user-select: none;\n  display: flex;\n  flex-direction: column;\n  pointer-events: all;\n}\n.monitor-inner {\n  display: flex;\n  flex-direction: column;\n  padding: 3px;\n}\n.monitor-row {\n  display: flex;\n  flex-direction: row;\n}\n.monitor-label {\n  font-weight: bold;\n  text-align: center;\n  margin: 0 5px;\n  white-space: nowrap;\n}\n.monitor-slider {\n  margin: 0;\n  width: 100%;\n}\n.monitor-value {\n  min-width: 40px;\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  text-align: center;\n  color: white;\n  margin: 0 5px;\n  border-radius: 4px;\n  padding: 0 2px;\n  white-space: pre-wrap;\n  background-color: #0fbd8c;\n}\n.monitor-root[opcode^=\"motion_\"] .monitor-value-color {\n  background-color: #4c97ff;\n}\n.monitor-root[opcode^=\"sensing_\"] .monitor-value-color {\n  background-color: #5cb1d6;\n}\n.monitor-root[opcode^=\"data_\"] .monitor-value-color {\n  background-color: #ff8c1a;\n}\n.monitor-root[opcode^=\"looks_\"] .monitor-value-color {\n  background-color: #9966ff;\n}\n.monitor-root[opcode^=\"sound_\"] .monitor-value-color {\n  background-color: #cf63cf;\n}\n.monitor-large-value {\n  min-height: 1.4rem;\n  min-width: 3rem;\n  padding: 0.1rem 0.25rem;\n  text-align: center;\n  color: white;\n  font-size: 1rem;\n  white-space: pre-wrap;\n}\n\n.monitor-list-label {\n  background-color: white;\n  text-align: center;\n  font-weight: bold;\n  border-bottom: 1px solid hsla(0, 0%, 0%, 0.15);\n  padding: 3px;\n}\n.monitor-list-footer {\n  display: flex;\n  background-color: white;\n  text-align: center;\n  font-weight: bold;\n  padding: 3px;\n}\n.monitor-list-footer-text {\n  text-align: center;\n  flex-grow: 1;\n}\n.monitor-row-delete, .monitor-list-add {\n  font-weight: bold;\n  background: none;\n  border: none;\n  outline: none;\n  font-family: inherit;\n  color: inherit;\n  cursor: pointer;\n  margin: 0;\n  padding: 0;\n}\n.monitor-rows-outer {\n  flex-grow: 1;\n}\n.monitor-rows-inner {\n  height: 100%;\n  overflow-y: auto;\n  overflow-x: hidden;\n  position: relative;\n}\n.monitor-rows-endpoint {\n  position: absolute;\n  top: 0;\n  left: 0;\n  width: 1px;\n  height: 1px;\n  pointer-events: none;\n}\n\n.monitor-row-root {\n  position: absolute;\n  top: 0;\n  left: 0;\n  display: flex;\n  justify-content: space-around;\n  align-items: center;\n  padding: 2px;\n  width: 100%;\n}\n.monitor-row-index {\n  font-weight: bold;\n  color: hsla(225, 15%, 40%, 1);\n  margin: 0 3px;\n}\n.monitor-row-value-outer {\n  display: flex;\n  align-items: center;\n  min-width: 40px;\n  height: 22px;\n  border: 1px solid hsla(0, 0%, 0%, 0.15);\n  background-color: #fc662c;\n  color: white;\n  margin: 0 3px;\n  border-radius: calc(0.5rem / 2);\n  flex-grow: 1;\n}\n.monitor-row-value-editing .monitor-row-value-outer {\n  background-color: #e25c28;\n}\n.monitor-row-value-inner {\n  padding: 3px 5px;\n  width: 100%;\n  color: inherit;\n  background: none;\n  border: none;\n  font: inherit;\n  outline: none;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  user-select: text;\n  -webkit-user-select: text;\n  white-space: pre;\n}\n.monitor-row-value-editing .monitor-row-value-inner {\n  padding-right: 0;\n}\n.monitor-row-delete {\n  display: none;\n  font-size: 150%;\n  padding: 0 2px;\n}\n.monitor-row-value-editing .monitor-row-delete {\n  display: block;\n}\n.monitor-empty {\n  text-align: center;\n  padding: 5px;\n}\n\n.controls-bar {\n  transform: translateY(-100%);\n  display: flex;\n  width: 100%;\n  justify-content: space-between;\n  flex-wrap: nowrap;\n}\n.controls-bar > * {\n  display: flex;\n  align-items: center;\n  flex-wrap: nowrap;\n}\n\n.context-menu {\n  position: absolute;\n  color: black;\n  box-shadow: 0px 0px 5px 1px hsla(0, 0%, 0%, 0.25);\n  min-width: 130px;\n  opacity: 0;\n  transition: .2s opacity;\n}\n.context-menu-item {\n  display: block;\n  width: 100%;\n  background-color: white;\n  border: none;\n  border-radius: 0;\n  cursor: pointer;\n  text-align: left;\n  font-family: inherit;\n  font-size: 0.85em;\n  padding: 8px 12px;\n  margin: 0;\n  transition: .1s background-color, .1s color;\n}\n.context-menu-item:focus {\n  outline: 2px solid black;\n}\n.context-menu-item:hover,\n.context-menu-item:active {\n  color: white;\n  background-color: var(--sc-accent-color);\n}\n.context-menu-item:first-child {\n  border-radius: 4px 4px 0 0;\n}\n.context-menu-item:last-child {\n  border-radius: 0 0 4px 4px;\n}\n\n.dropping {\n  background-color: var(--sc-accent-color-transparent);\n}\n"],"sourceRoot":""}]);
// Exports
___CSS_LOADER_EXPORT___.locals = {
	"root": "sc-root",
	"layers": "sc-layers",
	"scaled-overlays-outer": "sc-scaled-overlays-outer",
	"scaledOverlaysOuter": "sc-scaled-overlays-outer",
	"scaled-overlays-inner": "sc-scaled-overlays-inner",
	"scaledOverlaysInner": "sc-scaled-overlays-inner",
	"canvas": "sc-canvas",
	"question-root": "sc-question-root",
	"questionRoot": "sc-question-root",
	"question-inner": "sc-question-inner",
	"questionInner": "sc-question-inner",
	"question-text": "sc-question-text",
	"questionText": "sc-question-text",
	"question-input-outer": "sc-question-input-outer",
	"questionInputOuter": "sc-question-input-outer",
	"question-input": "sc-question-input",
	"questionInput": "sc-question-input",
	"question-submit-button": "sc-question-submit-button",
	"questionSubmitButton": "sc-question-submit-button",
	"question-submit-button-image": "sc-question-submit-button-image",
	"questionSubmitButtonImage": "sc-question-submit-button-image",
	"monitor-overlay": "sc-monitor-overlay",
	"monitorOverlay": "sc-monitor-overlay",
	"monitor-root": "sc-monitor-root",
	"monitorRoot": "sc-monitor-root",
	"monitor-inner": "sc-monitor-inner",
	"monitorInner": "sc-monitor-inner",
	"monitor-row": "sc-monitor-row",
	"monitorRow": "sc-monitor-row",
	"monitor-label": "sc-monitor-label",
	"monitorLabel": "sc-monitor-label",
	"monitor-slider": "sc-monitor-slider",
	"monitorSlider": "sc-monitor-slider",
	"monitor-value": "sc-monitor-value",
	"monitorValue": "sc-monitor-value",
	"monitor-value-color": "sc-monitor-value-color",
	"monitorValueColor": "sc-monitor-value-color",
	"monitor-large-value": "sc-monitor-large-value",
	"monitorLargeValue": "sc-monitor-large-value",
	"monitor-list-label": "sc-monitor-list-label",
	"monitorListLabel": "sc-monitor-list-label",
	"monitor-list-footer": "sc-monitor-list-footer",
	"monitorListFooter": "sc-monitor-list-footer",
	"monitor-list-footer-text": "sc-monitor-list-footer-text",
	"monitorListFooterText": "sc-monitor-list-footer-text",
	"monitor-row-delete": "sc-monitor-row-delete",
	"monitorRowDelete": "sc-monitor-row-delete",
	"monitor-list-add": "sc-monitor-list-add",
	"monitorListAdd": "sc-monitor-list-add",
	"monitor-rows-outer": "sc-monitor-rows-outer",
	"monitorRowsOuter": "sc-monitor-rows-outer",
	"monitor-rows-inner": "sc-monitor-rows-inner",
	"monitorRowsInner": "sc-monitor-rows-inner",
	"monitor-rows-endpoint": "sc-monitor-rows-endpoint",
	"monitorRowsEndpoint": "sc-monitor-rows-endpoint",
	"monitor-row-root": "sc-monitor-row-root",
	"monitorRowRoot": "sc-monitor-row-root",
	"monitor-row-index": "sc-monitor-row-index",
	"monitorRowIndex": "sc-monitor-row-index",
	"monitor-row-value-outer": "sc-monitor-row-value-outer",
	"monitorRowValueOuter": "sc-monitor-row-value-outer",
	"monitor-row-value-editing": "sc-monitor-row-value-editing",
	"monitorRowValueEditing": "sc-monitor-row-value-editing",
	"monitor-row-value-inner": "sc-monitor-row-value-inner",
	"monitorRowValueInner": "sc-monitor-row-value-inner",
	"monitor-empty": "sc-monitor-empty",
	"monitorEmpty": "sc-monitor-empty",
	"controls-bar": "sc-controls-bar",
	"controlsBar": "sc-controls-bar",
	"context-menu": "sc-context-menu",
	"contextMenu": "sc-context-menu",
	"context-menu-item": "sc-context-menu-item",
	"contextMenuItem": "sc-context-menu-item",
	"dropping": "sc-dropping"
};
/* harmony default export */ __webpack_exports__["default"] = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/api.js":
/*!*****************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/api.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
// eslint-disable-next-line func-names
module.exports = function (cssWithMappingToString) {
  var list = []; // return the list of modules as css string

  list.toString = function toString() {
    return this.map(function (item) {
      var content = cssWithMappingToString(item);

      if (item[2]) {
        return "@media ".concat(item[2], " {").concat(content, "}");
      }

      return content;
    }).join("");
  }; // import a list of modules into the list
  // eslint-disable-next-line func-names


  list.i = function (modules, mediaQuery, dedupe) {
    if (typeof modules === "string") {
      // eslint-disable-next-line no-param-reassign
      modules = [[null, modules, ""]];
    }

    var alreadyImportedModules = {};

    if (dedupe) {
      for (var i = 0; i < this.length; i++) {
        // eslint-disable-next-line prefer-destructuring
        var id = this[i][0];

        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }

    for (var _i = 0; _i < modules.length; _i++) {
      var item = [].concat(modules[_i]);

      if (dedupe && alreadyImportedModules[item[0]]) {
        // eslint-disable-next-line no-continue
        continue;
      }

      if (mediaQuery) {
        if (!item[2]) {
          item[2] = mediaQuery;
        } else {
          item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
        }
      }

      list.push(item);
    }
  };

  return list;
};

/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/cssWithMappingToString.js":
/*!************************************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/cssWithMappingToString.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

module.exports = function cssWithMappingToString(item) {
  var _item = _slicedToArray(item, 4),
      content = _item[1],
      cssMapping = _item[3];

  if (!cssMapping) {
    return content;
  }

  if (typeof btoa === "function") {
    // eslint-disable-next-line no-undef
    var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));
    var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
    var sourceMapping = "/*# ".concat(data, " */");
    var sourceURLs = cssMapping.sources.map(function (source) {
      return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
    });
    return [content].concat(sourceURLs).concat([sourceMapping]).join("\n");
  }

  return [content].join("\n");
};

/***/ }),

/***/ "./node_modules/css-loader/dist/runtime/getUrl.js":
/*!********************************************************!*\
  !*** ./node_modules/css-loader/dist/runtime/getUrl.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


module.exports = function (url, options) {
  if (!options) {
    // eslint-disable-next-line no-param-reassign
    options = {};
  } // eslint-disable-next-line no-underscore-dangle, no-param-reassign


  url = url && url.__esModule ? url.default : url;

  if (typeof url !== "string") {
    return url;
  } // If url is already wrapped in quotes, remove them


  if (/^['"].*['"]$/.test(url)) {
    // eslint-disable-next-line no-param-reassign
    url = url.slice(1, -1);
  }

  if (options.hash) {
    // eslint-disable-next-line no-param-reassign
    url += options.hash;
  } // Should url be wrapped?
  // See https://drafts.csswg.org/css-values-3/#urls


  if (/["'() \t\n]/.test(url) || options.needQuotes) {
    return "\"".concat(url.replace(/"/g, '\\"').replace(/\n/g, "\\n"), "\"");
  }

  return url;
};

/***/ }),

/***/ "./node_modules/decode-html/index.js":
/*!*******************************************!*\
  !*** ./node_modules/decode-html/index.js ***!
  \*******************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Store markers outside of the function scope,
// not to recreate them on every call
var entities = {
  'amp': '&',
  'apos': '\'',
  'lt': '<',
  'gt': '>',
  'quot': '"',
  'nbsp': '\xa0'
};
var entityPattern = /&([a-z]+);/ig;

module.exports = function decodeHTMLEntities(text) {
  // A single replace pass with a static RegExp is faster than a loop
  return text.replace(entityPattern, function(match, entity) {
    entity = entity.toLowerCase();
    if (entities.hasOwnProperty(entity)) {
      return entities[entity];
    }
    // return original string if there is no matching entity (no replace)
    return match;
  });
};


/***/ }),

/***/ "./node_modules/dompurify/dist/purify.js":
/*!***********************************************!*\
  !*** ./node_modules/dompurify/dist/purify.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */

(function (global, factory) {
   true ? module.exports = factory() :
  undefined;
}(this, function () { 'use strict';

  function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

  var hasOwnProperty = Object.hasOwnProperty,
      setPrototypeOf = Object.setPrototypeOf,
      isFrozen = Object.isFrozen,
      getPrototypeOf = Object.getPrototypeOf,
      getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
  var freeze = Object.freeze,
      seal = Object.seal,
      create = Object.create; // eslint-disable-line import/no-mutable-exports

  var _ref = typeof Reflect !== 'undefined' && Reflect,
      apply = _ref.apply,
      construct = _ref.construct;

  if (!apply) {
    apply = function apply(fun, thisValue, args) {
      return fun.apply(thisValue, args);
    };
  }

  if (!freeze) {
    freeze = function freeze(x) {
      return x;
    };
  }

  if (!seal) {
    seal = function seal(x) {
      return x;
    };
  }

  if (!construct) {
    construct = function construct(Func, args) {
      return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
    };
  }

  var arrayForEach = unapply(Array.prototype.forEach);
  var arrayPop = unapply(Array.prototype.pop);
  var arrayPush = unapply(Array.prototype.push);

  var stringToLowerCase = unapply(String.prototype.toLowerCase);
  var stringMatch = unapply(String.prototype.match);
  var stringReplace = unapply(String.prototype.replace);
  var stringIndexOf = unapply(String.prototype.indexOf);
  var stringTrim = unapply(String.prototype.trim);

  var regExpTest = unapply(RegExp.prototype.test);

  var typeErrorCreate = unconstruct(TypeError);

  function unapply(func) {
    return function (thisArg) {
      for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
        args[_key - 1] = arguments[_key];
      }

      return apply(func, thisArg, args);
    };
  }

  function unconstruct(func) {
    return function () {
      for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
        args[_key2] = arguments[_key2];
      }

      return construct(func, args);
    };
  }

  /* Add properties to a lookup table */
  function addToSet(set, array) {
    if (setPrototypeOf) {
      // Make 'in' and truthy checks like Boolean(set.constructor)
      // independent of any properties defined on Object.prototype.
      // Prevent prototype setters from intercepting set as a this value.
      setPrototypeOf(set, null);
    }

    var l = array.length;
    while (l--) {
      var element = array[l];
      if (typeof element === 'string') {
        var lcElement = stringToLowerCase(element);
        if (lcElement !== element) {
          // Config presets (e.g. tags.js, attrs.js) are immutable.
          if (!isFrozen(array)) {
            array[l] = lcElement;
          }

          element = lcElement;
        }
      }

      set[element] = true;
    }

    return set;
  }

  /* Shallow clone an object */
  function clone(object) {
    var newObject = create(null);

    var property = void 0;
    for (property in object) {
      if (apply(hasOwnProperty, object, [property])) {
        newObject[property] = object[property];
      }
    }

    return newObject;
  }

  /* IE10 doesn't support __lookupGetter__ so lets'
   * simulate it. It also automatically checks
   * if the prop is function or getter and behaves
   * accordingly. */
  function lookupGetter(object, prop) {
    while (object !== null) {
      var desc = getOwnPropertyDescriptor(object, prop);
      if (desc) {
        if (desc.get) {
          return unapply(desc.get);
        }

        if (typeof desc.value === 'function') {
          return unapply(desc.value);
        }
      }

      object = getPrototypeOf(object);
    }

    function fallbackValue(element) {
      console.warn('fallback value for', element);
      return null;
    }

    return fallbackValue;
  }

  var html = freeze(['a', 'abbr', 'acronym', 'address', 'area', 'article', 'aside', 'audio', 'b', 'bdi', 'bdo', 'big', 'blink', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'center', 'cite', 'code', 'col', 'colgroup', 'content', 'data', 'datalist', 'dd', 'decorator', 'del', 'details', 'dfn', 'dialog', 'dir', 'div', 'dl', 'dt', 'element', 'em', 'fieldset', 'figcaption', 'figure', 'font', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'main', 'map', 'mark', 'marquee', 'menu', 'menuitem', 'meter', 'nav', 'nobr', 'ol', 'optgroup', 'option', 'output', 'p', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'section', 'select', 'shadow', 'small', 'source', 'spacer', 'span', 'strike', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'tr', 'track', 'tt', 'u', 'ul', 'var', 'video', 'wbr']);

  // SVG
  var svg = freeze(['svg', 'a', 'altglyph', 'altglyphdef', 'altglyphitem', 'animatecolor', 'animatemotion', 'animatetransform', 'circle', 'clippath', 'defs', 'desc', 'ellipse', 'filter', 'font', 'g', 'glyph', 'glyphref', 'hkern', 'image', 'line', 'lineargradient', 'marker', 'mask', 'metadata', 'mpath', 'path', 'pattern', 'polygon', 'polyline', 'radialgradient', 'rect', 'stop', 'style', 'switch', 'symbol', 'text', 'textpath', 'title', 'tref', 'tspan', 'view', 'vkern']);

  var svgFilters = freeze(['feBlend', 'feColorMatrix', 'feComponentTransfer', 'feComposite', 'feConvolveMatrix', 'feDiffuseLighting', 'feDisplacementMap', 'feDistantLight', 'feFlood', 'feFuncA', 'feFuncB', 'feFuncG', 'feFuncR', 'feGaussianBlur', 'feMerge', 'feMergeNode', 'feMorphology', 'feOffset', 'fePointLight', 'feSpecularLighting', 'feSpotLight', 'feTile', 'feTurbulence']);

  // List of SVG elements that are disallowed by default.
  // We still need to know them so that we can do namespace
  // checks properly in case one wants to add them to
  // allow-list.
  var svgDisallowed = freeze(['animate', 'color-profile', 'cursor', 'discard', 'fedropshadow', 'feimage', 'font-face', 'font-face-format', 'font-face-name', 'font-face-src', 'font-face-uri', 'foreignobject', 'hatch', 'hatchpath', 'mesh', 'meshgradient', 'meshpatch', 'meshrow', 'missing-glyph', 'script', 'set', 'solidcolor', 'unknown', 'use']);

  var mathMl = freeze(['math', 'menclose', 'merror', 'mfenced', 'mfrac', 'mglyph', 'mi', 'mlabeledtr', 'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom', 'mroot', 'mrow', 'ms', 'mspace', 'msqrt', 'mstyle', 'msub', 'msup', 'msubsup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder', 'munderover']);

  // Similarly to SVG, we want to know all MathML elements,
  // even those that we disallow by default.
  var mathMlDisallowed = freeze(['maction', 'maligngroup', 'malignmark', 'mlongdiv', 'mscarries', 'mscarry', 'msgroup', 'mstack', 'msline', 'msrow', 'semantics', 'annotation', 'annotation-xml', 'mprescripts', 'none']);

  var text = freeze(['#text']);

  var html$1 = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'pattern', 'placeholder', 'playsinline', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'xmlns']);

  var svg$1 = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'targetx', 'targety', 'transform', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);

  var mathMl$1 = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnsalign', 'columnlines', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lspace', 'lquote', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);

  var xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);

  // eslint-disable-next-line unicorn/better-regex
  var MUSTACHE_EXPR = seal(/\{\{[\s\S]*|[\s\S]*\}\}/gm); // Specify template detection regex for SAFE_FOR_TEMPLATES mode
  var ERB_EXPR = seal(/<%[\s\S]*|[\s\S]*%>/gm);
  var DATA_ATTR = seal(/^data-[\-\w.\u00B7-\uFFFF]/); // eslint-disable-line no-useless-escape
  var ARIA_ATTR = seal(/^aria-[\-\w]+$/); // eslint-disable-line no-useless-escape
  var IS_ALLOWED_URI = seal(/^(?:(?:(?:f|ht)tps?|mailto|tel|callto|cid|xmpp):|[^a-z]|[a-z+.\-]+(?:[^a-z+.\-:]|$))/i // eslint-disable-line no-useless-escape
  );
  var IS_SCRIPT_OR_DATA = seal(/^(?:\w+script|data):/i);
  var ATTR_WHITESPACE = seal(/[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205F\u3000]/g // eslint-disable-line no-control-regex
  );

  var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

  function _toConsumableArray$1(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }

  var getGlobal = function getGlobal() {
    return typeof window === 'undefined' ? null : window;
  };

  /**
   * Creates a no-op policy for internal use only.
   * Don't export this function outside this module!
   * @param {?TrustedTypePolicyFactory} trustedTypes The policy factory.
   * @param {Document} document The document object (to determine policy name suffix)
   * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
   * are not supported).
   */
  var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
    if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
      return null;
    }

    // Allow the callers to control the unique policy name
    // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
    // Policy creation with duplicate names throws in Trusted Types.
    var suffix = null;
    var ATTR_NAME = 'data-tt-policy-suffix';
    if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
      suffix = document.currentScript.getAttribute(ATTR_NAME);
    }

    var policyName = 'dompurify' + (suffix ? '#' + suffix : '');

    try {
      return trustedTypes.createPolicy(policyName, {
        createHTML: function createHTML(html$$1) {
          return html$$1;
        }
      });
    } catch (_) {
      // Policy creation failed (most likely another DOMPurify script has
      // already run). Skip creating the policy, as this will only cause errors
      // if TT are enforced.
      console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
      return null;
    }
  };

  function createDOMPurify() {
    var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();

    var DOMPurify = function DOMPurify(root) {
      return createDOMPurify(root);
    };

    /**
     * Version label, exposed for easier checks
     * if DOMPurify is up to date or not
     */
    DOMPurify.version = '2.2.7';

    /**
     * Array of elements that DOMPurify removed during sanitation.
     * Empty if nothing was removed.
     */
    DOMPurify.removed = [];

    if (!window || !window.document || window.document.nodeType !== 9) {
      // Not running in a browser, provide a factory function
      // so that you can pass your own Window
      DOMPurify.isSupported = false;

      return DOMPurify;
    }

    var originalDocument = window.document;

    var document = window.document;
    var DocumentFragment = window.DocumentFragment,
        HTMLTemplateElement = window.HTMLTemplateElement,
        Node = window.Node,
        Element = window.Element,
        NodeFilter = window.NodeFilter,
        _window$NamedNodeMap = window.NamedNodeMap,
        NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
        Text = window.Text,
        Comment = window.Comment,
        DOMParser = window.DOMParser,
        trustedTypes = window.trustedTypes;


    var ElementPrototype = Element.prototype;

    var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
    var getNextSibling = lookupGetter(ElementPrototype, 'nextSibling');
    var getChildNodes = lookupGetter(ElementPrototype, 'childNodes');
    var getParentNode = lookupGetter(ElementPrototype, 'parentNode');

    // As per issue #47, the web-components registry is inherited by a
    // new document created via createHTMLDocument. As per the spec
    // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
    // a new empty registry is used when creating a template contents owner
    // document, so we use that as our parent document to ensure nothing
    // is inherited.
    if (typeof HTMLTemplateElement === 'function') {
      var template = document.createElement('template');
      if (template.content && template.content.ownerDocument) {
        document = template.content.ownerDocument;
      }
    }

    var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
    var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';

    var _document = document,
        implementation = _document.implementation,
        createNodeIterator = _document.createNodeIterator,
        getElementsByTagName = _document.getElementsByTagName,
        createDocumentFragment = _document.createDocumentFragment;
    var importNode = originalDocument.importNode;


    var documentMode = {};
    try {
      documentMode = clone(document).documentMode ? document.documentMode : {};
    } catch (_) {}

    var hooks = {};

    /**
     * Expose whether this browser supports running the full DOMPurify.
     */
    DOMPurify.isSupported = typeof getParentNode === 'function' && implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;

    var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
        ERB_EXPR$$1 = ERB_EXPR,
        DATA_ATTR$$1 = DATA_ATTR,
        ARIA_ATTR$$1 = ARIA_ATTR,
        IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
        ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
    var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;

    /**
     * We consider the elements and attributes below to be safe. Ideally
     * don't add any new ones but feel free to remove unwanted ones.
     */

    /* allowed element names */

    var ALLOWED_TAGS = null;
    var DEFAULT_ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(html), _toConsumableArray$1(svg), _toConsumableArray$1(svgFilters), _toConsumableArray$1(mathMl), _toConsumableArray$1(text)));

    /* Allowed attribute names */
    var ALLOWED_ATTR = null;
    var DEFAULT_ALLOWED_ATTR = addToSet({}, [].concat(_toConsumableArray$1(html$1), _toConsumableArray$1(svg$1), _toConsumableArray$1(mathMl$1), _toConsumableArray$1(xml)));

    /* Explicitly forbidden tags (overrides ALLOWED_TAGS/ADD_TAGS) */
    var FORBID_TAGS = null;

    /* Explicitly forbidden attributes (overrides ALLOWED_ATTR/ADD_ATTR) */
    var FORBID_ATTR = null;

    /* Decide if ARIA attributes are okay */
    var ALLOW_ARIA_ATTR = true;

    /* Decide if custom data attributes are okay */
    var ALLOW_DATA_ATTR = true;

    /* Decide if unknown protocols are okay */
    var ALLOW_UNKNOWN_PROTOCOLS = false;

    /* Output should be safe for common template engines.
     * This means, DOMPurify removes data attributes, mustaches and ERB
     */
    var SAFE_FOR_TEMPLATES = false;

    /* Decide if document with <html>... should be returned */
    var WHOLE_DOCUMENT = false;

    /* Track whether config is already set on this instance of DOMPurify. */
    var SET_CONFIG = false;

    /* Decide if all elements (e.g. style, script) must be children of
     * document.body. By default, browsers might move them to document.head */
    var FORCE_BODY = false;

    /* Decide if a DOM `HTMLBodyElement` should be returned, instead of a html
     * string (or a TrustedHTML object if Trusted Types are supported).
     * If `WHOLE_DOCUMENT` is enabled a `HTMLHtmlElement` will be returned instead
     */
    var RETURN_DOM = false;

    /* Decide if a DOM `DocumentFragment` should be returned, instead of a html
     * string  (or a TrustedHTML object if Trusted Types are supported) */
    var RETURN_DOM_FRAGMENT = false;

    /* If `RETURN_DOM` or `RETURN_DOM_FRAGMENT` is enabled, decide if the returned DOM
     * `Node` is imported into the current `Document`. If this flag is not enabled the
     * `Node` will belong (its ownerDocument) to a fresh `HTMLDocument`, created by
     * DOMPurify.
     *
     * This defaults to `true` starting DOMPurify 2.2.0. Note that setting it to `false`
     * might cause XSS from attacks hidden in closed shadowroots in case the browser
     * supports Declarative Shadow: DOM https://web.dev/declarative-shadow-dom/
     */
    var RETURN_DOM_IMPORT = true;

    /* Try to return a Trusted Type object instead of a string, return a string in
     * case Trusted Types are not supported  */
    var RETURN_TRUSTED_TYPE = false;

    /* Output should be free from DOM clobbering attacks? */
    var SANITIZE_DOM = true;

    /* Keep element content when removing element? */
    var KEEP_CONTENT = true;

    /* If a `Node` is passed to sanitize(), then performs sanitization in-place instead
     * of importing it into a new Document and returning a sanitized copy */
    var IN_PLACE = false;

    /* Allow usage of profiles like html, svg and mathMl */
    var USE_PROFILES = {};

    /* Tags to ignore content of when KEEP_CONTENT is true */
    var FORBID_CONTENTS = addToSet({}, ['annotation-xml', 'audio', 'colgroup', 'desc', 'foreignobject', 'head', 'iframe', 'math', 'mi', 'mn', 'mo', 'ms', 'mtext', 'noembed', 'noframes', 'noscript', 'plaintext', 'script', 'style', 'svg', 'template', 'thead', 'title', 'video', 'xmp']);

    /* Tags that are safe for data: URIs */
    var DATA_URI_TAGS = null;
    var DEFAULT_DATA_URI_TAGS = addToSet({}, ['audio', 'video', 'img', 'source', 'image', 'track']);

    /* Attributes safe for values like "javascript:" */
    var URI_SAFE_ATTRIBUTES = null;
    var DEFAULT_URI_SAFE_ATTRIBUTES = addToSet({}, ['alt', 'class', 'for', 'id', 'label', 'name', 'pattern', 'placeholder', 'summary', 'title', 'value', 'style', 'xmlns']);

    /* Keep a reference to config to pass to hooks */
    var CONFIG = null;

    /* Ideally, do not touch anything below this line */
    /* ______________________________________________ */

    var formElement = document.createElement('form');

    /**
     * _parseConfig
     *
     * @param  {Object} cfg optional config literal
     */
    // eslint-disable-next-line complexity
    var _parseConfig = function _parseConfig(cfg) {
      if (CONFIG && CONFIG === cfg) {
        return;
      }

      /* Shield configuration object from tampering */
      if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
        cfg = {};
      }

      /* Shield configuration object from prototype pollution */
      cfg = clone(cfg);

      /* Set configuration parameters */
      ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
      ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
      URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
      DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
      FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
      FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
      USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
      ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
      ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
      ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
      SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
      WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
      RETURN_DOM = cfg.RETURN_DOM || false; // Default false
      RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
      RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
      RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
      FORCE_BODY = cfg.FORCE_BODY || false; // Default false
      SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
      KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
      IN_PLACE = cfg.IN_PLACE || false; // Default false
      IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
      if (SAFE_FOR_TEMPLATES) {
        ALLOW_DATA_ATTR = false;
      }

      if (RETURN_DOM_FRAGMENT) {
        RETURN_DOM = true;
      }

      /* Parse profile info */
      if (USE_PROFILES) {
        ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
        ALLOWED_ATTR = [];
        if (USE_PROFILES.html === true) {
          addToSet(ALLOWED_TAGS, html);
          addToSet(ALLOWED_ATTR, html$1);
        }

        if (USE_PROFILES.svg === true) {
          addToSet(ALLOWED_TAGS, svg);
          addToSet(ALLOWED_ATTR, svg$1);
          addToSet(ALLOWED_ATTR, xml);
        }

        if (USE_PROFILES.svgFilters === true) {
          addToSet(ALLOWED_TAGS, svgFilters);
          addToSet(ALLOWED_ATTR, svg$1);
          addToSet(ALLOWED_ATTR, xml);
        }

        if (USE_PROFILES.mathMl === true) {
          addToSet(ALLOWED_TAGS, mathMl);
          addToSet(ALLOWED_ATTR, mathMl$1);
          addToSet(ALLOWED_ATTR, xml);
        }
      }

      /* Merge configuration parameters */
      if (cfg.ADD_TAGS) {
        if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
          ALLOWED_TAGS = clone(ALLOWED_TAGS);
        }

        addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
      }

      if (cfg.ADD_ATTR) {
        if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
          ALLOWED_ATTR = clone(ALLOWED_ATTR);
        }

        addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
      }

      if (cfg.ADD_URI_SAFE_ATTR) {
        addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
      }

      /* Add #text in case KEEP_CONTENT is set to true */
      if (KEEP_CONTENT) {
        ALLOWED_TAGS['#text'] = true;
      }

      /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
      if (WHOLE_DOCUMENT) {
        addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
      }

      /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
      if (ALLOWED_TAGS.table) {
        addToSet(ALLOWED_TAGS, ['tbody']);
        delete FORBID_TAGS.tbody;
      }

      // Prevent further manipulation of configuration.
      // Not available in IE8, Safari 5, etc.
      if (freeze) {
        freeze(cfg);
      }

      CONFIG = cfg;
    };

    var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);

    var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);

    /* Keep track of all possible SVG and MathML tags
     * so that we can perform the namespace checks
     * correctly. */
    var ALL_SVG_TAGS = addToSet({}, svg);
    addToSet(ALL_SVG_TAGS, svgFilters);
    addToSet(ALL_SVG_TAGS, svgDisallowed);

    var ALL_MATHML_TAGS = addToSet({}, mathMl);
    addToSet(ALL_MATHML_TAGS, mathMlDisallowed);

    var MATHML_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
    var SVG_NAMESPACE = 'http://www.w3.org/2000/svg';
    var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';

    /**
     *
     *
     * @param  {Element} element a DOM element whose namespace is being checked
     * @returns {boolean} Return false if the element has a
     *  namespace that a spec-compliant parser would never
     *  return. Return true otherwise.
     */
    var _checkValidNamespace = function _checkValidNamespace(element) {
      var parent = getParentNode(element);

      // In JSDOM, if we're inside shadow DOM, then parentNode
      // can be null. We just simulate parent in this case.
      if (!parent || !parent.tagName) {
        parent = {
          namespaceURI: HTML_NAMESPACE,
          tagName: 'template'
        };
      }

      var tagName = stringToLowerCase(element.tagName);
      var parentTagName = stringToLowerCase(parent.tagName);

      if (element.namespaceURI === SVG_NAMESPACE) {
        // The only way to switch from HTML namespace to SVG
        // is via <svg>. If it happens via any other tag, then
        // it should be killed.
        if (parent.namespaceURI === HTML_NAMESPACE) {
          return tagName === 'svg';
        }

        // The only way to switch from MathML to SVG is via
        // svg if parent is either <annotation-xml> or MathML
        // text integration points.
        if (parent.namespaceURI === MATHML_NAMESPACE) {
          return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
        }

        // We only allow elements that are defined in SVG
        // spec. All others are disallowed in SVG namespace.
        return Boolean(ALL_SVG_TAGS[tagName]);
      }

      if (element.namespaceURI === MATHML_NAMESPACE) {
        // The only way to switch from HTML namespace to MathML
        // is via <math>. If it happens via any other tag, then
        // it should be killed.
        if (parent.namespaceURI === HTML_NAMESPACE) {
          return tagName === 'math';
        }

        // The only way to switch from SVG to MathML is via
        // <math> and HTML integration points
        if (parent.namespaceURI === SVG_NAMESPACE) {
          return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
        }

        // We only allow elements that are defined in MathML
        // spec. All others are disallowed in MathML namespace.
        return Boolean(ALL_MATHML_TAGS[tagName]);
      }

      if (element.namespaceURI === HTML_NAMESPACE) {
        // The only way to switch from SVG to HTML is via
        // HTML integration points, and from MathML to HTML
        // is via MathML text integration points
        if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
          return false;
        }

        if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
          return false;
        }

        // Certain elements are allowed in both SVG and HTML
        // namespace. We need to specify them explicitly
        // so that they don't get erronously deleted from
        // HTML namespace.
        var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);

        // We disallow tags that are specific for MathML
        // or SVG and should never appear in HTML namespace
        return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
      }

      // The code should never reach this place (this means
      // that the element somehow got namespace that is not
      // HTML, SVG or MathML). Return false just in case.
      return false;
    };

    /**
     * _forceRemove
     *
     * @param  {Node} node a DOM node
     */
    var _forceRemove = function _forceRemove(node) {
      arrayPush(DOMPurify.removed, { element: node });
      try {
        node.parentNode.removeChild(node);
      } catch (_) {
        try {
          node.outerHTML = emptyHTML;
        } catch (_) {
          node.remove();
        }
      }
    };

    /**
     * _removeAttribute
     *
     * @param  {String} name an Attribute name
     * @param  {Node} node a DOM node
     */
    var _removeAttribute = function _removeAttribute(name, node) {
      try {
        arrayPush(DOMPurify.removed, {
          attribute: node.getAttributeNode(name),
          from: node
        });
      } catch (_) {
        arrayPush(DOMPurify.removed, {
          attribute: null,
          from: node
        });
      }

      node.removeAttribute(name);

      // We void attribute values for unremovable "is"" attributes
      if (name === 'is' && !ALLOWED_ATTR[name]) {
        if (RETURN_DOM || RETURN_DOM_FRAGMENT) {
          try {
            _forceRemove(node);
          } catch (_) {}
        } else {
          try {
            node.setAttribute(name, '');
          } catch (_) {}
        }
      }
    };

    /**
     * _initDocument
     *
     * @param  {String} dirty a string of dirty markup
     * @return {Document} a DOM, filled with the dirty markup
     */
    var _initDocument = function _initDocument(dirty) {
      /* Create a HTML document */
      var doc = void 0;
      var leadingWhitespace = void 0;

      if (FORCE_BODY) {
        dirty = '<remove></remove>' + dirty;
      } else {
        /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
        var matches = stringMatch(dirty, /^[\r\n\t ]+/);
        leadingWhitespace = matches && matches[0];
      }

      var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
      /* Use the DOMParser API by default, fallback later if needs be */
      try {
        doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
      } catch (_) {}

      /* Use createHTMLDocument in case DOMParser is not available */
      if (!doc || !doc.documentElement) {
        doc = implementation.createHTMLDocument('');
        var _doc = doc,
            body = _doc.body;

        body.parentNode.removeChild(body.parentNode.firstElementChild);
        body.outerHTML = dirtyPayload;
      }

      if (dirty && leadingWhitespace) {
        doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
      }

      /* Work on whole document or just its body */
      return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
    };

    /**
     * _createIterator
     *
     * @param  {Document} root document/fragment to create iterator for
     * @return {Iterator} iterator instance
     */
    var _createIterator = function _createIterator(root) {
      return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
        return NodeFilter.FILTER_ACCEPT;
      }, false);
    };

    /**
     * _isClobbered
     *
     * @param  {Node} elm element to check for clobbering attacks
     * @return {Boolean} true if clobbered, false if safe
     */
    var _isClobbered = function _isClobbered(elm) {
      if (elm instanceof Text || elm instanceof Comment) {
        return false;
      }

      if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') {
        return true;
      }

      return false;
    };

    /**
     * _isNode
     *
     * @param  {Node} obj object to check whether it's a DOM node
     * @return {Boolean} true is object is a DOM node
     */
    var _isNode = function _isNode(object) {
      return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
    };

    /**
     * _executeHook
     * Execute user configurable hooks
     *
     * @param  {String} entryPoint  Name of the hook's entry point
     * @param  {Node} currentNode node to work on with the hook
     * @param  {Object} data additional hook parameters
     */
    var _executeHook = function _executeHook(entryPoint, currentNode, data) {
      if (!hooks[entryPoint]) {
        return;
      }

      arrayForEach(hooks[entryPoint], function (hook) {
        hook.call(DOMPurify, currentNode, data, CONFIG);
      });
    };

    /**
     * _sanitizeElements
     *
     * @protect nodeName
     * @protect textContent
     * @protect removeChild
     *
     * @param   {Node} currentNode to check for permission to exist
     * @return  {Boolean} true if node was killed, false if left alive
     */
    var _sanitizeElements = function _sanitizeElements(currentNode) {
      var content = void 0;

      /* Execute a hook if present */
      _executeHook('beforeSanitizeElements', currentNode, null);

      /* Check if element is clobbered or can clobber */
      if (_isClobbered(currentNode)) {
        _forceRemove(currentNode);
        return true;
      }

      /* Check if tagname contains Unicode */
      if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
        _forceRemove(currentNode);
        return true;
      }

      /* Now let's check the element's type and name */
      var tagName = stringToLowerCase(currentNode.nodeName);

      /* Execute a hook if present */
      _executeHook('uponSanitizeElement', currentNode, {
        tagName: tagName,
        allowedTags: ALLOWED_TAGS
      });

      /* Detect mXSS attempts abusing namespace confusion */
      if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
        _forceRemove(currentNode);
        return true;
      }

      /* Remove element if anything forbids its presence */
      if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
        /* Keep content except for bad-listed elements */
        if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
          var parentNode = getParentNode(currentNode);
          var childNodes = getChildNodes(currentNode);

          if (childNodes && parentNode) {
            var childCount = childNodes.length;

            for (var i = childCount - 1; i >= 0; --i) {
              parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
            }
          }
        }

        _forceRemove(currentNode);
        return true;
      }

      /* Check whether element has a valid namespace */
      if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
        _forceRemove(currentNode);
        return true;
      }

      if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
        _forceRemove(currentNode);
        return true;
      }

      /* Sanitize element content to be template-safe */
      if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
        /* Get the element's text content */
        content = currentNode.textContent;
        content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
        content = stringReplace(content, ERB_EXPR$$1, ' ');
        if (currentNode.textContent !== content) {
          arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
          currentNode.textContent = content;
        }
      }

      /* Execute a hook if present */
      _executeHook('afterSanitizeElements', currentNode, null);

      return false;
    };

    /**
     * _isValidAttribute
     *
     * @param  {string} lcTag Lowercase tag name of containing element.
     * @param  {string} lcName Lowercase attribute name.
     * @param  {string} value Attribute value.
     * @return {Boolean} Returns true if `value` is valid, otherwise false.
     */
    // eslint-disable-next-line complexity
    var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
      /* Make sure attribute cannot clobber */
      if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
        return false;
      }

      /* Allow valid data-* attributes: At least one character after "-"
          (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
          XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
          We don't need to check the value; it's always URI safe. */
      if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
        return false;

        /* Check value is safe. First, is attr inert? If so, is safe */
      } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else {
        return false;
      }

      return true;
    };

    /**
     * _sanitizeAttributes
     *
     * @protect attributes
     * @protect nodeName
     * @protect removeAttribute
     * @protect setAttribute
     *
     * @param  {Node} currentNode to sanitize
     */
    var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
      var attr = void 0;
      var value = void 0;
      var lcName = void 0;
      var l = void 0;
      /* Execute a hook if present */
      _executeHook('beforeSanitizeAttributes', currentNode, null);

      var attributes = currentNode.attributes;

      /* Check if we have attributes; if not we might have a text node */

      if (!attributes) {
        return;
      }

      var hookEvent = {
        attrName: '',
        attrValue: '',
        keepAttr: true,
        allowedAttributes: ALLOWED_ATTR
      };
      l = attributes.length;

      /* Go backwards over all attributes; safely remove bad ones */
      while (l--) {
        attr = attributes[l];
        var _attr = attr,
            name = _attr.name,
            namespaceURI = _attr.namespaceURI;

        value = stringTrim(attr.value);
        lcName = stringToLowerCase(name);

        /* Execute a hook if present */
        hookEvent.attrName = lcName;
        hookEvent.attrValue = value;
        hookEvent.keepAttr = true;
        hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
        _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
        value = hookEvent.attrValue;
        /* Did the hooks approve of the attribute? */
        if (hookEvent.forceKeepAttr) {
          continue;
        }

        /* Remove attribute */
        _removeAttribute(name, currentNode);

        /* Did the hooks approve of the attribute? */
        if (!hookEvent.keepAttr) {
          continue;
        }

        /* Work around a security issue in jQuery 3.0 */
        if (regExpTest(/\/>/i, value)) {
          _removeAttribute(name, currentNode);
          continue;
        }

        /* Sanitize attribute content to be template-safe */
        if (SAFE_FOR_TEMPLATES) {
          value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
          value = stringReplace(value, ERB_EXPR$$1, ' ');
        }

        /* Is `value` valid for this attribute? */
        var lcTag = currentNode.nodeName.toLowerCase();
        if (!_isValidAttribute(lcTag, lcName, value)) {
          continue;
        }

        /* Handle invalid data-* attribute set by try-catching it */
        try {
          if (namespaceURI) {
            currentNode.setAttributeNS(namespaceURI, name, value);
          } else {
            /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
            currentNode.setAttribute(name, value);
          }

          arrayPop(DOMPurify.removed);
        } catch (_) {}
      }

      /* Execute a hook if present */
      _executeHook('afterSanitizeAttributes', currentNode, null);
    };

    /**
     * _sanitizeShadowDOM
     *
     * @param  {DocumentFragment} fragment to iterate over recursively
     */
    var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
      var shadowNode = void 0;
      var shadowIterator = _createIterator(fragment);

      /* Execute a hook if present */
      _executeHook('beforeSanitizeShadowDOM', fragment, null);

      while (shadowNode = shadowIterator.nextNode()) {
        /* Execute a hook if present */
        _executeHook('uponSanitizeShadowNode', shadowNode, null);

        /* Sanitize tags and elements */
        if (_sanitizeElements(shadowNode)) {
          continue;
        }

        /* Deep shadow DOM detected */
        if (shadowNode.content instanceof DocumentFragment) {
          _sanitizeShadowDOM(shadowNode.content);
        }

        /* Check attributes, sanitize if necessary */
        _sanitizeAttributes(shadowNode);
      }

      /* Execute a hook if present */
      _executeHook('afterSanitizeShadowDOM', fragment, null);
    };

    /**
     * Sanitize
     * Public method providing core sanitation functionality
     *
     * @param {String|Node} dirty string or DOM node
     * @param {Object} configuration object
     */
    // eslint-disable-next-line complexity
    DOMPurify.sanitize = function (dirty, cfg) {
      var body = void 0;
      var importedNode = void 0;
      var currentNode = void 0;
      var oldNode = void 0;
      var returnNode = void 0;
      /* Make sure we have a string to sanitize.
        DO NOT return early, as this will return the wrong type if
        the user has requested a DOM object rather than a string */
      if (!dirty) {
        dirty = '<!-->';
      }

      /* Stringify, in case dirty is an object */
      if (typeof dirty !== 'string' && !_isNode(dirty)) {
        // eslint-disable-next-line no-negated-condition
        if (typeof dirty.toString !== 'function') {
          throw typeErrorCreate('toString is not a function');
        } else {
          dirty = dirty.toString();
          if (typeof dirty !== 'string') {
            throw typeErrorCreate('dirty is not a string, aborting');
          }
        }
      }

      /* Check we can run. Otherwise fall back or ignore */
      if (!DOMPurify.isSupported) {
        if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
          if (typeof dirty === 'string') {
            return window.toStaticHTML(dirty);
          }

          if (_isNode(dirty)) {
            return window.toStaticHTML(dirty.outerHTML);
          }
        }

        return dirty;
      }

      /* Assign config vars */
      if (!SET_CONFIG) {
        _parseConfig(cfg);
      }

      /* Clean up removed elements */
      DOMPurify.removed = [];

      /* Check if dirty is correctly typed for IN_PLACE */
      if (typeof dirty === 'string') {
        IN_PLACE = false;
      }

      if (IN_PLACE) ; else if (dirty instanceof Node) {
        /* If dirty is a DOM element, append to an empty document to avoid
           elements being stripped by the parser */
        body = _initDocument('<!---->');
        importedNode = body.ownerDocument.importNode(dirty, true);
        if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
          /* Node is already a body, use as is */
          body = importedNode;
        } else if (importedNode.nodeName === 'HTML') {
          body = importedNode;
        } else {
          // eslint-disable-next-line unicorn/prefer-node-append
          body.appendChild(importedNode);
        }
      } else {
        /* Exit directly if we have nothing to do */
        if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
        // eslint-disable-next-line unicorn/prefer-includes
        dirty.indexOf('<') === -1) {
          return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
        }

        /* Initialize the document to work on */
        body = _initDocument(dirty);

        /* Check we have a DOM node from the data */
        if (!body) {
          return RETURN_DOM ? null : emptyHTML;
        }
      }

      /* Remove first element node (ours) if FORCE_BODY is set */
      if (body && FORCE_BODY) {
        _forceRemove(body.firstChild);
      }

      /* Get node iterator */
      var nodeIterator = _createIterator(IN_PLACE ? dirty : body);

      /* Now start iterating over the created document */
      while (currentNode = nodeIterator.nextNode()) {
        /* Fix IE's strange behavior with manipulated textNodes #89 */
        if (currentNode.nodeType === 3 && currentNode === oldNode) {
          continue;
        }

        /* Sanitize tags and elements */
        if (_sanitizeElements(currentNode)) {
          continue;
        }

        /* Shadow DOM detected, sanitize it */
        if (currentNode.content instanceof DocumentFragment) {
          _sanitizeShadowDOM(currentNode.content);
        }

        /* Check attributes, sanitize if necessary */
        _sanitizeAttributes(currentNode);

        oldNode = currentNode;
      }

      oldNode = null;

      /* If we sanitized `dirty` in-place, return it. */
      if (IN_PLACE) {
        return dirty;
      }

      /* Return sanitized string or DOM */
      if (RETURN_DOM) {
        if (RETURN_DOM_FRAGMENT) {
          returnNode = createDocumentFragment.call(body.ownerDocument);

          while (body.firstChild) {
            // eslint-disable-next-line unicorn/prefer-node-append
            returnNode.appendChild(body.firstChild);
          }
        } else {
          returnNode = body;
        }

        if (RETURN_DOM_IMPORT) {
          /*
            AdoptNode() is not used because internal state is not reset
            (e.g. the past names map of a HTMLFormElement), this is safe
            in theory but we would rather not risk another attack vector.
            The state that is cloned by importNode() is explicitly defined
            by the specs.
          */
          returnNode = importNode.call(originalDocument, returnNode, true);
        }

        return returnNode;
      }

      var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;

      /* Sanitize final string template-safe */
      if (SAFE_FOR_TEMPLATES) {
        serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
        serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
      }

      return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
    };

    /**
     * Public method to set the configuration once
     * setConfig
     *
     * @param {Object} cfg configuration object
     */
    DOMPurify.setConfig = function (cfg) {
      _parseConfig(cfg);
      SET_CONFIG = true;
    };

    /**
     * Public method to remove the configuration
     * clearConfig
     *
     */
    DOMPurify.clearConfig = function () {
      CONFIG = null;
      SET_CONFIG = false;
    };

    /**
     * Public method to check if an attribute value is valid.
     * Uses last set config, if any. Otherwise, uses config defaults.
     * isValidAttribute
     *
     * @param  {string} tag Tag name of containing element.
     * @param  {string} attr Attribute name.
     * @param  {string} value Attribute value.
     * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
     */
    DOMPurify.isValidAttribute = function (tag, attr, value) {
      /* Initialize shared config vars if necessary. */
      if (!CONFIG) {
        _parseConfig({});
      }

      var lcTag = stringToLowerCase(tag);
      var lcName = stringToLowerCase(attr);
      return _isValidAttribute(lcTag, lcName, value);
    };

    /**
     * AddHook
     * Public method to add DOMPurify hooks
     *
     * @param {String} entryPoint entry point for the hook to add
     * @param {Function} hookFunction function to execute
     */
    DOMPurify.addHook = function (entryPoint, hookFunction) {
      if (typeof hookFunction !== 'function') {
        return;
      }

      hooks[entryPoint] = hooks[entryPoint] || [];
      arrayPush(hooks[entryPoint], hookFunction);
    };

    /**
     * RemoveHook
     * Public method to remove a DOMPurify hook at a given entryPoint
     * (pops it from the stack of hooks if more are present)
     *
     * @param {String} entryPoint entry point for the hook to remove
     */
    DOMPurify.removeHook = function (entryPoint) {
      if (hooks[entryPoint]) {
        arrayPop(hooks[entryPoint]);
      }
    };

    /**
     * RemoveHooks
     * Public method to remove all DOMPurify hooks at a given entryPoint
     *
     * @param  {String} entryPoint entry point for the hooks to remove
     */
    DOMPurify.removeHooks = function (entryPoint) {
      if (hooks[entryPoint]) {
        hooks[entryPoint] = [];
      }
    };

    /**
     * RemoveAllHooks
     * Public method to remove all DOMPurify hooks
     *
     */
    DOMPurify.removeAllHooks = function () {
      hooks = {};
    };

    return DOMPurify;
  }

  var purify = createDOMPurify();

  return purify;

}));
//# sourceMappingURL=purify.js.map


/***/ }),

/***/ "./node_modules/events/events.js":
/*!***************************************!*\
  !*** ./node_modules/events/events.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.



var R = typeof Reflect === 'object' ? Reflect : null
var ReflectApply = R && typeof R.apply === 'function'
  ? R.apply
  : function ReflectApply(target, receiver, args) {
    return Function.prototype.apply.call(target, receiver, args);
  }

var ReflectOwnKeys
if (R && typeof R.ownKeys === 'function') {
  ReflectOwnKeys = R.ownKeys
} else if (Object.getOwnPropertySymbols) {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target)
      .concat(Object.getOwnPropertySymbols(target));
  };
} else {
  ReflectOwnKeys = function ReflectOwnKeys(target) {
    return Object.getOwnPropertyNames(target);
  };
}

function ProcessEmitWarning(warning) {
  if (console && console.warn) console.warn(warning);
}

var NumberIsNaN = Number.isNaN || function NumberIsNaN(value) {
  return value !== value;
}

function EventEmitter() {
  EventEmitter.init.call(this);
}
module.exports = EventEmitter;
module.exports.once = once;

// Backwards-compat with node 0.10.x
EventEmitter.EventEmitter = EventEmitter;

EventEmitter.prototype._events = undefined;
EventEmitter.prototype._eventsCount = 0;
EventEmitter.prototype._maxListeners = undefined;

// By default EventEmitters will print a warning if more than 10 listeners are
// added to it. This is a useful default which helps finding memory leaks.
var defaultMaxListeners = 10;

function checkListener(listener) {
  if (typeof listener !== 'function') {
    throw new TypeError('The "listener" argument must be of type Function. Received type ' + typeof listener);
  }
}

Object.defineProperty(EventEmitter, 'defaultMaxListeners', {
  enumerable: true,
  get: function() {
    return defaultMaxListeners;
  },
  set: function(arg) {
    if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
      throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received ' + arg + '.');
    }
    defaultMaxListeners = arg;
  }
});

EventEmitter.init = function() {

  if (this._events === undefined ||
      this._events === Object.getPrototypeOf(this)._events) {
    this._events = Object.create(null);
    this._eventsCount = 0;
  }

  this._maxListeners = this._maxListeners || undefined;
};

// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
  if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
    throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received ' + n + '.');
  }
  this._maxListeners = n;
  return this;
};

function _getMaxListeners(that) {
  if (that._maxListeners === undefined)
    return EventEmitter.defaultMaxListeners;
  return that._maxListeners;
}

EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
  return _getMaxListeners(this);
};

EventEmitter.prototype.emit = function emit(type) {
  var args = [];
  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
  var doError = (type === 'error');

  var events = this._events;
  if (events !== undefined)
    doError = (doError && events.error === undefined);
  else if (!doError)
    return false;

  // If there is no 'error' event listener then throw.
  if (doError) {
    var er;
    if (args.length > 0)
      er = args[0];
    if (er instanceof Error) {
      // Note: The comments on the `throw` lines are intentional, they show
      // up in Node's output if this results in an unhandled exception.
      throw er; // Unhandled 'error' event
    }
    // At least give some kind of context to the user
    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
    err.context = er;
    throw err; // Unhandled 'error' event
  }

  var handler = events[type];

  if (handler === undefined)
    return false;

  if (typeof handler === 'function') {
    ReflectApply(handler, this, args);
  } else {
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      ReflectApply(listeners[i], this, args);
  }

  return true;
};

function _addListener(target, type, listener, prepend) {
  var m;
  var events;
  var existing;

  checkListener(listener);

  events = target._events;
  if (events === undefined) {
    events = target._events = Object.create(null);
    target._eventsCount = 0;
  } else {
    // To avoid recursion in the case that type === "newListener"! Before
    // adding it to the listeners, first emit "newListener".
    if (events.newListener !== undefined) {
      target.emit('newListener', type,
                  listener.listener ? listener.listener : listener);

      // Re-assign `events` because a newListener handler could have caused the
      // this._events to be assigned to a new object
      events = target._events;
    }
    existing = events[type];
  }

  if (existing === undefined) {
    // Optimize the case of one listener. Don't need the extra array object.
    existing = events[type] = listener;
    ++target._eventsCount;
  } else {
    if (typeof existing === 'function') {
      // Adding the second element, need to change to array.
      existing = events[type] =
        prepend ? [listener, existing] : [existing, listener];
      // If we've already got an array, just append.
    } else if (prepend) {
      existing.unshift(listener);
    } else {
      existing.push(listener);
    }

    // Check for listener leak
    m = _getMaxListeners(target);
    if (m > 0 && existing.length > m && !existing.warned) {
      existing.warned = true;
      // No error code for this since it is a Warning
      // eslint-disable-next-line no-restricted-syntax
      var w = new Error('Possible EventEmitter memory leak detected. ' +
                          existing.length + ' ' + String(type) + ' listeners ' +
                          'added. Use emitter.setMaxListeners() to ' +
                          'increase limit');
      w.name = 'MaxListenersExceededWarning';
      w.emitter = target;
      w.type = type;
      w.count = existing.length;
      ProcessEmitWarning(w);
    }
  }

  return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
  return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

EventEmitter.prototype.prependListener =
    function prependListener(type, listener) {
      return _addListener(this, type, listener, true);
    };

function onceWrapper() {
  if (!this.fired) {
    this.target.removeListener(this.type, this.wrapFn);
    this.fired = true;
    if (arguments.length === 0)
      return this.listener.call(this.target);
    return this.listener.apply(this.target, arguments);
  }
}

function _onceWrap(target, type, listener) {
  var state = { fired: false, wrapFn: undefined, target: target, type: type, listener: listener };
  var wrapped = onceWrapper.bind(state);
  wrapped.listener = listener;
  state.wrapFn = wrapped;
  return wrapped;
}

EventEmitter.prototype.once = function once(type, listener) {
  checkListener(listener);
  this.on(type, _onceWrap(this, type, listener));
  return this;
};

EventEmitter.prototype.prependOnceListener =
    function prependOnceListener(type, listener) {
      checkListener(listener);
      this.prependListener(type, _onceWrap(this, type, listener));
      return this;
    };

// Emits a 'removeListener' event if and only if the listener was removed.
EventEmitter.prototype.removeListener =
    function removeListener(type, listener) {
      var list, events, position, i, originalListener;

      checkListener(listener);

      events = this._events;
      if (events === undefined)
        return this;

      list = events[type];
      if (list === undefined)
        return this;

      if (list === listener || list.listener === listener) {
        if (--this._eventsCount === 0)
          this._events = Object.create(null);
        else {
          delete events[type];
          if (events.removeListener)
            this.emit('removeListener', type, list.listener || listener);
        }
      } else if (typeof list !== 'function') {
        position = -1;

        for (i = list.length - 1; i >= 0; i--) {
          if (list[i] === listener || list[i].listener === listener) {
            originalListener = list[i].listener;
            position = i;
            break;
          }
        }

        if (position < 0)
          return this;

        if (position === 0)
          list.shift();
        else {
          spliceOne(list, position);
        }

        if (list.length === 1)
          events[type] = list[0];

        if (events.removeListener !== undefined)
          this.emit('removeListener', type, originalListener || listener);
      }

      return this;
    };

EventEmitter.prototype.off = EventEmitter.prototype.removeListener;

EventEmitter.prototype.removeAllListeners =
    function removeAllListeners(type) {
      var listeners, events, i;

      events = this._events;
      if (events === undefined)
        return this;

      // not listening for removeListener, no need to emit
      if (events.removeListener === undefined) {
        if (arguments.length === 0) {
          this._events = Object.create(null);
          this._eventsCount = 0;
        } else if (events[type] !== undefined) {
          if (--this._eventsCount === 0)
            this._events = Object.create(null);
          else
            delete events[type];
        }
        return this;
      }

      // emit removeListener for all listeners on all events
      if (arguments.length === 0) {
        var keys = Object.keys(events);
        var key;
        for (i = 0; i < keys.length; ++i) {
          key = keys[i];
          if (key === 'removeListener') continue;
          this.removeAllListeners(key);
        }
        this.removeAllListeners('removeListener');
        this._events = Object.create(null);
        this._eventsCount = 0;
        return this;
      }

      listeners = events[type];

      if (typeof listeners === 'function') {
        this.removeListener(type, listeners);
      } else if (listeners !== undefined) {
        // LIFO order
        for (i = listeners.length - 1; i >= 0; i--) {
          this.removeListener(type, listeners[i]);
        }
      }

      return this;
    };

function _listeners(target, type, unwrap) {
  var events = target._events;

  if (events === undefined)
    return [];

  var evlistener = events[type];
  if (evlistener === undefined)
    return [];

  if (typeof evlistener === 'function')
    return unwrap ? [evlistener.listener || evlistener] : [evlistener];

  return unwrap ?
    unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
}

EventEmitter.prototype.listeners = function listeners(type) {
  return _listeners(this, type, true);
};

EventEmitter.prototype.rawListeners = function rawListeners(type) {
  return _listeners(this, type, false);
};

EventEmitter.listenerCount = function(emitter, type) {
  if (typeof emitter.listenerCount === 'function') {
    return emitter.listenerCount(type);
  } else {
    return listenerCount.call(emitter, type);
  }
};

EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
  var events = this._events;

  if (events !== undefined) {
    var evlistener = events[type];

    if (typeof evlistener === 'function') {
      return 1;
    } else if (evlistener !== undefined) {
      return evlistener.length;
    }
  }

  return 0;
}

EventEmitter.prototype.eventNames = function eventNames() {
  return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};

function arrayClone(arr, n) {
  var copy = new Array(n);
  for (var i = 0; i < n; ++i)
    copy[i] = arr[i];
  return copy;
}

function spliceOne(list, index) {
  for (; index + 1 < list.length; index++)
    list[index] = list[index + 1];
  list.pop();
}

function unwrapListeners(arr) {
  var ret = new Array(arr.length);
  for (var i = 0; i < ret.length; ++i) {
    ret[i] = arr[i].listener || arr[i];
  }
  return ret;
}

function once(emitter, name) {
  return new Promise(function (resolve, reject) {
    function errorListener(err) {
      emitter.removeListener(name, resolver);
      reject(err);
    }

    function resolver() {
      if (typeof emitter.removeListener === 'function') {
        emitter.removeListener('error', errorListener);
      }
      resolve([].slice.call(arguments));
    };

    eventTargetAgnosticAddListener(emitter, name, resolver, { once: true });
    if (name !== 'error') {
      addErrorHandlerIfEventEmitter(emitter, errorListener, { once: true });
    }
  });
}

function addErrorHandlerIfEventEmitter(emitter, handler, flags) {
  if (typeof emitter.on === 'function') {
    eventTargetAgnosticAddListener(emitter, 'error', handler, flags);
  }
}

function eventTargetAgnosticAddListener(emitter, name, listener, flags) {
  if (typeof emitter.on === 'function') {
    if (flags.once) {
      emitter.once(name, listener);
    } else {
      emitter.on(name, listener);
    }
  } else if (typeof emitter.addEventListener === 'function') {
    // EventTarget does not have `error` event semantics like Node
    // EventEmitters, we do not listen for `error` events here.
    emitter.addEventListener(name, function wrapListener(arg) {
      // IE does not have builtin `{ once: true }` support so we
      // have to do it manually.
      if (flags.once) {
        emitter.removeEventListener(name, wrapListener);
      }
      listener(arg);
    });
  } else {
    throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type ' + typeof emitter);
  }
}


/***/ }),

/***/ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js":
/*!**************************************************************************************!*\
  !*** ./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js ***!
  \**************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/* WEBPACK VAR INJECTION */(function(global) {(function(r){function x(){}function y(){}var z=String.fromCharCode,v={}.toString,A=v.call(r.SharedArrayBuffer),B=v(),q=r.Uint8Array,t=q||Array,w=q?ArrayBuffer:t,C=w.isView||function(g){return g&&"length"in g},D=v.call(w.prototype);w=y.prototype;var E=r.TextEncoder,a=new (q?Uint16Array:t)(32);x.prototype.decode=function(g){if(!C(g)){var l=v.call(g);if(l!==D&&l!==A&&l!==B)throw TypeError("Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'");
g=q?new t(g):g||[]}for(var f=l="",b=0,c=g.length|0,u=c-32|0,e,d,h=0,p=0,m,k=0,n=-1;b<c;){for(e=b<=u?32:c-b|0;k<e;b=b+1|0,k=k+1|0){d=g[b]&255;switch(d>>4){case 15:m=g[b=b+1|0]&255;if(2!==m>>6||247<d){b=b-1|0;break}h=(d&7)<<6|m&63;p=5;d=256;case 14:m=g[b=b+1|0]&255,h<<=6,h|=(d&15)<<6|m&63,p=2===m>>6?p+4|0:24,d=d+256&768;case 13:case 12:m=g[b=b+1|0]&255,h<<=6,h|=(d&31)<<6|m&63,p=p+7|0,b<c&&2===m>>6&&h>>p&&1114112>h?(d=h,h=h-65536|0,0<=h&&(n=(h>>10)+55296|0,d=(h&1023)+56320|0,31>k?(a[k]=n,k=k+1|0,n=-1):
(m=n,n=d,d=m))):(d>>=8,b=b-d-1|0,d=65533),h=p=0,e=b<=u?32:c-b|0;default:a[k]=d;continue;case 11:case 10:case 9:case 8:}a[k]=65533}f+=z(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8],a[9],a[10],a[11],a[12],a[13],a[14],a[15],a[16],a[17],a[18],a[19],a[20],a[21],a[22],a[23],a[24],a[25],a[26],a[27],a[28],a[29],a[30],a[31]);32>k&&(f=f.slice(0,k-32|0));if(b<c){if(a[0]=n,k=~n>>>31,n=-1,f.length<l.length)continue}else-1!==n&&(f+=z(n));l+=f;f=""}return l};w.encode=function(g){g=void 0===g?"":""+g;var l=g.length|
0,f=new t((l<<1)+8|0),b,c=0,u=!q;for(b=0;b<l;b=b+1|0,c=c+1|0){var e=g.charCodeAt(b)|0;if(127>=e)f[c]=e;else{if(2047>=e)f[c]=192|e>>6;else{a:{if(55296<=e)if(56319>=e){var d=g.charCodeAt(b=b+1|0)|0;if(56320<=d&&57343>=d){e=(e<<10)+d-56613888|0;if(65535<e){f[c]=240|e>>18;f[c=c+1|0]=128|e>>12&63;f[c=c+1|0]=128|e>>6&63;f[c=c+1|0]=128|e&63;continue}break a}e=65533}else 57343>=e&&(e=65533);!u&&b<<1<c&&b<<1<(c-7|0)&&(u=!0,d=new t(3*l),d.set(f),f=d)}f[c]=224|e>>12;f[c=c+1|0]=128|e>>6&63}f[c=c+1|0]=128|e&63}}return q?
f.subarray(0,c):f.slice(0,c)};E||(r.TextDecoder=x,r.TextEncoder=y)})(""+void 0==typeof global?""+void 0==typeof self?this:self:global);//AnonyCo
//# sourceMappingURL=https://cdn.jsdelivr.net/gh/AnonyCo/FastestSmallestTextEncoderDecoder/EncoderDecoderTogether.min.js.map

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/format-message-formats/index.js":
/*!******************************************************!*\
  !*** ./node_modules/format-message-formats/index.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// @flow
var LONG = 'long'
var SHORT = 'short'
var NARROW = 'narrow'
var NUMERIC = 'numeric'
var TWODIGIT = '2-digit'

/**
 * formatting information
 **/
module.exports = {
  number: {
    decimal: {
      style: 'decimal'
    },
    integer: {
      style: 'decimal',
      maximumFractionDigits: 0
    },
    currency: {
      style: 'currency',
      currency: 'USD'
    },
    percent: {
      style: 'percent'
    },
    default: {
      style: 'decimal'
    }
  },
  date: {
    short: {
      month: NUMERIC,
      day: NUMERIC,
      year: TWODIGIT
    },
    medium: {
      month: SHORT,
      day: NUMERIC,
      year: NUMERIC
    },
    long: {
      month: LONG,
      day: NUMERIC,
      year: NUMERIC
    },
    full: {
      month: LONG,
      day: NUMERIC,
      year: NUMERIC,
      weekday: LONG
    },
    default: {
      month: SHORT,
      day: NUMERIC,
      year: NUMERIC
    }
  },
  time: {
    short: {
      hour: NUMERIC,
      minute: NUMERIC
    },
    medium: {
      hour: NUMERIC,
      minute: NUMERIC,
      second: NUMERIC
    },
    long: {
      hour: NUMERIC,
      minute: NUMERIC,
      second: NUMERIC,
      timeZoneName: SHORT
    },
    full: {
      hour: NUMERIC,
      minute: NUMERIC,
      second: NUMERIC,
      timeZoneName: SHORT
    },
    default: {
      hour: NUMERIC,
      minute: NUMERIC,
      second: NUMERIC
    }
  },
  duration: {
    default: {
      hours: {
        minimumIntegerDigits: 1,
        maximumFractionDigits: 0
      },
      minutes: {
        minimumIntegerDigits: 2,
        maximumFractionDigits: 0
      },
      seconds: {
        minimumIntegerDigits: 2,
        maximumFractionDigits: 3
      }
    }
  },
  parseNumberPattern: function (pattern/*: ?string */) {
    if (!pattern) return
    var options = {}
    var currency = pattern.match(/\b[A-Z]{3}\b/i)
    var syms = pattern.replace(/[^¤]/g, '').length
    if (!syms && currency) syms = 1
    if (syms) {
      options.style = 'currency'
      options.currencyDisplay = syms === 1 ? 'symbol' : syms === 2 ? 'code' : 'name'
      options.currency = currency ? currency[0].toUpperCase() : 'USD'
    } else if (pattern.indexOf('%') >= 0) {
      options.style = 'percent'
    }
    if (!/[@#0]/.test(pattern)) return options.style ? options : undefined
    options.useGrouping = pattern.indexOf(',') >= 0
    if (/E\+?[@#0]+/i.test(pattern) || pattern.indexOf('@') >= 0) {
      var size = pattern.replace(/E\+?[@#0]+|[^@#0]/gi, '')
      options.minimumSignificantDigits = Math.min(Math.max(size.replace(/[^@0]/g, '').length, 1), 21)
      options.maximumSignificantDigits = Math.min(Math.max(size.length, 1), 21)
    } else {
      var parts = pattern.replace(/[^#0.]/g, '').split('.')
      var integer = parts[0]
      var n = integer.length - 1
      while (integer[n] === '0') --n
      options.minimumIntegerDigits = Math.min(Math.max(integer.length - 1 - n, 1), 21)
      var fraction = parts[1] || ''
      n = 0
      while (fraction[n] === '0') ++n
      options.minimumFractionDigits = Math.min(Math.max(n, 0), 20)
      while (fraction[n] === '#') ++n
      options.maximumFractionDigits = Math.min(Math.max(n, 0), 20)
    }
    return options
  },
  parseDatePattern: function (pattern/*: ?string */) {
    if (!pattern) return
    var options = {}
    for (var i = 0; i < pattern.length;) {
      var current = pattern[i]
      var n = 1
      while (pattern[++i] === current) ++n
      switch (current) {
        case 'G':
          options.era = n === 5 ? NARROW : n === 4 ? LONG : SHORT
          break
        case 'y':
        case 'Y':
          options.year = n === 2 ? TWODIGIT : NUMERIC
          break
        case 'M':
        case 'L':
          n = Math.min(Math.max(n - 1, 0), 4)
          options.month = [NUMERIC, TWODIGIT, SHORT, LONG, NARROW][n]
          break
        case 'E':
        case 'e':
        case 'c':
          options.weekday = n === 5 ? NARROW : n === 4 ? LONG : SHORT
          break
        case 'd':
        case 'D':
          options.day = n === 2 ? TWODIGIT : NUMERIC
          break
        case 'h':
        case 'K':
          options.hour12 = true
          options.hour = n === 2 ? TWODIGIT : NUMERIC
          break
        case 'H':
        case 'k':
          options.hour12 = false
          options.hour = n === 2 ? TWODIGIT : NUMERIC
          break
        case 'm':
          options.minute = n === 2 ? TWODIGIT : NUMERIC
          break
        case 's':
        case 'S':
          options.second = n === 2 ? TWODIGIT : NUMERIC
          break
        case 'z':
        case 'Z':
        case 'v':
        case 'V':
          options.timeZoneName = n === 1 ? SHORT : LONG
          break
      }
    }
    return Object.keys(options).length ? options : undefined
  }
}


/***/ }),

/***/ "./node_modules/format-message-interpret/index.js":
/*!********************************************************!*\
  !*** ./node_modules/format-message-interpret/index.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// @flow

var formats = __webpack_require__(/*! format-message-formats */ "./node_modules/format-message-formats/index.js")
var lookupClosestLocale = __webpack_require__(/*! lookup-closest-locale */ "./node_modules/lookup-closest-locale/index.js")
var plurals = __webpack_require__(/*! ./plurals */ "./node_modules/format-message-interpret/plurals.js")

/*::
import type {
  AST,
  SubMessages
} from '../format-message-parse'
type Locale = string
type Locales = Locale | Locale[]
type Placeholder = any[] // https://github.com/facebook/flow/issues/4050
export type Type = (Placeholder, Locales) => (any, ?Object) => any
export type Types = { [string]: Type }
*/

exports = module.exports = function interpret (
  ast/*: AST */,
  locale/*:: ?: Locales */,
  types/*:: ?: Types */
)/*: (args?: Object) => string */ {
  return interpretAST(ast, null, locale || 'en', types || {}, true)
}

exports.toParts = function toParts (
  ast/*: AST */,
  locale/*:: ?: Locales */,
  types/*:: ?: Types */
)/*: (args?: Object) => any[] */ {
  return interpretAST(ast, null, locale || 'en', types || {}, false)
}

function interpretAST (
  elements/*: any[] */,
  parent/*: ?Placeholder */,
  locale/*: Locales */,
  types/*: Types */,
  join/*: boolean */
)/*: Function */ {
  var parts = elements.map(function (element) {
    return interpretElement(element, parent, locale, types, join)
  })

  if (!join) {
    return function format (args) {
      return parts.reduce(function (parts, part) {
        return parts.concat(part(args))
      }, [])
    }
  }

  if (parts.length === 1) return parts[0]
  return function format (args) {
    var message = ''
    for (var e = 0; e < parts.length; ++e) {
      message += parts[e](args)
    }
    return message
  }
}

function interpretElement (
  element/*: Placeholder */,
  parent/*: ?Placeholder */,
  locale/*: Locales */,
  types/*: Types */,
  join/*: boolean */
)/*: Function */ {
  if (typeof element === 'string') {
    var value/*: string */ = element
    return function format () { return value }
  }

  var id = element[0]
  var type = element[1]

  if (parent && element[0] === '#') {
    id = parent[0]
    var offset = parent[2]
    var formatter = (types.number || defaults.number)([id, 'number'], locale)
    return function format (args) {
      return formatter(getArg(id, args) - offset, args)
    }
  }

  // pre-process children
  var children
  if (type === 'plural' || type === 'selectordinal') {
    children = {}
    Object.keys(element[3]).forEach(function (key) {
      children[key] = interpretAST(element[3][key], element, locale, types, join)
    })
    element = [element[0], element[1], element[2], children]
  } else if (element[2] && typeof element[2] === 'object') {
    children = {}
    Object.keys(element[2]).forEach(function (key) {
      children[key] = interpretAST(element[2][key], element, locale, types, join)
    })
    element = [element[0], element[1], children]
  }

  var getFrmt = type && (types[type] || defaults[type])
  if (getFrmt) {
    var frmt = getFrmt(element, locale)
    return function format (args) {
      return frmt(getArg(id, args), args)
    }
  }

  return join
    ? function format (args) { return String(getArg(id, args)) }
    : function format (args) { return getArg(id, args) }
}

function getArg (id/*: string */, args/*: ?Object */)/*: any */ {
  if (args && (id in args)) return args[id]
  var parts = id.split('.')
  var a = args
  for (var i = 0, ii = parts.length; a && i < ii; ++i) {
    a = a[parts[i]]
  }
  return a
}

function interpretNumber (element/*: Placeholder */, locales/*: Locales */) {
  var style = element[2]
  var options = formats.number[style] || formats.parseNumberPattern(style) || formats.number.default
  return new Intl.NumberFormat(locales, options).format
}

function interpretDuration (element/*: Placeholder */, locales/*: Locales */) {
  var style = element[2]
  var options = formats.duration[style] || formats.duration.default
  var fs = new Intl.NumberFormat(locales, options.seconds).format
  var fm = new Intl.NumberFormat(locales, options.minutes).format
  var fh = new Intl.NumberFormat(locales, options.hours).format
  var sep = /^fi$|^fi-|^da/.test(String(locales)) ? '.' : ':'

  return function (s, args) {
    s = +s
    if (!isFinite(s)) return fs(s)
    var h = ~~(s / 60 / 60) // ~~ acts much like Math.trunc
    var m = ~~(s / 60 % 60)
    var dur = (h ? (fh(Math.abs(h)) + sep) : '') +
      fm(Math.abs(m)) + sep + fs(Math.abs(s % 60))
    return s < 0 ? fh(-1).replace(fh(1), dur) : dur
  }
}

function interpretDateTime (element/*: Placeholder */, locales/*: Locales */) {
  var type = element[1]
  var style = element[2]
  var options = formats[type][style] || formats.parseDatePattern(style) || formats[type].default
  return new Intl.DateTimeFormat(locales, options).format
}

function interpretPlural (element/*: Placeholder */, locales/*: Locales */) {
  var type = element[1]
  var pluralType = type === 'selectordinal' ? 'ordinal' : 'cardinal'
  var offset = element[2]
  var children = element[3]
  var pluralRules
  if (Intl.PluralRules && Intl.PluralRules.supportedLocalesOf(locales).length > 0) {
    pluralRules = new Intl.PluralRules(locales, { type: pluralType })
  } else {
    var locale = lookupClosestLocale(locales, plurals)
    var select = (locale && plurals[locale][pluralType]) || returnOther
    pluralRules = { select: select }
  }

  return function (value, args) {
    var clause =
      children['=' + +value] ||
      children[pluralRules.select(value - offset)] ||
      children.other
    return clause(args)
  }
}

function returnOther (/*:: n:number */) { return 'other' }

function interpretSelect (element/*: Placeholder */, locales/*: Locales */) {
  var children = element[2]
  return function (value, args) {
    var clause = children[value] || children.other
    return clause(args)
  }
}

var defaults/*: Types */ = {
  number: interpretNumber,
  ordinal: interpretNumber, // TODO: support rbnf
  spellout: interpretNumber, // TODO: support rbnf
  duration: interpretDuration,
  date: interpretDateTime,
  time: interpretDateTime,
  plural: interpretPlural,
  selectordinal: interpretPlural,
  select: interpretSelect
}
exports.types = defaults


/***/ }),

/***/ "./node_modules/format-message-interpret/plurals.js":
/*!**********************************************************!*\
  !*** ./node_modules/format-message-interpret/plurals.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// @flow


/*:: export type Rule = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other' */
var zero = 'zero', one = 'one', two = 'two', few = 'few', many = 'many', other = 'other'
var f = [
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return 0 <= n && n <= 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var n = +s
    return i === 0 || n === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 0 ? zero
      : n === 1 ? one
      : n === 2 ? two
      : 3 <= n % 100 && n % 100 <= 10 ? few
      : 11 <= n % 100 && n % 100 <= 99 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return i === 1 && v === 0 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n % 10 === 1 && n % 100 !== 11 ? one
      : (2 <= n % 10 && n % 10 <= 4) && (n % 100 < 12 || 14 < n % 100) ? few
      : n % 10 === 0 || (5 <= n % 10 && n % 10 <= 9) || (11 <= n % 100 && n % 100 <= 14) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n % 10 === 1 && (n % 100 !== 11 && n % 100 !== 71 && n % 100 !== 91) ? one
      : n % 10 === 2 && (n % 100 !== 12 && n % 100 !== 72 && n % 100 !== 92) ? two
      : ((3 <= n % 10 && n % 10 <= 4) || n % 10 === 9) && ((n % 100 < 10 || 19 < n % 100) && (n % 100 < 70 || 79 < n % 100) && (n % 100 < 90 || 99 < n % 100)) ? few
      : n !== 0 && n % 1000000 === 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var f = +(s + '.').split('.')[1]
    return v === 0 && i % 10 === 1 && i % 100 !== 11 || f % 10 === 1 && f % 100 !== 11 ? one
      : v === 0 && (2 <= i % 10 && i % 10 <= 4) && (i % 100 < 12 || 14 < i % 100) || (2 <= f % 10 && f % 10 <= 4) && (f % 100 < 12 || 14 < f % 100) ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return i === 1 && v === 0 ? one
      : (2 <= i && i <= 4) && v === 0 ? few
      : v !== 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 0 ? zero
      : n === 1 ? one
      : n === 2 ? two
      : n === 3 ? few
      : n === 6 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var t = +('' + s).replace(/^[^.]*.?|0+$/g, '')
    var n = +s
    return n === 1 || t !== 0 && (i === 0 || i === 1) ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var f = +(s + '.').split('.')[1]
    return v === 0 && i % 100 === 1 || f % 100 === 1 ? one
      : v === 0 && i % 100 === 2 || f % 100 === 2 ? two
      : v === 0 && (3 <= i % 100 && i % 100 <= 4) || (3 <= f % 100 && f % 100 <= 4) ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    return i === 0 || i === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var f = +(s + '.').split('.')[1]
    return v === 0 && (i === 1 || i === 2 || i === 3) || v === 0 && (i % 10 !== 4 && i % 10 !== 6 && i % 10 !== 9) || v !== 0 && (f % 10 !== 4 && f % 10 !== 6 && f % 10 !== 9) ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n === 2 ? two
      : 3 <= n && n <= 6 ? few
      : 7 <= n && n <= 10 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 || n === 11 ? one
      : n === 2 || n === 12 ? two
      : ((3 <= n && n <= 10) || (13 <= n && n <= 19)) ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return v === 0 && i % 10 === 1 ? one
      : v === 0 && i % 10 === 2 ? two
      : v === 0 && (i % 100 === 0 || i % 100 === 20 || i % 100 === 40 || i % 100 === 60 || i % 100 === 80) ? few
      : v !== 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var n = +s
    return i === 1 && v === 0 ? one
      : i === 2 && v === 0 ? two
      : v === 0 && (n < 0 || 10 < n) && n % 10 === 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var t = +('' + s).replace(/^[^.]*.?|0+$/g, '')
    return t === 0 && i % 10 === 1 && i % 100 !== 11 || t !== 0 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n === 2 ? two
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 0 ? zero
      : n === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var n = +s
    return n === 0 ? zero
      : (i === 0 || i === 1) && n !== 0 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var f = +(s + '.').split('.')[1]
    var n = +s
    return n % 10 === 1 && (n % 100 < 11 || 19 < n % 100) ? one
      : (2 <= n % 10 && n % 10 <= 9) && (n % 100 < 11 || 19 < n % 100) ? few
      : f !== 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var v = (s + '.').split('.')[1].length
    var f = +(s + '.').split('.')[1]
    var n = +s
    return n % 10 === 0 || (11 <= n % 100 && n % 100 <= 19) || v === 2 && (11 <= f % 100 && f % 100 <= 19) ? zero
      : n % 10 === 1 && n % 100 !== 11 || v === 2 && f % 10 === 1 && f % 100 !== 11 || v !== 2 && f % 10 === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var f = +(s + '.').split('.')[1]
    return v === 0 && i % 10 === 1 && i % 100 !== 11 || f % 10 === 1 && f % 100 !== 11 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    var n = +s
    return i === 1 && v === 0 ? one
      : v !== 0 || n === 0 || n !== 1 && (1 <= n % 100 && n % 100 <= 19) ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n === 0 || (2 <= n % 100 && n % 100 <= 10) ? few
      : 11 <= n % 100 && n % 100 <= 19 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return i === 1 && v === 0 ? one
      : v === 0 && (2 <= i % 10 && i % 10 <= 4) && (i % 100 < 12 || 14 < i % 100) ? few
      : v === 0 && i !== 1 && (0 <= i % 10 && i % 10 <= 1) || v === 0 && (5 <= i % 10 && i % 10 <= 9) || v === 0 && (12 <= i % 100 && i % 100 <= 14) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    return 0 <= i && i <= 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return v === 0 && i % 10 === 1 && i % 100 !== 11 ? one
      : v === 0 && (2 <= i % 10 && i % 10 <= 4) && (i % 100 < 12 || 14 < i % 100) ? few
      : v === 0 && i % 10 === 0 || v === 0 && (5 <= i % 10 && i % 10 <= 9) || v === 0 && (11 <= i % 100 && i % 100 <= 14) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var n = +s
    return i === 0 || n === 1 ? one
      : 2 <= n && n <= 10 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var f = +(s + '.').split('.')[1]
    var n = +s
    return (n === 0 || n === 1) || i === 0 && f === 1 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    var v = (s + '.').split('.')[1].length
    return v === 0 && i % 100 === 1 ? one
      : v === 0 && i % 100 === 2 ? two
      : v === 0 && (3 <= i % 100 && i % 100 <= 4) || v !== 0 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return (0 <= n && n <= 1) || (11 <= n && n <= 99) ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 || n === 5 || n === 7 || n === 8 || n === 9 || n === 10 ? one
      : n === 2 || n === 3 ? two
      : n === 4 ? few
      : n === 6 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    return (i % 10 === 1 || i % 10 === 2 || i % 10 === 5 || i % 10 === 7 || i % 10 === 8) || (i % 100 === 20 || i % 100 === 50 || i % 100 === 70 || i % 100 === 80) ? one
      : (i % 10 === 3 || i % 10 === 4) || (i % 1000 === 100 || i % 1000 === 200 || i % 1000 === 300 || i % 1000 === 400 || i % 1000 === 500 || i % 1000 === 600 || i % 1000 === 700 || i % 1000 === 800 || i % 1000 === 900) ? few
      : i === 0 || i % 10 === 6 || (i % 100 === 40 || i % 100 === 60 || i % 100 === 90) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return (n % 10 === 2 || n % 10 === 3) && (n % 100 !== 12 && n % 100 !== 13) ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 || n === 3 ? one
      : n === 2 ? two
      : n === 4 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 0 || n === 7 || n === 8 || n === 9 ? zero
      : n === 1 ? one
      : n === 2 ? two
      : n === 3 || n === 4 ? few
      : n === 5 || n === 6 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n % 10 === 1 && n % 100 !== 11 ? one
      : n % 10 === 2 && n % 100 !== 12 ? two
      : n % 10 === 3 && n % 100 !== 13 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 || n === 11 ? one
      : n === 2 || n === 12 ? two
      : n === 3 || n === 13 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n === 2 || n === 3 ? two
      : n === 4 ? few
      : n === 6 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 || n === 5 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 11 || n === 8 || n === 80 || n === 800 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    return i === 1 ? one
      : i === 0 || ((2 <= i % 100 && i % 100 <= 20) || i % 100 === 40 || i % 100 === 60 || i % 100 === 80) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n % 10 === 6 || n % 10 === 9 || n % 10 === 0 && n !== 0 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var i = Math.floor(Math.abs(+s))
    return i % 10 === 1 && i % 100 !== 11 ? one
      : i % 10 === 2 && i % 100 !== 12 ? two
      : (i % 10 === 7 || i % 10 === 8) && (i % 100 !== 17 && i % 100 !== 18) ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n === 2 || n === 3 ? two
      : n === 4 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return 1 <= n && n <= 4 ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return (n === 1 || n === 5 || (7 <= n && n <= 9)) ? one
      : n === 2 || n === 3 ? two
      : n === 4 ? few
      : n === 6 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n === 1 ? one
      : n % 10 === 4 && n % 100 !== 14 ? many
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return (n % 10 === 1 || n % 10 === 2) && (n % 100 !== 11 && n % 100 !== 12) ? one
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return (n % 10 === 6 || n % 10 === 9) || n === 10 ? few
      : other
  },
  function (s/*: string | number */)/*: Rule */ {
    var n = +s
    return n % 10 === 3 && n % 100 !== 13 ? few
      : other
  }
]

module.exports = {
  af: { cardinal: f[0] },
  ak: { cardinal: f[1] },
  am: { cardinal: f[2] },
  ar: { cardinal: f[3] },
  ars: { cardinal: f[3] },
  as: { cardinal: f[2], ordinal: f[34] },
  asa: { cardinal: f[0] },
  ast: { cardinal: f[4] },
  az: { cardinal: f[0], ordinal: f[35] },
  be: { cardinal: f[5], ordinal: f[36] },
  bem: { cardinal: f[0] },
  bez: { cardinal: f[0] },
  bg: { cardinal: f[0] },
  bh: { cardinal: f[1] },
  bn: { cardinal: f[2], ordinal: f[34] },
  br: { cardinal: f[6] },
  brx: { cardinal: f[0] },
  bs: { cardinal: f[7] },
  ca: { cardinal: f[4], ordinal: f[37] },
  ce: { cardinal: f[0] },
  cgg: { cardinal: f[0] },
  chr: { cardinal: f[0] },
  ckb: { cardinal: f[0] },
  cs: { cardinal: f[8] },
  cy: { cardinal: f[9], ordinal: f[38] },
  da: { cardinal: f[10] },
  de: { cardinal: f[4] },
  dsb: { cardinal: f[11] },
  dv: { cardinal: f[0] },
  ee: { cardinal: f[0] },
  el: { cardinal: f[0] },
  en: { cardinal: f[4], ordinal: f[39] },
  eo: { cardinal: f[0] },
  es: { cardinal: f[0] },
  et: { cardinal: f[4] },
  eu: { cardinal: f[0] },
  fa: { cardinal: f[2] },
  ff: { cardinal: f[12] },
  fi: { cardinal: f[4] },
  fil: { cardinal: f[13], ordinal: f[0] },
  fo: { cardinal: f[0] },
  fr: { cardinal: f[12], ordinal: f[0] },
  fur: { cardinal: f[0] },
  fy: { cardinal: f[4] },
  ga: { cardinal: f[14], ordinal: f[0] },
  gd: { cardinal: f[15], ordinal: f[40] },
  gl: { cardinal: f[4] },
  gsw: { cardinal: f[0] },
  gu: { cardinal: f[2], ordinal: f[41] },
  guw: { cardinal: f[1] },
  gv: { cardinal: f[16] },
  ha: { cardinal: f[0] },
  haw: { cardinal: f[0] },
  he: { cardinal: f[17] },
  hi: { cardinal: f[2], ordinal: f[41] },
  hr: { cardinal: f[7] },
  hsb: { cardinal: f[11] },
  hu: { cardinal: f[0], ordinal: f[42] },
  hy: { cardinal: f[12], ordinal: f[0] },
  ia: { cardinal: f[4] },
  io: { cardinal: f[4] },
  is: { cardinal: f[18] },
  it: { cardinal: f[4], ordinal: f[43] },
  iu: { cardinal: f[19] },
  iw: { cardinal: f[17] },
  jgo: { cardinal: f[0] },
  ji: { cardinal: f[4] },
  jmc: { cardinal: f[0] },
  ka: { cardinal: f[0], ordinal: f[44] },
  kab: { cardinal: f[12] },
  kaj: { cardinal: f[0] },
  kcg: { cardinal: f[0] },
  kk: { cardinal: f[0], ordinal: f[45] },
  kkj: { cardinal: f[0] },
  kl: { cardinal: f[0] },
  kn: { cardinal: f[2] },
  ks: { cardinal: f[0] },
  ksb: { cardinal: f[0] },
  ksh: { cardinal: f[20] },
  ku: { cardinal: f[0] },
  kw: { cardinal: f[19] },
  ky: { cardinal: f[0] },
  lag: { cardinal: f[21] },
  lb: { cardinal: f[0] },
  lg: { cardinal: f[0] },
  ln: { cardinal: f[1] },
  lt: { cardinal: f[22] },
  lv: { cardinal: f[23] },
  mas: { cardinal: f[0] },
  mg: { cardinal: f[1] },
  mgo: { cardinal: f[0] },
  mk: { cardinal: f[24], ordinal: f[46] },
  ml: { cardinal: f[0] },
  mn: { cardinal: f[0] },
  mo: { cardinal: f[25], ordinal: f[0] },
  mr: { cardinal: f[2], ordinal: f[47] },
  mt: { cardinal: f[26] },
  nah: { cardinal: f[0] },
  naq: { cardinal: f[19] },
  nb: { cardinal: f[0] },
  nd: { cardinal: f[0] },
  ne: { cardinal: f[0], ordinal: f[48] },
  nl: { cardinal: f[4] },
  nn: { cardinal: f[0] },
  nnh: { cardinal: f[0] },
  no: { cardinal: f[0] },
  nr: { cardinal: f[0] },
  nso: { cardinal: f[1] },
  ny: { cardinal: f[0] },
  nyn: { cardinal: f[0] },
  om: { cardinal: f[0] },
  or: { cardinal: f[0], ordinal: f[49] },
  os: { cardinal: f[0] },
  pa: { cardinal: f[1] },
  pap: { cardinal: f[0] },
  pl: { cardinal: f[27] },
  prg: { cardinal: f[23] },
  ps: { cardinal: f[0] },
  pt: { cardinal: f[28] },
  'pt-PT': { cardinal: f[4] },
  rm: { cardinal: f[0] },
  ro: { cardinal: f[25], ordinal: f[0] },
  rof: { cardinal: f[0] },
  ru: { cardinal: f[29] },
  rwk: { cardinal: f[0] },
  saq: { cardinal: f[0] },
  sc: { cardinal: f[4], ordinal: f[43] },
  scn: { cardinal: f[4], ordinal: f[43] },
  sd: { cardinal: f[0] },
  sdh: { cardinal: f[0] },
  se: { cardinal: f[19] },
  seh: { cardinal: f[0] },
  sh: { cardinal: f[7] },
  shi: { cardinal: f[30] },
  si: { cardinal: f[31] },
  sk: { cardinal: f[8] },
  sl: { cardinal: f[32] },
  sma: { cardinal: f[19] },
  smi: { cardinal: f[19] },
  smj: { cardinal: f[19] },
  smn: { cardinal: f[19] },
  sms: { cardinal: f[19] },
  sn: { cardinal: f[0] },
  so: { cardinal: f[0] },
  sq: { cardinal: f[0], ordinal: f[50] },
  sr: { cardinal: f[7] },
  ss: { cardinal: f[0] },
  ssy: { cardinal: f[0] },
  st: { cardinal: f[0] },
  sv: { cardinal: f[4], ordinal: f[51] },
  sw: { cardinal: f[4] },
  syr: { cardinal: f[0] },
  ta: { cardinal: f[0] },
  te: { cardinal: f[0] },
  teo: { cardinal: f[0] },
  ti: { cardinal: f[1] },
  tig: { cardinal: f[0] },
  tk: { cardinal: f[0], ordinal: f[52] },
  tl: { cardinal: f[13], ordinal: f[0] },
  tn: { cardinal: f[0] },
  tr: { cardinal: f[0] },
  ts: { cardinal: f[0] },
  tzm: { cardinal: f[33] },
  ug: { cardinal: f[0] },
  uk: { cardinal: f[29], ordinal: f[53] },
  ur: { cardinal: f[4] },
  uz: { cardinal: f[0] },
  ve: { cardinal: f[0] },
  vo: { cardinal: f[0] },
  vun: { cardinal: f[0] },
  wa: { cardinal: f[1] },
  wae: { cardinal: f[0] },
  xh: { cardinal: f[0] },
  xog: { cardinal: f[0] },
  yi: { cardinal: f[4] },
  zu: { cardinal: f[2] },
  lo: { ordinal: f[0] },
  ms: { ordinal: f[0] },
  vi: { ordinal: f[0] }
}


/***/ }),

/***/ "./node_modules/format-message-parse/index.js":
/*!****************************************************!*\
  !*** ./node_modules/format-message-parse/index.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// @flow


/*::
export type AST = Element[]
export type Element = string | Placeholder
export type Placeholder = Plural | Styled | Typed | Simple
export type Plural = [ string, 'plural' | 'selectordinal', number, SubMessages ]
export type Styled = [ string, string, string | SubMessages ]
export type Typed = [ string, string ]
export type Simple = [ string ]
export type SubMessages = { [string]: AST }
export type Token = [ TokenType, string ]
export type TokenType = 'text' | 'space' | 'id' | 'type' | 'style' | 'offset' | 'number' | 'selector' | 'syntax'
type Context = {|
  pattern: string,
  index: number,
  tagsType: ?string,
  tokens: ?Token[]
|}
*/

var ARG_OPN = '{'
var ARG_CLS = '}'
var ARG_SEP = ','
var NUM_ARG = '#'
var TAG_OPN = '<'
var TAG_CLS = '>'
var TAG_END = '</'
var TAG_SELF_CLS = '/>'
var ESC = '\''
var OFFSET = 'offset:'
var simpleTypes = [
  'number',
  'date',
  'time',
  'ordinal',
  'duration',
  'spellout'
]
var submTypes = [
  'plural',
  'select',
  'selectordinal'
]

/**
 * parse
 *
 * Turns this:
 *  `You have { numBananas, plural,
 *       =0 {no bananas}
 *      one {a banana}
 *    other {# bananas}
 *  } for sale`
 *
 * into this:
 *  [ "You have ", [ "numBananas", "plural", 0, {
 *       "=0": [ "no bananas" ],
 *      "one": [ "a banana" ],
 *    "other": [ [ '#' ], " bananas" ]
 *  } ], " for sale." ]
 *
 * tokens:
 *  [
 *    [ "text", "You have " ],
 *    [ "syntax", "{" ],
 *    [ "space", " " ],
 *    [ "id", "numBananas" ],
 *    [ "syntax", ", " ],
 *    [ "space", " " ],
 *    [ "type", "plural" ],
 *    [ "syntax", "," ],
 *    [ "space", "\n     " ],
 *    [ "selector", "=0" ],
 *    [ "space", " " ],
 *    [ "syntax", "{" ],
 *    [ "text", "no bananas" ],
 *    [ "syntax", "}" ],
 *    [ "space", "\n    " ],
 *    [ "selector", "one" ],
 *    [ "space", " " ],
 *    [ "syntax", "{" ],
 *    [ "text", "a banana" ],
 *    [ "syntax", "}" ],
 *    [ "space", "\n  " ],
 *    [ "selector", "other" ],
 *    [ "space", " " ],
 *    [ "syntax", "{" ],
 *    [ "syntax", "#" ],
 *    [ "text", " bananas" ],
 *    [ "syntax", "}" ],
 *    [ "space", "\n" ],
 *    [ "syntax", "}" ],
 *    [ "text", " for sale." ]
 *  ]
 **/
exports = module.exports = function parse (
  pattern/*: string */,
  options/*:: ?: { tagsType?: string, tokens?: Token[] } */
)/*: AST */ {
  return parseAST({
    pattern: String(pattern),
    index: 0,
    tagsType: (options && options.tagsType) || null,
    tokens: (options && options.tokens) || null
  }, '')
}

function parseAST (current/*: Context */, parentType/*: string */)/*: AST */ {
  var pattern = current.pattern
  var length = pattern.length
  var elements/*: AST */ = []
  var start = current.index
  var text = parseText(current, parentType)
  if (text) elements.push(text)
  if (text && current.tokens) current.tokens.push(['text', pattern.slice(start, current.index)])
  while (current.index < length) {
    if (pattern[current.index] === ARG_CLS) {
      if (!parentType) throw expected(current)
      break
    }
    if (parentType && current.tagsType && pattern.slice(current.index, current.index + TAG_END.length) === TAG_END) break
    elements.push(parsePlaceholder(current))
    start = current.index
    text = parseText(current, parentType)
    if (text) elements.push(text)
    if (text && current.tokens) current.tokens.push(['text', pattern.slice(start, current.index)])
  }
  return elements
}

function parseText (current/*: Context */, parentType/*: string */)/*: string */ {
  var pattern = current.pattern
  var length = pattern.length
  var isHashSpecial = (parentType === 'plural' || parentType === 'selectordinal')
  var isAngleSpecial = !!current.tagsType
  var isArgStyle = (parentType === '{style}')
  var text = ''
  while (current.index < length) {
    var char = pattern[current.index]
    if (
      char === ARG_OPN || char === ARG_CLS ||
      (isHashSpecial && char === NUM_ARG) ||
      (isAngleSpecial && char === TAG_OPN) ||
      (isArgStyle && isWhitespace(char.charCodeAt(0)))
    ) {
      break
    } else if (char === ESC) {
      char = pattern[++current.index]
      if (char === ESC) { // double is always 1 '
        text += char
        ++current.index
      } else if (
        // only when necessary
        char === ARG_OPN || char === ARG_CLS ||
        (isHashSpecial && char === NUM_ARG) ||
        (isAngleSpecial && char === TAG_OPN) ||
        isArgStyle
      ) {
        text += char
        while (++current.index < length) {
          char = pattern[current.index]
          if (char === ESC && pattern[current.index + 1] === ESC) { // double is always 1 '
            text += ESC
            ++current.index
          } else if (char === ESC) { // end of quoted
            ++current.index
            break
          } else {
            text += char
          }
        }
      } else { // lone ' is just a '
        text += ESC
        // already incremented
      }
    } else {
      text += char
      ++current.index
    }
  }
  return text
}

function isWhitespace (code/*: number */)/*: boolean */ {
  return (
    (code >= 0x09 && code <= 0x0D) ||
    code === 0x20 || code === 0x85 || code === 0xA0 || code === 0x180E ||
    (code >= 0x2000 && code <= 0x200D) ||
    code === 0x2028 || code === 0x2029 || code === 0x202F || code === 0x205F ||
    code === 0x2060 || code === 0x3000 || code === 0xFEFF
  )
}

function skipWhitespace (current/*: Context */)/*: void */ {
  var pattern = current.pattern
  var length = pattern.length
  var start = current.index
  while (current.index < length && isWhitespace(pattern.charCodeAt(current.index))) {
    ++current.index
  }
  if (start < current.index && current.tokens) {
    current.tokens.push(['space', current.pattern.slice(start, current.index)])
  }
}

function parsePlaceholder (current/*: Context */)/*: Placeholder */ {
  var pattern = current.pattern
  if (pattern[current.index] === NUM_ARG) {
    if (current.tokens) current.tokens.push(['syntax', NUM_ARG])
    ++current.index // move passed #
    return [NUM_ARG]
  }

  var tag = parseTag(current)
  if (tag) return tag

  /* istanbul ignore if should be unreachable if parseAST and parseText are right */
  if (pattern[current.index] !== ARG_OPN) throw expected(current, ARG_OPN)
  if (current.tokens) current.tokens.push(['syntax', ARG_OPN])
  ++current.index // move passed {
  skipWhitespace(current)

  var id = parseId(current)
  if (!id) throw expected(current, 'placeholder id')
  if (current.tokens) current.tokens.push(['id', id])
  skipWhitespace(current)

  var char = pattern[current.index]
  if (char === ARG_CLS) { // end placeholder
    if (current.tokens) current.tokens.push(['syntax', ARG_CLS])
    ++current.index // move passed }
    return [id]
  }

  if (char !== ARG_SEP) throw expected(current, ARG_SEP + ' or ' + ARG_CLS)
  if (current.tokens) current.tokens.push(['syntax', ARG_SEP])
  ++current.index // move passed ,
  skipWhitespace(current)

  var type = parseId(current)
  if (!type) throw expected(current, 'placeholder type')
  if (current.tokens) current.tokens.push(['type', type])
  skipWhitespace(current)
  char = pattern[current.index]
  if (char === ARG_CLS) { // end placeholder
    if (current.tokens) current.tokens.push(['syntax', ARG_CLS])
    if (type === 'plural' || type === 'selectordinal' || type === 'select') {
      throw expected(current, type + ' sub-messages')
    }
    ++current.index // move passed }
    return [id, type]
  }

  if (char !== ARG_SEP) throw expected(current, ARG_SEP + ' or ' + ARG_CLS)
  if (current.tokens) current.tokens.push(['syntax', ARG_SEP])
  ++current.index // move passed ,
  skipWhitespace(current)

  var arg
  if (type === 'plural' || type === 'selectordinal') {
    var offset = parsePluralOffset(current)
    skipWhitespace(current)
    arg = [id, type, offset, parseSubMessages(current, type)]
  } else if (type === 'select') {
    arg = [id, type, parseSubMessages(current, type)]
  } else if (simpleTypes.indexOf(type) >= 0) {
    arg = [id, type, parseSimpleFormat(current)]
  } else { // custom placeholder type
    var index = current.index
    var format/*: string | SubMessages */ = parseSimpleFormat(current)
    skipWhitespace(current)
    if (pattern[current.index] === ARG_OPN) {
      current.index = index // rewind, since should have been submessages
      format = parseSubMessages(current, type)
    }
    arg = [id, type, format]
  }

  skipWhitespace(current)
  if (pattern[current.index] !== ARG_CLS) throw expected(current, ARG_CLS)
  if (current.tokens) current.tokens.push(['syntax', ARG_CLS])
  ++current.index // move passed }
  return arg
}

function parseTag (current/*: Context */)/*: ?Placeholder */ {
  var tagsType = current.tagsType
  if (!tagsType || current.pattern[current.index] !== TAG_OPN) return

  if (current.pattern.slice(current.index, current.index + TAG_END.length) === TAG_END) {
    throw expected(current, null, 'closing tag without matching opening tag')
  }
  if (current.tokens) current.tokens.push(['syntax', TAG_OPN])
  ++current.index // move passed <

  var id = parseId(current, true)
  if (!id) throw expected(current, 'placeholder id')
  if (current.tokens) current.tokens.push(['id', id])
  skipWhitespace(current)

  if (current.pattern.slice(current.index, current.index + TAG_SELF_CLS.length) === TAG_SELF_CLS) {
    if (current.tokens) current.tokens.push(['syntax', TAG_SELF_CLS])
    current.index += TAG_SELF_CLS.length
    return [id, tagsType]
  }
  if (current.pattern[current.index] !== TAG_CLS) throw expected(current, TAG_CLS)
  if (current.tokens) current.tokens.push(['syntax', TAG_CLS])
  ++current.index // move passed >

  var children = parseAST(current, tagsType)

  var end = current.index
  if (current.pattern.slice(current.index, current.index + TAG_END.length) !== TAG_END) throw expected(current, TAG_END + id + TAG_CLS)
  if (current.tokens) current.tokens.push(['syntax', TAG_END])
  current.index += TAG_END.length
  var closeId = parseId(current, true)
  if (closeId && current.tokens) current.tokens.push(['id', closeId])
  if (id !== closeId) {
    current.index = end // rewind for better error message
    throw expected(current, TAG_END + id + TAG_CLS, TAG_END + closeId + TAG_CLS)
  }
  skipWhitespace(current)
  if (current.pattern[current.index] !== TAG_CLS) throw expected(current, TAG_CLS)
  if (current.tokens) current.tokens.push(['syntax', TAG_CLS])
  ++current.index // move passed >

  return [id, tagsType, { children: children }]
}

function parseId (current/*: Context */, isTag/*:: ?: boolean */)/*: string */ {
  var pattern = current.pattern
  var length = pattern.length
  var id = ''
  while (current.index < length) {
    var char = pattern[current.index]
    if (
      char === ARG_OPN || char === ARG_CLS || char === ARG_SEP ||
      char === NUM_ARG || char === ESC || isWhitespace(char.charCodeAt(0)) ||
      (isTag && (char === TAG_OPN || char === TAG_CLS || char === '/'))
    ) break
    id += char
    ++current.index
  }
  return id
}

function parseSimpleFormat (current/*: Context */)/*: string */ {
  var start = current.index
  var style = parseText(current, '{style}')
  if (!style) throw expected(current, 'placeholder style name')
  if (current.tokens) current.tokens.push(['style', current.pattern.slice(start, current.index)])
  return style
}

function parsePluralOffset (current/*: Context */)/*: number */ {
  var pattern = current.pattern
  var length = pattern.length
  var offset = 0
  if (pattern.slice(current.index, current.index + OFFSET.length) === OFFSET) {
    if (current.tokens) current.tokens.push(['offset', 'offset'], ['syntax', ':'])
    current.index += OFFSET.length // move passed offset:
    skipWhitespace(current)
    var start = current.index
    while (current.index < length && isDigit(pattern.charCodeAt(current.index))) {
      ++current.index
    }
    if (start === current.index) throw expected(current, 'offset number')
    if (current.tokens) current.tokens.push(['number', pattern.slice(start, current.index)])
    offset = +pattern.slice(start, current.index)
  }
  return offset
}

function isDigit (code/*: number */)/*: boolean */ {
  return (code >= 0x30 && code <= 0x39)
}

function parseSubMessages (current/*: Context */, parentType/*: string */)/*: SubMessages */ {
  var pattern = current.pattern
  var length = pattern.length
  var options/*: SubMessages */ = {}
  while (current.index < length && pattern[current.index] !== ARG_CLS) {
    var selector = parseId(current)
    if (!selector) throw expected(current, 'sub-message selector')
    if (current.tokens) current.tokens.push(['selector', selector])
    skipWhitespace(current)
    options[selector] = parseSubMessage(current, parentType)
    skipWhitespace(current)
  }
  if (!options.other && submTypes.indexOf(parentType) >= 0) {
    throw expected(current, null, null, '"other" sub-message must be specified in ' + parentType)
  }
  return options
}

function parseSubMessage (current/*: Context */, parentType/*: string */)/*: AST */ {
  if (current.pattern[current.index] !== ARG_OPN) throw expected(current, ARG_OPN + ' to start sub-message')
  if (current.tokens) current.tokens.push(['syntax', ARG_OPN])
  ++current.index // move passed {
  var message = parseAST(current, parentType)
  if (current.pattern[current.index] !== ARG_CLS) throw expected(current, ARG_CLS + ' to end sub-message')
  if (current.tokens) current.tokens.push(['syntax', ARG_CLS])
  ++current.index // move passed }
  return message
}

function expected (current/*: Context */, expected/*:: ?: ?string */, found/*:: ?: ?string */, message/*:: ?: string */) {
  var pattern = current.pattern
  var lines = pattern.slice(0, current.index).split(/\r?\n/)
  var offset = current.index
  var line = lines.length
  var column = lines.slice(-1)[0].length
  found = found || (
    (current.index >= pattern.length) ? 'end of message pattern'
      : (parseId(current) || pattern[current.index])
  )
  if (!message) message = errorMessage(expected, found)
  message += ' in ' + pattern.replace(/\r?\n/g, '\n')
  return new SyntaxError(message, expected, found, offset, line, column)
}

function errorMessage (expected/*: ?string */, found/* string */) {
  if (!expected) return 'Unexpected ' + found + ' found'
  return 'Expected ' + expected + ' but found ' + found
}

/**
 * SyntaxError
 *  Holds information about bad syntax found in a message pattern
 **/
function SyntaxError (message/*: string */, expected/*: ?string */, found/*: ?string */, offset/*: number */, line/*: number */, column/*: number */) {
  Error.call(this, message)
  this.name = 'SyntaxError'
  this.message = message
  this.expected = expected
  this.found = found
  this.offset = offset
  this.line = line
  this.column = column
}
SyntaxError.prototype = Object.create(Error.prototype)
exports.SyntaxError = SyntaxError


/***/ }),

/***/ "./node_modules/format-message/index.js":
/*!**********************************************!*\
  !*** ./node_modules/format-message/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
// @flow

var parse = __webpack_require__(/*! format-message-parse */ "./node_modules/format-message-parse/index.js")
var interpret = __webpack_require__(/*! format-message-interpret */ "./node_modules/format-message-interpret/index.js")
var plurals = __webpack_require__(/*! format-message-interpret/plurals */ "./node_modules/format-message-interpret/plurals.js")
var lookupClosestLocale = __webpack_require__(/*! lookup-closest-locale */ "./node_modules/lookup-closest-locale/index.js")
var origFormats = __webpack_require__(/*! format-message-formats */ "./node_modules/format-message-formats/index.js")

/*::
import type { Types } from 'format-message-interpret'
type Locale = string
type Locales = Locale | Locale[]
type Message = string | {|
  id?: string,
  default: string,
  description?: string
|}
type Translations = { [string]: ?{ [string]: string | Translation } }
type Translation = {
  message: string,
  format?: (args?: Object) => string,
  toParts?: (args?: Object) => any[],
}
type Replacement = ?string | (string, string, locales?: Locales) => ?string
type GenerateId = (string) => string
type MissingTranslation = 'ignore' | 'warning' | 'error'
type FormatObject = { [string]: * }
type Options = {
  locale?: Locales,
  translations?: ?Translations,
  generateId?: GenerateId,
  missingReplacement?: Replacement,
  missingTranslation?: MissingTranslation,
  formats?: {
    number?: FormatObject,
    date?: FormatObject,
    time?: FormatObject
  },
  types?: Types
}
type Setup = {|
  locale: Locales,
  translations: Translations,
  generateId: GenerateId,
  missingReplacement: Replacement,
  missingTranslation: MissingTranslation,
  formats: {
    number: FormatObject,
    date: FormatObject,
    time: FormatObject
  },
  types: Types
|}
type FormatMessage = {
  (msg: Message, args?: Object, locales?: Locales): string,
  rich (msg: Message, args?: Object, locales?: Locales): any[],
  setup (opt?: Options): Setup,
  number (value: number, style?: string, locales?: Locales): string,
  date (value: number | Date, style?: string, locales?: Locales): string,
  time (value: number | Date, style?: string, locales?: Locales): string,
  select (value: any, options: Object): any,
  custom (placeholder: any[], locales: Locales, value: any, args: Object): any,
  plural (value: number, offset: any, options: any, locale: any): any,
  selectordinal (value: number, offset: any, options: any, locale: any): any,
  namespace (): FormatMessage
}
*/

function assign/*:: <T: Object> */ (target/*: T */, source/*: Object */) {
  Object.keys(source).forEach(function (key) { target[key] = source[key] })
  return target
}

function namespace ()/*: FormatMessage */ {
  var formats = assign({}, origFormats)
  var currentLocales/*: Locales */ = 'en'
  var translations/*: Translations */ = {}
  var generateId/*: GenerateId */ = function (pattern) { return pattern }
  var missingReplacement/*: Replacement */ = null
  var missingTranslation/*: MissingTranslation */ = 'warning'
  var types/*: Types */ = {}

  function formatMessage (msg/*: Message */, args/*:: ?: Object */, locales/*:: ?: Locales */) {
    var pattern = typeof msg === 'string' ? msg : msg.default
    var id = (typeof msg === 'object' && msg.id) || generateId(pattern)
    var translated = translate(pattern, id, locales || currentLocales)
    var format = translated.format || (
      translated.format = interpret(parse(translated.message), locales || currentLocales, types)
    )
    return format(args)
  }

  formatMessage.rich = function rich (msg/*: Message */, args/*:: ?: Object */, locales/*:: ?: Locales */) {
    var pattern = typeof msg === 'string' ? msg : msg.default
    var id = (typeof msg === 'object' && msg.id) || generateId(pattern)
    var translated = translate(pattern, id, locales || currentLocales)
    var format = translated.toParts || (
      translated.toParts = interpret.toParts(parse(translated.message, { tagsType: tagsType }), locales || currentLocales, types)
    )
    return format(args)
  }

  var tagsType = '<>'
  function richType (node/*: any[] */, locales/*: Locales */) {
    var style = node[2]
    return function (fn, args) {
      var props = typeof style === 'object' ? mapObject(style, args) : style
      return typeof fn === 'function' ? fn(props) : fn
    }
  }
  types[tagsType] = richType

  function mapObject (object/* { [string]: (args?: Object) => any } */, args/*: ?Object */) {
    return Object.keys(object).reduce(function (mapped, key) {
      mapped[key] = object[key](args)
      return mapped
    }, {})
  }

  function translate (pattern/*: string */, id/*: string */, locales/*: Locales */)/*: Translation */ {
    var locale = lookupClosestLocale(locales, translations) || 'en'
    var messages = translations[locale] || (translations[locale] = {})
    var translated = messages[id]
    if (typeof translated === 'string') {
      translated = messages[id] = { message: translated }
    }
    if (!translated) {
      var message = 'Translation for "' + id + '" in "' + locale + '" is missing'
      if (missingTranslation === 'warning') {
        /* istanbul ignore else */
        if (typeof console !== 'undefined') console.warn(message)
      } else if (missingTranslation !== 'ignore') { // 'error'
        throw new Error(message)
      }
      var replacement = typeof missingReplacement === 'function'
        ? missingReplacement(pattern, id, locale) || pattern
        : missingReplacement || pattern
      translated = messages[id] = { message: replacement }
    }
    return translated
  }

  formatMessage.setup = function setup (opt/*:: ?: Options */) {
    opt = opt || {}
    if (opt.locale) currentLocales = opt.locale
    if ('translations' in opt) translations = opt.translations || {}
    if (opt.generateId) generateId = opt.generateId
    if ('missingReplacement' in opt) missingReplacement = opt.missingReplacement
    if (opt.missingTranslation) missingTranslation = opt.missingTranslation
    if (opt.formats) {
      if (opt.formats.number) assign(formats.number, opt.formats.number)
      if (opt.formats.date) assign(formats.date, opt.formats.date)
      if (opt.formats.time) assign(formats.time, opt.formats.time)
    }
    if (opt.types) {
      types = opt.types
      types[tagsType] = richType
    }
    return {
      locale: currentLocales,
      translations: translations,
      generateId: generateId,
      missingReplacement: missingReplacement,
      missingTranslation: missingTranslation,
      formats: formats,
      types: types
    }
  }

  formatMessage.number = function (value/*: number */, style/*:: ?: string */, locales/*:: ?: Locales */) {
    var options = (style && formats.number[style]) ||
      formats.parseNumberPattern(style) ||
      formats.number.default
    return new Intl.NumberFormat(locales || currentLocales, options).format(value)
  }

  formatMessage.date = function (value/*:: ?: number | Date */, style/*:: ?: string */, locales/*:: ?: Locales */) {
    var options = (style && formats.date[style]) ||
      formats.parseDatePattern(style) ||
      formats.date.default
    return new Intl.DateTimeFormat(locales || currentLocales, options).format(value)
  }

  formatMessage.time = function (value/*:: ?: number | Date */, style/*:: ?: string */, locales/*:: ?: Locales */) {
    var options = (style && formats.time[style]) ||
      formats.parseDatePattern(style) ||
      formats.time.default
    return new Intl.DateTimeFormat(locales || currentLocales, options).format(value)
  }

  formatMessage.select = function (value/*: any */, options/*: Object */) {
    return options[value] || options.other
  }

  formatMessage.custom = function (placeholder/*: any[] */, locales/*: Locales */, value/*: any */, args/*: Object */) {
    if (!(placeholder[1] in types)) return value
    return types[placeholder[1]](placeholder, locales)(value, args)
  }

  formatMessage.plural = plural.bind(null, 'cardinal')
  formatMessage.selectordinal = plural.bind(null, 'ordinal')
  function plural (
    pluralType/*: 'cardinal' | 'ordinal' */,
    value/*: number */,
    offset/*: any */,
    options/*: any */,
    locale/*: any */
  ) {
    if (typeof offset === 'object' && typeof options !== 'object') { // offset is optional
      locale = options
      options = offset
      offset = 0
    }
    var closest = lookupClosestLocale(locale || currentLocales, plurals)
    var plural = (closest && plurals[closest][pluralType]) || returnOther
    return options['=' + +value] || options[plural(value - offset)] || options.other
  }
  function returnOther (/*:: n:number */) { return 'other' }

  formatMessage.namespace = namespace

  return formatMessage
}

module.exports = exports = namespace()


/***/ }),

/***/ "./node_modules/global/window.js":
/*!***************************************!*\
  !*** ./node_modules/global/window.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {var win;

if (typeof window !== "undefined") {
    win = window;
} else if (typeof global !== "undefined") {
    win = global;
} else if (typeof self !== "undefined"){
    win = self;
} else {
    win = {};
}

module.exports = win;

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/grapheme-breaker/src/classes.json":
/*!********************************************************!*\
  !*** ./node_modules/grapheme-breaker/src/classes.json ***!
  \********************************************************/
/*! exports provided: Other, CR, LF, Control, Extend, Regional_Indicator, SpacingMark, L, V, T, LV, LVT, default */
/***/ (function(module) {

module.exports = JSON.parse("{\"Other\":0,\"CR\":1,\"LF\":2,\"Control\":3,\"Extend\":4,\"Regional_Indicator\":5,\"SpacingMark\":6,\"L\":7,\"V\":8,\"T\":9,\"LV\":10,\"LVT\":11}");

/***/ }),

/***/ "./node_modules/hull.js/src/convex.js":
/*!********************************************!*\
  !*** ./node_modules/hull.js/src/convex.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

function _cross(o, a, b) {
    return (a[0] - o[0]) * (b[1] - o[1]) - (a[1] - o[1]) * (b[0] - o[0]);
}

function _upperTangent(pointset) {
    var lower = [];
    for (var l = 0; l < pointset.length; l++) {
        while (lower.length >= 2 && (_cross(lower[lower.length - 2], lower[lower.length - 1], pointset[l]) <= 0)) {
            lower.pop();
        }
        lower.push(pointset[l]);
    }
    lower.pop();
    return lower;
}

function _lowerTangent(pointset) {
    var reversed = pointset.reverse(),
        upper = [];
    for (var u = 0; u < reversed.length; u++) {
        while (upper.length >= 2 && (_cross(upper[upper.length - 2], upper[upper.length - 1], reversed[u]) <= 0)) {
            upper.pop();
        }
        upper.push(reversed[u]);
    }
    upper.pop();
    return upper;
}

// pointset has to be sorted by X
function convex(pointset) {
    var convex,
        upper = _upperTangent(pointset),
        lower = _lowerTangent(pointset);
    convex = lower.concat(upper);
    convex.push(pointset[0]);  
    return convex;  
}

module.exports = convex;


/***/ }),

/***/ "./node_modules/hull.js/src/format.js":
/*!********************************************!*\
  !*** ./node_modules/hull.js/src/format.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = {

    toXy: function(pointset, format) {
        if (format === undefined) {
            return pointset.slice();
        }
        return pointset.map(function(pt) {
            /*jslint evil: true */
            var _getXY = new Function('pt', 'return [pt' + format[0] + ',' + 'pt' + format[1] + '];');
            return _getXY(pt);
        });
    },

    fromXy: function(pointset, format) {
        if (format === undefined) {
            return pointset.slice();
        }
        return pointset.map(function(pt) {
            /*jslint evil: true */
            var _getObj = new Function('pt', 'var o = {}; o' + format[0] + '= pt[0]; o' + format[1] + '= pt[1]; return o;');
            return _getObj(pt);
        });
    }

}

/***/ }),

/***/ "./node_modules/hull.js/src/grid.js":
/*!******************************************!*\
  !*** ./node_modules/hull.js/src/grid.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports) {

function Grid(points, cellSize) {
    this._cells = [];
    this._cellSize = cellSize;

    points.forEach(function(point) {
        var cellXY = this.point2CellXY(point),
            x = cellXY[0],
            y = cellXY[1];
        if (this._cells[x] === undefined) {
            this._cells[x] = [];
        }
        if (this._cells[x][y] === undefined) {
            this._cells[x][y] = [];
        }
        this._cells[x][y].push(point);
    }, this);
}

Grid.prototype = {
    cellPoints: function(x, y) { // (Number, Number) -> Array
        return (this._cells[x] !== undefined && this._cells[x][y] !== undefined) ? this._cells[x][y] : [];
    },

    rangePoints: function(bbox) { // (Array) -> Array
        var tlCellXY = this.point2CellXY([bbox[0], bbox[1]]),
            brCellXY = this.point2CellXY([bbox[2], bbox[3]]),
            points = [];

        for (var x = tlCellXY[0]; x <= brCellXY[0]; x++) {
            for (var y = tlCellXY[1]; y <= brCellXY[1]; y++) {
                points = points.concat(this.cellPoints(x, y));
            }
        }

        return points;
    },

    removePoint: function(point) { // (Array) -> Array
        var cellXY = this.point2CellXY(point),
            cell = this._cells[cellXY[0]][cellXY[1]],
            pointIdxInCell;
        
        for (var i = 0; i < cell.length; i++) {
            if (cell[i][0] === point[0] && cell[i][1] === point[1]) {
                pointIdxInCell = i;
                break;
            }
        }

        cell.splice(pointIdxInCell, 1);

        return cell;
    },

    point2CellXY: function(point) { // (Array) -> Array
        var x = parseInt(point[0] / this._cellSize),
            y = parseInt(point[1] / this._cellSize);
        return [x, y];
    },

    extendBbox: function(bbox, scaleFactor) { // (Array, Number) -> Array
        return [
            bbox[0] - (scaleFactor * this._cellSize),
            bbox[1] - (scaleFactor * this._cellSize),
            bbox[2] + (scaleFactor * this._cellSize),
            bbox[3] + (scaleFactor * this._cellSize)
        ];
    }
};

function grid(points, cellSize) {
    return new Grid(points, cellSize);
}

module.exports = grid;

/***/ }),

/***/ "./node_modules/hull.js/src/hull.js":
/*!******************************************!*\
  !*** ./node_modules/hull.js/src/hull.js ***!
  \******************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";
/*
 (c) 2014-2016, Andrii Heonia
 Hull.js, a JavaScript library for concave hull generation by set of points.
 https://github.com/AndriiHeonia/hull
*/



var intersect = __webpack_require__(/*! ./intersect.js */ "./node_modules/hull.js/src/intersect.js");
var grid = __webpack_require__(/*! ./grid.js */ "./node_modules/hull.js/src/grid.js");
var formatUtil = __webpack_require__(/*! ./format.js */ "./node_modules/hull.js/src/format.js");
var convexHull = __webpack_require__(/*! ./convex.js */ "./node_modules/hull.js/src/convex.js");

function _filterDuplicates(pointset) {
    return pointset.filter(function(el, idx, arr) {
        var prevEl = arr[idx - 1];
        return idx === 0 || !(prevEl[0] === el[0] && prevEl[1] === el[1]);
    });
}

function _sortByX(pointset) {
    return pointset.sort(function(a, b) {
        if (a[0] == b[0]) {
            return a[1] - b[1];
        } else {
            return a[0] - b[0];
        }
    });
}

function _sqLength(a, b) {
    return Math.pow(b[0] - a[0], 2) + Math.pow(b[1] - a[1], 2);
}

function _cos(o, a, b) {
    var aShifted = [a[0] - o[0], a[1] - o[1]],
        bShifted = [b[0] - o[0], b[1] - o[1]],
        sqALen = _sqLength(o, a),
        sqBLen = _sqLength(o, b),
        dot = aShifted[0] * bShifted[0] + aShifted[1] * bShifted[1];

    return dot / Math.sqrt(sqALen * sqBLen);
}

function _intersect(segment, pointset) {
    for (var i = 0; i < pointset.length - 1; i++) {
        var seg = [pointset[i], pointset[i + 1]];
        if (segment[0][0] === seg[0][0] && segment[0][1] === seg[0][1] ||
            segment[0][0] === seg[1][0] && segment[0][1] === seg[1][1]) {
            continue;
        }
        if (intersect(segment, seg)) {
            return true;
        }
    }
    return false;
}

function _occupiedArea(pointset) {
    var minX = Infinity,
        minY = Infinity,
        maxX = -Infinity,
        maxY = -Infinity;

    for (var i = pointset.length - 1; i >= 0; i--) {
        if (pointset[i][0] < minX) {
            minX = pointset[i][0];
        }
        if (pointset[i][1] < minY) {
            minY = pointset[i][1];
        }
        if (pointset[i][0] > maxX) {
            maxX = pointset[i][0];
        }
        if (pointset[i][1] > maxY) {
            maxY = pointset[i][1];
        }
    }

    return [
        maxX - minX, // width
        maxY - minY  // height
    ];
}

function _bBoxAround(edge) {
    return [
        Math.min(edge[0][0], edge[1][0]), // left
        Math.min(edge[0][1], edge[1][1]), // top
        Math.max(edge[0][0], edge[1][0]), // right
        Math.max(edge[0][1], edge[1][1])  // bottom
    ];
}

function _midPoint(edge, innerPoints, convex) {
    var point = null,
        angle1Cos = MAX_CONCAVE_ANGLE_COS,
        angle2Cos = MAX_CONCAVE_ANGLE_COS,
        a1Cos, a2Cos;

    for (var i = 0; i < innerPoints.length; i++) {
        a1Cos = _cos(edge[0], edge[1], innerPoints[i]);
        a2Cos = _cos(edge[1], edge[0], innerPoints[i]);

        if (a1Cos > angle1Cos && a2Cos > angle2Cos &&
            !_intersect([edge[0], innerPoints[i]], convex) &&
            !_intersect([edge[1], innerPoints[i]], convex)) {

            angle1Cos = a1Cos;
            angle2Cos = a2Cos;
            point = innerPoints[i];
        }
    }

    return point;
}

function _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList) {
    var edge,
        keyInSkipList,
        scaleFactor,
        midPoint,
        bBoxAround,
        bBoxWidth,
        bBoxHeight,
        midPointInserted = false;

    for (var i = 0; i < convex.length - 1; i++) {
        edge = [convex[i], convex[i + 1]];
        keyInSkipList = edge[0].join() + ',' + edge[1].join();

        if (_sqLength(edge[0], edge[1]) < maxSqEdgeLen ||
            edgeSkipList[keyInSkipList] === true) { continue; }

        scaleFactor = 0;
        bBoxAround = _bBoxAround(edge);
        do {
            bBoxAround = grid.extendBbox(bBoxAround, scaleFactor);
            bBoxWidth = bBoxAround[2] - bBoxAround[0];
            bBoxHeight = bBoxAround[3] - bBoxAround[1];

            midPoint = _midPoint(edge, grid.rangePoints(bBoxAround), convex);            
            scaleFactor++;
        }  while (midPoint === null && (maxSearchArea[0] > bBoxWidth || maxSearchArea[1] > bBoxHeight));

        if (bBoxWidth >= maxSearchArea[0] && bBoxHeight >= maxSearchArea[1]) {
            edgeSkipList[keyInSkipList] = true;
        }

        if (midPoint !== null) {
            convex.splice(i + 1, 0, midPoint);
            grid.removePoint(midPoint);
            midPointInserted = true;
        }
    }

    if (midPointInserted) {
        return _concave(convex, maxSqEdgeLen, maxSearchArea, grid, edgeSkipList);
    }

    return convex;
}

function hull(pointset, concavity, format) {
    var convex,
        concave,
        innerPoints,
        occupiedArea,
        maxSearchArea,
        cellSize,
        points,
        maxEdgeLen = concavity || 20;

    if (pointset.length < 4) {
        return pointset.slice();
    }

    points = _filterDuplicates(_sortByX(formatUtil.toXy(pointset, format)));

    occupiedArea = _occupiedArea(points);
    maxSearchArea = [
        occupiedArea[0] * MAX_SEARCH_BBOX_SIZE_PERCENT,
        occupiedArea[1] * MAX_SEARCH_BBOX_SIZE_PERCENT
    ];

    convex = convexHull(points);
    innerPoints = points.filter(function(pt) {
        return convex.indexOf(pt) < 0;
    });

    cellSize = Math.ceil(1 / (points.length / (occupiedArea[0] * occupiedArea[1])));

    concave = _concave(
        convex, Math.pow(maxEdgeLen, 2),
        maxSearchArea, grid(innerPoints, cellSize), {});
 
    return formatUtil.fromXy(concave, format);
}

var MAX_CONCAVE_ANGLE_COS = Math.cos(90 / (180 / Math.PI)); // angle = 90 deg
var MAX_SEARCH_BBOX_SIZE_PERCENT = 0.6;

module.exports = hull;

/***/ }),

/***/ "./node_modules/hull.js/src/intersect.js":
/*!***********************************************!*\
  !*** ./node_modules/hull.js/src/intersect.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

function ccw(x1, y1, x2, y2, x3, y3) {           
    var cw = ((y3 - y1) * (x2 - x1)) - ((y2 - y1) * (x3 - x1));
    return cw > 0 ? true : cw < 0 ? false : true; // colinear
}

function intersect(seg1, seg2) {
  var x1 = seg1[0][0], y1 = seg1[0][1],
      x2 = seg1[1][0], y2 = seg1[1][1],
      x3 = seg2[0][0], y3 = seg2[0][1],
      x4 = seg2[1][0], y4 = seg2[1][1];

    return ccw(x1, y1, x3, y3, x4, y4) !== ccw(x2, y2, x3, y3, x4, y4) && ccw(x1, y1, x2, y2, x3, y3) !== ccw(x1, y1, x2, y2, x4, y4);
}

module.exports = intersect;

/***/ }),

/***/ "./node_modules/ieee754/index.js":
/*!***************************************!*\
  !*** ./node_modules/ieee754/index.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
  var e, m
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var nBits = -7
  var i = isLE ? (nBytes - 1) : 0
  var d = isLE ? -1 : 1
  var s = buffer[offset + i]

  i += d

  e = s & ((1 << (-nBits)) - 1)
  s >>= (-nBits)
  nBits += eLen
  for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  m = e & ((1 << (-nBits)) - 1)
  e >>= (-nBits)
  nBits += mLen
  for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}

  if (e === 0) {
    e = 1 - eBias
  } else if (e === eMax) {
    return m ? NaN : ((s ? -1 : 1) * Infinity)
  } else {
    m = m + Math.pow(2, mLen)
    e = e - eBias
  }
  return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
}

exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
  var e, m, c
  var eLen = (nBytes * 8) - mLen - 1
  var eMax = (1 << eLen) - 1
  var eBias = eMax >> 1
  var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
  var i = isLE ? 0 : (nBytes - 1)
  var d = isLE ? 1 : -1
  var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0

  value = Math.abs(value)

  if (isNaN(value) || value === Infinity) {
    m = isNaN(value) ? 1 : 0
    e = eMax
  } else {
    e = Math.floor(Math.log(value) / Math.LN2)
    if (value * (c = Math.pow(2, -e)) < 1) {
      e--
      c *= 2
    }
    if (e + eBias >= 1) {
      value += rt / c
    } else {
      value += rt * Math.pow(2, 1 - eBias)
    }
    if (value * c >= 2) {
      e++
      c /= 2
    }

    if (e + eBias >= eMax) {
      m = 0
      e = eMax
    } else if (e + eBias >= 1) {
      m = ((value * c) - 1) * Math.pow(2, mLen)
      e = e + eBias
    } else {
      m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
      e = 0
    }
  }

  for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}

  e = (e << mLen) | m
  eLen += mLen
  for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}

  buffer[offset + i - d] |= s * 128
}


/***/ }),

/***/ "./node_modules/ify-loader/index.js!./node_modules/grapheme-breaker/src/GraphemeBreaker.js":
/*!****************************************************************************************!*\
  !*** ./node_modules/ify-loader!./node_modules/grapheme-breaker/src/GraphemeBreaker.js ***!
  \****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer) {// Generated by CoffeeScript 1.8.0
(function() {
  var CR, Control, Extend, L, LF, LV, LVT, Regional_Indicator, SpacingMark, T, UnicodeTrie, V, classTrie, codePointAt, fs, shouldBreak, _ref;

  _ref = __webpack_require__(/*! ./classes.json */ "./node_modules/grapheme-breaker/src/classes.json"), CR = _ref.CR, LF = _ref.LF, Control = _ref.Control, Extend = _ref.Extend, Regional_Indicator = _ref.Regional_Indicator, SpacingMark = _ref.SpacingMark, L = _ref.L, V = _ref.V, T = _ref.T, LV = _ref.LV, LVT = _ref.LVT;

  UnicodeTrie = __webpack_require__(/*! unicode-trie */ "./node_modules/unicode-trie/index.js");

  

  classTrie = new UnicodeTrie(Buffer("AA4QAAAAAAAAAHbgAQgG9/ntmkuIXjUUxzN+r3k4bUWQVotSHVCsoov6qIoiToWKFYvMuLHVtlaoLqQilLrwtakuxFYoLmQQYWalRYpUKYJV0am4mMUooojgSEG7EC2CdiHq/3rzMcc0yT333jyu0xz4kdwkN+ckOXncfN9QS4jzwCqwBqwHt5O0uuFGsBlsAhOM8lvATkv+LrAb7AXPgRfBAfAqeJ2UmwZvgcPgKDgGjoNZMAe+AN+C5W0hLgAXtvN3KZci7UpwFVgHbgHjYAPYJJ8nwCTYCnaQ58dI+cfBHvn8DFgL9kl9LyP8LLOflJ8CM+Q5K39IPo/28vfeyd6X8fcR/5jYP4v4nHyeR/iNjC8gPAl+BU+T8qcRFx0hBsGKzn/74LreIrdKxsGkRO0zE48wy7lmZSfnYkmWdhnCtTK+oHnnWqUPbuyY679N5t2J8B4ZnyTltyK+Dezq5P62G+Femf+sDPdp6n8JaQcterN5NWXJ5/Ij+FnGR0n6BvCbZk4kwjGjjO8rGh9woedNoudtBz6VSCQSiUQikUgkEomET97t5Hdp/ecvGfcXH+CdWfLNu6onxGowh7SvZPp3CE+A63v5feBJxMcQPyXz/0D4N2h18+cRhEcQnt+1674I+Q+inofANrAd7AAPg529lJfyUl7KS3mu8+4G94H7e/H3rPWRid3+RGIpc0nBGbAuE63F39VV1mjS6Pn4VCv++jN9bs4JMM5gbFSIdaNnpj+ppE3j+QQYWybEA8vytP0IPwF/gpXLsQ+AhWH0xYgQPwwJMTjA46YRXrnVw4vxzYjvke8dzvQx60gkEonE0uQA9oU3wB04J7yH/fDDVv4/j+x/QqfJXv0RuEueJe7t5vkTCLeQ88V2zVkjq+tRpD/Rzf+39hTC55lnkhdQbr+l7EHkTZH8GcTnSf4hkpf9/+uI57NQFT6HTSsC6hMYg3no/FrTF983sH84FJ3xNlroteOfQWNTp+8vL/CZeeX5mgb62A2w6WaDXa/9D/6DeFTafqwBfXtFT4irwacObMnm50/dPPwF4e/grwa0kUsTxiMEnQbcY9ZlsDXwL4iyOIfEB5jvcEgST1L/u/PjkP7vctzaZzkuJZSepknsMaw67jQ0xZe61F2XyvZ5k/ecJq4voXzQ1oZWQRm1Dl1ZH0LtiiVN8pUmy9nQD77bppuTLqWl1O9Ch+9vv9Dfm12COrZqOrXRJv13TX6i00XHyISLNamp3/e6eWWab9xyoYSr1+XeUoWug7ZWFTonhLDPO9M8pOX7cVHwbhn7Yu1VantC61ZtMPWhaiMtX0YXp1wsf7X5p65sW/OslnXpV3XrN803WneXlC0zvj5EZ5sP/6yyXsQQ01rRVdJV/+XWXUZ/rPmp7gf9dNuZoKjOmOOZibqv6fY43fi6bp9pfoXyL1tZ0x5Fy6u+UcVOrm1FZxdOPS7OLi7sFaKaXt+2c/X71qELqbhcD4v8wgRnb6+rr459rqgr3H5T21tmza0r3LOnj/6oWkcmnP6pa7OPvve9dvmqm+PD1HdteyP3e7xsX/mcK7Y26tJV0bXfVI/vOa9bZ3wIbS9nraehKHiH248cn/KxtpX1bV3bQoptnGx+S9ND2xujn6jo+ku3Jvic16oO3djo7CsrnHWdM1dd9UPR/OFQ9rtKl2ZaQ4vaWWe9KGOzSV8dcenPZdvhUny1QZdW1ce4fuhSdGuYb/F1h8IV3/PPlR0+pOya6dofdPuDbt8oug9uis+YvguqjiHnnVDz1KbfR30637f1Y5U+1o2VrVxZMX37qvfcof1XJzFtCKG76plJCJ7fhTq/FJ0hqI/FFtMaGWOv69vjUsrePZTZQ331h8lm07dj1fpCn2Fi3EX09atn2L6Ynsv4AFfUernj4HucbGc8dU0w+aDL+4M6YmtLX0z3I7Ha4Fpn1bufKucck2/YfIhrP3dfci0h5puv9TfUPs21g8bbmvzQZ4tQfhNSiuZ4HVzp4rShTHt9icl2l31YVTqB6Eus81pd/U2xuwyxpYrNPsik1wCoDEZmyDMjCmXFZVtV8d12DqoMizP7zCeh9anyDw==","base64"));

  codePointAt = function(str, idx) {
    var code, hi, low;
    idx = idx || 0;
    code = str.charCodeAt(idx);
    if ((0xD800 <= code && code <= 0xDBFF)) {
      hi = code;
      low = str.charCodeAt(idx + 1);
      if ((0xDC00 <= low && low <= 0xDFFF)) {
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
      }
      return hi;
    }
    if ((0xDC00 <= code && code <= 0xDFFF)) {
      hi = str.charCodeAt(idx - 1);
      low = code;
      if ((0xD800 <= hi && hi <= 0xDBFF)) {
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
      }
      return low;
    }
    return code;
  };

  shouldBreak = function(previous, current) {
    if (previous === CR && current === LF) {
      return false;
    } else if (previous === Control || previous === CR || previous === LF) {
      return true;
    } else if (current === Control || current === CR || current === LF) {
      return true;
    } else if (previous === L && (current === L || current === V || current === LV || current === LVT)) {
      return false;
    } else if ((previous === LV || previous === V) && (current === V || current === T)) {
      return false;
    } else if ((previous === LVT || previous === T) && current === T) {
      return false;
    } else if (previous === Regional_Indicator && current === Regional_Indicator) {
      return false;
    } else if (current === Extend) {
      return false;
    } else if (current === SpacingMark) {
      return false;
    }
    return true;
  };

  exports.nextBreak = function(string, index) {
    var i, next, prev, _i, _ref1, _ref2, _ref3, _ref4;
    if (index == null) {
      index = 0;
    }
    if (index < 0) {
      return 0;
    }
    if (index >= string.length - 1) {
      return string.length;
    }
    prev = classTrie.get(codePointAt(string, index));
    for (i = _i = _ref1 = index + 1, _ref2 = string.length; _i < _ref2; i = _i += 1) {
      if ((0xd800 <= (_ref3 = string.charCodeAt(i - 1)) && _ref3 <= 0xdbff) && (0xdc00 <= (_ref4 = string.charCodeAt(i)) && _ref4 <= 0xdfff)) {
        continue;
      }
      next = classTrie.get(codePointAt(string, i));
      if (shouldBreak(prev, next)) {
        return i;
      }
      prev = next;
    }
    return string.length;
  };

  exports.previousBreak = function(string, index) {
    var i, next, prev, _i, _ref1, _ref2, _ref3;
    if (index == null) {
      index = string.length;
    }
    if (index > string.length) {
      return string.length;
    }
    if (index <= 1) {
      return 0;
    }
    index--;
    next = classTrie.get(codePointAt(string, index));
    for (i = _i = _ref1 = index - 1; _i >= 0; i = _i += -1) {
      if ((0xd800 <= (_ref2 = string.charCodeAt(i)) && _ref2 <= 0xdbff) && (0xdc00 <= (_ref3 = string.charCodeAt(i + 1)) && _ref3 <= 0xdfff)) {
        continue;
      }
      prev = classTrie.get(codePointAt(string, i));
      if (shouldBreak(prev, next)) {
        return i + 1;
      }
      next = prev;
    }
    return 0;
  };

  exports["break"] = function(str) {
    var brk, index, res;
    res = [];
    index = 0;
    while ((brk = exports.nextBreak(str, index)) < str.length) {
      res.push(str.slice(index, brk));
      index = brk;
    }
    if (index < str.length) {
      res.push(str.slice(index));
    }
    return res;
  };

  exports.countBreaks = function(str) {
    var brk, count, index;
    count = 0;
    index = 0;
    while ((brk = exports.nextBreak(str, index)) < str.length) {
      index = brk;
      count++;
    }
    if (index < str.length) {
      count++;
    }
    return count;
  };

}).call(this);

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

/***/ }),

/***/ "./node_modules/ify-loader/index.js!./node_modules/linebreak/src/linebreaker.js":
/*!*****************************************************************************!*\
  !*** ./node_modules/ify-loader!./node_modules/linebreak/src/linebreaker.js ***!
  \*****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Generated by CoffeeScript 1.7.1
(function() {
  var AI, AL, BA, BK, CB, CI_BRK, CJ, CP_BRK, CR, DI_BRK, ID, IN_BRK, LF, LineBreaker, NL, NS, PR_BRK, SA, SG, SP, UnicodeTrie, WJ, XX, base64, characterClasses, classTrie, data, fs, pairTable, _ref, _ref1;

  UnicodeTrie = __webpack_require__(/*! unicode-trie */ "./node_modules/unicode-trie/index.js");

  

  base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/lib/b64.js");

  _ref = __webpack_require__(/*! ./classes */ "./node_modules/linebreak/src/classes.js"), BK = _ref.BK, CR = _ref.CR, LF = _ref.LF, NL = _ref.NL, CB = _ref.CB, BA = _ref.BA, SP = _ref.SP, WJ = _ref.WJ, SP = _ref.SP, BK = _ref.BK, LF = _ref.LF, NL = _ref.NL, AI = _ref.AI, AL = _ref.AL, SA = _ref.SA, SG = _ref.SG, XX = _ref.XX, CJ = _ref.CJ, ID = _ref.ID, NS = _ref.NS, characterClasses = _ref.characterClasses;

  _ref1 = __webpack_require__(/*! ./pairs */ "./node_modules/linebreak/src/pairs.js"), DI_BRK = _ref1.DI_BRK, IN_BRK = _ref1.IN_BRK, CI_BRK = _ref1.CI_BRK, CP_BRK = _ref1.CP_BRK, PR_BRK = _ref1.PR_BRK, pairTable = _ref1.pairTable;

  data = base64.toByteArray(" ");

  classTrie = new UnicodeTrie(data);

  LineBreaker = (function() {
    var Break, mapClass, mapFirst;

    function LineBreaker(string) {
      this.string = string;
      this.pos = 0;
      this.lastPos = 0;
      this.curClass = null;
      this.nextClass = null;
    }

    LineBreaker.prototype.nextCodePoint = function() {
      var code, next;
      code = this.string.charCodeAt(this.pos++);
      next = this.string.charCodeAt(this.pos);
      if ((0xd800 <= code && code <= 0xdbff) && (0xdc00 <= next && next <= 0xdfff)) {
        this.pos++;
        return ((code - 0xd800) * 0x400) + (next - 0xdc00) + 0x10000;
      }
      return code;
    };

    mapClass = function(c) {
      switch (c) {
        case AI:
          return AL;
        case SA:
        case SG:
        case XX:
          return AL;
        case CJ:
          return NS;
        default:
          return c;
      }
    };

    mapFirst = function(c) {
      switch (c) {
        case LF:
        case NL:
          return BK;
        case CB:
          return BA;
        case SP:
          return WJ;
        default:
          return c;
      }
    };

    LineBreaker.prototype.nextCharClass = function(first) {
      if (first == null) {
        first = false;
      }
      return mapClass(classTrie.get(this.nextCodePoint()));
    };

    Break = (function() {
      function Break(position, required) {
        this.position = position;
        this.required = required != null ? required : false;
      }

      return Break;

    })();

    LineBreaker.prototype.nextBreak = function() {
      var cur, lastClass, shouldBreak;
      if (this.curClass == null) {
        this.curClass = mapFirst(this.nextCharClass());
      }
      while (this.pos < this.string.length) {
        this.lastPos = this.pos;
        lastClass = this.nextClass;
        this.nextClass = this.nextCharClass();
        if (this.curClass === BK || (this.curClass === CR && this.nextClass !== LF)) {
          this.curClass = mapFirst(mapClass(this.nextClass));
          return new Break(this.lastPos, true);
        }
        cur = (function() {
          switch (this.nextClass) {
            case SP:
              return this.curClass;
            case BK:
            case LF:
            case NL:
              return BK;
            case CR:
              return CR;
            case CB:
              return BA;
          }
        }).call(this);
        if (cur != null) {
          this.curClass = cur;
          if (this.nextClass === CB) {
            return new Break(this.lastPos);
          }
          continue;
        }
        shouldBreak = false;
        switch (pairTable[this.curClass][this.nextClass]) {
          case DI_BRK:
            shouldBreak = true;
            break;
          case IN_BRK:
            shouldBreak = lastClass === SP;
            break;
          case CI_BRK:
            shouldBreak = lastClass === SP;
            if (!shouldBreak) {
              continue;
            }
            break;
          case CP_BRK:
            if (lastClass !== SP) {
              continue;
            }
        }
        this.curClass = this.nextClass;
        if (shouldBreak) {
          return new Break(this.lastPos);
        }
      }
      if (this.pos >= this.string.length) {
        if (this.lastPos < this.string.length) {
          this.lastPos = this.string.length;
          return new Break(this.string.length);
        } else {
          return null;
        }
      }
    };

    return LineBreaker;

  })();

  module.exports = LineBreaker;

}).call(this);


/***/ }),

/***/ "./node_modules/immutable/dist/immutable.js":
/*!**************************************************!*\
  !*** ./node_modules/immutable/dist/immutable.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * Copyright (c) 2014-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

(function (global, factory) {
   true ? module.exports = factory() :
  undefined;
}(this, function () { 'use strict';var SLICE$0 = Array.prototype.slice;

  function createClass(ctor, superClass) {
    if (superClass) {
      ctor.prototype = Object.create(superClass.prototype);
    }
    ctor.prototype.constructor = ctor;
  }

  function Iterable(value) {
      return isIterable(value) ? value : Seq(value);
    }


  createClass(KeyedIterable, Iterable);
    function KeyedIterable(value) {
      return isKeyed(value) ? value : KeyedSeq(value);
    }


  createClass(IndexedIterable, Iterable);
    function IndexedIterable(value) {
      return isIndexed(value) ? value : IndexedSeq(value);
    }


  createClass(SetIterable, Iterable);
    function SetIterable(value) {
      return isIterable(value) && !isAssociative(value) ? value : SetSeq(value);
    }



  function isIterable(maybeIterable) {
    return !!(maybeIterable && maybeIterable[IS_ITERABLE_SENTINEL]);
  }

  function isKeyed(maybeKeyed) {
    return !!(maybeKeyed && maybeKeyed[IS_KEYED_SENTINEL]);
  }

  function isIndexed(maybeIndexed) {
    return !!(maybeIndexed && maybeIndexed[IS_INDEXED_SENTINEL]);
  }

  function isAssociative(maybeAssociative) {
    return isKeyed(maybeAssociative) || isIndexed(maybeAssociative);
  }

  function isOrdered(maybeOrdered) {
    return !!(maybeOrdered && maybeOrdered[IS_ORDERED_SENTINEL]);
  }

  Iterable.isIterable = isIterable;
  Iterable.isKeyed = isKeyed;
  Iterable.isIndexed = isIndexed;
  Iterable.isAssociative = isAssociative;
  Iterable.isOrdered = isOrdered;

  Iterable.Keyed = KeyedIterable;
  Iterable.Indexed = IndexedIterable;
  Iterable.Set = SetIterable;


  var IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@';
  var IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@';
  var IS_INDEXED_SENTINEL = '@@__IMMUTABLE_INDEXED__@@';
  var IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@';

  // Used for setting prototype methods that IE8 chokes on.
  var DELETE = 'delete';

  // Constants describing the size of trie nodes.
  var SHIFT = 5; // Resulted in best performance after ______?
  var SIZE = 1 << SHIFT;
  var MASK = SIZE - 1;

  // A consistent shared value representing "not set" which equals nothing other
  // than itself, and nothing that could be provided externally.
  var NOT_SET = {};

  // Boolean references, Rough equivalent of `bool &`.
  var CHANGE_LENGTH = { value: false };
  var DID_ALTER = { value: false };

  function MakeRef(ref) {
    ref.value = false;
    return ref;
  }

  function SetRef(ref) {
    ref && (ref.value = true);
  }

  // A function which returns a value representing an "owner" for transient writes
  // to tries. The return value will only ever equal itself, and will not equal
  // the return of any subsequent call of this function.
  function OwnerID() {}

  // http://jsperf.com/copy-array-inline
  function arrCopy(arr, offset) {
    offset = offset || 0;
    var len = Math.max(0, arr.length - offset);
    var newArr = new Array(len);
    for (var ii = 0; ii < len; ii++) {
      newArr[ii] = arr[ii + offset];
    }
    return newArr;
  }

  function ensureSize(iter) {
    if (iter.size === undefined) {
      iter.size = iter.__iterate(returnTrue);
    }
    return iter.size;
  }

  function wrapIndex(iter, index) {
    // This implements "is array index" which the ECMAString spec defines as:
    //
    //     A String property name P is an array index if and only if
    //     ToString(ToUint32(P)) is equal to P and ToUint32(P) is not equal
    //     to 2^32−1.
    //
    // http://www.ecma-international.org/ecma-262/6.0/#sec-array-exotic-objects
    if (typeof index !== 'number') {
      var uint32Index = index >>> 0; // N >>> 0 is shorthand for ToUint32
      if ('' + uint32Index !== index || uint32Index === 4294967295) {
        return NaN;
      }
      index = uint32Index;
    }
    return index < 0 ? ensureSize(iter) + index : index;
  }

  function returnTrue() {
    return true;
  }

  function wholeSlice(begin, end, size) {
    return (begin === 0 || (size !== undefined && begin <= -size)) &&
      (end === undefined || (size !== undefined && end >= size));
  }

  function resolveBegin(begin, size) {
    return resolveIndex(begin, size, 0);
  }

  function resolveEnd(end, size) {
    return resolveIndex(end, size, size);
  }

  function resolveIndex(index, size, defaultIndex) {
    return index === undefined ?
      defaultIndex :
      index < 0 ?
        Math.max(0, size + index) :
        size === undefined ?
          index :
          Math.min(size, index);
  }

  /* global Symbol */

  var ITERATE_KEYS = 0;
  var ITERATE_VALUES = 1;
  var ITERATE_ENTRIES = 2;

  var REAL_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator;
  var FAUX_ITERATOR_SYMBOL = '@@iterator';

  var ITERATOR_SYMBOL = REAL_ITERATOR_SYMBOL || FAUX_ITERATOR_SYMBOL;


  function Iterator(next) {
      this.next = next;
    }

    Iterator.prototype.toString = function() {
      return '[Iterator]';
    };


  Iterator.KEYS = ITERATE_KEYS;
  Iterator.VALUES = ITERATE_VALUES;
  Iterator.ENTRIES = ITERATE_ENTRIES;

  Iterator.prototype.inspect =
  Iterator.prototype.toSource = function () { return this.toString(); }
  Iterator.prototype[ITERATOR_SYMBOL] = function () {
    return this;
  };


  function iteratorValue(type, k, v, iteratorResult) {
    var value = type === 0 ? k : type === 1 ? v : [k, v];
    iteratorResult ? (iteratorResult.value = value) : (iteratorResult = {
      value: value, done: false
    });
    return iteratorResult;
  }

  function iteratorDone() {
    return { value: undefined, done: true };
  }

  function hasIterator(maybeIterable) {
    return !!getIteratorFn(maybeIterable);
  }

  function isIterator(maybeIterator) {
    return maybeIterator && typeof maybeIterator.next === 'function';
  }

  function getIterator(iterable) {
    var iteratorFn = getIteratorFn(iterable);
    return iteratorFn && iteratorFn.call(iterable);
  }

  function getIteratorFn(iterable) {
    var iteratorFn = iterable && (
      (REAL_ITERATOR_SYMBOL && iterable[REAL_ITERATOR_SYMBOL]) ||
      iterable[FAUX_ITERATOR_SYMBOL]
    );
    if (typeof iteratorFn === 'function') {
      return iteratorFn;
    }
  }

  function isArrayLike(value) {
    return value && typeof value.length === 'number';
  }

  createClass(Seq, Iterable);
    function Seq(value) {
      return value === null || value === undefined ? emptySequence() :
        isIterable(value) ? value.toSeq() : seqFromValue(value);
    }

    Seq.of = function(/*...values*/) {
      return Seq(arguments);
    };

    Seq.prototype.toSeq = function() {
      return this;
    };

    Seq.prototype.toString = function() {
      return this.__toString('Seq {', '}');
    };

    Seq.prototype.cacheResult = function() {
      if (!this._cache && this.__iterateUncached) {
        this._cache = this.entrySeq().toArray();
        this.size = this._cache.length;
      }
      return this;
    };

    // abstract __iterateUncached(fn, reverse)

    Seq.prototype.__iterate = function(fn, reverse) {
      return seqIterate(this, fn, reverse, true);
    };

    // abstract __iteratorUncached(type, reverse)

    Seq.prototype.__iterator = function(type, reverse) {
      return seqIterator(this, type, reverse, true);
    };



  createClass(KeyedSeq, Seq);
    function KeyedSeq(value) {
      return value === null || value === undefined ?
        emptySequence().toKeyedSeq() :
        isIterable(value) ?
          (isKeyed(value) ? value.toSeq() : value.fromEntrySeq()) :
          keyedSeqFromValue(value);
    }

    KeyedSeq.prototype.toKeyedSeq = function() {
      return this;
    };



  createClass(IndexedSeq, Seq);
    function IndexedSeq(value) {
      return value === null || value === undefined ? emptySequence() :
        !isIterable(value) ? indexedSeqFromValue(value) :
        isKeyed(value) ? value.entrySeq() : value.toIndexedSeq();
    }

    IndexedSeq.of = function(/*...values*/) {
      return IndexedSeq(arguments);
    };

    IndexedSeq.prototype.toIndexedSeq = function() {
      return this;
    };

    IndexedSeq.prototype.toString = function() {
      return this.__toString('Seq [', ']');
    };

    IndexedSeq.prototype.__iterate = function(fn, reverse) {
      return seqIterate(this, fn, reverse, false);
    };

    IndexedSeq.prototype.__iterator = function(type, reverse) {
      return seqIterator(this, type, reverse, false);
    };



  createClass(SetSeq, Seq);
    function SetSeq(value) {
      return (
        value === null || value === undefined ? emptySequence() :
        !isIterable(value) ? indexedSeqFromValue(value) :
        isKeyed(value) ? value.entrySeq() : value
      ).toSetSeq();
    }

    SetSeq.of = function(/*...values*/) {
      return SetSeq(arguments);
    };

    SetSeq.prototype.toSetSeq = function() {
      return this;
    };



  Seq.isSeq = isSeq;
  Seq.Keyed = KeyedSeq;
  Seq.Set = SetSeq;
  Seq.Indexed = IndexedSeq;

  var IS_SEQ_SENTINEL = '@@__IMMUTABLE_SEQ__@@';

  Seq.prototype[IS_SEQ_SENTINEL] = true;



  createClass(ArraySeq, IndexedSeq);
    function ArraySeq(array) {
      this._array = array;
      this.size = array.length;
    }

    ArraySeq.prototype.get = function(index, notSetValue) {
      return this.has(index) ? this._array[wrapIndex(this, index)] : notSetValue;
    };

    ArraySeq.prototype.__iterate = function(fn, reverse) {
      var array = this._array;
      var maxIndex = array.length - 1;
      for (var ii = 0; ii <= maxIndex; ii++) {
        if (fn(array[reverse ? maxIndex - ii : ii], ii, this) === false) {
          return ii + 1;
        }
      }
      return ii;
    };

    ArraySeq.prototype.__iterator = function(type, reverse) {
      var array = this._array;
      var maxIndex = array.length - 1;
      var ii = 0;
      return new Iterator(function() 
        {return ii > maxIndex ?
          iteratorDone() :
          iteratorValue(type, ii, array[reverse ? maxIndex - ii++ : ii++])}
      );
    };



  createClass(ObjectSeq, KeyedSeq);
    function ObjectSeq(object) {
      var keys = Object.keys(object);
      this._object = object;
      this._keys = keys;
      this.size = keys.length;
    }

    ObjectSeq.prototype.get = function(key, notSetValue) {
      if (notSetValue !== undefined && !this.has(key)) {
        return notSetValue;
      }
      return this._object[key];
    };

    ObjectSeq.prototype.has = function(key) {
      return this._object.hasOwnProperty(key);
    };

    ObjectSeq.prototype.__iterate = function(fn, reverse) {
      var object = this._object;
      var keys = this._keys;
      var maxIndex = keys.length - 1;
      for (var ii = 0; ii <= maxIndex; ii++) {
        var key = keys[reverse ? maxIndex - ii : ii];
        if (fn(object[key], key, this) === false) {
          return ii + 1;
        }
      }
      return ii;
    };

    ObjectSeq.prototype.__iterator = function(type, reverse) {
      var object = this._object;
      var keys = this._keys;
      var maxIndex = keys.length - 1;
      var ii = 0;
      return new Iterator(function()  {
        var key = keys[reverse ? maxIndex - ii : ii];
        return ii++ > maxIndex ?
          iteratorDone() :
          iteratorValue(type, key, object[key]);
      });
    };

  ObjectSeq.prototype[IS_ORDERED_SENTINEL] = true;


  createClass(IterableSeq, IndexedSeq);
    function IterableSeq(iterable) {
      this._iterable = iterable;
      this.size = iterable.length || iterable.size;
    }

    IterableSeq.prototype.__iterateUncached = function(fn, reverse) {
      if (reverse) {
        return this.cacheResult().__iterate(fn, reverse);
      }
      var iterable = this._iterable;
      var iterator = getIterator(iterable);
      var iterations = 0;
      if (isIterator(iterator)) {
        var step;
        while (!(step = iterator.next()).done) {
          if (fn(step.value, iterations++, this) === false) {
            break;
          }
        }
      }
      return iterations;
    };

    IterableSeq.prototype.__iteratorUncached = function(type, reverse) {
      if (reverse) {
        return this.cacheResult().__iterator(type, reverse);
      }
      var iterable = this._iterable;
      var iterator = getIterator(iterable);
      if (!isIterator(iterator)) {
        return new Iterator(iteratorDone);
      }
      var iterations = 0;
      return new Iterator(function()  {
        var step = iterator.next();
        return step.done ? step : iteratorValue(type, iterations++, step.value);
      });
    };



  createClass(IteratorSeq, IndexedSeq);
    function IteratorSeq(iterator) {
      this._iterator = iterator;
      this._iteratorCache = [];
    }

    IteratorSeq.prototype.__iterateUncached = function(fn, reverse) {
      if (reverse) {
        return this.cacheResult().__iterate(fn, reverse);
      }
      var iterator = this._iterator;
      var cache = this._iteratorCache;
      var iterations = 0;
      while (iterations < cache.length) {
        if (fn(cache[iterations], iterations++, this) === false) {
          return iterations;
        }
      }
      var step;
      while (!(step = iterator.next()).done) {
        var val = step.value;
        cache[iterations] = val;
        if (fn(val, iterations++, this) === false) {
          break;
        }
      }
      return iterations;
    };

    IteratorSeq.prototype.__iteratorUncached = function(type, reverse) {
      if (reverse) {
        return this.cacheResult().__iterator(type, reverse);
      }
      var iterator = this._iterator;
      var cache = this._iteratorCache;
      var iterations = 0;
      return new Iterator(function()  {
        if (iterations >= cache.length) {
          var step = iterator.next();
          if (step.done) {
            return step;
          }
          cache[iterations] = step.value;
        }
        return iteratorValue(type, iterations, cache[iterations++]);
      });
    };




  // # pragma Helper functions

  function isSeq(maybeSeq) {
    return !!(maybeSeq && maybeSeq[IS_SEQ_SENTINEL]);
  }

  var EMPTY_SEQ;

  function emptySequence() {
    return EMPTY_SEQ || (EMPTY_SEQ = new ArraySeq([]));
  }

  function keyedSeqFromValue(value) {
    var seq =
      Array.isArray(value) ? new ArraySeq(value).fromEntrySeq() :
      isIterator(value) ? new IteratorSeq(value).fromEntrySeq() :
      hasIterator(value) ? new IterableSeq(value).fromEntrySeq() :
      typeof value === 'object' ? new ObjectSeq(value) :
      undefined;
    if (!seq) {
      throw new TypeError(
        'Expected Array or iterable object of [k, v] entries, '+
        'or keyed object: ' + value
      );
    }
    return seq;
  }

  function indexedSeqFromValue(value) {
    var seq = maybeIndexedSeqFromValue(value);
    if (!seq) {
      throw new TypeError(
        'Expected Array or iterable object of values: ' + value
      );
    }
    return seq;
  }

  function seqFromValue(value) {
    var seq = maybeIndexedSeqFromValue(value) ||
      (typeof value === 'object' && new ObjectSeq(value));
    if (!seq) {
      throw new TypeError(
        'Expected Array or iterable object of values, or keyed object: ' + value
      );
    }
    return seq;
  }

  function maybeIndexedSeqFromValue(value) {
    return (
      isArrayLike(value) ? new ArraySeq(value) :
      isIterator(value) ? new IteratorSeq(value) :
      hasIterator(value) ? new IterableSeq(value) :
      undefined
    );
  }

  function seqIterate(seq, fn, reverse, useKeys) {
    var cache = seq._cache;
    if (cache) {
      var maxIndex = cache.length - 1;
      for (var ii = 0; ii <= maxIndex; ii++) {
        var entry = cache[reverse ? maxIndex - ii : ii];
        if (fn(entry[1], useKeys ? entry[0] : ii, seq) === false) {
          return ii + 1;
        }
      }
      return ii;
    }
    return seq.__iterateUncached(fn, reverse);
  }

  function seqIterator(seq, type, reverse, useKeys) {
    var cache = seq._cache;
    if (cache) {
      var maxIndex = cache.length - 1;
      var ii = 0;
      return new Iterator(function()  {
        var entry = cache[reverse ? maxIndex - ii : ii];
        return ii++ > maxIndex ?
          iteratorDone() :
          iteratorValue(type, useKeys ? entry[0] : ii - 1, entry[1]);
      });
    }
    return seq.__iteratorUncached(type, reverse);
  }

  function fromJS(json, converter) {
    return converter ?
      fromJSWith(converter, json, '', {'': json}) :
      fromJSDefault(json);
  }

  function fromJSWith(converter, json, key, parentJSON) {
    if (Array.isArray(json)) {
      return converter.call(parentJSON, key, IndexedSeq(json).map(function(v, k)  {return fromJSWith(converter, v, k, json)}));
    }
    if (isPlainObj(json)) {
      return converter.call(parentJSON, key, KeyedSeq(json).map(function(v, k)  {return fromJSWith(converter, v, k, json)}));
    }
    return json;
  }

  function fromJSDefault(json) {
    if (Array.isArray(json)) {
      return IndexedSeq(json).map(fromJSDefault).toList();
    }
    if (isPlainObj(json)) {
      return KeyedSeq(json).map(fromJSDefault).toMap();
    }
    return json;
  }

  function isPlainObj(value) {
    return value && (value.constructor === Object || value.constructor === undefined);
  }

  /**
   * An extension of the "same-value" algorithm as [described for use by ES6 Map
   * and Set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Key_equality)
   *
   * NaN is considered the same as NaN, however -0 and 0 are considered the same
   * value, which is different from the algorithm described by
   * [`Object.is`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is).
   *
   * This is extended further to allow Objects to describe the values they
   * represent, by way of `valueOf` or `equals` (and `hashCode`).
   *
   * Note: because of this extension, the key equality of Immutable.Map and the
   * value equality of Immutable.Set will differ from ES6 Map and Set.
   *
   * ### Defining custom values
   *
   * The easiest way to describe the value an object represents is by implementing
   * `valueOf`. For example, `Date` represents a value by returning a unix
   * timestamp for `valueOf`:
   *
   *     var date1 = new Date(1234567890000); // Fri Feb 13 2009 ...
   *     var date2 = new Date(1234567890000);
   *     date1.valueOf(); // 1234567890000
   *     assert( date1 !== date2 );
   *     assert( Immutable.is( date1, date2 ) );
   *
   * Note: overriding `valueOf` may have other implications if you use this object
   * where JavaScript expects a primitive, such as implicit string coercion.
   *
   * For more complex types, especially collections, implementing `valueOf` may
   * not be performant. An alternative is to implement `equals` and `hashCode`.
   *
   * `equals` takes another object, presumably of similar type, and returns true
   * if the it is equal. Equality is symmetrical, so the same result should be
   * returned if this and the argument are flipped.
   *
   *     assert( a.equals(b) === b.equals(a) );
   *
   * `hashCode` returns a 32bit integer number representing the object which will
   * be used to determine how to store the value object in a Map or Set. You must
   * provide both or neither methods, one must not exist without the other.
   *
   * Also, an important relationship between these methods must be upheld: if two
   * values are equal, they *must* return the same hashCode. If the values are not
   * equal, they might have the same hashCode; this is called a hash collision,
   * and while undesirable for performance reasons, it is acceptable.
   *
   *     if (a.equals(b)) {
   *       assert( a.hashCode() === b.hashCode() );
   *     }
   *
   * All Immutable collections implement `equals` and `hashCode`.
   *
   */
  function is(valueA, valueB) {
    if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
      return true;
    }
    if (!valueA || !valueB) {
      return false;
    }
    if (typeof valueA.valueOf === 'function' &&
        typeof valueB.valueOf === 'function') {
      valueA = valueA.valueOf();
      valueB = valueB.valueOf();
      if (valueA === valueB || (valueA !== valueA && valueB !== valueB)) {
        return true;
      }
      if (!valueA || !valueB) {
        return false;
      }
    }
    if (typeof valueA.equals === 'function' &&
        typeof valueB.equals === 'function' &&
        valueA.equals(valueB)) {
      return true;
    }
    return false;
  }

  function deepEqual(a, b) {
    if (a === b) {
      return true;
    }

    if (
      !isIterable(b) ||
      a.size !== undefined && b.size !== undefined && a.size !== b.size ||
      a.__hash !== undefined && b.__hash !== undefined && a.__hash !== b.__hash ||
      isKeyed(a) !== isKeyed(b) ||
      isIndexed(a) !== isIndexed(b) ||
      isOrdered(a) !== isOrdered(b)
    ) {
      return false;
    }

    if (a.size === 0 && b.size === 0) {
      return true;
    }

    var notAssociative = !isAssociative(a);

    if (isOrdered(a)) {
      var entries = a.entries();
      return b.every(function(v, k)  {
        var entry = entries.next().value;
        return entry && is(entry[1], v) && (notAssociative || is(entry[0], k));
      }) && entries.next().done;
    }

    var flipped = false;

    if (a.size === undefined) {
      if (b.size === undefined) {
        if (typeof a.cacheResult === 'function') {
          a.cacheResult();
        }
      } else {
        flipped = true;
        var _ = a;
        a = b;
        b = _;
      }
    }

    var allEqual = true;
    var bSize = b.__iterate(function(v, k)  {
      if (notAssociative ? !a.has(v) :
          flipped ? !is(v, a.get(k, NOT_SET)) : !is(a.get(k, NOT_SET), v)) {
        allEqual = false;
        return false;
      }
    });

    return allEqual && a.size === bSize;
  }

  createClass(Repeat, IndexedSeq);

    function Repeat(value, times) {
      if (!(this instanceof Repeat)) {
        return new Repeat(value, times);
      }
      this._value = value;
      this.size = times === undefined ? Infinity : Math.max(0, times);
      if (this.size === 0) {
        if (EMPTY_REPEAT) {
          return EMPTY_REPEAT;
        }
        EMPTY_REPEAT = this;
      }
    }

    Repeat.prototype.toString = function() {
      if (this.size === 0) {
        return 'Repeat []';
      }
      return 'Repeat [ ' + this._value + ' ' + this.size + ' times ]';
    };

    Repeat.prototype.get = function(index, notSetValue) {
      return this.has(index) ? this._value : notSetValue;
    };

    Repeat.prototype.includes = function(searchValue) {
      return is(this._value, searchValue);
    };

    Repeat.prototype.slice = function(begin, end) {
      var size = this.size;
      return wholeSlice(begin, end, size) ? this :
        new Repeat(this._value, resolveEnd(end, size) - resolveBegin(begin, size));
    };

    Repeat.prototype.reverse = function() {
      return this;
    };

    Repeat.prototype.indexOf = function(searchValue) {
      if (is(this._value, searchValue)) {
        return 0;
      }
      return -1;
    };

    Repeat.prototype.lastIndexOf = function(searchValue) {
      if (is(this._value, searchValue)) {
        return this.size;
      }
      return -1;
    };

    Repeat.prototype.__iterate = function(fn, reverse) {
      for (var ii = 0; ii < this.size; ii++) {
        if (fn(this._value, ii, this) === false) {
          return ii + 1;
        }
      }
      return ii;
    };

    Repeat.prototype.__iterator = function(type, reverse) {var this$0 = this;
      var ii = 0;
      return new Iterator(function() 
        {return ii < this$0.size ? iteratorValue(type, ii++, this$0._value) : iteratorDone()}
      );
    };

    Repeat.prototype.equals = function(other) {
      return other instanceof Repeat ?
        is(this._value, other._value) :
        deepEqual(other);
    };


  var EMPTY_REPEAT;

  function invariant(condition, error) {
    if (!condition) throw new Error(error);
  }

  createClass(Range, IndexedSeq);

    function Range(start, end, step) {
      if (!(this instanceof Range)) {
        return new Range(start, end, step);
      }
      invariant(step !== 0, 'Cannot step a Range by 0');
      start = start || 0;
      if (end === undefined) {
        end = Infinity;
      }
      step = step === undefined ? 1 : Math.abs(step);
      if (end < start) {
        step = -step;
      }
      this._start = start;
      this._end = end;
      this._step = step;
      this.size = Math.max(0, Math.ceil((end - start) / step - 1) + 1);
      if (this.size === 0) {
        if (EMPTY_RANGE) {
          return EMPTY_RANGE;
        }
        EMPTY_RANGE = this;
      }
    }

    Range.prototype.toString = function() {
      if (this.size === 0) {
        return 'Range []';
      }
      return 'Range [ ' +
        this._start + '...' + this._end +
        (this._step !== 1 ? ' by ' + this._step : '') +
      ' ]';
    };

    Range.prototype.get = function(index, notSetValue) {
      return this.has(index) ?
        this._start + wrapIndex(this, index) * this._step :
        notSetValue;
    };

    Range.prototype.includes = function(searchValue) {
      var possibleIndex = (searchValue - this._start) / this._step;
      return possibleIndex >= 0 &&
        possibleIndex < this.size &&
        possibleIndex === Math.floor(possibleIndex);
    };

    Range.prototype.slice = function(begin, end) {
      if (wholeSlice(begin, end, this.size)) {
        return this;
      }
      begin = resolveBegin(begin, this.size);
      end = resolveEnd(end, this.size);
      if (end <= begin) {
        return new Range(0, 0);
      }
      return new Range(this.get(begin, this._end), this.get(end, this._end), this._step);
    };

    Range.prototype.indexOf = function(searchValue) {
      var offsetValue = searchValue - this._start;
      if (offsetValue % this._step === 0) {
        var index = offsetValue / this._step;
        if (index >= 0 && index < this.size) {
          return index
        }
      }
      return -1;
    };

    Range.prototype.lastIndexOf = function(searchValue) {
      return this.indexOf(searchValue);
    };

    Range.prototype.__iterate = function(fn, reverse) {
      var maxIndex = this.size - 1;
      var step = this._step;
      var value = reverse ? this._start + maxIndex * step : this._start;
      for (var ii = 0; ii <= maxIndex; ii++) {
        if (fn(value, ii, this) === false) {
          return ii + 1;
        }
        value += reverse ? -step : step;
      }
      return ii;
    };

    Range.prototype.__iterator = function(type, reverse) {
      var maxIndex = this.size - 1;
      var step = this._step;
      var value = reverse ? this._start + maxIndex * step : this._start;
      var ii = 0;
      return new Iterator(function()  {
        var v = value;
        value += reverse ? -step : step;
        return ii > maxIndex ? iteratorDone() : iteratorValue(type, ii++, v);
      });
    };

    Range.prototype.equals = function(other) {
      return other instanceof Range ?
        this._start === other._start &&
        this._end === other._end &&
        this._step === other._step :
        deepEqual(this, other);
    };


  var EMPTY_RANGE;

  createClass(Collection, Iterable);
    function Collection() {
      throw TypeError('Abstract');
    }


  createClass(KeyedCollection, Collection);function KeyedCollection() {}

  createClass(IndexedCollection, Collection);function IndexedCollection() {}

  createClass(SetCollection, Collection);function SetCollection() {}


  Collection.Keyed = KeyedCollection;
  Collection.Indexed = IndexedCollection;
  Collection.Set = SetCollection;

  var imul =
    typeof Math.imul === 'function' && Math.imul(0xffffffff, 2) === -2 ?
    Math.imul :
    function imul(a, b) {
      a = a | 0; // int
      b = b | 0; // int
      var c = a & 0xffff;
      var d = b & 0xffff;
      // Shift by 0 fixes the sign on the high part.
      return (c * d) + ((((a >>> 16) * d + c * (b >>> 16)) << 16) >>> 0) | 0; // int
    };

  // v8 has an optimization for storing 31-bit signed numbers.
  // Values which have either 00 or 11 as the high order bits qualify.
  // This function drops the highest order bit in a signed number, maintaining
  // the sign bit.
  function smi(i32) {
    return ((i32 >>> 1) & 0x40000000) | (i32 & 0xBFFFFFFF);
  }

  function hash(o) {
    if (o === false || o === null || o === undefined) {
      return 0;
    }
    if (typeof o.valueOf === 'function') {
      o = o.valueOf();
      if (o === false || o === null || o === undefined) {
        return 0;
      }
    }
    if (o === true) {
      return 1;
    }
    var type = typeof o;
    if (type === 'number') {
      if (o !== o || o === Infinity) {
        return 0;
      }
      var h = o | 0;
      if (h !== o) {
        h ^= o * 0xFFFFFFFF;
      }
      while (o > 0xFFFFFFFF) {
        o /= 0xFFFFFFFF;
        h ^= o;
      }
      return smi(h);
    }
    if (type === 'string') {
      return o.length > STRING_HASH_CACHE_MIN_STRLEN ? cachedHashString(o) : hashString(o);
    }
    if (typeof o.hashCode === 'function') {
      return o.hashCode();
    }
    if (type === 'object') {
      return hashJSObj(o);
    }
    if (typeof o.toString === 'function') {
      return hashString(o.toString());
    }
    throw new Error('Value type ' + type + ' cannot be hashed.');
  }

  function cachedHashString(string) {
    var hash = stringHashCache[string];
    if (hash === undefined) {
      hash = hashString(string);
      if (STRING_HASH_CACHE_SIZE === STRING_HASH_CACHE_MAX_SIZE) {
        STRING_HASH_CACHE_SIZE = 0;
        stringHashCache = {};
      }
      STRING_HASH_CACHE_SIZE++;
      stringHashCache[string] = hash;
    }
    return hash;
  }

  // http://jsperf.com/hashing-strings
  function hashString(string) {
    // This is the hash from JVM
    // The hash code for a string is computed as
    // s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
    // where s[i] is the ith character of the string and n is the length of
    // the string. We "mod" the result to make it between 0 (inclusive) and 2^31
    // (exclusive) by dropping high bits.
    var hash = 0;
    for (var ii = 0; ii < string.length; ii++) {
      hash = 31 * hash + string.charCodeAt(ii) | 0;
    }
    return smi(hash);
  }

  function hashJSObj(obj) {
    var hash;
    if (usingWeakMap) {
      hash = weakMap.get(obj);
      if (hash !== undefined) {
        return hash;
      }
    }

    hash = obj[UID_HASH_KEY];
    if (hash !== undefined) {
      return hash;
    }

    if (!canDefineProperty) {
      hash = obj.propertyIsEnumerable && obj.propertyIsEnumerable[UID_HASH_KEY];
      if (hash !== undefined) {
        return hash;
      }

      hash = getIENodeHash(obj);
      if (hash !== undefined) {
        return hash;
      }
    }

    hash = ++objHashUID;
    if (objHashUID & 0x40000000) {
      objHashUID = 0;
    }

    if (usingWeakMap) {
      weakMap.set(obj, hash);
    } else if (isExtensible !== undefined && isExtensible(obj) === false) {
      throw new Error('Non-extensible objects are not allowed as keys.');
    } else if (canDefineProperty) {
      Object.defineProperty(obj, UID_HASH_KEY, {
        'enumerable': false,
        'configurable': false,
        'writable': false,
        'value': hash
      });
    } else if (obj.propertyIsEnumerable !== undefined &&
               obj.propertyIsEnumerable === obj.constructor.prototype.propertyIsEnumerable) {
      // Since we can't define a non-enumerable property on the object
      // we'll hijack one of the less-used non-enumerable properties to
      // save our hash on it. Since this is a function it will not show up in
      // `JSON.stringify` which is what we want.
      obj.propertyIsEnumerable = function() {
        return this.constructor.prototype.propertyIsEnumerable.apply(this, arguments);
      };
      obj.propertyIsEnumerable[UID_HASH_KEY] = hash;
    } else if (obj.nodeType !== undefined) {
      // At this point we couldn't get the IE `uniqueID` to use as a hash
      // and we couldn't use a non-enumerable property to exploit the
      // dontEnum bug so we simply add the `UID_HASH_KEY` on the node
      // itself.
      obj[UID_HASH_KEY] = hash;
    } else {
      throw new Error('Unable to set a non-enumerable property on object.');
    }

    return hash;
  }

  // Get references to ES5 object methods.
  var isExtensible = Object.isExtensible;

  // True if Object.defineProperty works as expected. IE8 fails this test.
  var canDefineProperty = (function() {
    try {
      Object.defineProperty({}, '@', {});
      return true;
    } catch (e) {
      return false;
    }
  }());

  // IE has a `uniqueID` property on DOM nodes. We can construct the hash from it
  // and avoid memory leaks from the IE cloneNode bug.
  function getIENodeHash(node) {
    if (node && node.nodeType > 0) {
      switch (node.nodeType) {
        case 1: // Element
          return node.uniqueID;
        case 9: // Document
          return node.documentElement && node.documentElement.uniqueID;
      }
    }
  }

  // If possible, use a WeakMap.
  var usingWeakMap = typeof WeakMap === 'function';
  var weakMap;
  if (usingWeakMap) {
    weakMap = new WeakMap();
  }

  var objHashUID = 0;

  var UID_HASH_KEY = '__immutablehash__';
  if (typeof Symbol === 'function') {
    UID_HASH_KEY = Symbol(UID_HASH_KEY);
  }

  var STRING_HASH_CACHE_MIN_STRLEN = 16;
  var STRING_HASH_CACHE_MAX_SIZE = 255;
  var STRING_HASH_CACHE_SIZE = 0;
  var stringHashCache = {};

  function assertNotInfinite(size) {
    invariant(
      size !== Infinity,
      'Cannot perform this action with an infinite size.'
    );
  }

  createClass(Map, KeyedCollection);

    // @pragma Construction

    function Map(value) {
      return value === null || value === undefined ? emptyMap() :
        isMap(value) && !isOrdered(value) ? value :
        emptyMap().withMutations(function(map ) {
          var iter = KeyedIterable(value);
          assertNotInfinite(iter.size);
          iter.forEach(function(v, k)  {return map.set(k, v)});
        });
    }

    Map.of = function() {var keyValues = SLICE$0.call(arguments, 0);
      return emptyMap().withMutations(function(map ) {
        for (var i = 0; i < keyValues.length; i += 2) {
          if (i + 1 >= keyValues.length) {
            throw new Error('Missing value for key: ' + keyValues[i]);
          }
          map.set(keyValues[i], keyValues[i + 1]);
        }
      });
    };

    Map.prototype.toString = function() {
      return this.__toString('Map {', '}');
    };

    // @pragma Access

    Map.prototype.get = function(k, notSetValue) {
      return this._root ?
        this._root.get(0, undefined, k, notSetValue) :
        notSetValue;
    };

    // @pragma Modification

    Map.prototype.set = function(k, v) {
      return updateMap(this, k, v);
    };

    Map.prototype.setIn = function(keyPath, v) {
      return this.updateIn(keyPath, NOT_SET, function()  {return v});
    };

    Map.prototype.remove = function(k) {
      return updateMap(this, k, NOT_SET);
    };

    Map.prototype.deleteIn = function(keyPath) {
      return this.updateIn(keyPath, function()  {return NOT_SET});
    };

    Map.prototype.update = function(k, notSetValue, updater) {
      return arguments.length === 1 ?
        k(this) :
        this.updateIn([k], notSetValue, updater);
    };

    Map.prototype.updateIn = function(keyPath, notSetValue, updater) {
      if (!updater) {
        updater = notSetValue;
        notSetValue = undefined;
      }
      var updatedValue = updateInDeepMap(
        this,
        forceIterator(keyPath),
        notSetValue,
        updater
      );
      return updatedValue === NOT_SET ? undefined : updatedValue;
    };

    Map.prototype.clear = function() {
      if (this.size === 0) {
        return this;
      }
      if (this.__ownerID) {
        this.size = 0;
        this._root = null;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return emptyMap();
    };

    // @pragma Composition

    Map.prototype.merge = function(/*...iters*/) {
      return mergeIntoMapWith(this, undefined, arguments);
    };

    Map.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
      return mergeIntoMapWith(this, merger, iters);
    };

    Map.prototype.mergeIn = function(keyPath) {var iters = SLICE$0.call(arguments, 1);
      return this.updateIn(
        keyPath,
        emptyMap(),
        function(m ) {return typeof m.merge === 'function' ?
          m.merge.apply(m, iters) :
          iters[iters.length - 1]}
      );
    };

    Map.prototype.mergeDeep = function(/*...iters*/) {
      return mergeIntoMapWith(this, deepMerger, arguments);
    };

    Map.prototype.mergeDeepWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
      return mergeIntoMapWith(this, deepMergerWith(merger), iters);
    };

    Map.prototype.mergeDeepIn = function(keyPath) {var iters = SLICE$0.call(arguments, 1);
      return this.updateIn(
        keyPath,
        emptyMap(),
        function(m ) {return typeof m.mergeDeep === 'function' ?
          m.mergeDeep.apply(m, iters) :
          iters[iters.length - 1]}
      );
    };

    Map.prototype.sort = function(comparator) {
      // Late binding
      return OrderedMap(sortFactory(this, comparator));
    };

    Map.prototype.sortBy = function(mapper, comparator) {
      // Late binding
      return OrderedMap(sortFactory(this, comparator, mapper));
    };

    // @pragma Mutability

    Map.prototype.withMutations = function(fn) {
      var mutable = this.asMutable();
      fn(mutable);
      return mutable.wasAltered() ? mutable.__ensureOwner(this.__ownerID) : this;
    };

    Map.prototype.asMutable = function() {
      return this.__ownerID ? this : this.__ensureOwner(new OwnerID());
    };

    Map.prototype.asImmutable = function() {
      return this.__ensureOwner();
    };

    Map.prototype.wasAltered = function() {
      return this.__altered;
    };

    Map.prototype.__iterator = function(type, reverse) {
      return new MapIterator(this, type, reverse);
    };

    Map.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      var iterations = 0;
      this._root && this._root.iterate(function(entry ) {
        iterations++;
        return fn(entry[1], entry[0], this$0);
      }, reverse);
      return iterations;
    };

    Map.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      if (!ownerID) {
        this.__ownerID = ownerID;
        this.__altered = false;
        return this;
      }
      return makeMap(this.size, this._root, ownerID, this.__hash);
    };


  function isMap(maybeMap) {
    return !!(maybeMap && maybeMap[IS_MAP_SENTINEL]);
  }

  Map.isMap = isMap;

  var IS_MAP_SENTINEL = '@@__IMMUTABLE_MAP__@@';

  var MapPrototype = Map.prototype;
  MapPrototype[IS_MAP_SENTINEL] = true;
  MapPrototype[DELETE] = MapPrototype.remove;
  MapPrototype.removeIn = MapPrototype.deleteIn;


  // #pragma Trie Nodes



    function ArrayMapNode(ownerID, entries) {
      this.ownerID = ownerID;
      this.entries = entries;
    }

    ArrayMapNode.prototype.get = function(shift, keyHash, key, notSetValue) {
      var entries = this.entries;
      for (var ii = 0, len = entries.length; ii < len; ii++) {
        if (is(key, entries[ii][0])) {
          return entries[ii][1];
        }
      }
      return notSetValue;
    };

    ArrayMapNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
      var removed = value === NOT_SET;

      var entries = this.entries;
      var idx = 0;
      for (var len = entries.length; idx < len; idx++) {
        if (is(key, entries[idx][0])) {
          break;
        }
      }
      var exists = idx < len;

      if (exists ? entries[idx][1] === value : removed) {
        return this;
      }

      SetRef(didAlter);
      (removed || !exists) && SetRef(didChangeSize);

      if (removed && entries.length === 1) {
        return; // undefined
      }

      if (!exists && !removed && entries.length >= MAX_ARRAY_MAP_SIZE) {
        return createNodes(ownerID, entries, key, value);
      }

      var isEditable = ownerID && ownerID === this.ownerID;
      var newEntries = isEditable ? entries : arrCopy(entries);

      if (exists) {
        if (removed) {
          idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop());
        } else {
          newEntries[idx] = [key, value];
        }
      } else {
        newEntries.push([key, value]);
      }

      if (isEditable) {
        this.entries = newEntries;
        return this;
      }

      return new ArrayMapNode(ownerID, newEntries);
    };




    function BitmapIndexedNode(ownerID, bitmap, nodes) {
      this.ownerID = ownerID;
      this.bitmap = bitmap;
      this.nodes = nodes;
    }

    BitmapIndexedNode.prototype.get = function(shift, keyHash, key, notSetValue) {
      if (keyHash === undefined) {
        keyHash = hash(key);
      }
      var bit = (1 << ((shift === 0 ? keyHash : keyHash >>> shift) & MASK));
      var bitmap = this.bitmap;
      return (bitmap & bit) === 0 ? notSetValue :
        this.nodes[popCount(bitmap & (bit - 1))].get(shift + SHIFT, keyHash, key, notSetValue);
    };

    BitmapIndexedNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
      if (keyHash === undefined) {
        keyHash = hash(key);
      }
      var keyHashFrag = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
      var bit = 1 << keyHashFrag;
      var bitmap = this.bitmap;
      var exists = (bitmap & bit) !== 0;

      if (!exists && value === NOT_SET) {
        return this;
      }

      var idx = popCount(bitmap & (bit - 1));
      var nodes = this.nodes;
      var node = exists ? nodes[idx] : undefined;
      var newNode = updateNode(node, ownerID, shift + SHIFT, keyHash, key, value, didChangeSize, didAlter);

      if (newNode === node) {
        return this;
      }

      if (!exists && newNode && nodes.length >= MAX_BITMAP_INDEXED_SIZE) {
        return expandNodes(ownerID, nodes, bitmap, keyHashFrag, newNode);
      }

      if (exists && !newNode && nodes.length === 2 && isLeafNode(nodes[idx ^ 1])) {
        return nodes[idx ^ 1];
      }

      if (exists && newNode && nodes.length === 1 && isLeafNode(newNode)) {
        return newNode;
      }

      var isEditable = ownerID && ownerID === this.ownerID;
      var newBitmap = exists ? newNode ? bitmap : bitmap ^ bit : bitmap | bit;
      var newNodes = exists ? newNode ?
        setIn(nodes, idx, newNode, isEditable) :
        spliceOut(nodes, idx, isEditable) :
        spliceIn(nodes, idx, newNode, isEditable);

      if (isEditable) {
        this.bitmap = newBitmap;
        this.nodes = newNodes;
        return this;
      }

      return new BitmapIndexedNode(ownerID, newBitmap, newNodes);
    };




    function HashArrayMapNode(ownerID, count, nodes) {
      this.ownerID = ownerID;
      this.count = count;
      this.nodes = nodes;
    }

    HashArrayMapNode.prototype.get = function(shift, keyHash, key, notSetValue) {
      if (keyHash === undefined) {
        keyHash = hash(key);
      }
      var idx = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
      var node = this.nodes[idx];
      return node ? node.get(shift + SHIFT, keyHash, key, notSetValue) : notSetValue;
    };

    HashArrayMapNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
      if (keyHash === undefined) {
        keyHash = hash(key);
      }
      var idx = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;
      var removed = value === NOT_SET;
      var nodes = this.nodes;
      var node = nodes[idx];

      if (removed && !node) {
        return this;
      }

      var newNode = updateNode(node, ownerID, shift + SHIFT, keyHash, key, value, didChangeSize, didAlter);
      if (newNode === node) {
        return this;
      }

      var newCount = this.count;
      if (!node) {
        newCount++;
      } else if (!newNode) {
        newCount--;
        if (newCount < MIN_HASH_ARRAY_MAP_SIZE) {
          return packNodes(ownerID, nodes, newCount, idx);
        }
      }

      var isEditable = ownerID && ownerID === this.ownerID;
      var newNodes = setIn(nodes, idx, newNode, isEditable);

      if (isEditable) {
        this.count = newCount;
        this.nodes = newNodes;
        return this;
      }

      return new HashArrayMapNode(ownerID, newCount, newNodes);
    };




    function HashCollisionNode(ownerID, keyHash, entries) {
      this.ownerID = ownerID;
      this.keyHash = keyHash;
      this.entries = entries;
    }

    HashCollisionNode.prototype.get = function(shift, keyHash, key, notSetValue) {
      var entries = this.entries;
      for (var ii = 0, len = entries.length; ii < len; ii++) {
        if (is(key, entries[ii][0])) {
          return entries[ii][1];
        }
      }
      return notSetValue;
    };

    HashCollisionNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
      if (keyHash === undefined) {
        keyHash = hash(key);
      }

      var removed = value === NOT_SET;

      if (keyHash !== this.keyHash) {
        if (removed) {
          return this;
        }
        SetRef(didAlter);
        SetRef(didChangeSize);
        return mergeIntoNode(this, ownerID, shift, keyHash, [key, value]);
      }

      var entries = this.entries;
      var idx = 0;
      for (var len = entries.length; idx < len; idx++) {
        if (is(key, entries[idx][0])) {
          break;
        }
      }
      var exists = idx < len;

      if (exists ? entries[idx][1] === value : removed) {
        return this;
      }

      SetRef(didAlter);
      (removed || !exists) && SetRef(didChangeSize);

      if (removed && len === 2) {
        return new ValueNode(ownerID, this.keyHash, entries[idx ^ 1]);
      }

      var isEditable = ownerID && ownerID === this.ownerID;
      var newEntries = isEditable ? entries : arrCopy(entries);

      if (exists) {
        if (removed) {
          idx === len - 1 ? newEntries.pop() : (newEntries[idx] = newEntries.pop());
        } else {
          newEntries[idx] = [key, value];
        }
      } else {
        newEntries.push([key, value]);
      }

      if (isEditable) {
        this.entries = newEntries;
        return this;
      }

      return new HashCollisionNode(ownerID, this.keyHash, newEntries);
    };




    function ValueNode(ownerID, keyHash, entry) {
      this.ownerID = ownerID;
      this.keyHash = keyHash;
      this.entry = entry;
    }

    ValueNode.prototype.get = function(shift, keyHash, key, notSetValue) {
      return is(key, this.entry[0]) ? this.entry[1] : notSetValue;
    };

    ValueNode.prototype.update = function(ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
      var removed = value === NOT_SET;
      var keyMatch = is(key, this.entry[0]);
      if (keyMatch ? value === this.entry[1] : removed) {
        return this;
      }

      SetRef(didAlter);

      if (removed) {
        SetRef(didChangeSize);
        return; // undefined
      }

      if (keyMatch) {
        if (ownerID && ownerID === this.ownerID) {
          this.entry[1] = value;
          return this;
        }
        return new ValueNode(ownerID, this.keyHash, [key, value]);
      }

      SetRef(didChangeSize);
      return mergeIntoNode(this, ownerID, shift, hash(key), [key, value]);
    };



  // #pragma Iterators

  ArrayMapNode.prototype.iterate =
  HashCollisionNode.prototype.iterate = function (fn, reverse) {
    var entries = this.entries;
    for (var ii = 0, maxIndex = entries.length - 1; ii <= maxIndex; ii++) {
      if (fn(entries[reverse ? maxIndex - ii : ii]) === false) {
        return false;
      }
    }
  }

  BitmapIndexedNode.prototype.iterate =
  HashArrayMapNode.prototype.iterate = function (fn, reverse) {
    var nodes = this.nodes;
    for (var ii = 0, maxIndex = nodes.length - 1; ii <= maxIndex; ii++) {
      var node = nodes[reverse ? maxIndex - ii : ii];
      if (node && node.iterate(fn, reverse) === false) {
        return false;
      }
    }
  }

  ValueNode.prototype.iterate = function (fn, reverse) {
    return fn(this.entry);
  }

  createClass(MapIterator, Iterator);

    function MapIterator(map, type, reverse) {
      this._type = type;
      this._reverse = reverse;
      this._stack = map._root && mapIteratorFrame(map._root);
    }

    MapIterator.prototype.next = function() {
      var type = this._type;
      var stack = this._stack;
      while (stack) {
        var node = stack.node;
        var index = stack.index++;
        var maxIndex;
        if (node.entry) {
          if (index === 0) {
            return mapIteratorValue(type, node.entry);
          }
        } else if (node.entries) {
          maxIndex = node.entries.length - 1;
          if (index <= maxIndex) {
            return mapIteratorValue(type, node.entries[this._reverse ? maxIndex - index : index]);
          }
        } else {
          maxIndex = node.nodes.length - 1;
          if (index <= maxIndex) {
            var subNode = node.nodes[this._reverse ? maxIndex - index : index];
            if (subNode) {
              if (subNode.entry) {
                return mapIteratorValue(type, subNode.entry);
              }
              stack = this._stack = mapIteratorFrame(subNode, stack);
            }
            continue;
          }
        }
        stack = this._stack = this._stack.__prev;
      }
      return iteratorDone();
    };


  function mapIteratorValue(type, entry) {
    return iteratorValue(type, entry[0], entry[1]);
  }

  function mapIteratorFrame(node, prev) {
    return {
      node: node,
      index: 0,
      __prev: prev
    };
  }

  function makeMap(size, root, ownerID, hash) {
    var map = Object.create(MapPrototype);
    map.size = size;
    map._root = root;
    map.__ownerID = ownerID;
    map.__hash = hash;
    map.__altered = false;
    return map;
  }

  var EMPTY_MAP;
  function emptyMap() {
    return EMPTY_MAP || (EMPTY_MAP = makeMap(0));
  }

  function updateMap(map, k, v) {
    var newRoot;
    var newSize;
    if (!map._root) {
      if (v === NOT_SET) {
        return map;
      }
      newSize = 1;
      newRoot = new ArrayMapNode(map.__ownerID, [[k, v]]);
    } else {
      var didChangeSize = MakeRef(CHANGE_LENGTH);
      var didAlter = MakeRef(DID_ALTER);
      newRoot = updateNode(map._root, map.__ownerID, 0, undefined, k, v, didChangeSize, didAlter);
      if (!didAlter.value) {
        return map;
      }
      newSize = map.size + (didChangeSize.value ? v === NOT_SET ? -1 : 1 : 0);
    }
    if (map.__ownerID) {
      map.size = newSize;
      map._root = newRoot;
      map.__hash = undefined;
      map.__altered = true;
      return map;
    }
    return newRoot ? makeMap(newSize, newRoot) : emptyMap();
  }

  function updateNode(node, ownerID, shift, keyHash, key, value, didChangeSize, didAlter) {
    if (!node) {
      if (value === NOT_SET) {
        return node;
      }
      SetRef(didAlter);
      SetRef(didChangeSize);
      return new ValueNode(ownerID, keyHash, [key, value]);
    }
    return node.update(ownerID, shift, keyHash, key, value, didChangeSize, didAlter);
  }

  function isLeafNode(node) {
    return node.constructor === ValueNode || node.constructor === HashCollisionNode;
  }

  function mergeIntoNode(node, ownerID, shift, keyHash, entry) {
    if (node.keyHash === keyHash) {
      return new HashCollisionNode(ownerID, keyHash, [node.entry, entry]);
    }

    var idx1 = (shift === 0 ? node.keyHash : node.keyHash >>> shift) & MASK;
    var idx2 = (shift === 0 ? keyHash : keyHash >>> shift) & MASK;

    var newNode;
    var nodes = idx1 === idx2 ?
      [mergeIntoNode(node, ownerID, shift + SHIFT, keyHash, entry)] :
      ((newNode = new ValueNode(ownerID, keyHash, entry)), idx1 < idx2 ? [node, newNode] : [newNode, node]);

    return new BitmapIndexedNode(ownerID, (1 << idx1) | (1 << idx2), nodes);
  }

  function createNodes(ownerID, entries, key, value) {
    if (!ownerID) {
      ownerID = new OwnerID();
    }
    var node = new ValueNode(ownerID, hash(key), [key, value]);
    for (var ii = 0; ii < entries.length; ii++) {
      var entry = entries[ii];
      node = node.update(ownerID, 0, undefined, entry[0], entry[1]);
    }
    return node;
  }

  function packNodes(ownerID, nodes, count, excluding) {
    var bitmap = 0;
    var packedII = 0;
    var packedNodes = new Array(count);
    for (var ii = 0, bit = 1, len = nodes.length; ii < len; ii++, bit <<= 1) {
      var node = nodes[ii];
      if (node !== undefined && ii !== excluding) {
        bitmap |= bit;
        packedNodes[packedII++] = node;
      }
    }
    return new BitmapIndexedNode(ownerID, bitmap, packedNodes);
  }

  function expandNodes(ownerID, nodes, bitmap, including, node) {
    var count = 0;
    var expandedNodes = new Array(SIZE);
    for (var ii = 0; bitmap !== 0; ii++, bitmap >>>= 1) {
      expandedNodes[ii] = bitmap & 1 ? nodes[count++] : undefined;
    }
    expandedNodes[including] = node;
    return new HashArrayMapNode(ownerID, count + 1, expandedNodes);
  }

  function mergeIntoMapWith(map, merger, iterables) {
    var iters = [];
    for (var ii = 0; ii < iterables.length; ii++) {
      var value = iterables[ii];
      var iter = KeyedIterable(value);
      if (!isIterable(value)) {
        iter = iter.map(function(v ) {return fromJS(v)});
      }
      iters.push(iter);
    }
    return mergeIntoCollectionWith(map, merger, iters);
  }

  function deepMerger(existing, value, key) {
    return existing && existing.mergeDeep && isIterable(value) ?
      existing.mergeDeep(value) :
      is(existing, value) ? existing : value;
  }

  function deepMergerWith(merger) {
    return function(existing, value, key)  {
      if (existing && existing.mergeDeepWith && isIterable(value)) {
        return existing.mergeDeepWith(merger, value);
      }
      var nextValue = merger(existing, value, key);
      return is(existing, nextValue) ? existing : nextValue;
    };
  }

  function mergeIntoCollectionWith(collection, merger, iters) {
    iters = iters.filter(function(x ) {return x.size !== 0});
    if (iters.length === 0) {
      return collection;
    }
    if (collection.size === 0 && !collection.__ownerID && iters.length === 1) {
      return collection.constructor(iters[0]);
    }
    return collection.withMutations(function(collection ) {
      var mergeIntoMap = merger ?
        function(value, key)  {
          collection.update(key, NOT_SET, function(existing )
            {return existing === NOT_SET ? value : merger(existing, value, key)}
          );
        } :
        function(value, key)  {
          collection.set(key, value);
        }
      for (var ii = 0; ii < iters.length; ii++) {
        iters[ii].forEach(mergeIntoMap);
      }
    });
  }

  function updateInDeepMap(existing, keyPathIter, notSetValue, updater) {
    var isNotSet = existing === NOT_SET;
    var step = keyPathIter.next();
    if (step.done) {
      var existingValue = isNotSet ? notSetValue : existing;
      var newValue = updater(existingValue);
      return newValue === existingValue ? existing : newValue;
    }
    invariant(
      isNotSet || (existing && existing.set),
      'invalid keyPath'
    );
    var key = step.value;
    var nextExisting = isNotSet ? NOT_SET : existing.get(key, NOT_SET);
    var nextUpdated = updateInDeepMap(
      nextExisting,
      keyPathIter,
      notSetValue,
      updater
    );
    return nextUpdated === nextExisting ? existing :
      nextUpdated === NOT_SET ? existing.remove(key) :
      (isNotSet ? emptyMap() : existing).set(key, nextUpdated);
  }

  function popCount(x) {
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0f0f0f0f;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x7f;
  }

  function setIn(array, idx, val, canEdit) {
    var newArray = canEdit ? array : arrCopy(array);
    newArray[idx] = val;
    return newArray;
  }

  function spliceIn(array, idx, val, canEdit) {
    var newLen = array.length + 1;
    if (canEdit && idx + 1 === newLen) {
      array[idx] = val;
      return array;
    }
    var newArray = new Array(newLen);
    var after = 0;
    for (var ii = 0; ii < newLen; ii++) {
      if (ii === idx) {
        newArray[ii] = val;
        after = -1;
      } else {
        newArray[ii] = array[ii + after];
      }
    }
    return newArray;
  }

  function spliceOut(array, idx, canEdit) {
    var newLen = array.length - 1;
    if (canEdit && idx === newLen) {
      array.pop();
      return array;
    }
    var newArray = new Array(newLen);
    var after = 0;
    for (var ii = 0; ii < newLen; ii++) {
      if (ii === idx) {
        after = 1;
      }
      newArray[ii] = array[ii + after];
    }
    return newArray;
  }

  var MAX_ARRAY_MAP_SIZE = SIZE / 4;
  var MAX_BITMAP_INDEXED_SIZE = SIZE / 2;
  var MIN_HASH_ARRAY_MAP_SIZE = SIZE / 4;

  createClass(List, IndexedCollection);

    // @pragma Construction

    function List(value) {
      var empty = emptyList();
      if (value === null || value === undefined) {
        return empty;
      }
      if (isList(value)) {
        return value;
      }
      var iter = IndexedIterable(value);
      var size = iter.size;
      if (size === 0) {
        return empty;
      }
      assertNotInfinite(size);
      if (size > 0 && size < SIZE) {
        return makeList(0, size, SHIFT, null, new VNode(iter.toArray()));
      }
      return empty.withMutations(function(list ) {
        list.setSize(size);
        iter.forEach(function(v, i)  {return list.set(i, v)});
      });
    }

    List.of = function(/*...values*/) {
      return this(arguments);
    };

    List.prototype.toString = function() {
      return this.__toString('List [', ']');
    };

    // @pragma Access

    List.prototype.get = function(index, notSetValue) {
      index = wrapIndex(this, index);
      if (index >= 0 && index < this.size) {
        index += this._origin;
        var node = listNodeFor(this, index);
        return node && node.array[index & MASK];
      }
      return notSetValue;
    };

    // @pragma Modification

    List.prototype.set = function(index, value) {
      return updateList(this, index, value);
    };

    List.prototype.remove = function(index) {
      return !this.has(index) ? this :
        index === 0 ? this.shift() :
        index === this.size - 1 ? this.pop() :
        this.splice(index, 1);
    };

    List.prototype.insert = function(index, value) {
      return this.splice(index, 0, value);
    };

    List.prototype.clear = function() {
      if (this.size === 0) {
        return this;
      }
      if (this.__ownerID) {
        this.size = this._origin = this._capacity = 0;
        this._level = SHIFT;
        this._root = this._tail = null;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return emptyList();
    };

    List.prototype.push = function(/*...values*/) {
      var values = arguments;
      var oldSize = this.size;
      return this.withMutations(function(list ) {
        setListBounds(list, 0, oldSize + values.length);
        for (var ii = 0; ii < values.length; ii++) {
          list.set(oldSize + ii, values[ii]);
        }
      });
    };

    List.prototype.pop = function() {
      return setListBounds(this, 0, -1);
    };

    List.prototype.unshift = function(/*...values*/) {
      var values = arguments;
      return this.withMutations(function(list ) {
        setListBounds(list, -values.length);
        for (var ii = 0; ii < values.length; ii++) {
          list.set(ii, values[ii]);
        }
      });
    };

    List.prototype.shift = function() {
      return setListBounds(this, 1);
    };

    // @pragma Composition

    List.prototype.merge = function(/*...iters*/) {
      return mergeIntoListWith(this, undefined, arguments);
    };

    List.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
      return mergeIntoListWith(this, merger, iters);
    };

    List.prototype.mergeDeep = function(/*...iters*/) {
      return mergeIntoListWith(this, deepMerger, arguments);
    };

    List.prototype.mergeDeepWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
      return mergeIntoListWith(this, deepMergerWith(merger), iters);
    };

    List.prototype.setSize = function(size) {
      return setListBounds(this, 0, size);
    };

    // @pragma Iteration

    List.prototype.slice = function(begin, end) {
      var size = this.size;
      if (wholeSlice(begin, end, size)) {
        return this;
      }
      return setListBounds(
        this,
        resolveBegin(begin, size),
        resolveEnd(end, size)
      );
    };

    List.prototype.__iterator = function(type, reverse) {
      var index = 0;
      var values = iterateList(this, reverse);
      return new Iterator(function()  {
        var value = values();
        return value === DONE ?
          iteratorDone() :
          iteratorValue(type, index++, value);
      });
    };

    List.prototype.__iterate = function(fn, reverse) {
      var index = 0;
      var values = iterateList(this, reverse);
      var value;
      while ((value = values()) !== DONE) {
        if (fn(value, index++, this) === false) {
          break;
        }
      }
      return index;
    };

    List.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      if (!ownerID) {
        this.__ownerID = ownerID;
        return this;
      }
      return makeList(this._origin, this._capacity, this._level, this._root, this._tail, ownerID, this.__hash);
    };


  function isList(maybeList) {
    return !!(maybeList && maybeList[IS_LIST_SENTINEL]);
  }

  List.isList = isList;

  var IS_LIST_SENTINEL = '@@__IMMUTABLE_LIST__@@';

  var ListPrototype = List.prototype;
  ListPrototype[IS_LIST_SENTINEL] = true;
  ListPrototype[DELETE] = ListPrototype.remove;
  ListPrototype.setIn = MapPrototype.setIn;
  ListPrototype.deleteIn =
  ListPrototype.removeIn = MapPrototype.removeIn;
  ListPrototype.update = MapPrototype.update;
  ListPrototype.updateIn = MapPrototype.updateIn;
  ListPrototype.mergeIn = MapPrototype.mergeIn;
  ListPrototype.mergeDeepIn = MapPrototype.mergeDeepIn;
  ListPrototype.withMutations = MapPrototype.withMutations;
  ListPrototype.asMutable = MapPrototype.asMutable;
  ListPrototype.asImmutable = MapPrototype.asImmutable;
  ListPrototype.wasAltered = MapPrototype.wasAltered;



    function VNode(array, ownerID) {
      this.array = array;
      this.ownerID = ownerID;
    }

    // TODO: seems like these methods are very similar

    VNode.prototype.removeBefore = function(ownerID, level, index) {
      if (index === level ? 1 << level :  false || this.array.length === 0) {
        return this;
      }
      var originIndex = (index >>> level) & MASK;
      if (originIndex >= this.array.length) {
        return new VNode([], ownerID);
      }
      var removingFirst = originIndex === 0;
      var newChild;
      if (level > 0) {
        var oldChild = this.array[originIndex];
        newChild = oldChild && oldChild.removeBefore(ownerID, level - SHIFT, index);
        if (newChild === oldChild && removingFirst) {
          return this;
        }
      }
      if (removingFirst && !newChild) {
        return this;
      }
      var editable = editableVNode(this, ownerID);
      if (!removingFirst) {
        for (var ii = 0; ii < originIndex; ii++) {
          editable.array[ii] = undefined;
        }
      }
      if (newChild) {
        editable.array[originIndex] = newChild;
      }
      return editable;
    };

    VNode.prototype.removeAfter = function(ownerID, level, index) {
      if (index === (level ? 1 << level : 0) || this.array.length === 0) {
        return this;
      }
      var sizeIndex = ((index - 1) >>> level) & MASK;
      if (sizeIndex >= this.array.length) {
        return this;
      }

      var newChild;
      if (level > 0) {
        var oldChild = this.array[sizeIndex];
        newChild = oldChild && oldChild.removeAfter(ownerID, level - SHIFT, index);
        if (newChild === oldChild && sizeIndex === this.array.length - 1) {
          return this;
        }
      }

      var editable = editableVNode(this, ownerID);
      editable.array.splice(sizeIndex + 1);
      if (newChild) {
        editable.array[sizeIndex] = newChild;
      }
      return editable;
    };



  var DONE = {};

  function iterateList(list, reverse) {
    var left = list._origin;
    var right = list._capacity;
    var tailPos = getTailOffset(right);
    var tail = list._tail;

    return iterateNodeOrLeaf(list._root, list._level, 0);

    function iterateNodeOrLeaf(node, level, offset) {
      return level === 0 ?
        iterateLeaf(node, offset) :
        iterateNode(node, level, offset);
    }

    function iterateLeaf(node, offset) {
      var array = offset === tailPos ? tail && tail.array : node && node.array;
      var from = offset > left ? 0 : left - offset;
      var to = right - offset;
      if (to > SIZE) {
        to = SIZE;
      }
      return function()  {
        if (from === to) {
          return DONE;
        }
        var idx = reverse ? --to : from++;
        return array && array[idx];
      };
    }

    function iterateNode(node, level, offset) {
      var values;
      var array = node && node.array;
      var from = offset > left ? 0 : (left - offset) >> level;
      var to = ((right - offset) >> level) + 1;
      if (to > SIZE) {
        to = SIZE;
      }
      return function()  {
        do {
          if (values) {
            var value = values();
            if (value !== DONE) {
              return value;
            }
            values = null;
          }
          if (from === to) {
            return DONE;
          }
          var idx = reverse ? --to : from++;
          values = iterateNodeOrLeaf(
            array && array[idx], level - SHIFT, offset + (idx << level)
          );
        } while (true);
      };
    }
  }

  function makeList(origin, capacity, level, root, tail, ownerID, hash) {
    var list = Object.create(ListPrototype);
    list.size = capacity - origin;
    list._origin = origin;
    list._capacity = capacity;
    list._level = level;
    list._root = root;
    list._tail = tail;
    list.__ownerID = ownerID;
    list.__hash = hash;
    list.__altered = false;
    return list;
  }

  var EMPTY_LIST;
  function emptyList() {
    return EMPTY_LIST || (EMPTY_LIST = makeList(0, 0, SHIFT));
  }

  function updateList(list, index, value) {
    index = wrapIndex(list, index);

    if (index !== index) {
      return list;
    }

    if (index >= list.size || index < 0) {
      return list.withMutations(function(list ) {
        index < 0 ?
          setListBounds(list, index).set(0, value) :
          setListBounds(list, 0, index + 1).set(index, value)
      });
    }

    index += list._origin;

    var newTail = list._tail;
    var newRoot = list._root;
    var didAlter = MakeRef(DID_ALTER);
    if (index >= getTailOffset(list._capacity)) {
      newTail = updateVNode(newTail, list.__ownerID, 0, index, value, didAlter);
    } else {
      newRoot = updateVNode(newRoot, list.__ownerID, list._level, index, value, didAlter);
    }

    if (!didAlter.value) {
      return list;
    }

    if (list.__ownerID) {
      list._root = newRoot;
      list._tail = newTail;
      list.__hash = undefined;
      list.__altered = true;
      return list;
    }
    return makeList(list._origin, list._capacity, list._level, newRoot, newTail);
  }

  function updateVNode(node, ownerID, level, index, value, didAlter) {
    var idx = (index >>> level) & MASK;
    var nodeHas = node && idx < node.array.length;
    if (!nodeHas && value === undefined) {
      return node;
    }

    var newNode;

    if (level > 0) {
      var lowerNode = node && node.array[idx];
      var newLowerNode = updateVNode(lowerNode, ownerID, level - SHIFT, index, value, didAlter);
      if (newLowerNode === lowerNode) {
        return node;
      }
      newNode = editableVNode(node, ownerID);
      newNode.array[idx] = newLowerNode;
      return newNode;
    }

    if (nodeHas && node.array[idx] === value) {
      return node;
    }

    SetRef(didAlter);

    newNode = editableVNode(node, ownerID);
    if (value === undefined && idx === newNode.array.length - 1) {
      newNode.array.pop();
    } else {
      newNode.array[idx] = value;
    }
    return newNode;
  }

  function editableVNode(node, ownerID) {
    if (ownerID && node && ownerID === node.ownerID) {
      return node;
    }
    return new VNode(node ? node.array.slice() : [], ownerID);
  }

  function listNodeFor(list, rawIndex) {
    if (rawIndex >= getTailOffset(list._capacity)) {
      return list._tail;
    }
    if (rawIndex < 1 << (list._level + SHIFT)) {
      var node = list._root;
      var level = list._level;
      while (node && level > 0) {
        node = node.array[(rawIndex >>> level) & MASK];
        level -= SHIFT;
      }
      return node;
    }
  }

  function setListBounds(list, begin, end) {
    // Sanitize begin & end using this shorthand for ToInt32(argument)
    // http://www.ecma-international.org/ecma-262/6.0/#sec-toint32
    if (begin !== undefined) {
      begin = begin | 0;
    }
    if (end !== undefined) {
      end = end | 0;
    }
    var owner = list.__ownerID || new OwnerID();
    var oldOrigin = list._origin;
    var oldCapacity = list._capacity;
    var newOrigin = oldOrigin + begin;
    var newCapacity = end === undefined ? oldCapacity : end < 0 ? oldCapacity + end : oldOrigin + end;
    if (newOrigin === oldOrigin && newCapacity === oldCapacity) {
      return list;
    }

    // If it's going to end after it starts, it's empty.
    if (newOrigin >= newCapacity) {
      return list.clear();
    }

    var newLevel = list._level;
    var newRoot = list._root;

    // New origin might need creating a higher root.
    var offsetShift = 0;
    while (newOrigin + offsetShift < 0) {
      newRoot = new VNode(newRoot && newRoot.array.length ? [undefined, newRoot] : [], owner);
      newLevel += SHIFT;
      offsetShift += 1 << newLevel;
    }
    if (offsetShift) {
      newOrigin += offsetShift;
      oldOrigin += offsetShift;
      newCapacity += offsetShift;
      oldCapacity += offsetShift;
    }

    var oldTailOffset = getTailOffset(oldCapacity);
    var newTailOffset = getTailOffset(newCapacity);

    // New size might need creating a higher root.
    while (newTailOffset >= 1 << (newLevel + SHIFT)) {
      newRoot = new VNode(newRoot && newRoot.array.length ? [newRoot] : [], owner);
      newLevel += SHIFT;
    }

    // Locate or create the new tail.
    var oldTail = list._tail;
    var newTail = newTailOffset < oldTailOffset ?
      listNodeFor(list, newCapacity - 1) :
      newTailOffset > oldTailOffset ? new VNode([], owner) : oldTail;

    // Merge Tail into tree.
    if (oldTail && newTailOffset > oldTailOffset && newOrigin < oldCapacity && oldTail.array.length) {
      newRoot = editableVNode(newRoot, owner);
      var node = newRoot;
      for (var level = newLevel; level > SHIFT; level -= SHIFT) {
        var idx = (oldTailOffset >>> level) & MASK;
        node = node.array[idx] = editableVNode(node.array[idx], owner);
      }
      node.array[(oldTailOffset >>> SHIFT) & MASK] = oldTail;
    }

    // If the size has been reduced, there's a chance the tail needs to be trimmed.
    if (newCapacity < oldCapacity) {
      newTail = newTail && newTail.removeAfter(owner, 0, newCapacity);
    }

    // If the new origin is within the tail, then we do not need a root.
    if (newOrigin >= newTailOffset) {
      newOrigin -= newTailOffset;
      newCapacity -= newTailOffset;
      newLevel = SHIFT;
      newRoot = null;
      newTail = newTail && newTail.removeBefore(owner, 0, newOrigin);

    // Otherwise, if the root has been trimmed, garbage collect.
    } else if (newOrigin > oldOrigin || newTailOffset < oldTailOffset) {
      offsetShift = 0;

      // Identify the new top root node of the subtree of the old root.
      while (newRoot) {
        var beginIndex = (newOrigin >>> newLevel) & MASK;
        if (beginIndex !== (newTailOffset >>> newLevel) & MASK) {
          break;
        }
        if (beginIndex) {
          offsetShift += (1 << newLevel) * beginIndex;
        }
        newLevel -= SHIFT;
        newRoot = newRoot.array[beginIndex];
      }

      // Trim the new sides of the new root.
      if (newRoot && newOrigin > oldOrigin) {
        newRoot = newRoot.removeBefore(owner, newLevel, newOrigin - offsetShift);
      }
      if (newRoot && newTailOffset < oldTailOffset) {
        newRoot = newRoot.removeAfter(owner, newLevel, newTailOffset - offsetShift);
      }
      if (offsetShift) {
        newOrigin -= offsetShift;
        newCapacity -= offsetShift;
      }
    }

    if (list.__ownerID) {
      list.size = newCapacity - newOrigin;
      list._origin = newOrigin;
      list._capacity = newCapacity;
      list._level = newLevel;
      list._root = newRoot;
      list._tail = newTail;
      list.__hash = undefined;
      list.__altered = true;
      return list;
    }
    return makeList(newOrigin, newCapacity, newLevel, newRoot, newTail);
  }

  function mergeIntoListWith(list, merger, iterables) {
    var iters = [];
    var maxSize = 0;
    for (var ii = 0; ii < iterables.length; ii++) {
      var value = iterables[ii];
      var iter = IndexedIterable(value);
      if (iter.size > maxSize) {
        maxSize = iter.size;
      }
      if (!isIterable(value)) {
        iter = iter.map(function(v ) {return fromJS(v)});
      }
      iters.push(iter);
    }
    if (maxSize > list.size) {
      list = list.setSize(maxSize);
    }
    return mergeIntoCollectionWith(list, merger, iters);
  }

  function getTailOffset(size) {
    return size < SIZE ? 0 : (((size - 1) >>> SHIFT) << SHIFT);
  }

  createClass(OrderedMap, Map);

    // @pragma Construction

    function OrderedMap(value) {
      return value === null || value === undefined ? emptyOrderedMap() :
        isOrderedMap(value) ? value :
        emptyOrderedMap().withMutations(function(map ) {
          var iter = KeyedIterable(value);
          assertNotInfinite(iter.size);
          iter.forEach(function(v, k)  {return map.set(k, v)});
        });
    }

    OrderedMap.of = function(/*...values*/) {
      return this(arguments);
    };

    OrderedMap.prototype.toString = function() {
      return this.__toString('OrderedMap {', '}');
    };

    // @pragma Access

    OrderedMap.prototype.get = function(k, notSetValue) {
      var index = this._map.get(k);
      return index !== undefined ? this._list.get(index)[1] : notSetValue;
    };

    // @pragma Modification

    OrderedMap.prototype.clear = function() {
      if (this.size === 0) {
        return this;
      }
      if (this.__ownerID) {
        this.size = 0;
        this._map.clear();
        this._list.clear();
        return this;
      }
      return emptyOrderedMap();
    };

    OrderedMap.prototype.set = function(k, v) {
      return updateOrderedMap(this, k, v);
    };

    OrderedMap.prototype.remove = function(k) {
      return updateOrderedMap(this, k, NOT_SET);
    };

    OrderedMap.prototype.wasAltered = function() {
      return this._map.wasAltered() || this._list.wasAltered();
    };

    OrderedMap.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      return this._list.__iterate(
        function(entry ) {return entry && fn(entry[1], entry[0], this$0)},
        reverse
      );
    };

    OrderedMap.prototype.__iterator = function(type, reverse) {
      return this._list.fromEntrySeq().__iterator(type, reverse);
    };

    OrderedMap.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      var newMap = this._map.__ensureOwner(ownerID);
      var newList = this._list.__ensureOwner(ownerID);
      if (!ownerID) {
        this.__ownerID = ownerID;
        this._map = newMap;
        this._list = newList;
        return this;
      }
      return makeOrderedMap(newMap, newList, ownerID, this.__hash);
    };


  function isOrderedMap(maybeOrderedMap) {
    return isMap(maybeOrderedMap) && isOrdered(maybeOrderedMap);
  }

  OrderedMap.isOrderedMap = isOrderedMap;

  OrderedMap.prototype[IS_ORDERED_SENTINEL] = true;
  OrderedMap.prototype[DELETE] = OrderedMap.prototype.remove;



  function makeOrderedMap(map, list, ownerID, hash) {
    var omap = Object.create(OrderedMap.prototype);
    omap.size = map ? map.size : 0;
    omap._map = map;
    omap._list = list;
    omap.__ownerID = ownerID;
    omap.__hash = hash;
    return omap;
  }

  var EMPTY_ORDERED_MAP;
  function emptyOrderedMap() {
    return EMPTY_ORDERED_MAP || (EMPTY_ORDERED_MAP = makeOrderedMap(emptyMap(), emptyList()));
  }

  function updateOrderedMap(omap, k, v) {
    var map = omap._map;
    var list = omap._list;
    var i = map.get(k);
    var has = i !== undefined;
    var newMap;
    var newList;
    if (v === NOT_SET) { // removed
      if (!has) {
        return omap;
      }
      if (list.size >= SIZE && list.size >= map.size * 2) {
        newList = list.filter(function(entry, idx)  {return entry !== undefined && i !== idx});
        newMap = newList.toKeyedSeq().map(function(entry ) {return entry[0]}).flip().toMap();
        if (omap.__ownerID) {
          newMap.__ownerID = newList.__ownerID = omap.__ownerID;
        }
      } else {
        newMap = map.remove(k);
        newList = i === list.size - 1 ? list.pop() : list.set(i, undefined);
      }
    } else {
      if (has) {
        if (v === list.get(i)[1]) {
          return omap;
        }
        newMap = map;
        newList = list.set(i, [k, v]);
      } else {
        newMap = map.set(k, list.size);
        newList = list.set(list.size, [k, v]);
      }
    }
    if (omap.__ownerID) {
      omap.size = newMap.size;
      omap._map = newMap;
      omap._list = newList;
      omap.__hash = undefined;
      return omap;
    }
    return makeOrderedMap(newMap, newList);
  }

  createClass(ToKeyedSequence, KeyedSeq);
    function ToKeyedSequence(indexed, useKeys) {
      this._iter = indexed;
      this._useKeys = useKeys;
      this.size = indexed.size;
    }

    ToKeyedSequence.prototype.get = function(key, notSetValue) {
      return this._iter.get(key, notSetValue);
    };

    ToKeyedSequence.prototype.has = function(key) {
      return this._iter.has(key);
    };

    ToKeyedSequence.prototype.valueSeq = function() {
      return this._iter.valueSeq();
    };

    ToKeyedSequence.prototype.reverse = function() {var this$0 = this;
      var reversedSequence = reverseFactory(this, true);
      if (!this._useKeys) {
        reversedSequence.valueSeq = function()  {return this$0._iter.toSeq().reverse()};
      }
      return reversedSequence;
    };

    ToKeyedSequence.prototype.map = function(mapper, context) {var this$0 = this;
      var mappedSequence = mapFactory(this, mapper, context);
      if (!this._useKeys) {
        mappedSequence.valueSeq = function()  {return this$0._iter.toSeq().map(mapper, context)};
      }
      return mappedSequence;
    };

    ToKeyedSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      var ii;
      return this._iter.__iterate(
        this._useKeys ?
          function(v, k)  {return fn(v, k, this$0)} :
          ((ii = reverse ? resolveSize(this) : 0),
            function(v ) {return fn(v, reverse ? --ii : ii++, this$0)}),
        reverse
      );
    };

    ToKeyedSequence.prototype.__iterator = function(type, reverse) {
      if (this._useKeys) {
        return this._iter.__iterator(type, reverse);
      }
      var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
      var ii = reverse ? resolveSize(this) : 0;
      return new Iterator(function()  {
        var step = iterator.next();
        return step.done ? step :
          iteratorValue(type, reverse ? --ii : ii++, step.value, step);
      });
    };

  ToKeyedSequence.prototype[IS_ORDERED_SENTINEL] = true;


  createClass(ToIndexedSequence, IndexedSeq);
    function ToIndexedSequence(iter) {
      this._iter = iter;
      this.size = iter.size;
    }

    ToIndexedSequence.prototype.includes = function(value) {
      return this._iter.includes(value);
    };

    ToIndexedSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      var iterations = 0;
      return this._iter.__iterate(function(v ) {return fn(v, iterations++, this$0)}, reverse);
    };

    ToIndexedSequence.prototype.__iterator = function(type, reverse) {
      var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
      var iterations = 0;
      return new Iterator(function()  {
        var step = iterator.next();
        return step.done ? step :
          iteratorValue(type, iterations++, step.value, step)
      });
    };



  createClass(ToSetSequence, SetSeq);
    function ToSetSequence(iter) {
      this._iter = iter;
      this.size = iter.size;
    }

    ToSetSequence.prototype.has = function(key) {
      return this._iter.includes(key);
    };

    ToSetSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      return this._iter.__iterate(function(v ) {return fn(v, v, this$0)}, reverse);
    };

    ToSetSequence.prototype.__iterator = function(type, reverse) {
      var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
      return new Iterator(function()  {
        var step = iterator.next();
        return step.done ? step :
          iteratorValue(type, step.value, step.value, step);
      });
    };



  createClass(FromEntriesSequence, KeyedSeq);
    function FromEntriesSequence(entries) {
      this._iter = entries;
      this.size = entries.size;
    }

    FromEntriesSequence.prototype.entrySeq = function() {
      return this._iter.toSeq();
    };

    FromEntriesSequence.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      return this._iter.__iterate(function(entry ) {
        // Check if entry exists first so array access doesn't throw for holes
        // in the parent iteration.
        if (entry) {
          validateEntry(entry);
          var indexedIterable = isIterable(entry);
          return fn(
            indexedIterable ? entry.get(1) : entry[1],
            indexedIterable ? entry.get(0) : entry[0],
            this$0
          );
        }
      }, reverse);
    };

    FromEntriesSequence.prototype.__iterator = function(type, reverse) {
      var iterator = this._iter.__iterator(ITERATE_VALUES, reverse);
      return new Iterator(function()  {
        while (true) {
          var step = iterator.next();
          if (step.done) {
            return step;
          }
          var entry = step.value;
          // Check if entry exists first so array access doesn't throw for holes
          // in the parent iteration.
          if (entry) {
            validateEntry(entry);
            var indexedIterable = isIterable(entry);
            return iteratorValue(
              type,
              indexedIterable ? entry.get(0) : entry[0],
              indexedIterable ? entry.get(1) : entry[1],
              step
            );
          }
        }
      });
    };


  ToIndexedSequence.prototype.cacheResult =
  ToKeyedSequence.prototype.cacheResult =
  ToSetSequence.prototype.cacheResult =
  FromEntriesSequence.prototype.cacheResult =
    cacheResultThrough;


  function flipFactory(iterable) {
    var flipSequence = makeSequence(iterable);
    flipSequence._iter = iterable;
    flipSequence.size = iterable.size;
    flipSequence.flip = function()  {return iterable};
    flipSequence.reverse = function () {
      var reversedSequence = iterable.reverse.apply(this); // super.reverse()
      reversedSequence.flip = function()  {return iterable.reverse()};
      return reversedSequence;
    };
    flipSequence.has = function(key ) {return iterable.includes(key)};
    flipSequence.includes = function(key ) {return iterable.has(key)};
    flipSequence.cacheResult = cacheResultThrough;
    flipSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
      return iterable.__iterate(function(v, k)  {return fn(k, v, this$0) !== false}, reverse);
    }
    flipSequence.__iteratorUncached = function(type, reverse) {
      if (type === ITERATE_ENTRIES) {
        var iterator = iterable.__iterator(type, reverse);
        return new Iterator(function()  {
          var step = iterator.next();
          if (!step.done) {
            var k = step.value[0];
            step.value[0] = step.value[1];
            step.value[1] = k;
          }
          return step;
        });
      }
      return iterable.__iterator(
        type === ITERATE_VALUES ? ITERATE_KEYS : ITERATE_VALUES,
        reverse
      );
    }
    return flipSequence;
  }


  function mapFactory(iterable, mapper, context) {
    var mappedSequence = makeSequence(iterable);
    mappedSequence.size = iterable.size;
    mappedSequence.has = function(key ) {return iterable.has(key)};
    mappedSequence.get = function(key, notSetValue)  {
      var v = iterable.get(key, NOT_SET);
      return v === NOT_SET ?
        notSetValue :
        mapper.call(context, v, key, iterable);
    };
    mappedSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
      return iterable.__iterate(
        function(v, k, c)  {return fn(mapper.call(context, v, k, c), k, this$0) !== false},
        reverse
      );
    }
    mappedSequence.__iteratorUncached = function (type, reverse) {
      var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
      return new Iterator(function()  {
        var step = iterator.next();
        if (step.done) {
          return step;
        }
        var entry = step.value;
        var key = entry[0];
        return iteratorValue(
          type,
          key,
          mapper.call(context, entry[1], key, iterable),
          step
        );
      });
    }
    return mappedSequence;
  }


  function reverseFactory(iterable, useKeys) {
    var reversedSequence = makeSequence(iterable);
    reversedSequence._iter = iterable;
    reversedSequence.size = iterable.size;
    reversedSequence.reverse = function()  {return iterable};
    if (iterable.flip) {
      reversedSequence.flip = function () {
        var flipSequence = flipFactory(iterable);
        flipSequence.reverse = function()  {return iterable.flip()};
        return flipSequence;
      };
    }
    reversedSequence.get = function(key, notSetValue) 
      {return iterable.get(useKeys ? key : -1 - key, notSetValue)};
    reversedSequence.has = function(key )
      {return iterable.has(useKeys ? key : -1 - key)};
    reversedSequence.includes = function(value ) {return iterable.includes(value)};
    reversedSequence.cacheResult = cacheResultThrough;
    reversedSequence.__iterate = function (fn, reverse) {var this$0 = this;
      return iterable.__iterate(function(v, k)  {return fn(v, k, this$0)}, !reverse);
    };
    reversedSequence.__iterator =
      function(type, reverse)  {return iterable.__iterator(type, !reverse)};
    return reversedSequence;
  }


  function filterFactory(iterable, predicate, context, useKeys) {
    var filterSequence = makeSequence(iterable);
    if (useKeys) {
      filterSequence.has = function(key ) {
        var v = iterable.get(key, NOT_SET);
        return v !== NOT_SET && !!predicate.call(context, v, key, iterable);
      };
      filterSequence.get = function(key, notSetValue)  {
        var v = iterable.get(key, NOT_SET);
        return v !== NOT_SET && predicate.call(context, v, key, iterable) ?
          v : notSetValue;
      };
    }
    filterSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
      var iterations = 0;
      iterable.__iterate(function(v, k, c)  {
        if (predicate.call(context, v, k, c)) {
          iterations++;
          return fn(v, useKeys ? k : iterations - 1, this$0);
        }
      }, reverse);
      return iterations;
    };
    filterSequence.__iteratorUncached = function (type, reverse) {
      var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
      var iterations = 0;
      return new Iterator(function()  {
        while (true) {
          var step = iterator.next();
          if (step.done) {
            return step;
          }
          var entry = step.value;
          var key = entry[0];
          var value = entry[1];
          if (predicate.call(context, value, key, iterable)) {
            return iteratorValue(type, useKeys ? key : iterations++, value, step);
          }
        }
      });
    }
    return filterSequence;
  }


  function countByFactory(iterable, grouper, context) {
    var groups = Map().asMutable();
    iterable.__iterate(function(v, k)  {
      groups.update(
        grouper.call(context, v, k, iterable),
        0,
        function(a ) {return a + 1}
      );
    });
    return groups.asImmutable();
  }


  function groupByFactory(iterable, grouper, context) {
    var isKeyedIter = isKeyed(iterable);
    var groups = (isOrdered(iterable) ? OrderedMap() : Map()).asMutable();
    iterable.__iterate(function(v, k)  {
      groups.update(
        grouper.call(context, v, k, iterable),
        function(a ) {return (a = a || [], a.push(isKeyedIter ? [k, v] : v), a)}
      );
    });
    var coerce = iterableClass(iterable);
    return groups.map(function(arr ) {return reify(iterable, coerce(arr))});
  }


  function sliceFactory(iterable, begin, end, useKeys) {
    var originalSize = iterable.size;

    // Sanitize begin & end using this shorthand for ToInt32(argument)
    // http://www.ecma-international.org/ecma-262/6.0/#sec-toint32
    if (begin !== undefined) {
      begin = begin | 0;
    }
    if (end !== undefined) {
      if (end === Infinity) {
        end = originalSize;
      } else {
        end = end | 0;
      }
    }

    if (wholeSlice(begin, end, originalSize)) {
      return iterable;
    }

    var resolvedBegin = resolveBegin(begin, originalSize);
    var resolvedEnd = resolveEnd(end, originalSize);

    // begin or end will be NaN if they were provided as negative numbers and
    // this iterable's size is unknown. In that case, cache first so there is
    // a known size and these do not resolve to NaN.
    if (resolvedBegin !== resolvedBegin || resolvedEnd !== resolvedEnd) {
      return sliceFactory(iterable.toSeq().cacheResult(), begin, end, useKeys);
    }

    // Note: resolvedEnd is undefined when the original sequence's length is
    // unknown and this slice did not supply an end and should contain all
    // elements after resolvedBegin.
    // In that case, resolvedSize will be NaN and sliceSize will remain undefined.
    var resolvedSize = resolvedEnd - resolvedBegin;
    var sliceSize;
    if (resolvedSize === resolvedSize) {
      sliceSize = resolvedSize < 0 ? 0 : resolvedSize;
    }

    var sliceSeq = makeSequence(iterable);

    // If iterable.size is undefined, the size of the realized sliceSeq is
    // unknown at this point unless the number of items to slice is 0
    sliceSeq.size = sliceSize === 0 ? sliceSize : iterable.size && sliceSize || undefined;

    if (!useKeys && isSeq(iterable) && sliceSize >= 0) {
      sliceSeq.get = function (index, notSetValue) {
        index = wrapIndex(this, index);
        return index >= 0 && index < sliceSize ?
          iterable.get(index + resolvedBegin, notSetValue) :
          notSetValue;
      }
    }

    sliceSeq.__iterateUncached = function(fn, reverse) {var this$0 = this;
      if (sliceSize === 0) {
        return 0;
      }
      if (reverse) {
        return this.cacheResult().__iterate(fn, reverse);
      }
      var skipped = 0;
      var isSkipping = true;
      var iterations = 0;
      iterable.__iterate(function(v, k)  {
        if (!(isSkipping && (isSkipping = skipped++ < resolvedBegin))) {
          iterations++;
          return fn(v, useKeys ? k : iterations - 1, this$0) !== false &&
                 iterations !== sliceSize;
        }
      });
      return iterations;
    };

    sliceSeq.__iteratorUncached = function(type, reverse) {
      if (sliceSize !== 0 && reverse) {
        return this.cacheResult().__iterator(type, reverse);
      }
      // Don't bother instantiating parent iterator if taking 0.
      var iterator = sliceSize !== 0 && iterable.__iterator(type, reverse);
      var skipped = 0;
      var iterations = 0;
      return new Iterator(function()  {
        while (skipped++ < resolvedBegin) {
          iterator.next();
        }
        if (++iterations > sliceSize) {
          return iteratorDone();
        }
        var step = iterator.next();
        if (useKeys || type === ITERATE_VALUES) {
          return step;
        } else if (type === ITERATE_KEYS) {
          return iteratorValue(type, iterations - 1, undefined, step);
        } else {
          return iteratorValue(type, iterations - 1, step.value[1], step);
        }
      });
    }

    return sliceSeq;
  }


  function takeWhileFactory(iterable, predicate, context) {
    var takeSequence = makeSequence(iterable);
    takeSequence.__iterateUncached = function(fn, reverse) {var this$0 = this;
      if (reverse) {
        return this.cacheResult().__iterate(fn, reverse);
      }
      var iterations = 0;
      iterable.__iterate(function(v, k, c) 
        {return predicate.call(context, v, k, c) && ++iterations && fn(v, k, this$0)}
      );
      return iterations;
    };
    takeSequence.__iteratorUncached = function(type, reverse) {var this$0 = this;
      if (reverse) {
        return this.cacheResult().__iterator(type, reverse);
      }
      var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
      var iterating = true;
      return new Iterator(function()  {
        if (!iterating) {
          return iteratorDone();
        }
        var step = iterator.next();
        if (step.done) {
          return step;
        }
        var entry = step.value;
        var k = entry[0];
        var v = entry[1];
        if (!predicate.call(context, v, k, this$0)) {
          iterating = false;
          return iteratorDone();
        }
        return type === ITERATE_ENTRIES ? step :
          iteratorValue(type, k, v, step);
      });
    };
    return takeSequence;
  }


  function skipWhileFactory(iterable, predicate, context, useKeys) {
    var skipSequence = makeSequence(iterable);
    skipSequence.__iterateUncached = function (fn, reverse) {var this$0 = this;
      if (reverse) {
        return this.cacheResult().__iterate(fn, reverse);
      }
      var isSkipping = true;
      var iterations = 0;
      iterable.__iterate(function(v, k, c)  {
        if (!(isSkipping && (isSkipping = predicate.call(context, v, k, c)))) {
          iterations++;
          return fn(v, useKeys ? k : iterations - 1, this$0);
        }
      });
      return iterations;
    };
    skipSequence.__iteratorUncached = function(type, reverse) {var this$0 = this;
      if (reverse) {
        return this.cacheResult().__iterator(type, reverse);
      }
      var iterator = iterable.__iterator(ITERATE_ENTRIES, reverse);
      var skipping = true;
      var iterations = 0;
      return new Iterator(function()  {
        var step, k, v;
        do {
          step = iterator.next();
          if (step.done) {
            if (useKeys || type === ITERATE_VALUES) {
              return step;
            } else if (type === ITERATE_KEYS) {
              return iteratorValue(type, iterations++, undefined, step);
            } else {
              return iteratorValue(type, iterations++, step.value[1], step);
            }
          }
          var entry = step.value;
          k = entry[0];
          v = entry[1];
          skipping && (skipping = predicate.call(context, v, k, this$0));
        } while (skipping);
        return type === ITERATE_ENTRIES ? step :
          iteratorValue(type, k, v, step);
      });
    };
    return skipSequence;
  }


  function concatFactory(iterable, values) {
    var isKeyedIterable = isKeyed(iterable);
    var iters = [iterable].concat(values).map(function(v ) {
      if (!isIterable(v)) {
        v = isKeyedIterable ?
          keyedSeqFromValue(v) :
          indexedSeqFromValue(Array.isArray(v) ? v : [v]);
      } else if (isKeyedIterable) {
        v = KeyedIterable(v);
      }
      return v;
    }).filter(function(v ) {return v.size !== 0});

    if (iters.length === 0) {
      return iterable;
    }

    if (iters.length === 1) {
      var singleton = iters[0];
      if (singleton === iterable ||
          isKeyedIterable && isKeyed(singleton) ||
          isIndexed(iterable) && isIndexed(singleton)) {
        return singleton;
      }
    }

    var concatSeq = new ArraySeq(iters);
    if (isKeyedIterable) {
      concatSeq = concatSeq.toKeyedSeq();
    } else if (!isIndexed(iterable)) {
      concatSeq = concatSeq.toSetSeq();
    }
    concatSeq = concatSeq.flatten(true);
    concatSeq.size = iters.reduce(
      function(sum, seq)  {
        if (sum !== undefined) {
          var size = seq.size;
          if (size !== undefined) {
            return sum + size;
          }
        }
      },
      0
    );
    return concatSeq;
  }


  function flattenFactory(iterable, depth, useKeys) {
    var flatSequence = makeSequence(iterable);
    flatSequence.__iterateUncached = function(fn, reverse) {
      var iterations = 0;
      var stopped = false;
      function flatDeep(iter, currentDepth) {var this$0 = this;
        iter.__iterate(function(v, k)  {
          if ((!depth || currentDepth < depth) && isIterable(v)) {
            flatDeep(v, currentDepth + 1);
          } else if (fn(v, useKeys ? k : iterations++, this$0) === false) {
            stopped = true;
          }
          return !stopped;
        }, reverse);
      }
      flatDeep(iterable, 0);
      return iterations;
    }
    flatSequence.__iteratorUncached = function(type, reverse) {
      var iterator = iterable.__iterator(type, reverse);
      var stack = [];
      var iterations = 0;
      return new Iterator(function()  {
        while (iterator) {
          var step = iterator.next();
          if (step.done !== false) {
            iterator = stack.pop();
            continue;
          }
          var v = step.value;
          if (type === ITERATE_ENTRIES) {
            v = v[1];
          }
          if ((!depth || stack.length < depth) && isIterable(v)) {
            stack.push(iterator);
            iterator = v.__iterator(type, reverse);
          } else {
            return useKeys ? step : iteratorValue(type, iterations++, v, step);
          }
        }
        return iteratorDone();
      });
    }
    return flatSequence;
  }


  function flatMapFactory(iterable, mapper, context) {
    var coerce = iterableClass(iterable);
    return iterable.toSeq().map(
      function(v, k)  {return coerce(mapper.call(context, v, k, iterable))}
    ).flatten(true);
  }


  function interposeFactory(iterable, separator) {
    var interposedSequence = makeSequence(iterable);
    interposedSequence.size = iterable.size && iterable.size * 2 -1;
    interposedSequence.__iterateUncached = function(fn, reverse) {var this$0 = this;
      var iterations = 0;
      iterable.__iterate(function(v, k) 
        {return (!iterations || fn(separator, iterations++, this$0) !== false) &&
        fn(v, iterations++, this$0) !== false},
        reverse
      );
      return iterations;
    };
    interposedSequence.__iteratorUncached = function(type, reverse) {
      var iterator = iterable.__iterator(ITERATE_VALUES, reverse);
      var iterations = 0;
      var step;
      return new Iterator(function()  {
        if (!step || iterations % 2) {
          step = iterator.next();
          if (step.done) {
            return step;
          }
        }
        return iterations % 2 ?
          iteratorValue(type, iterations++, separator) :
          iteratorValue(type, iterations++, step.value, step);
      });
    };
    return interposedSequence;
  }


  function sortFactory(iterable, comparator, mapper) {
    if (!comparator) {
      comparator = defaultComparator;
    }
    var isKeyedIterable = isKeyed(iterable);
    var index = 0;
    var entries = iterable.toSeq().map(
      function(v, k)  {return [k, v, index++, mapper ? mapper(v, k, iterable) : v]}
    ).toArray();
    entries.sort(function(a, b)  {return comparator(a[3], b[3]) || a[2] - b[2]}).forEach(
      isKeyedIterable ?
      function(v, i)  { entries[i].length = 2; } :
      function(v, i)  { entries[i] = v[1]; }
    );
    return isKeyedIterable ? KeyedSeq(entries) :
      isIndexed(iterable) ? IndexedSeq(entries) :
      SetSeq(entries);
  }


  function maxFactory(iterable, comparator, mapper) {
    if (!comparator) {
      comparator = defaultComparator;
    }
    if (mapper) {
      var entry = iterable.toSeq()
        .map(function(v, k)  {return [v, mapper(v, k, iterable)]})
        .reduce(function(a, b)  {return maxCompare(comparator, a[1], b[1]) ? b : a});
      return entry && entry[0];
    } else {
      return iterable.reduce(function(a, b)  {return maxCompare(comparator, a, b) ? b : a});
    }
  }

  function maxCompare(comparator, a, b) {
    var comp = comparator(b, a);
    // b is considered the new max if the comparator declares them equal, but
    // they are not equal and b is in fact a nullish value.
    return (comp === 0 && b !== a && (b === undefined || b === null || b !== b)) || comp > 0;
  }


  function zipWithFactory(keyIter, zipper, iters) {
    var zipSequence = makeSequence(keyIter);
    zipSequence.size = new ArraySeq(iters).map(function(i ) {return i.size}).min();
    // Note: this a generic base implementation of __iterate in terms of
    // __iterator which may be more generically useful in the future.
    zipSequence.__iterate = function(fn, reverse) {
      /* generic:
      var iterator = this.__iterator(ITERATE_ENTRIES, reverse);
      var step;
      var iterations = 0;
      while (!(step = iterator.next()).done) {
        iterations++;
        if (fn(step.value[1], step.value[0], this) === false) {
          break;
        }
      }
      return iterations;
      */
      // indexed:
      var iterator = this.__iterator(ITERATE_VALUES, reverse);
      var step;
      var iterations = 0;
      while (!(step = iterator.next()).done) {
        if (fn(step.value, iterations++, this) === false) {
          break;
        }
      }
      return iterations;
    };
    zipSequence.__iteratorUncached = function(type, reverse) {
      var iterators = iters.map(function(i )
        {return (i = Iterable(i), getIterator(reverse ? i.reverse() : i))}
      );
      var iterations = 0;
      var isDone = false;
      return new Iterator(function()  {
        var steps;
        if (!isDone) {
          steps = iterators.map(function(i ) {return i.next()});
          isDone = steps.some(function(s ) {return s.done});
        }
        if (isDone) {
          return iteratorDone();
        }
        return iteratorValue(
          type,
          iterations++,
          zipper.apply(null, steps.map(function(s ) {return s.value}))
        );
      });
    };
    return zipSequence
  }


  // #pragma Helper Functions

  function reify(iter, seq) {
    return isSeq(iter) ? seq : iter.constructor(seq);
  }

  function validateEntry(entry) {
    if (entry !== Object(entry)) {
      throw new TypeError('Expected [K, V] tuple: ' + entry);
    }
  }

  function resolveSize(iter) {
    assertNotInfinite(iter.size);
    return ensureSize(iter);
  }

  function iterableClass(iterable) {
    return isKeyed(iterable) ? KeyedIterable :
      isIndexed(iterable) ? IndexedIterable :
      SetIterable;
  }

  function makeSequence(iterable) {
    return Object.create(
      (
        isKeyed(iterable) ? KeyedSeq :
        isIndexed(iterable) ? IndexedSeq :
        SetSeq
      ).prototype
    );
  }

  function cacheResultThrough() {
    if (this._iter.cacheResult) {
      this._iter.cacheResult();
      this.size = this._iter.size;
      return this;
    } else {
      return Seq.prototype.cacheResult.call(this);
    }
  }

  function defaultComparator(a, b) {
    return a > b ? 1 : a < b ? -1 : 0;
  }

  function forceIterator(keyPath) {
    var iter = getIterator(keyPath);
    if (!iter) {
      // Array might not be iterable in this environment, so we need a fallback
      // to our wrapped type.
      if (!isArrayLike(keyPath)) {
        throw new TypeError('Expected iterable or array-like: ' + keyPath);
      }
      iter = getIterator(Iterable(keyPath));
    }
    return iter;
  }

  createClass(Record, KeyedCollection);

    function Record(defaultValues, name) {
      var hasInitialized;

      var RecordType = function Record(values) {
        if (values instanceof RecordType) {
          return values;
        }
        if (!(this instanceof RecordType)) {
          return new RecordType(values);
        }
        if (!hasInitialized) {
          hasInitialized = true;
          var keys = Object.keys(defaultValues);
          setProps(RecordTypePrototype, keys);
          RecordTypePrototype.size = keys.length;
          RecordTypePrototype._name = name;
          RecordTypePrototype._keys = keys;
          RecordTypePrototype._defaultValues = defaultValues;
        }
        this._map = Map(values);
      };

      var RecordTypePrototype = RecordType.prototype = Object.create(RecordPrototype);
      RecordTypePrototype.constructor = RecordType;

      return RecordType;
    }

    Record.prototype.toString = function() {
      return this.__toString(recordName(this) + ' {', '}');
    };

    // @pragma Access

    Record.prototype.has = function(k) {
      return this._defaultValues.hasOwnProperty(k);
    };

    Record.prototype.get = function(k, notSetValue) {
      if (!this.has(k)) {
        return notSetValue;
      }
      var defaultVal = this._defaultValues[k];
      return this._map ? this._map.get(k, defaultVal) : defaultVal;
    };

    // @pragma Modification

    Record.prototype.clear = function() {
      if (this.__ownerID) {
        this._map && this._map.clear();
        return this;
      }
      var RecordType = this.constructor;
      return RecordType._empty || (RecordType._empty = makeRecord(this, emptyMap()));
    };

    Record.prototype.set = function(k, v) {
      if (!this.has(k)) {
        throw new Error('Cannot set unknown key "' + k + '" on ' + recordName(this));
      }
      if (this._map && !this._map.has(k)) {
        var defaultVal = this._defaultValues[k];
        if (v === defaultVal) {
          return this;
        }
      }
      var newMap = this._map && this._map.set(k, v);
      if (this.__ownerID || newMap === this._map) {
        return this;
      }
      return makeRecord(this, newMap);
    };

    Record.prototype.remove = function(k) {
      if (!this.has(k)) {
        return this;
      }
      var newMap = this._map && this._map.remove(k);
      if (this.__ownerID || newMap === this._map) {
        return this;
      }
      return makeRecord(this, newMap);
    };

    Record.prototype.wasAltered = function() {
      return this._map.wasAltered();
    };

    Record.prototype.__iterator = function(type, reverse) {var this$0 = this;
      return KeyedIterable(this._defaultValues).map(function(_, k)  {return this$0.get(k)}).__iterator(type, reverse);
    };

    Record.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      return KeyedIterable(this._defaultValues).map(function(_, k)  {return this$0.get(k)}).__iterate(fn, reverse);
    };

    Record.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      var newMap = this._map && this._map.__ensureOwner(ownerID);
      if (!ownerID) {
        this.__ownerID = ownerID;
        this._map = newMap;
        return this;
      }
      return makeRecord(this, newMap, ownerID);
    };


  var RecordPrototype = Record.prototype;
  RecordPrototype[DELETE] = RecordPrototype.remove;
  RecordPrototype.deleteIn =
  RecordPrototype.removeIn = MapPrototype.removeIn;
  RecordPrototype.merge = MapPrototype.merge;
  RecordPrototype.mergeWith = MapPrototype.mergeWith;
  RecordPrototype.mergeIn = MapPrototype.mergeIn;
  RecordPrototype.mergeDeep = MapPrototype.mergeDeep;
  RecordPrototype.mergeDeepWith = MapPrototype.mergeDeepWith;
  RecordPrototype.mergeDeepIn = MapPrototype.mergeDeepIn;
  RecordPrototype.setIn = MapPrototype.setIn;
  RecordPrototype.update = MapPrototype.update;
  RecordPrototype.updateIn = MapPrototype.updateIn;
  RecordPrototype.withMutations = MapPrototype.withMutations;
  RecordPrototype.asMutable = MapPrototype.asMutable;
  RecordPrototype.asImmutable = MapPrototype.asImmutable;


  function makeRecord(likeRecord, map, ownerID) {
    var record = Object.create(Object.getPrototypeOf(likeRecord));
    record._map = map;
    record.__ownerID = ownerID;
    return record;
  }

  function recordName(record) {
    return record._name || record.constructor.name || 'Record';
  }

  function setProps(prototype, names) {
    try {
      names.forEach(setProp.bind(undefined, prototype));
    } catch (error) {
      // Object.defineProperty failed. Probably IE8.
    }
  }

  function setProp(prototype, name) {
    Object.defineProperty(prototype, name, {
      get: function() {
        return this.get(name);
      },
      set: function(value) {
        invariant(this.__ownerID, 'Cannot set on an immutable record.');
        this.set(name, value);
      }
    });
  }

  createClass(Set, SetCollection);

    // @pragma Construction

    function Set(value) {
      return value === null || value === undefined ? emptySet() :
        isSet(value) && !isOrdered(value) ? value :
        emptySet().withMutations(function(set ) {
          var iter = SetIterable(value);
          assertNotInfinite(iter.size);
          iter.forEach(function(v ) {return set.add(v)});
        });
    }

    Set.of = function(/*...values*/) {
      return this(arguments);
    };

    Set.fromKeys = function(value) {
      return this(KeyedIterable(value).keySeq());
    };

    Set.prototype.toString = function() {
      return this.__toString('Set {', '}');
    };

    // @pragma Access

    Set.prototype.has = function(value) {
      return this._map.has(value);
    };

    // @pragma Modification

    Set.prototype.add = function(value) {
      return updateSet(this, this._map.set(value, true));
    };

    Set.prototype.remove = function(value) {
      return updateSet(this, this._map.remove(value));
    };

    Set.prototype.clear = function() {
      return updateSet(this, this._map.clear());
    };

    // @pragma Composition

    Set.prototype.union = function() {var iters = SLICE$0.call(arguments, 0);
      iters = iters.filter(function(x ) {return x.size !== 0});
      if (iters.length === 0) {
        return this;
      }
      if (this.size === 0 && !this.__ownerID && iters.length === 1) {
        return this.constructor(iters[0]);
      }
      return this.withMutations(function(set ) {
        for (var ii = 0; ii < iters.length; ii++) {
          SetIterable(iters[ii]).forEach(function(value ) {return set.add(value)});
        }
      });
    };

    Set.prototype.intersect = function() {var iters = SLICE$0.call(arguments, 0);
      if (iters.length === 0) {
        return this;
      }
      iters = iters.map(function(iter ) {return SetIterable(iter)});
      var originalSet = this;
      return this.withMutations(function(set ) {
        originalSet.forEach(function(value ) {
          if (!iters.every(function(iter ) {return iter.includes(value)})) {
            set.remove(value);
          }
        });
      });
    };

    Set.prototype.subtract = function() {var iters = SLICE$0.call(arguments, 0);
      if (iters.length === 0) {
        return this;
      }
      iters = iters.map(function(iter ) {return SetIterable(iter)});
      var originalSet = this;
      return this.withMutations(function(set ) {
        originalSet.forEach(function(value ) {
          if (iters.some(function(iter ) {return iter.includes(value)})) {
            set.remove(value);
          }
        });
      });
    };

    Set.prototype.merge = function() {
      return this.union.apply(this, arguments);
    };

    Set.prototype.mergeWith = function(merger) {var iters = SLICE$0.call(arguments, 1);
      return this.union.apply(this, iters);
    };

    Set.prototype.sort = function(comparator) {
      // Late binding
      return OrderedSet(sortFactory(this, comparator));
    };

    Set.prototype.sortBy = function(mapper, comparator) {
      // Late binding
      return OrderedSet(sortFactory(this, comparator, mapper));
    };

    Set.prototype.wasAltered = function() {
      return this._map.wasAltered();
    };

    Set.prototype.__iterate = function(fn, reverse) {var this$0 = this;
      return this._map.__iterate(function(_, k)  {return fn(k, k, this$0)}, reverse);
    };

    Set.prototype.__iterator = function(type, reverse) {
      return this._map.map(function(_, k)  {return k}).__iterator(type, reverse);
    };

    Set.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      var newMap = this._map.__ensureOwner(ownerID);
      if (!ownerID) {
        this.__ownerID = ownerID;
        this._map = newMap;
        return this;
      }
      return this.__make(newMap, ownerID);
    };


  function isSet(maybeSet) {
    return !!(maybeSet && maybeSet[IS_SET_SENTINEL]);
  }

  Set.isSet = isSet;

  var IS_SET_SENTINEL = '@@__IMMUTABLE_SET__@@';

  var SetPrototype = Set.prototype;
  SetPrototype[IS_SET_SENTINEL] = true;
  SetPrototype[DELETE] = SetPrototype.remove;
  SetPrototype.mergeDeep = SetPrototype.merge;
  SetPrototype.mergeDeepWith = SetPrototype.mergeWith;
  SetPrototype.withMutations = MapPrototype.withMutations;
  SetPrototype.asMutable = MapPrototype.asMutable;
  SetPrototype.asImmutable = MapPrototype.asImmutable;

  SetPrototype.__empty = emptySet;
  SetPrototype.__make = makeSet;

  function updateSet(set, newMap) {
    if (set.__ownerID) {
      set.size = newMap.size;
      set._map = newMap;
      return set;
    }
    return newMap === set._map ? set :
      newMap.size === 0 ? set.__empty() :
      set.__make(newMap);
  }

  function makeSet(map, ownerID) {
    var set = Object.create(SetPrototype);
    set.size = map ? map.size : 0;
    set._map = map;
    set.__ownerID = ownerID;
    return set;
  }

  var EMPTY_SET;
  function emptySet() {
    return EMPTY_SET || (EMPTY_SET = makeSet(emptyMap()));
  }

  createClass(OrderedSet, Set);

    // @pragma Construction

    function OrderedSet(value) {
      return value === null || value === undefined ? emptyOrderedSet() :
        isOrderedSet(value) ? value :
        emptyOrderedSet().withMutations(function(set ) {
          var iter = SetIterable(value);
          assertNotInfinite(iter.size);
          iter.forEach(function(v ) {return set.add(v)});
        });
    }

    OrderedSet.of = function(/*...values*/) {
      return this(arguments);
    };

    OrderedSet.fromKeys = function(value) {
      return this(KeyedIterable(value).keySeq());
    };

    OrderedSet.prototype.toString = function() {
      return this.__toString('OrderedSet {', '}');
    };


  function isOrderedSet(maybeOrderedSet) {
    return isSet(maybeOrderedSet) && isOrdered(maybeOrderedSet);
  }

  OrderedSet.isOrderedSet = isOrderedSet;

  var OrderedSetPrototype = OrderedSet.prototype;
  OrderedSetPrototype[IS_ORDERED_SENTINEL] = true;

  OrderedSetPrototype.__empty = emptyOrderedSet;
  OrderedSetPrototype.__make = makeOrderedSet;

  function makeOrderedSet(map, ownerID) {
    var set = Object.create(OrderedSetPrototype);
    set.size = map ? map.size : 0;
    set._map = map;
    set.__ownerID = ownerID;
    return set;
  }

  var EMPTY_ORDERED_SET;
  function emptyOrderedSet() {
    return EMPTY_ORDERED_SET || (EMPTY_ORDERED_SET = makeOrderedSet(emptyOrderedMap()));
  }

  createClass(Stack, IndexedCollection);

    // @pragma Construction

    function Stack(value) {
      return value === null || value === undefined ? emptyStack() :
        isStack(value) ? value :
        emptyStack().unshiftAll(value);
    }

    Stack.of = function(/*...values*/) {
      return this(arguments);
    };

    Stack.prototype.toString = function() {
      return this.__toString('Stack [', ']');
    };

    // @pragma Access

    Stack.prototype.get = function(index, notSetValue) {
      var head = this._head;
      index = wrapIndex(this, index);
      while (head && index--) {
        head = head.next;
      }
      return head ? head.value : notSetValue;
    };

    Stack.prototype.peek = function() {
      return this._head && this._head.value;
    };

    // @pragma Modification

    Stack.prototype.push = function(/*...values*/) {
      if (arguments.length === 0) {
        return this;
      }
      var newSize = this.size + arguments.length;
      var head = this._head;
      for (var ii = arguments.length - 1; ii >= 0; ii--) {
        head = {
          value: arguments[ii],
          next: head
        };
      }
      if (this.__ownerID) {
        this.size = newSize;
        this._head = head;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return makeStack(newSize, head);
    };

    Stack.prototype.pushAll = function(iter) {
      iter = IndexedIterable(iter);
      if (iter.size === 0) {
        return this;
      }
      assertNotInfinite(iter.size);
      var newSize = this.size;
      var head = this._head;
      iter.reverse().forEach(function(value ) {
        newSize++;
        head = {
          value: value,
          next: head
        };
      });
      if (this.__ownerID) {
        this.size = newSize;
        this._head = head;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return makeStack(newSize, head);
    };

    Stack.prototype.pop = function() {
      return this.slice(1);
    };

    Stack.prototype.unshift = function(/*...values*/) {
      return this.push.apply(this, arguments);
    };

    Stack.prototype.unshiftAll = function(iter) {
      return this.pushAll(iter);
    };

    Stack.prototype.shift = function() {
      return this.pop.apply(this, arguments);
    };

    Stack.prototype.clear = function() {
      if (this.size === 0) {
        return this;
      }
      if (this.__ownerID) {
        this.size = 0;
        this._head = undefined;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return emptyStack();
    };

    Stack.prototype.slice = function(begin, end) {
      if (wholeSlice(begin, end, this.size)) {
        return this;
      }
      var resolvedBegin = resolveBegin(begin, this.size);
      var resolvedEnd = resolveEnd(end, this.size);
      if (resolvedEnd !== this.size) {
        // super.slice(begin, end);
        return IndexedCollection.prototype.slice.call(this, begin, end);
      }
      var newSize = this.size - resolvedBegin;
      var head = this._head;
      while (resolvedBegin--) {
        head = head.next;
      }
      if (this.__ownerID) {
        this.size = newSize;
        this._head = head;
        this.__hash = undefined;
        this.__altered = true;
        return this;
      }
      return makeStack(newSize, head);
    };

    // @pragma Mutability

    Stack.prototype.__ensureOwner = function(ownerID) {
      if (ownerID === this.__ownerID) {
        return this;
      }
      if (!ownerID) {
        this.__ownerID = ownerID;
        this.__altered = false;
        return this;
      }
      return makeStack(this.size, this._head, ownerID, this.__hash);
    };

    // @pragma Iteration

    Stack.prototype.__iterate = function(fn, reverse) {
      if (reverse) {
        return this.reverse().__iterate(fn);
      }
      var iterations = 0;
      var node = this._head;
      while (node) {
        if (fn(node.value, iterations++, this) === false) {
          break;
        }
        node = node.next;
      }
      return iterations;
    };

    Stack.prototype.__iterator = function(type, reverse) {
      if (reverse) {
        return this.reverse().__iterator(type);
      }
      var iterations = 0;
      var node = this._head;
      return new Iterator(function()  {
        if (node) {
          var value = node.value;
          node = node.next;
          return iteratorValue(type, iterations++, value);
        }
        return iteratorDone();
      });
    };


  function isStack(maybeStack) {
    return !!(maybeStack && maybeStack[IS_STACK_SENTINEL]);
  }

  Stack.isStack = isStack;

  var IS_STACK_SENTINEL = '@@__IMMUTABLE_STACK__@@';

  var StackPrototype = Stack.prototype;
  StackPrototype[IS_STACK_SENTINEL] = true;
  StackPrototype.withMutations = MapPrototype.withMutations;
  StackPrototype.asMutable = MapPrototype.asMutable;
  StackPrototype.asImmutable = MapPrototype.asImmutable;
  StackPrototype.wasAltered = MapPrototype.wasAltered;


  function makeStack(size, head, ownerID, hash) {
    var map = Object.create(StackPrototype);
    map.size = size;
    map._head = head;
    map.__ownerID = ownerID;
    map.__hash = hash;
    map.__altered = false;
    return map;
  }

  var EMPTY_STACK;
  function emptyStack() {
    return EMPTY_STACK || (EMPTY_STACK = makeStack(0));
  }

  /**
   * Contributes additional methods to a constructor
   */
  function mixin(ctor, methods) {
    var keyCopier = function(key ) { ctor.prototype[key] = methods[key]; };
    Object.keys(methods).forEach(keyCopier);
    Object.getOwnPropertySymbols &&
      Object.getOwnPropertySymbols(methods).forEach(keyCopier);
    return ctor;
  }

  Iterable.Iterator = Iterator;

  mixin(Iterable, {

    // ### Conversion to other types

    toArray: function() {
      assertNotInfinite(this.size);
      var array = new Array(this.size || 0);
      this.valueSeq().__iterate(function(v, i)  { array[i] = v; });
      return array;
    },

    toIndexedSeq: function() {
      return new ToIndexedSequence(this);
    },

    toJS: function() {
      return this.toSeq().map(
        function(value ) {return value && typeof value.toJS === 'function' ? value.toJS() : value}
      ).__toJS();
    },

    toJSON: function() {
      return this.toSeq().map(
        function(value ) {return value && typeof value.toJSON === 'function' ? value.toJSON() : value}
      ).__toJS();
    },

    toKeyedSeq: function() {
      return new ToKeyedSequence(this, true);
    },

    toMap: function() {
      // Use Late Binding here to solve the circular dependency.
      return Map(this.toKeyedSeq());
    },

    toObject: function() {
      assertNotInfinite(this.size);
      var object = {};
      this.__iterate(function(v, k)  { object[k] = v; });
      return object;
    },

    toOrderedMap: function() {
      // Use Late Binding here to solve the circular dependency.
      return OrderedMap(this.toKeyedSeq());
    },

    toOrderedSet: function() {
      // Use Late Binding here to solve the circular dependency.
      return OrderedSet(isKeyed(this) ? this.valueSeq() : this);
    },

    toSet: function() {
      // Use Late Binding here to solve the circular dependency.
      return Set(isKeyed(this) ? this.valueSeq() : this);
    },

    toSetSeq: function() {
      return new ToSetSequence(this);
    },

    toSeq: function() {
      return isIndexed(this) ? this.toIndexedSeq() :
        isKeyed(this) ? this.toKeyedSeq() :
        this.toSetSeq();
    },

    toStack: function() {
      // Use Late Binding here to solve the circular dependency.
      return Stack(isKeyed(this) ? this.valueSeq() : this);
    },

    toList: function() {
      // Use Late Binding here to solve the circular dependency.
      return List(isKeyed(this) ? this.valueSeq() : this);
    },


    // ### Common JavaScript methods and properties

    toString: function() {
      return '[Iterable]';
    },

    __toString: function(head, tail) {
      if (this.size === 0) {
        return head + tail;
      }
      return head + ' ' + this.toSeq().map(this.__toStringMapper).join(', ') + ' ' + tail;
    },


    // ### ES6 Collection methods (ES6 Array and Map)

    concat: function() {var values = SLICE$0.call(arguments, 0);
      return reify(this, concatFactory(this, values));
    },

    includes: function(searchValue) {
      return this.some(function(value ) {return is(value, searchValue)});
    },

    entries: function() {
      return this.__iterator(ITERATE_ENTRIES);
    },

    every: function(predicate, context) {
      assertNotInfinite(this.size);
      var returnValue = true;
      this.__iterate(function(v, k, c)  {
        if (!predicate.call(context, v, k, c)) {
          returnValue = false;
          return false;
        }
      });
      return returnValue;
    },

    filter: function(predicate, context) {
      return reify(this, filterFactory(this, predicate, context, true));
    },

    find: function(predicate, context, notSetValue) {
      var entry = this.findEntry(predicate, context);
      return entry ? entry[1] : notSetValue;
    },

    forEach: function(sideEffect, context) {
      assertNotInfinite(this.size);
      return this.__iterate(context ? sideEffect.bind(context) : sideEffect);
    },

    join: function(separator) {
      assertNotInfinite(this.size);
      separator = separator !== undefined ? '' + separator : ',';
      var joined = '';
      var isFirst = true;
      this.__iterate(function(v ) {
        isFirst ? (isFirst = false) : (joined += separator);
        joined += v !== null && v !== undefined ? v.toString() : '';
      });
      return joined;
    },

    keys: function() {
      return this.__iterator(ITERATE_KEYS);
    },

    map: function(mapper, context) {
      return reify(this, mapFactory(this, mapper, context));
    },

    reduce: function(reducer, initialReduction, context) {
      assertNotInfinite(this.size);
      var reduction;
      var useFirst;
      if (arguments.length < 2) {
        useFirst = true;
      } else {
        reduction = initialReduction;
      }
      this.__iterate(function(v, k, c)  {
        if (useFirst) {
          useFirst = false;
          reduction = v;
        } else {
          reduction = reducer.call(context, reduction, v, k, c);
        }
      });
      return reduction;
    },

    reduceRight: function(reducer, initialReduction, context) {
      var reversed = this.toKeyedSeq().reverse();
      return reversed.reduce.apply(reversed, arguments);
    },

    reverse: function() {
      return reify(this, reverseFactory(this, true));
    },

    slice: function(begin, end) {
      return reify(this, sliceFactory(this, begin, end, true));
    },

    some: function(predicate, context) {
      return !this.every(not(predicate), context);
    },

    sort: function(comparator) {
      return reify(this, sortFactory(this, comparator));
    },

    values: function() {
      return this.__iterator(ITERATE_VALUES);
    },


    // ### More sequential methods

    butLast: function() {
      return this.slice(0, -1);
    },

    isEmpty: function() {
      return this.size !== undefined ? this.size === 0 : !this.some(function()  {return true});
    },

    count: function(predicate, context) {
      return ensureSize(
        predicate ? this.toSeq().filter(predicate, context) : this
      );
    },

    countBy: function(grouper, context) {
      return countByFactory(this, grouper, context);
    },

    equals: function(other) {
      return deepEqual(this, other);
    },

    entrySeq: function() {
      var iterable = this;
      if (iterable._cache) {
        // We cache as an entries array, so we can just return the cache!
        return new ArraySeq(iterable._cache);
      }
      var entriesSequence = iterable.toSeq().map(entryMapper).toIndexedSeq();
      entriesSequence.fromEntrySeq = function()  {return iterable.toSeq()};
      return entriesSequence;
    },

    filterNot: function(predicate, context) {
      return this.filter(not(predicate), context);
    },

    findEntry: function(predicate, context, notSetValue) {
      var found = notSetValue;
      this.__iterate(function(v, k, c)  {
        if (predicate.call(context, v, k, c)) {
          found = [k, v];
          return false;
        }
      });
      return found;
    },

    findKey: function(predicate, context) {
      var entry = this.findEntry(predicate, context);
      return entry && entry[0];
    },

    findLast: function(predicate, context, notSetValue) {
      return this.toKeyedSeq().reverse().find(predicate, context, notSetValue);
    },

    findLastEntry: function(predicate, context, notSetValue) {
      return this.toKeyedSeq().reverse().findEntry(predicate, context, notSetValue);
    },

    findLastKey: function(predicate, context) {
      return this.toKeyedSeq().reverse().findKey(predicate, context);
    },

    first: function() {
      return this.find(returnTrue);
    },

    flatMap: function(mapper, context) {
      return reify(this, flatMapFactory(this, mapper, context));
    },

    flatten: function(depth) {
      return reify(this, flattenFactory(this, depth, true));
    },

    fromEntrySeq: function() {
      return new FromEntriesSequence(this);
    },

    get: function(searchKey, notSetValue) {
      return this.find(function(_, key)  {return is(key, searchKey)}, undefined, notSetValue);
    },

    getIn: function(searchKeyPath, notSetValue) {
      var nested = this;
      // Note: in an ES6 environment, we would prefer:
      // for (var key of searchKeyPath) {
      var iter = forceIterator(searchKeyPath);
      var step;
      while (!(step = iter.next()).done) {
        var key = step.value;
        nested = nested && nested.get ? nested.get(key, NOT_SET) : NOT_SET;
        if (nested === NOT_SET) {
          return notSetValue;
        }
      }
      return nested;
    },

    groupBy: function(grouper, context) {
      return groupByFactory(this, grouper, context);
    },

    has: function(searchKey) {
      return this.get(searchKey, NOT_SET) !== NOT_SET;
    },

    hasIn: function(searchKeyPath) {
      return this.getIn(searchKeyPath, NOT_SET) !== NOT_SET;
    },

    isSubset: function(iter) {
      iter = typeof iter.includes === 'function' ? iter : Iterable(iter);
      return this.every(function(value ) {return iter.includes(value)});
    },

    isSuperset: function(iter) {
      iter = typeof iter.isSubset === 'function' ? iter : Iterable(iter);
      return iter.isSubset(this);
    },

    keyOf: function(searchValue) {
      return this.findKey(function(value ) {return is(value, searchValue)});
    },

    keySeq: function() {
      return this.toSeq().map(keyMapper).toIndexedSeq();
    },

    last: function() {
      return this.toSeq().reverse().first();
    },

    lastKeyOf: function(searchValue) {
      return this.toKeyedSeq().reverse().keyOf(searchValue);
    },

    max: function(comparator) {
      return maxFactory(this, comparator);
    },

    maxBy: function(mapper, comparator) {
      return maxFactory(this, comparator, mapper);
    },

    min: function(comparator) {
      return maxFactory(this, comparator ? neg(comparator) : defaultNegComparator);
    },

    minBy: function(mapper, comparator) {
      return maxFactory(this, comparator ? neg(comparator) : defaultNegComparator, mapper);
    },

    rest: function() {
      return this.slice(1);
    },

    skip: function(amount) {
      return this.slice(Math.max(0, amount));
    },

    skipLast: function(amount) {
      return reify(this, this.toSeq().reverse().skip(amount).reverse());
    },

    skipWhile: function(predicate, context) {
      return reify(this, skipWhileFactory(this, predicate, context, true));
    },

    skipUntil: function(predicate, context) {
      return this.skipWhile(not(predicate), context);
    },

    sortBy: function(mapper, comparator) {
      return reify(this, sortFactory(this, comparator, mapper));
    },

    take: function(amount) {
      return this.slice(0, Math.max(0, amount));
    },

    takeLast: function(amount) {
      return reify(this, this.toSeq().reverse().take(amount).reverse());
    },

    takeWhile: function(predicate, context) {
      return reify(this, takeWhileFactory(this, predicate, context));
    },

    takeUntil: function(predicate, context) {
      return this.takeWhile(not(predicate), context);
    },

    valueSeq: function() {
      return this.toIndexedSeq();
    },


    // ### Hashable Object

    hashCode: function() {
      return this.__hash || (this.__hash = hashIterable(this));
    }


    // ### Internal

    // abstract __iterate(fn, reverse)

    // abstract __iterator(type, reverse)
  });

  // var IS_ITERABLE_SENTINEL = '@@__IMMUTABLE_ITERABLE__@@';
  // var IS_KEYED_SENTINEL = '@@__IMMUTABLE_KEYED__@@';
  // var IS_INDEXED_SENTINEL = '@@__IMMUTABLE_INDEXED__@@';
  // var IS_ORDERED_SENTINEL = '@@__IMMUTABLE_ORDERED__@@';

  var IterablePrototype = Iterable.prototype;
  IterablePrototype[IS_ITERABLE_SENTINEL] = true;
  IterablePrototype[ITERATOR_SYMBOL] = IterablePrototype.values;
  IterablePrototype.__toJS = IterablePrototype.toArray;
  IterablePrototype.__toStringMapper = quoteString;
  IterablePrototype.inspect =
  IterablePrototype.toSource = function() { return this.toString(); };
  IterablePrototype.chain = IterablePrototype.flatMap;
  IterablePrototype.contains = IterablePrototype.includes;

  mixin(KeyedIterable, {

    // ### More sequential methods

    flip: function() {
      return reify(this, flipFactory(this));
    },

    mapEntries: function(mapper, context) {var this$0 = this;
      var iterations = 0;
      return reify(this,
        this.toSeq().map(
          function(v, k)  {return mapper.call(context, [k, v], iterations++, this$0)}
        ).fromEntrySeq()
      );
    },

    mapKeys: function(mapper, context) {var this$0 = this;
      return reify(this,
        this.toSeq().flip().map(
          function(k, v)  {return mapper.call(context, k, v, this$0)}
        ).flip()
      );
    }

  });

  var KeyedIterablePrototype = KeyedIterable.prototype;
  KeyedIterablePrototype[IS_KEYED_SENTINEL] = true;
  KeyedIterablePrototype[ITERATOR_SYMBOL] = IterablePrototype.entries;
  KeyedIterablePrototype.__toJS = IterablePrototype.toObject;
  KeyedIterablePrototype.__toStringMapper = function(v, k)  {return JSON.stringify(k) + ': ' + quoteString(v)};



  mixin(IndexedIterable, {

    // ### Conversion to other types

    toKeyedSeq: function() {
      return new ToKeyedSequence(this, false);
    },


    // ### ES6 Collection methods (ES6 Array and Map)

    filter: function(predicate, context) {
      return reify(this, filterFactory(this, predicate, context, false));
    },

    findIndex: function(predicate, context) {
      var entry = this.findEntry(predicate, context);
      return entry ? entry[0] : -1;
    },

    indexOf: function(searchValue) {
      var key = this.keyOf(searchValue);
      return key === undefined ? -1 : key;
    },

    lastIndexOf: function(searchValue) {
      var key = this.lastKeyOf(searchValue);
      return key === undefined ? -1 : key;
    },

    reverse: function() {
      return reify(this, reverseFactory(this, false));
    },

    slice: function(begin, end) {
      return reify(this, sliceFactory(this, begin, end, false));
    },

    splice: function(index, removeNum /*, ...values*/) {
      var numArgs = arguments.length;
      removeNum = Math.max(removeNum | 0, 0);
      if (numArgs === 0 || (numArgs === 2 && !removeNum)) {
        return this;
      }
      // If index is negative, it should resolve relative to the size of the
      // collection. However size may be expensive to compute if not cached, so
      // only call count() if the number is in fact negative.
      index = resolveBegin(index, index < 0 ? this.count() : this.size);
      var spliced = this.slice(0, index);
      return reify(
        this,
        numArgs === 1 ?
          spliced :
          spliced.concat(arrCopy(arguments, 2), this.slice(index + removeNum))
      );
    },


    // ### More collection methods

    findLastIndex: function(predicate, context) {
      var entry = this.findLastEntry(predicate, context);
      return entry ? entry[0] : -1;
    },

    first: function() {
      return this.get(0);
    },

    flatten: function(depth) {
      return reify(this, flattenFactory(this, depth, false));
    },

    get: function(index, notSetValue) {
      index = wrapIndex(this, index);
      return (index < 0 || (this.size === Infinity ||
          (this.size !== undefined && index > this.size))) ?
        notSetValue :
        this.find(function(_, key)  {return key === index}, undefined, notSetValue);
    },

    has: function(index) {
      index = wrapIndex(this, index);
      return index >= 0 && (this.size !== undefined ?
        this.size === Infinity || index < this.size :
        this.indexOf(index) !== -1
      );
    },

    interpose: function(separator) {
      return reify(this, interposeFactory(this, separator));
    },

    interleave: function(/*...iterables*/) {
      var iterables = [this].concat(arrCopy(arguments));
      var zipped = zipWithFactory(this.toSeq(), IndexedSeq.of, iterables);
      var interleaved = zipped.flatten(true);
      if (zipped.size) {
        interleaved.size = zipped.size * iterables.length;
      }
      return reify(this, interleaved);
    },

    keySeq: function() {
      return Range(0, this.size);
    },

    last: function() {
      return this.get(-1);
    },

    skipWhile: function(predicate, context) {
      return reify(this, skipWhileFactory(this, predicate, context, false));
    },

    zip: function(/*, ...iterables */) {
      var iterables = [this].concat(arrCopy(arguments));
      return reify(this, zipWithFactory(this, defaultZipper, iterables));
    },

    zipWith: function(zipper/*, ...iterables */) {
      var iterables = arrCopy(arguments);
      iterables[0] = this;
      return reify(this, zipWithFactory(this, zipper, iterables));
    }

  });

  IndexedIterable.prototype[IS_INDEXED_SENTINEL] = true;
  IndexedIterable.prototype[IS_ORDERED_SENTINEL] = true;



  mixin(SetIterable, {

    // ### ES6 Collection methods (ES6 Array and Map)

    get: function(value, notSetValue) {
      return this.has(value) ? value : notSetValue;
    },

    includes: function(value) {
      return this.has(value);
    },


    // ### More sequential methods

    keySeq: function() {
      return this.valueSeq();
    }

  });

  SetIterable.prototype.has = IterablePrototype.includes;
  SetIterable.prototype.contains = SetIterable.prototype.includes;


  // Mixin subclasses

  mixin(KeyedSeq, KeyedIterable.prototype);
  mixin(IndexedSeq, IndexedIterable.prototype);
  mixin(SetSeq, SetIterable.prototype);

  mixin(KeyedCollection, KeyedIterable.prototype);
  mixin(IndexedCollection, IndexedIterable.prototype);
  mixin(SetCollection, SetIterable.prototype);


  // #pragma Helper functions

  function keyMapper(v, k) {
    return k;
  }

  function entryMapper(v, k) {
    return [k, v];
  }

  function not(predicate) {
    return function() {
      return !predicate.apply(this, arguments);
    }
  }

  function neg(predicate) {
    return function() {
      return -predicate.apply(this, arguments);
    }
  }

  function quoteString(value) {
    return typeof value === 'string' ? JSON.stringify(value) : String(value);
  }

  function defaultZipper() {
    return arrCopy(arguments);
  }

  function defaultNegComparator(a, b) {
    return a < b ? 1 : a > b ? -1 : 0;
  }

  function hashIterable(iterable) {
    if (iterable.size === Infinity) {
      return 0;
    }
    var ordered = isOrdered(iterable);
    var keyed = isKeyed(iterable);
    var h = ordered ? 1 : 0;
    var size = iterable.__iterate(
      keyed ?
        ordered ?
          function(v, k)  { h = 31 * h + hashMerge(hash(v), hash(k)) | 0; } :
          function(v, k)  { h = h + hashMerge(hash(v), hash(k)) | 0; } :
        ordered ?
          function(v ) { h = 31 * h + hash(v) | 0; } :
          function(v ) { h = h + hash(v) | 0; }
    );
    return murmurHashOfSize(size, h);
  }

  function murmurHashOfSize(size, h) {
    h = imul(h, 0xCC9E2D51);
    h = imul(h << 15 | h >>> -15, 0x1B873593);
    h = imul(h << 13 | h >>> -13, 5);
    h = (h + 0xE6546B64 | 0) ^ size;
    h = imul(h ^ h >>> 16, 0x85EBCA6B);
    h = imul(h ^ h >>> 13, 0xC2B2AE35);
    h = smi(h ^ h >>> 16);
    return h;
  }

  function hashMerge(a, b) {
    return a ^ b + 0x9E3779B9 + (a << 6) + (a >> 2) | 0; // int
  }

  var Immutable = {

    Iterable: Iterable,

    Seq: Seq,
    Collection: Collection,
    Map: Map,
    OrderedMap: OrderedMap,
    List: List,
    Stack: Stack,
    Set: Set,
    OrderedSet: OrderedSet,

    Record: Record,
    Range: Range,
    Repeat: Repeat,

    is: is,
    fromJS: fromJS

  };

  return Immutable;

}));

/***/ }),

/***/ "./node_modules/isarray/index.js":
/*!***************************************!*\
  !*** ./node_modules/isarray/index.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {

var toString = {}.toString;

module.exports = Array.isArray || function (arr) {
  return toString.call(arr) == '[object Array]';
};


/***/ }),

/***/ "./node_modules/js-md5/src/md5.js":
/*!****************************************!*\
  !*** ./node_modules/js-md5/src/md5.js ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(process, global) {var __WEBPACK_AMD_DEFINE_RESULT__;/**
 * [js-md5]{@link https://github.com/emn178/js-md5}
 *
 * @namespace md5
 * @version 0.7.3
 * @author Chen, Yi-Cyuan [emn178@gmail.com]
 * @copyright Chen, Yi-Cyuan 2014-2017
 * @license MIT
 */
(function () {
  'use strict';

  var ERROR = 'input is invalid type';
  var WINDOW = typeof window === 'object';
  var root = WINDOW ? window : {};
  if (root.JS_MD5_NO_WINDOW) {
    WINDOW = false;
  }
  var WEB_WORKER = !WINDOW && typeof self === 'object';
  var NODE_JS = !root.JS_MD5_NO_NODE_JS && typeof process === 'object' && process.versions && process.versions.node;
  if (NODE_JS) {
    root = global;
  } else if (WEB_WORKER) {
    root = self;
  }
  var COMMON_JS = !root.JS_MD5_NO_COMMON_JS && typeof module === 'object' && module.exports;
  var AMD =  true && __webpack_require__(/*! !webpack amd options */ "./node_modules/webpack/buildin/amd-options.js");
  var ARRAY_BUFFER = !root.JS_MD5_NO_ARRAY_BUFFER && typeof ArrayBuffer !== 'undefined';
  var HEX_CHARS = '0123456789abcdef'.split('');
  var EXTRA = [128, 32768, 8388608, -2147483648];
  var SHIFT = [0, 8, 16, 24];
  var OUTPUT_TYPES = ['hex', 'array', 'digest', 'buffer', 'arrayBuffer', 'base64'];
  var BASE64_ENCODE_CHAR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');

  var blocks = [], buffer8;
  if (ARRAY_BUFFER) {
    var buffer = new ArrayBuffer(68);
    buffer8 = new Uint8Array(buffer);
    blocks = new Uint32Array(buffer);
  }

  if (root.JS_MD5_NO_NODE_JS || !Array.isArray) {
    Array.isArray = function (obj) {
      return Object.prototype.toString.call(obj) === '[object Array]';
    };
  }

  if (ARRAY_BUFFER && (root.JS_MD5_NO_ARRAY_BUFFER_IS_VIEW || !ArrayBuffer.isView)) {
    ArrayBuffer.isView = function (obj) {
      return typeof obj === 'object' && obj.buffer && obj.buffer.constructor === ArrayBuffer;
    };
  }

  /**
   * @method hex
   * @memberof md5
   * @description Output hash as hex string
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {String} Hex string
   * @example
   * md5.hex('The quick brown fox jumps over the lazy dog');
   * // equal to
   * md5('The quick brown fox jumps over the lazy dog');
   */
  /**
   * @method digest
   * @memberof md5
   * @description Output hash as bytes array
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {Array} Bytes array
   * @example
   * md5.digest('The quick brown fox jumps over the lazy dog');
   */
  /**
   * @method array
   * @memberof md5
   * @description Output hash as bytes array
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {Array} Bytes array
   * @example
   * md5.array('The quick brown fox jumps over the lazy dog');
   */
  /**
   * @method arrayBuffer
   * @memberof md5
   * @description Output hash as ArrayBuffer
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {ArrayBuffer} ArrayBuffer
   * @example
   * md5.arrayBuffer('The quick brown fox jumps over the lazy dog');
   */
  /**
   * @method buffer
   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
   * @memberof md5
   * @description Output hash as ArrayBuffer
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {ArrayBuffer} ArrayBuffer
   * @example
   * md5.buffer('The quick brown fox jumps over the lazy dog');
   */
  /**
   * @method base64
   * @memberof md5
   * @description Output hash as base64 string
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {String} base64 string
   * @example
   * md5.base64('The quick brown fox jumps over the lazy dog');
   */
  var createOutputMethod = function (outputType) {
    return function (message) {
      return new Md5(true).update(message)[outputType]();
    };
  };

  /**
   * @method create
   * @memberof md5
   * @description Create Md5 object
   * @returns {Md5} Md5 object.
   * @example
   * var hash = md5.create();
   */
  /**
   * @method update
   * @memberof md5
   * @description Create and update Md5 object
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {Md5} Md5 object.
   * @example
   * var hash = md5.update('The quick brown fox jumps over the lazy dog');
   * // equal to
   * var hash = md5.create();
   * hash.update('The quick brown fox jumps over the lazy dog');
   */
  var createMethod = function () {
    var method = createOutputMethod('hex');
    if (NODE_JS) {
      method = nodeWrap(method);
    }
    method.create = function () {
      return new Md5();
    };
    method.update = function (message) {
      return method.create().update(message);
    };
    for (var i = 0; i < OUTPUT_TYPES.length; ++i) {
      var type = OUTPUT_TYPES[i];
      method[type] = createOutputMethod(type);
    }
    return method;
  };

  var nodeWrap = function (method) {
    var crypto = eval("require('crypto')");
    var Buffer = eval("require('buffer').Buffer");
    var nodeMethod = function (message) {
      if (typeof message === 'string') {
        return crypto.createHash('md5').update(message, 'utf8').digest('hex');
      } else {
        if (message === null || message === undefined) {
          throw ERROR;
        } else if (message.constructor === ArrayBuffer) {
          message = new Uint8Array(message);
        }
      }
      if (Array.isArray(message) || ArrayBuffer.isView(message) ||
        message.constructor === Buffer) {
        return crypto.createHash('md5').update(new Buffer(message)).digest('hex');
      } else {
        return method(message);
      }
    };
    return nodeMethod;
  };

  /**
   * Md5 class
   * @class Md5
   * @description This is internal class.
   * @see {@link md5.create}
   */
  function Md5(sharedMemory) {
    if (sharedMemory) {
      blocks[0] = blocks[16] = blocks[1] = blocks[2] = blocks[3] =
      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
      this.blocks = blocks;
      this.buffer8 = buffer8;
    } else {
      if (ARRAY_BUFFER) {
        var buffer = new ArrayBuffer(68);
        this.buffer8 = new Uint8Array(buffer);
        this.blocks = new Uint32Array(buffer);
      } else {
        this.blocks = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
      }
    }
    this.h0 = this.h1 = this.h2 = this.h3 = this.start = this.bytes = this.hBytes = 0;
    this.finalized = this.hashed = false;
    this.first = true;
  }

  /**
   * @method update
   * @memberof Md5
   * @instance
   * @description Update hash
   * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
   * @returns {Md5} Md5 object.
   * @see {@link md5.update}
   */
  Md5.prototype.update = function (message) {
    if (this.finalized) {
      return;
    }

    var notString, type = typeof message;
    if (type !== 'string') {
      if (type === 'object') {
        if (message === null) {
          throw ERROR;
        } else if (ARRAY_BUFFER && message.constructor === ArrayBuffer) {
          message = new Uint8Array(message);
        } else if (!Array.isArray(message)) {
          if (!ARRAY_BUFFER || !ArrayBuffer.isView(message)) {
            throw ERROR;
          }
        }
      } else {
        throw ERROR;
      }
      notString = true;
    }
    var code, index = 0, i, length = message.length, blocks = this.blocks;
    var buffer8 = this.buffer8;

    while (index < length) {
      if (this.hashed) {
        this.hashed = false;
        blocks[0] = blocks[16];
        blocks[16] = blocks[1] = blocks[2] = blocks[3] =
        blocks[4] = blocks[5] = blocks[6] = blocks[7] =
        blocks[8] = blocks[9] = blocks[10] = blocks[11] =
        blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
      }

      if (notString) {
        if (ARRAY_BUFFER) {
          for (i = this.start; index < length && i < 64; ++index) {
            buffer8[i++] = message[index];
          }
        } else {
          for (i = this.start; index < length && i < 64; ++index) {
            blocks[i >> 2] |= message[index] << SHIFT[i++ & 3];
          }
        }
      } else {
        if (ARRAY_BUFFER) {
          for (i = this.start; index < length && i < 64; ++index) {
            code = message.charCodeAt(index);
            if (code < 0x80) {
              buffer8[i++] = code;
            } else if (code < 0x800) {
              buffer8[i++] = 0xc0 | (code >> 6);
              buffer8[i++] = 0x80 | (code & 0x3f);
            } else if (code < 0xd800 || code >= 0xe000) {
              buffer8[i++] = 0xe0 | (code >> 12);
              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
              buffer8[i++] = 0x80 | (code & 0x3f);
            } else {
              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
              buffer8[i++] = 0xf0 | (code >> 18);
              buffer8[i++] = 0x80 | ((code >> 12) & 0x3f);
              buffer8[i++] = 0x80 | ((code >> 6) & 0x3f);
              buffer8[i++] = 0x80 | (code & 0x3f);
            }
          }
        } else {
          for (i = this.start; index < length && i < 64; ++index) {
            code = message.charCodeAt(index);
            if (code < 0x80) {
              blocks[i >> 2] |= code << SHIFT[i++ & 3];
            } else if (code < 0x800) {
              blocks[i >> 2] |= (0xc0 | (code >> 6)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
            } else if (code < 0xd800 || code >= 0xe000) {
              blocks[i >> 2] |= (0xe0 | (code >> 12)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
            } else {
              code = 0x10000 + (((code & 0x3ff) << 10) | (message.charCodeAt(++index) & 0x3ff));
              blocks[i >> 2] |= (0xf0 | (code >> 18)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | ((code >> 12) & 0x3f)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | ((code >> 6) & 0x3f)) << SHIFT[i++ & 3];
              blocks[i >> 2] |= (0x80 | (code & 0x3f)) << SHIFT[i++ & 3];
            }
          }
        }
      }
      this.lastByteIndex = i;
      this.bytes += i - this.start;
      if (i >= 64) {
        this.start = i - 64;
        this.hash();
        this.hashed = true;
      } else {
        this.start = i;
      }
    }
    if (this.bytes > 4294967295) {
      this.hBytes += this.bytes / 4294967296 << 0;
      this.bytes = this.bytes % 4294967296;
    }
    return this;
  };

  Md5.prototype.finalize = function () {
    if (this.finalized) {
      return;
    }
    this.finalized = true;
    var blocks = this.blocks, i = this.lastByteIndex;
    blocks[i >> 2] |= EXTRA[i & 3];
    if (i >= 56) {
      if (!this.hashed) {
        this.hash();
      }
      blocks[0] = blocks[16];
      blocks[16] = blocks[1] = blocks[2] = blocks[3] =
      blocks[4] = blocks[5] = blocks[6] = blocks[7] =
      blocks[8] = blocks[9] = blocks[10] = blocks[11] =
      blocks[12] = blocks[13] = blocks[14] = blocks[15] = 0;
    }
    blocks[14] = this.bytes << 3;
    blocks[15] = this.hBytes << 3 | this.bytes >>> 29;
    this.hash();
  };

  Md5.prototype.hash = function () {
    var a, b, c, d, bc, da, blocks = this.blocks;

    if (this.first) {
      a = blocks[0] - 680876937;
      a = (a << 7 | a >>> 25) - 271733879 << 0;
      d = (-1732584194 ^ a & 2004318071) + blocks[1] - 117830708;
      d = (d << 12 | d >>> 20) + a << 0;
      c = (-271733879 ^ (d & (a ^ -271733879))) + blocks[2] - 1126478375;
      c = (c << 17 | c >>> 15) + d << 0;
      b = (a ^ (c & (d ^ a))) + blocks[3] - 1316259209;
      b = (b << 22 | b >>> 10) + c << 0;
    } else {
      a = this.h0;
      b = this.h1;
      c = this.h2;
      d = this.h3;
      a += (d ^ (b & (c ^ d))) + blocks[0] - 680876936;
      a = (a << 7 | a >>> 25) + b << 0;
      d += (c ^ (a & (b ^ c))) + blocks[1] - 389564586;
      d = (d << 12 | d >>> 20) + a << 0;
      c += (b ^ (d & (a ^ b))) + blocks[2] + 606105819;
      c = (c << 17 | c >>> 15) + d << 0;
      b += (a ^ (c & (d ^ a))) + blocks[3] - 1044525330;
      b = (b << 22 | b >>> 10) + c << 0;
    }

    a += (d ^ (b & (c ^ d))) + blocks[4] - 176418897;
    a = (a << 7 | a >>> 25) + b << 0;
    d += (c ^ (a & (b ^ c))) + blocks[5] + 1200080426;
    d = (d << 12 | d >>> 20) + a << 0;
    c += (b ^ (d & (a ^ b))) + blocks[6] - 1473231341;
    c = (c << 17 | c >>> 15) + d << 0;
    b += (a ^ (c & (d ^ a))) + blocks[7] - 45705983;
    b = (b << 22 | b >>> 10) + c << 0;
    a += (d ^ (b & (c ^ d))) + blocks[8] + 1770035416;
    a = (a << 7 | a >>> 25) + b << 0;
    d += (c ^ (a & (b ^ c))) + blocks[9] - 1958414417;
    d = (d << 12 | d >>> 20) + a << 0;
    c += (b ^ (d & (a ^ b))) + blocks[10] - 42063;
    c = (c << 17 | c >>> 15) + d << 0;
    b += (a ^ (c & (d ^ a))) + blocks[11] - 1990404162;
    b = (b << 22 | b >>> 10) + c << 0;
    a += (d ^ (b & (c ^ d))) + blocks[12] + 1804603682;
    a = (a << 7 | a >>> 25) + b << 0;
    d += (c ^ (a & (b ^ c))) + blocks[13] - 40341101;
    d = (d << 12 | d >>> 20) + a << 0;
    c += (b ^ (d & (a ^ b))) + blocks[14] - 1502002290;
    c = (c << 17 | c >>> 15) + d << 0;
    b += (a ^ (c & (d ^ a))) + blocks[15] + 1236535329;
    b = (b << 22 | b >>> 10) + c << 0;
    a += (c ^ (d & (b ^ c))) + blocks[1] - 165796510;
    a = (a << 5 | a >>> 27) + b << 0;
    d += (b ^ (c & (a ^ b))) + blocks[6] - 1069501632;
    d = (d << 9 | d >>> 23) + a << 0;
    c += (a ^ (b & (d ^ a))) + blocks[11] + 643717713;
    c = (c << 14 | c >>> 18) + d << 0;
    b += (d ^ (a & (c ^ d))) + blocks[0] - 373897302;
    b = (b << 20 | b >>> 12) + c << 0;
    a += (c ^ (d & (b ^ c))) + blocks[5] - 701558691;
    a = (a << 5 | a >>> 27) + b << 0;
    d += (b ^ (c & (a ^ b))) + blocks[10] + 38016083;
    d = (d << 9 | d >>> 23) + a << 0;
    c += (a ^ (b & (d ^ a))) + blocks[15] - 660478335;
    c = (c << 14 | c >>> 18) + d << 0;
    b += (d ^ (a & (c ^ d))) + blocks[4] - 405537848;
    b = (b << 20 | b >>> 12) + c << 0;
    a += (c ^ (d & (b ^ c))) + blocks[9] + 568446438;
    a = (a << 5 | a >>> 27) + b << 0;
    d += (b ^ (c & (a ^ b))) + blocks[14] - 1019803690;
    d = (d << 9 | d >>> 23) + a << 0;
    c += (a ^ (b & (d ^ a))) + blocks[3] - 187363961;
    c = (c << 14 | c >>> 18) + d << 0;
    b += (d ^ (a & (c ^ d))) + blocks[8] + 1163531501;
    b = (b << 20 | b >>> 12) + c << 0;
    a += (c ^ (d & (b ^ c))) + blocks[13] - 1444681467;
    a = (a << 5 | a >>> 27) + b << 0;
    d += (b ^ (c & (a ^ b))) + blocks[2] - 51403784;
    d = (d << 9 | d >>> 23) + a << 0;
    c += (a ^ (b & (d ^ a))) + blocks[7] + 1735328473;
    c = (c << 14 | c >>> 18) + d << 0;
    b += (d ^ (a & (c ^ d))) + blocks[12] - 1926607734;
    b = (b << 20 | b >>> 12) + c << 0;
    bc = b ^ c;
    a += (bc ^ d) + blocks[5] - 378558;
    a = (a << 4 | a >>> 28) + b << 0;
    d += (bc ^ a) + blocks[8] - 2022574463;
    d = (d << 11 | d >>> 21) + a << 0;
    da = d ^ a;
    c += (da ^ b) + blocks[11] + 1839030562;
    c = (c << 16 | c >>> 16) + d << 0;
    b += (da ^ c) + blocks[14] - 35309556;
    b = (b << 23 | b >>> 9) + c << 0;
    bc = b ^ c;
    a += (bc ^ d) + blocks[1] - 1530992060;
    a = (a << 4 | a >>> 28) + b << 0;
    d += (bc ^ a) + blocks[4] + 1272893353;
    d = (d << 11 | d >>> 21) + a << 0;
    da = d ^ a;
    c += (da ^ b) + blocks[7] - 155497632;
    c = (c << 16 | c >>> 16) + d << 0;
    b += (da ^ c) + blocks[10] - 1094730640;
    b = (b << 23 | b >>> 9) + c << 0;
    bc = b ^ c;
    a += (bc ^ d) + blocks[13] + 681279174;
    a = (a << 4 | a >>> 28) + b << 0;
    d += (bc ^ a) + blocks[0] - 358537222;
    d = (d << 11 | d >>> 21) + a << 0;
    da = d ^ a;
    c += (da ^ b) + blocks[3] - 722521979;
    c = (c << 16 | c >>> 16) + d << 0;
    b += (da ^ c) + blocks[6] + 76029189;
    b = (b << 23 | b >>> 9) + c << 0;
    bc = b ^ c;
    a += (bc ^ d) + blocks[9] - 640364487;
    a = (a << 4 | a >>> 28) + b << 0;
    d += (bc ^ a) + blocks[12] - 421815835;
    d = (d << 11 | d >>> 21) + a << 0;
    da = d ^ a;
    c += (da ^ b) + blocks[15] + 530742520;
    c = (c << 16 | c >>> 16) + d << 0;
    b += (da ^ c) + blocks[2] - 995338651;
    b = (b << 23 | b >>> 9) + c << 0;
    a += (c ^ (b | ~d)) + blocks[0] - 198630844;
    a = (a << 6 | a >>> 26) + b << 0;
    d += (b ^ (a | ~c)) + blocks[7] + 1126891415;
    d = (d << 10 | d >>> 22) + a << 0;
    c += (a ^ (d | ~b)) + blocks[14] - 1416354905;
    c = (c << 15 | c >>> 17) + d << 0;
    b += (d ^ (c | ~a)) + blocks[5] - 57434055;
    b = (b << 21 | b >>> 11) + c << 0;
    a += (c ^ (b | ~d)) + blocks[12] + 1700485571;
    a = (a << 6 | a >>> 26) + b << 0;
    d += (b ^ (a | ~c)) + blocks[3] - 1894986606;
    d = (d << 10 | d >>> 22) + a << 0;
    c += (a ^ (d | ~b)) + blocks[10] - 1051523;
    c = (c << 15 | c >>> 17) + d << 0;
    b += (d ^ (c | ~a)) + blocks[1] - 2054922799;
    b = (b << 21 | b >>> 11) + c << 0;
    a += (c ^ (b | ~d)) + blocks[8] + 1873313359;
    a = (a << 6 | a >>> 26) + b << 0;
    d += (b ^ (a | ~c)) + blocks[15] - 30611744;
    d = (d << 10 | d >>> 22) + a << 0;
    c += (a ^ (d | ~b)) + blocks[6] - 1560198380;
    c = (c << 15 | c >>> 17) + d << 0;
    b += (d ^ (c | ~a)) + blocks[13] + 1309151649;
    b = (b << 21 | b >>> 11) + c << 0;
    a += (c ^ (b | ~d)) + blocks[4] - 145523070;
    a = (a << 6 | a >>> 26) + b << 0;
    d += (b ^ (a | ~c)) + blocks[11] - 1120210379;
    d = (d << 10 | d >>> 22) + a << 0;
    c += (a ^ (d | ~b)) + blocks[2] + 718787259;
    c = (c << 15 | c >>> 17) + d << 0;
    b += (d ^ (c | ~a)) + blocks[9] - 343485551;
    b = (b << 21 | b >>> 11) + c << 0;

    if (this.first) {
      this.h0 = a + 1732584193 << 0;
      this.h1 = b - 271733879 << 0;
      this.h2 = c - 1732584194 << 0;
      this.h3 = d + 271733878 << 0;
      this.first = false;
    } else {
      this.h0 = this.h0 + a << 0;
      this.h1 = this.h1 + b << 0;
      this.h2 = this.h2 + c << 0;
      this.h3 = this.h3 + d << 0;
    }
  };

  /**
   * @method hex
   * @memberof Md5
   * @instance
   * @description Output hash as hex string
   * @returns {String} Hex string
   * @see {@link md5.hex}
   * @example
   * hash.hex();
   */
  Md5.prototype.hex = function () {
    this.finalize();

    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;

    return HEX_CHARS[(h0 >> 4) & 0x0F] + HEX_CHARS[h0 & 0x0F] +
      HEX_CHARS[(h0 >> 12) & 0x0F] + HEX_CHARS[(h0 >> 8) & 0x0F] +
      HEX_CHARS[(h0 >> 20) & 0x0F] + HEX_CHARS[(h0 >> 16) & 0x0F] +
      HEX_CHARS[(h0 >> 28) & 0x0F] + HEX_CHARS[(h0 >> 24) & 0x0F] +
      HEX_CHARS[(h1 >> 4) & 0x0F] + HEX_CHARS[h1 & 0x0F] +
      HEX_CHARS[(h1 >> 12) & 0x0F] + HEX_CHARS[(h1 >> 8) & 0x0F] +
      HEX_CHARS[(h1 >> 20) & 0x0F] + HEX_CHARS[(h1 >> 16) & 0x0F] +
      HEX_CHARS[(h1 >> 28) & 0x0F] + HEX_CHARS[(h1 >> 24) & 0x0F] +
      HEX_CHARS[(h2 >> 4) & 0x0F] + HEX_CHARS[h2 & 0x0F] +
      HEX_CHARS[(h2 >> 12) & 0x0F] + HEX_CHARS[(h2 >> 8) & 0x0F] +
      HEX_CHARS[(h2 >> 20) & 0x0F] + HEX_CHARS[(h2 >> 16) & 0x0F] +
      HEX_CHARS[(h2 >> 28) & 0x0F] + HEX_CHARS[(h2 >> 24) & 0x0F] +
      HEX_CHARS[(h3 >> 4) & 0x0F] + HEX_CHARS[h3 & 0x0F] +
      HEX_CHARS[(h3 >> 12) & 0x0F] + HEX_CHARS[(h3 >> 8) & 0x0F] +
      HEX_CHARS[(h3 >> 20) & 0x0F] + HEX_CHARS[(h3 >> 16) & 0x0F] +
      HEX_CHARS[(h3 >> 28) & 0x0F] + HEX_CHARS[(h3 >> 24) & 0x0F];
  };

  /**
   * @method toString
   * @memberof Md5
   * @instance
   * @description Output hash as hex string
   * @returns {String} Hex string
   * @see {@link md5.hex}
   * @example
   * hash.toString();
   */
  Md5.prototype.toString = Md5.prototype.hex;

  /**
   * @method digest
   * @memberof Md5
   * @instance
   * @description Output hash as bytes array
   * @returns {Array} Bytes array
   * @see {@link md5.digest}
   * @example
   * hash.digest();
   */
  Md5.prototype.digest = function () {
    this.finalize();

    var h0 = this.h0, h1 = this.h1, h2 = this.h2, h3 = this.h3;
    return [
      h0 & 0xFF, (h0 >> 8) & 0xFF, (h0 >> 16) & 0xFF, (h0 >> 24) & 0xFF,
      h1 & 0xFF, (h1 >> 8) & 0xFF, (h1 >> 16) & 0xFF, (h1 >> 24) & 0xFF,
      h2 & 0xFF, (h2 >> 8) & 0xFF, (h2 >> 16) & 0xFF, (h2 >> 24) & 0xFF,
      h3 & 0xFF, (h3 >> 8) & 0xFF, (h3 >> 16) & 0xFF, (h3 >> 24) & 0xFF
    ];
  };

  /**
   * @method array
   * @memberof Md5
   * @instance
   * @description Output hash as bytes array
   * @returns {Array} Bytes array
   * @see {@link md5.array}
   * @example
   * hash.array();
   */
  Md5.prototype.array = Md5.prototype.digest;

  /**
   * @method arrayBuffer
   * @memberof Md5
   * @instance
   * @description Output hash as ArrayBuffer
   * @returns {ArrayBuffer} ArrayBuffer
   * @see {@link md5.arrayBuffer}
   * @example
   * hash.arrayBuffer();
   */
  Md5.prototype.arrayBuffer = function () {
    this.finalize();

    var buffer = new ArrayBuffer(16);
    var blocks = new Uint32Array(buffer);
    blocks[0] = this.h0;
    blocks[1] = this.h1;
    blocks[2] = this.h2;
    blocks[3] = this.h3;
    return buffer;
  };

  /**
   * @method buffer
   * @deprecated This maybe confuse with Buffer in node.js. Please use arrayBuffer instead.
   * @memberof Md5
   * @instance
   * @description Output hash as ArrayBuffer
   * @returns {ArrayBuffer} ArrayBuffer
   * @see {@link md5.buffer}
   * @example
   * hash.buffer();
   */
  Md5.prototype.buffer = Md5.prototype.arrayBuffer;

  /**
   * @method base64
   * @memberof Md5
   * @instance
   * @description Output hash as base64 string
   * @returns {String} base64 string
   * @see {@link md5.base64}
   * @example
   * hash.base64();
   */
  Md5.prototype.base64 = function () {
    var v1, v2, v3, base64Str = '', bytes = this.array();
    for (var i = 0; i < 15;) {
      v1 = bytes[i++];
      v2 = bytes[i++];
      v3 = bytes[i++];
      base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
        BASE64_ENCODE_CHAR[(v1 << 4 | v2 >>> 4) & 63] +
        BASE64_ENCODE_CHAR[(v2 << 2 | v3 >>> 6) & 63] +
        BASE64_ENCODE_CHAR[v3 & 63];
    }
    v1 = bytes[i];
    base64Str += BASE64_ENCODE_CHAR[v1 >>> 2] +
      BASE64_ENCODE_CHAR[(v1 << 4) & 63] +
      '==';
    return base64Str;
  };

  var exports = createMethod();

  if (COMMON_JS) {
    module.exports = exports;
  } else {
    /**
     * @method md5
     * @description Md5 hash function, export to global in browsers.
     * @param {String|Array|Uint8Array|ArrayBuffer} message message to hash
     * @returns {String} md5 hashes
     * @example
     * md5(''); // d41d8cd98f00b204e9800998ecf8427e
     * md5('The quick brown fox jumps over the lazy dog'); // 9e107d9d372bb6826bd81d3542a419d6
     * md5('The quick brown fox jumps over the lazy dog.'); // e4d909c290d0fb1ca068ffaddf22cbd0
     *
     * // It also supports UTF-8 encoding
     * md5('中文'); // a7bac2239fcdcb3a067903d8077c4a07
     *
     * // It also supports byte `Array`, `Uint8Array`, `ArrayBuffer`
     * md5([]); // d41d8cd98f00b204e9800998ecf8427e
     * md5(new Uint8Array([])); // d41d8cd98f00b204e9800998ecf8427e
     */
    root.md5 = exports;
    if (AMD) {
      !(__WEBPACK_AMD_DEFINE_RESULT__ = (function () {
        return exports;
      }).call(exports, __webpack_require__, exports, module),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
    }
  }
})();

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../process/browser.js */ "./node_modules/process/browser.js"), __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/jszip/dist/jszip.min.js":
/*!**********************************************!*\
  !*** ./node_modules/jszip/dist/jszip.min.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer, setImmediate, global, process) {var require;var require;/*!

JSZip v3.10.1 - A JavaScript class for generating and reading zip files
<http://stuartk.com/jszip>

(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.

JSZip uses the library pako released under the MIT license :
https://github.com/nodeca/pako/blob/main/LICENSE
*/

!function(e){if(true)module.exports=e();else {}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t="function"==typeof require&&require;if(!e&&t)return require(r,!0);if(l)return l(r,!0);var n=new Error("Cannot find module '"+r+"'");throw n.code="MODULE_NOT_FOUND",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l="function"==typeof require&&require,e=0;e<h.length;e++)u(h[e]);return u}({1:[function(e,t,r){"use strict";var d=e("./utils"),c=e("./support"),p="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";r.encode=function(e){for(var t,r,n,i,s,a,o,h=[],u=0,l=e.length,f=l,c="string"!==d.getTypeOf(e);u<e.length;)f=l-u,n=c?(t=e[u++],r=u<l?e[u++]:0,u<l?e[u++]:0):(t=e.charCodeAt(u++),r=u<l?e.charCodeAt(u++):0,u<l?e.charCodeAt(u++):0),i=t>>2,s=(3&t)<<4|r>>4,a=1<f?(15&r)<<2|n>>6:64,o=2<f?63&n:64,h.push(p.charAt(i)+p.charAt(s)+p.charAt(a)+p.charAt(o));return h.join("")},r.decode=function(e){var t,r,n,i,s,a,o=0,h=0,u="data:";if(e.substr(0,u.length)===u)throw new Error("Invalid base64 input, it looks like a data url.");var l,f=3*(e=e.replace(/[^A-Za-z0-9+/=]/g,"")).length/4;if(e.charAt(e.length-1)===p.charAt(64)&&f--,e.charAt(e.length-2)===p.charAt(64)&&f--,f%1!=0)throw new Error("Invalid base64 input, bad content length.");for(l=c.uint8array?new Uint8Array(0|f):new Array(0|f);o<e.length;)t=p.indexOf(e.charAt(o++))<<2|(i=p.indexOf(e.charAt(o++)))>>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{"./support":30,"./utils":32}],2:[function(e,t,r){"use strict";var n=e("./external"),i=e("./stream/DataWorker"),s=e("./stream/Crc32Probe"),a=e("./stream/DataLengthProbe");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a("data_length")),t=this;return e.on("end",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error("Bug : uncompressed data size mismatch")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo("compressedSize",this.compressedSize).withStreamInfo("uncompressedSize",this.uncompressedSize).withStreamInfo("crc32",this.crc32).withStreamInfo("compression",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a("uncompressedSize")).pipe(t.compressWorker(r)).pipe(new a("compressedSize")).withStreamInfo("compression",t)},t.exports=o},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(e,t,r){"use strict";var n=e("./stream/GenericWorker");r.STORE={magic:"\0\0",compressWorker:function(){return new n("STORE compression")},uncompressWorker:function(){return new n("STORE decompression")}},r.DEFLATE=e("./flate")},{"./flate":7,"./stream/GenericWorker":28}],4:[function(e,t,r){"use strict";var n=e("./utils");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?"string"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{"./utils":32}],5:[function(e,t,r){"use strict";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){"use strict";var n=null;n="undefined"!=typeof Promise?Promise:e("lie"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Uint32Array,i=e("pako"),s=e("./utils"),a=e("./stream/GenericWorker"),o=n?"uint8array":"array";function h(e,t){a.call(this,"FlateWorker/"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic="\b\0",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h("Deflate",e)},r.uncompressWorker=function(){return new h("Inflate",{})}},{"./stream/GenericWorker":28,"./utils":32,pako:38}],8:[function(e,t,r){"use strict";function A(e,t){var r,n="";for(r=0;r<t;r++)n+=String.fromCharCode(255&e),e>>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo("string",s(h.name)),c=I.transformTo("string",O.utf8encode(h.name)),d=h.comment,p=I.transformTo("string",s(d)),m=I.transformTo("string",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b="",v="",y="",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),"UNIX"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+="up"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+="uc"+A(y.length,2)+y);var E="";return E+="\n\0",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+"\0\0\0\0"+A(z,4)+A(n,4)+f+b+p}}var I=e("../utils"),i=e("../stream/GenericWorker"),O=e("../utf8"),B=e("../crc32"),R=e("../signature");function s(e,t,r,n){i.call(this,"ZipFileWorker"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t<this.dirRecords.length;t++)this.push({data:this.dirRecords[t],meta:{percent:100}});var r=this.bytesWritten-e,n=function(e,t,r,n,i){var s=I.transformTo("string",i(n));return R.CENTRAL_DIRECTORY_END+"\0\0\0\0"+A(e,2)+A(e,2)+A(t,4)+A(r,4)+A(s.length,2)+s}(this.dirRecords.length,r,e,this.zipComment,this.encodeFileName);this.push({data:n,meta:{percent:100}})},s.prototype.prepareNextSource=function(){this.previous=this._sources.shift(),this.openedSource(this.previous.streamInfo),this.isPaused?this.previous.pause():this.previous.resume()},s.prototype.registerPrevious=function(e){this._sources.push(e);var t=this;return e.on("data",function(e){t.processChunk(e)}),e.on("end",function(){t.closedSource(t.previous.streamInfo),t._sources.length?t.prepareNextSource():t.end()}),e.on("error",function(e){t.error(e)}),this},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this.previous&&this._sources.length?(this.prepareNextSource(),!0):this.previous||this._sources.length||this.generatedError?void 0:(this.end(),!0))},s.prototype.error=function(e){var t=this._sources;if(!i.prototype.error.call(this,e))return!1;for(var r=0;r<t.length;r++)try{t[r].error(e)}catch(e){}return!0},s.prototype.lock=function(){i.prototype.lock.call(this);for(var e=this._sources,t=0;t<e.length;t++)e[t].lock()},t.exports=s},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(e,t,r){"use strict";var u=e("../compressions"),n=e("./ZipFileWorker");r.generateWorker=function(e,a,t){var o=new n(a.streamFiles,t,a.platform,a.encodeFileName),h=0;try{e.forEach(function(e,t){h++;var r=function(e,t){var r=e||t,n=u[r];if(!n)throw new Error(r+" is not a valid compression method !");return n}(t.options.compression,a.compression),n=t.options.compressionOptions||a.compressionOptions||{},i=t.dir,s=t.date;t._compressWorker(r,n).withStreamInfo("file",{name:e,dir:i,date:s,comment:t.comment||"",unixPermissions:t.unixPermissions,dosPermissions:t.dosPermissions}).pipe(o)}),o.entriesCount=h}catch(e){o.error(e)}return o}},{"../compressions":3,"./ZipFileWorker":8}],10:[function(e,t,r){"use strict";function n(){if(!(this instanceof n))return new n;if(arguments.length)throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");this.files=Object.create(null),this.comment=null,this.root="",this.clone=function(){var e=new n;for(var t in this)"function"!=typeof this[t]&&(e[t]=this[t]);return e}}(n.prototype=e("./object")).loadAsync=e("./load"),n.support=e("./support"),n.defaults=e("./defaults"),n.version="3.10.1",n.loadAsync=function(e,t){return(new n).loadAsync(e,t)},n.external=e("./external"),t.exports=n},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(e,t,r){"use strict";var u=e("./utils"),i=e("./external"),n=e("./utf8"),s=e("./zipEntries"),a=e("./stream/Crc32Probe"),l=e("./nodejsUtils");function f(n){return new i.Promise(function(e,t){var r=n.decompressed.getContentWorker().pipe(new a);r.on("error",function(e){t(e)}).on("end",function(){r.streamInfo.crc32!==n.decompressed.crc32?t(new Error("Corrupted zip : CRC32 mismatch")):e()}).resume()})}t.exports=function(e,o){var h=this;return o=u.extend(o||{},{base64:!1,checkCRC32:!1,optimizedBinaryString:!1,createFolders:!1,decodeFileName:n.utf8decode}),l.isNode&&l.isStream(e)?i.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")):u.prepareContent("the loaded zip file",e,!0,o.optimizedBinaryString,o.base64).then(function(e){var t=new s(o);return t.load(e),t}).then(function(e){var t=[i.Promise.resolve(e)],r=e.files;if(o.checkCRC32)for(var n=0;n<r.length;n++)t.push(f(r[n]));return i.Promise.all(t)}).then(function(e){for(var t=e.shift(),r=t.files,n=0;n<r.length;n++){var i=r[n],s=i.fileNameStr,a=u.resolve(i.fileNameStr);h.file(a,i.decompressed,{binary:!0,optimizedBinaryString:!0,date:i.date,dir:i.dir,comment:i.fileCommentStr.length?i.fileCommentStr:null,unixPermissions:i.unixPermissions,dosPermissions:i.dosPermissions,createFolders:o.createFolders}),i.dir||(h.file(a).unsafeOriginalName=s)}return t.zipComment.length&&(h.comment=t.zipComment),h})}},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../stream/GenericWorker");function s(e,t){i.call(this,"Nodejs stream input adapter for "+e),this._upstreamEnded=!1,this._bindStream(t)}n.inherits(s,i),s.prototype._bindStream=function(e){var t=this;(this._stream=e).pause(),e.on("data",function(e){t.push({data:e,meta:{percent:0}})}).on("error",function(e){t.isPaused?this.generatedError=e:t.error(e)}).on("end",function(){t.isPaused?t._upstreamEnded=!0:t.end()})},s.prototype.pause=function(){return!!i.prototype.pause.call(this)&&(this._stream.pause(),!0)},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(this._upstreamEnded?this.end():this._stream.resume(),!0)},t.exports=s},{"../stream/GenericWorker":28,"../utils":32}],13:[function(e,t,r){"use strict";var i=e("readable-stream").Readable;function n(e,t,r){i.call(this,t),this._helper=e;var n=this;e.on("data",function(e,t){n.push(e)||n._helper.pause(),r&&r(t)}).on("error",function(e){n.emit("error",e)}).on("end",function(){n.push(null)})}e("../utils").inherits(n,i),n.prototype._read=function(){this._helper.resume()},t.exports=n},{"../utils":32,"readable-stream":16}],14:[function(e,t,r){"use strict";t.exports={isNode:"undefined"!=typeof Buffer,newBufferFrom:function(e,t){if(Buffer.from&&Buffer.from!==Uint8Array.from)return Buffer.from(e,t);if("number"==typeof e)throw new Error('The "data" argument must not be a number');return new Buffer(e,t)},allocBuffer:function(e){if(Buffer.alloc)return Buffer.alloc(e);var t=new Buffer(e);return t.fill(0),t},isBuffer:function(e){return Buffer.isBuffer(e)},isStream:function(e){return e&&"function"==typeof e.on&&"function"==typeof e.pause&&"function"==typeof e.resume}}},{}],15:[function(e,t,r){"use strict";function s(e,t,r){var n,i=u.getTypeOf(t),s=u.extend(r||{},f);s.date=s.date||new Date,null!==s.compression&&(s.compression=s.compression.toUpperCase()),"string"==typeof s.unixPermissions&&(s.unixPermissions=parseInt(s.unixPermissions,8)),s.unixPermissions&&16384&s.unixPermissions&&(s.dir=!0),s.dosPermissions&&16&s.dosPermissions&&(s.dir=!0),s.dir&&(e=g(e)),s.createFolders&&(n=_(e))&&b.call(this,n,!0);var a="string"===i&&!1===s.binary&&!1===s.base64;r&&void 0!==r.binary||(s.binary=!a),(t instanceof c&&0===t.uncompressedSize||s.dir||!t||0===t.length)&&(s.base64=!1,s.binary=!0,t="",s.compression="STORE",i="string");var o=null;o=t instanceof c||t instanceof l?t:p.isNode&&p.isStream(t)?new m(e,t):u.prepareContent(e,t,s.binary,s.optimizedBinaryString,s.base64);var h=new d(e,o,s);this.files[e]=h}var i=e("./utf8"),u=e("./utils"),l=e("./stream/GenericWorker"),a=e("./stream/StreamHelper"),f=e("./defaults"),c=e("./compressedObject"),d=e("./zipObject"),o=e("./generate"),p=e("./nodejsUtils"),m=e("./nodejs/NodejsStreamInputAdapter"),_=function(e){"/"===e.slice(-1)&&(e=e.substring(0,e.length-1));var t=e.lastIndexOf("/");return 0<t?e.substring(0,t):""},g=function(e){return"/"!==e.slice(-1)&&(e+="/"),e},b=function(e,t){return t=void 0!==t?t:f.createFolders,e=g(e),this.files[e]||s.call(this,e,null,{dir:!0,createFolders:t}),this.files[e]};function h(e){return"[object RegExp]"===Object.prototype.toString.call(e)}var n={load:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},forEach:function(e){var t,r,n;for(t in this.files)n=this.files[t],(r=t.slice(this.root.length,t.length))&&t.slice(0,this.root.length)===this.root&&e(r,n)},filter:function(r){var n=[];return this.forEach(function(e,t){r(e,t)&&n.push(t)}),n},file:function(e,t,r){if(1!==arguments.length)return e=this.root+e,s.call(this,e,t,r),this;if(h(e)){var n=e;return this.filter(function(e,t){return!t.dir&&n.test(e)})}var i=this.files[this.root+e];return i&&!i.dir?i:null},folder:function(r){if(!r)return this;if(h(r))return this.filter(function(e,t){return t.dir&&r.test(e)});var e=this.root+r,t=b.call(this,e),n=this.clone();return n.root=t.name,n},remove:function(r){r=this.root+r;var e=this.files[r];if(e||("/"!==r.slice(-1)&&(r+="/"),e=this.files[r]),e&&!e.dir)delete this.files[r];else for(var t=this.filter(function(e,t){return t.name.slice(0,r.length)===r}),n=0;n<t.length;n++)delete this.files[t[n].name];return this},generate:function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},generateInternalStream:function(e){var t,r={};try{if((r=u.extend(e||{},{streamFiles:!1,compression:"STORE",compressionOptions:null,type:"",platform:"DOS",comment:null,mimeType:"application/zip",encodeFileName:i.utf8encode})).type=r.type.toLowerCase(),r.compression=r.compression.toUpperCase(),"binarystring"===r.type&&(r.type="string"),!r.type)throw new Error("No output type specified.");u.checkSupport(r.type),"darwin"!==r.platform&&"freebsd"!==r.platform&&"linux"!==r.platform&&"sunos"!==r.platform||(r.platform="UNIX"),"win32"===r.platform&&(r.platform="DOS");var n=r.comment||this.comment||"";t=o.generateWorker(this,r,n)}catch(e){(t=new l("error")).error(e)}return new a(t,r.type||"string",r.mimeType)},generateAsync:function(e,t){return this.generateInternalStream(e).accumulate(t)},generateNodeStream:function(e,t){return(e=e||{}).type||(e.type="nodebuffer"),this.generateInternalStream(e).toNodejsStream(t)}};t.exports=n},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(e,t,r){"use strict";t.exports=e("stream")},{stream:void 0}],17:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e);for(var t=0;t<this.data.length;t++)e[t]=255&e[t]}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data[this.zero+e]},i.prototype.lastIndexOfSignature=function(e){for(var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.length-4;0<=s;--s)if(this.data[s]===t&&this.data[s+1]===r&&this.data[s+2]===n&&this.data[s+3]===i)return s-this.zero;return-1},i.prototype.readAndCheckSignature=function(e){var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.readData(4);return t===s[0]&&r===s[1]&&n===s[2]&&i===s[3]},i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return[];var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],18:[function(e,t,r){"use strict";var n=e("../utils");function i(e){this.data=e,this.length=e.length,this.index=0,this.zero=0}i.prototype={checkOffset:function(e){this.checkIndex(this.index+e)},checkIndex:function(e){if(this.length<this.zero+e||e<0)throw new Error("End of data reached (data length = "+this.length+", asked index = "+e+"). Corrupted zip ?")},setIndex:function(e){this.checkIndex(e),this.index=e},skip:function(e){this.setIndex(this.index+e)},byteAt:function(){},readInt:function(e){var t,r=0;for(this.checkOffset(e),t=this.index+e-1;t>=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo("string",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{"../utils":32}],19:[function(e,t,r){"use strict";var n=e("./Uint8ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(e,t,r){"use strict";var n=e("./DataReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./DataReader":18}],21:[function(e,t,r){"use strict";var n=e("./ArrayReader");function i(e){n.call(this,e)}e("../utils").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{"../utils":32,"./ArrayReader":17}],22:[function(e,t,r){"use strict";var n=e("../utils"),i=e("../support"),s=e("./ArrayReader"),a=e("./StringReader"),o=e("./NodeBufferReader"),h=e("./Uint8ArrayReader");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),"string"!==t||i.uint8array?"nodebuffer"===t?new o(e):i.uint8array?new h(n.transformTo("uint8array",e)):new s(n.transformTo("array",e)):new a(e)}},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(e,t,r){"use strict";r.LOCAL_FILE_HEADER="PK",r.CENTRAL_FILE_HEADER="PK",r.CENTRAL_DIRECTORY_END="PK",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR="PK",r.ZIP64_CENTRAL_DIRECTORY_END="PK",r.DATA_DESCRIPTOR="PK\b"},{}],24:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../utils");function s(e){n.call(this,"ConvertWorker to "+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{"../utils":32,"./GenericWorker":28}],25:[function(e,t,r){"use strict";var n=e("./GenericWorker"),i=e("../crc32");function s(){n.call(this,"Crc32Probe"),this.withStreamInfo("crc32",0)}e("../utils").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataLengthProbe for "+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{"../utils":32,"./GenericWorker":28}],27:[function(e,t,r){"use strict";var n=e("../utils"),i=e("./GenericWorker");function s(e){i.call(this,"DataWorker");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type="",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case"string":e=this.data.substring(this.index,t);break;case"uint8array":e=this.data.subarray(this.index,t);break;case"array":case"nodebuffer":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{"../utils":32,"./GenericWorker":28}],28:[function(e,t,r){"use strict";function n(e){this.name=e||"default",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit("data",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit("end"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit("error",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit("error",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r<this._listeners[e].length;r++)this._listeners[e][r].call(this,t)},pipe:function(e){return e.registerPrevious(this)},registerPrevious:function(e){if(this.isLocked)throw new Error("The stream '"+this+"' has already been used.");this.streamInfo=e.streamInfo,this.mergeStreamInfo(),this.previous=e;var t=this;return e.on("data",function(e){t.processChunk(e)}),e.on("end",function(){t.end()}),e.on("error",function(e){t.error(e)}),this},pause:function(){return!this.isPaused&&!this.isFinished&&(this.isPaused=!0,this.previous&&this.previous.pause(),!0)},resume:function(){if(!this.isPaused||this.isFinished)return!1;var e=this.isPaused=!1;return this.generatedError&&(this.error(this.generatedError),e=!0),this.previous&&this.previous.resume(),!e},flush:function(){},processChunk:function(e){this.push(e)},withStreamInfo:function(e,t){return this.extraStreamInfo[e]=t,this.mergeStreamInfo(),this},mergeStreamInfo:function(){for(var e in this.extraStreamInfo)Object.prototype.hasOwnProperty.call(this.extraStreamInfo,e)&&(this.streamInfo[e]=this.extraStreamInfo[e])},lock:function(){if(this.isLocked)throw new Error("The stream '"+this+"' has already been used.");this.isLocked=!0,this.previous&&this.previous.lock()},toString:function(){var e="Worker "+this.name;return this.previous?this.previous+" -> "+e:e}},t.exports=n},{}],29:[function(e,t,r){"use strict";var h=e("../utils"),i=e("./ConvertWorker"),s=e("./GenericWorker"),u=e("../base64"),n=e("../support"),a=e("../external"),o=null;if(n.nodestream)try{o=e("../nodejs/NodejsStreamOutputAdapter")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on("data",function(e,t){n.push(e),o&&o(t)}).on("error",function(e){n=[],r(e)}).on("end",function(){try{var e=function(e,t,r){switch(e){case"blob":return h.newBlob(h.transformTo("arraybuffer",t),r);case"base64":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r<t.length;r++)s+=t[r].length;switch(e){case"string":return t.join("");case"array":return Array.prototype.concat.apply([],t);case"uint8array":for(i=new Uint8Array(s),r=0;r<t.length;r++)i.set(t[r],n),n+=t[r].length;return i;case"nodebuffer":return Buffer.concat(t);default:throw new Error("concat : unsupported type '"+e+"'")}}(i,n),a);t(e)}catch(e){r(e)}n=[]}).resume()})}function f(e,t,r){var n=t;switch(t){case"blob":case"arraybuffer":n="uint8array";break;case"base64":n="string"}try{this._internalType=n,this._outputType=t,this._mimeType=r,h.checkSupport(n),this._worker=e.pipe(new i(n)),e.lock()}catch(e){this._worker=new s("error"),this._worker.error(e)}}f.prototype={accumulate:function(e){return l(this,e)},on:function(e,t){var r=this;return"data"===e?this._worker.on(e,function(e){t.call(r,e.data,e.meta)}):this._worker.on(e,function(){h.delay(t,arguments,r)}),this},resume:function(){return h.delay(this._worker.resume,[],this._worker),this},pause:function(){return this._worker.pause(),this},toNodejsStream:function(e){if(h.checkSupport("nodestream"),"nodebuffer"!==this._outputType)throw new Error(this._outputType+" is not supported by this method");return new o(this,{objectMode:"nodebuffer"!==this._outputType},e)}},t.exports=f},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(e,t,r){"use strict";if(r.base64=!0,r.array=!0,r.string=!0,r.arraybuffer="undefined"!=typeof ArrayBuffer&&"undefined"!=typeof Uint8Array,r.nodebuffer="undefined"!=typeof Buffer,r.uint8array="undefined"!=typeof Uint8Array,"undefined"==typeof ArrayBuffer)r.blob=!1;else{var n=new ArrayBuffer(0);try{r.blob=0===new Blob([n],{type:"application/zip"}).size}catch(e){try{var i=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);i.append(n),r.blob=0===i.getBlob("application/zip").size}catch(e){r.blob=!1}}}try{r.nodestream=!!e("readable-stream").Readable}catch(e){r.nodestream=!1}},{"readable-stream":16}],31:[function(e,t,s){"use strict";for(var o=e("./utils"),h=e("./support"),r=e("./nodejsUtils"),n=e("./stream/GenericWorker"),u=new Array(256),i=0;i<256;i++)u[i]=252<=i?6:248<=i?5:240<=i?4:224<=i?3:192<=i?2:1;u[254]=u[254]=1;function a(){n.call(this,"utf-8 decode"),this.leftOver=null}function l(){n.call(this,"utf-8 encode")}s.utf8encode=function(e){return h.nodebuffer?r.newBufferFrom(e,"utf-8"):function(e){var t,r,n,i,s,a=e.length,o=0;for(i=0;i<a;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),o+=r<128?1:r<2048?2:r<65536?3:4;for(t=h.uint8array?new Uint8Array(o):new Array(o),i=s=0;s<o;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),r<128?t[s++]=r:(r<2048?t[s++]=192|r>>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo("nodebuffer",e).toString("utf-8"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t<s;)if((n=e[t++])<128)a[r++]=n;else if(4<(i=u[n]))a[r++]=65533,t+=i-1;else{for(n&=2===i?31:3===i?15:7;1<i&&t<s;)n=n<<6|63&e[t++],i--;1<i?a[r++]=65533:n<65536?a[r++]=n:(n-=65536,a[r++]=55296|n>>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?"uint8array":"array",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?"uint8array":"array",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(e,t,a){"use strict";var o=e("./support"),h=e("./base64"),r=e("./nodejsUtils"),u=e("./external");function n(e){return e}function l(e,t){for(var r=0;r<e.length;++r)t[r]=255&e.charCodeAt(r);return t}e("setimmediate"),a.newBlob=function(t,r){a.checkSupport("blob");try{return new Blob([t],{type:r})}catch(e){try{var n=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);return n.append(t),n.getBlob(r)}catch(e){throw new Error("Bug : can't construct the Blob.")}}};var i={stringifyByChunk:function(e,t,r){var n=[],i=0,s=e.length;if(s<=r)return String.fromCharCode.apply(null,e);for(;i<s;)"array"===t||"nodebuffer"===t?n.push(String.fromCharCode.apply(null,e.slice(i,Math.min(i+r,s)))):n.push(String.fromCharCode.apply(null,e.subarray(i,Math.min(i+r,s)))),i+=r;return n.join("")},stringifyByChar:function(e){for(var t="",r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},applyCanBeUsed:{uint8array:function(){try{return o.uint8array&&1===String.fromCharCode.apply(null,new Uint8Array(1)).length}catch(e){return!1}}(),nodebuffer:function(){try{return o.nodebuffer&&1===String.fromCharCode.apply(null,r.allocBuffer(1)).length}catch(e){return!1}}()}};function s(e){var t=65536,r=a.getTypeOf(e),n=!0;if("uint8array"===r?n=i.applyCanBeUsed.uint8array:"nodebuffer"===r&&(n=i.applyCanBeUsed.nodebuffer),n)for(;1<t;)try{return i.stringifyByChunk(e,r,t)}catch(e){t=Math.floor(t/2)}return i.stringifyByChar(e)}function f(e,t){for(var r=0;r<e.length;r++)t[r]=e[r];return t}a.applyFromCharCode=s;var c={};c.string={string:n,array:function(e){return l(e,new Array(e.length))},arraybuffer:function(e){return c.string.uint8array(e).buffer},uint8array:function(e){return l(e,new Uint8Array(e.length))},nodebuffer:function(e){return l(e,r.allocBuffer(e.length))}},c.array={string:s,array:n,arraybuffer:function(e){return new Uint8Array(e).buffer},uint8array:function(e){return new Uint8Array(e)},nodebuffer:function(e){return r.newBufferFrom(e)}},c.arraybuffer={string:function(e){return s(new Uint8Array(e))},array:function(e){return f(new Uint8Array(e),new Array(e.byteLength))},arraybuffer:n,uint8array:function(e){return new Uint8Array(e)},nodebuffer:function(e){return r.newBufferFrom(new Uint8Array(e))}},c.uint8array={string:s,array:function(e){return f(e,new Array(e.length))},arraybuffer:function(e){return e.buffer},uint8array:n,nodebuffer:function(e){return r.newBufferFrom(e)}},c.nodebuffer={string:s,array:function(e){return f(e,new Array(e.length))},arraybuffer:function(e){return c.nodebuffer.uint8array(e).buffer},uint8array:function(e){return f(e,new Uint8Array(e.length))},nodebuffer:n},a.transformTo=function(e,t){if(t=t||"",!e)return t;a.checkSupport(e);var r=a.getTypeOf(t);return c[r][e](t)},a.resolve=function(e){for(var t=e.split("/"),r=[],n=0;n<t.length;n++){var i=t[n];"."===i||""===i&&0!==n&&n!==t.length-1||(".."===i?r.pop():r.push(i))}return r.join("/")},a.getTypeOf=function(e){return"string"==typeof e?"string":"[object Array]"===Object.prototype.toString.call(e)?"array":o.nodebuffer&&r.isBuffer(e)?"nodebuffer":o.uint8array&&e instanceof Uint8Array?"uint8array":o.arraybuffer&&e instanceof ArrayBuffer?"arraybuffer":void 0},a.checkSupport=function(e){if(!o[e.toLowerCase()])throw new Error(e+" is not supported by this platform")},a.MAX_VALUE_16BITS=65535,a.MAX_VALUE_32BITS=-1,a.pretty=function(e){var t,r,n="";for(r=0;r<(e||"").length;r++)n+="\\x"+((t=e.charCodeAt(r))<16?"0":"")+t.toString(16).toUpperCase();return n},a.delay=function(e,t,r){setImmediate(function(){e.apply(r||null,t||[])})},a.inherits=function(e,t){function r(){}r.prototype=t.prototype,e.prototype=new r},a.extend=function(){var e,t,r={};for(e=0;e<arguments.length;e++)for(t in arguments[e])Object.prototype.hasOwnProperty.call(arguments[e],t)&&void 0===r[t]&&(r[t]=arguments[e][t]);return r},a.prepareContent=function(r,e,n,i,s){return u.Promise.resolve(e).then(function(n){return o.blob&&(n instanceof Blob||-1!==["[object File]","[object Blob]"].indexOf(Object.prototype.toString.call(n)))&&"undefined"!=typeof FileReader?new u.Promise(function(t,r){var e=new FileReader;e.onload=function(e){t(e.target.result)},e.onerror=function(e){r(e.target.error)},e.readAsArrayBuffer(n)}):n}).then(function(e){var t=a.getTypeOf(e);return t?("arraybuffer"===t?e=a.transformTo("uint8array",e):"string"===t&&(s?e=h.decode(e):n&&!0!==i&&(e=function(e){return l(e,o.uint8array?new Uint8Array(e.length):new Array(e.length))}(e))),e):u.Promise.reject(new Error("Can't read the data of '"+r+"'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?"))})}},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,setimmediate:54}],33:[function(e,t,r){"use strict";var n=e("./reader/readerFor"),i=e("./utils"),s=e("./signature"),a=e("./zipEntry"),o=e("./support");function h(e){this.files=[],this.loadOptions=e}h.prototype={checkSignature:function(e){if(!this.reader.readAndCheckSignature(e)){this.reader.index-=4;var t=this.reader.readString(4);throw new Error("Corrupted zip or bug: unexpected signature ("+i.pretty(t)+", expected "+i.pretty(e)+")")}},isSignature:function(e,t){var r=this.reader.index;this.reader.setIndex(e);var n=this.reader.readString(4)===t;return this.reader.setIndex(r),n},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2);var e=this.reader.readData(this.zipCommentLength),t=o.uint8array?"uint8array":"array",r=i.transformTo(t,e);this.zipComment=this.loadOptions.decodeFileName(r)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.reader.skip(4),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var e,t,r,n=this.zip64EndOfCentralSize-44;0<n;)e=this.reader.readInt(2),t=this.reader.readInt(4),r=this.reader.readData(t),this.zip64ExtensibleData[e]={id:e,length:t,value:r}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),1<this.disksCount)throw new Error("Multi-volumes zip are not supported")},readLocalFiles:function(){var e,t;for(e=0;e<this.files.length;e++)t=this.files[e],this.reader.setIndex(t.localHeaderOffset),this.checkSignature(s.LOCAL_FILE_HEADER),t.readLocalPart(this.reader),t.handleUTF8(),t.processAttributes()},readCentralDir:function(){var e;for(this.reader.setIndex(this.centralDirOffset);this.reader.readAndCheckSignature(s.CENTRAL_FILE_HEADER);)(e=new a({zip64:this.zip64},this.loadOptions)).readCentralPart(this.reader),this.files.push(e);if(this.centralDirRecords!==this.files.length&&0!==this.centralDirRecords&&0===this.files.length)throw new Error("Corrupted zip or bug: expected "+this.centralDirRecords+" records in central dir, got "+this.files.length)},readEndOfCentral:function(){var e=this.reader.lastIndexOfSignature(s.CENTRAL_DIRECTORY_END);if(e<0)throw!this.isSignature(0,s.LOCAL_FILE_HEADER)?new Error("Can't find end of central directory : is this a zip file ? If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html"):new Error("Corrupted zip: can't find end of central directory");this.reader.setIndex(e);var t=e;if(this.checkSignature(s.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===i.MAX_VALUE_16BITS||this.diskWithCentralDirStart===i.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===i.MAX_VALUE_16BITS||this.centralDirRecords===i.MAX_VALUE_16BITS||this.centralDirSize===i.MAX_VALUE_32BITS||this.centralDirOffset===i.MAX_VALUE_32BITS){if(this.zip64=!0,(e=this.reader.lastIndexOfSignature(s.ZIP64_CENTRAL_DIRECTORY_LOCATOR))<0)throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator");if(this.reader.setIndex(e),this.checkSignature(s.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),!this.isSignature(this.relativeOffsetEndOfZip64CentralDir,s.ZIP64_CENTRAL_DIRECTORY_END)&&(this.relativeOffsetEndOfZip64CentralDir=this.reader.lastIndexOfSignature(s.ZIP64_CENTRAL_DIRECTORY_END),this.relativeOffsetEndOfZip64CentralDir<0))throw new Error("Corrupted zip: can't find the ZIP64 end of central directory");this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(s.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}var r=this.centralDirOffset+this.centralDirSize;this.zip64&&(r+=20,r+=12+this.zip64EndOfCentralSize);var n=t-r;if(0<n)this.isSignature(t,s.CENTRAL_FILE_HEADER)||(this.reader.zero=n);else if(n<0)throw new Error("Corrupted zip: missing "+Math.abs(n)+" bytes.")},prepareReader:function(e){this.reader=n(e)},load:function(e){this.prepareReader(e),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},t.exports=h},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utils":32,"./zipEntry":34}],34:[function(e,t,r){"use strict";var n=e("./reader/readerFor"),s=e("./utils"),i=e("./compressedObject"),a=e("./crc32"),o=e("./utf8"),h=e("./compressions"),u=e("./support");function l(e,t){this.options=e,this.loadOptions=t}l.prototype={isEncrypted:function(){return 1==(1&this.bitFlag)},useUTF8:function(){return 2048==(2048&this.bitFlag)},readLocalPart:function(e){var t,r;if(e.skip(22),this.fileNameLength=e.readInt(2),r=e.readInt(2),this.fileName=e.readData(this.fileNameLength),e.skip(r),-1===this.compressedSize||-1===this.uncompressedSize)throw new Error("Bug or corrupted zip : didn't get enough information from the central directory (compressedSize === -1 || uncompressedSize === -1)");if(null===(t=function(e){for(var t in h)if(Object.prototype.hasOwnProperty.call(h,t)&&h[t].magic===e)return h[t];return null}(this.compressionMethod)))throw new Error("Corrupted zip : compression "+s.pretty(this.compressionMethod)+" unknown (inner file : "+s.transformTo("string",this.fileName)+")");this.decompressed=new i(this.compressedSize,this.uncompressedSize,this.crc32,t,e.readData(this.compressedSize))},readCentralPart:function(e){this.versionMadeBy=e.readInt(2),e.skip(2),this.bitFlag=e.readInt(2),this.compressionMethod=e.readString(2),this.date=e.readDate(),this.crc32=e.readInt(4),this.compressedSize=e.readInt(4),this.uncompressedSize=e.readInt(4);var t=e.readInt(2);if(this.extraFieldsLength=e.readInt(2),this.fileCommentLength=e.readInt(2),this.diskNumberStart=e.readInt(2),this.internalFileAttributes=e.readInt(2),this.externalFileAttributes=e.readInt(4),this.localHeaderOffset=e.readInt(4),this.isEncrypted())throw new Error("Encrypted zip are not supported");e.skip(t),this.readExtraFields(e),this.parseZIP64ExtraField(e),this.fileComment=e.readData(this.fileCommentLength)},processAttributes:function(){this.unixPermissions=null,this.dosPermissions=null;var e=this.versionMadeBy>>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||"/"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4<i;)t=e.readInt(2),r=e.readInt(2),n=e.readData(r),this.extraFields[t]={id:t,length:r,value:n};e.setIndex(i)},handleUTF8:function(){var e=u.uint8array?"uint8array":"array";if(this.useUTF8())this.fileNameStr=o.utf8decode(this.fileName),this.fileCommentStr=o.utf8decode(this.fileComment);else{var t=this.findExtraFieldUnicodePath();if(null!==t)this.fileNameStr=t;else{var r=s.transformTo(e,this.fileName);this.fileNameStr=this.loadOptions.decodeFileName(r)}var n=this.findExtraFieldUnicodeComment();if(null!==n)this.fileCommentStr=n;else{var i=s.transformTo(e,this.fileComment);this.fileCommentStr=this.loadOptions.decodeFileName(i)}}},findExtraFieldUnicodePath:function(){var e=this.extraFields[28789];if(e){var t=n(e.value);return 1!==t.readInt(1)?null:a(this.fileName)!==t.readInt(4)?null:o.utf8decode(t.readData(e.length-5))}return null},findExtraFieldUnicodeComment:function(){var e=this.extraFields[25461];if(e){var t=n(e.value);return 1!==t.readInt(1)?null:a(this.fileComment)!==t.readInt(4)?null:o.utf8decode(t.readData(e.length-5))}return null}},t.exports=l},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(e,t,r){"use strict";function n(e,t,r){this.name=e,this.dir=r.dir,this.date=r.date,this.comment=r.comment,this.unixPermissions=r.unixPermissions,this.dosPermissions=r.dosPermissions,this._data=t,this._dataBinary=r.binary,this.options={compression:r.compression,compressionOptions:r.compressionOptions}}var s=e("./stream/StreamHelper"),i=e("./stream/DataWorker"),a=e("./utf8"),o=e("./compressedObject"),h=e("./stream/GenericWorker");n.prototype={internalStream:function(e){var t=null,r="string";try{if(!e)throw new Error("No output type specified.");var n="string"===(r=e.toLowerCase())||"text"===r;"binarystring"!==r&&"text"!==r||(r="string"),t=this._decompressWorker();var i=!this._dataBinary;i&&!n&&(t=t.pipe(new a.Utf8EncodeWorker)),!i&&n&&(t=t.pipe(new a.Utf8DecodeWorker))}catch(e){(t=new h("error")).error(e)}return new s(t,r,"")},async:function(e,t){return this.internalStream(e).accumulate(t)},nodeStream:function(e,t){return this.internalStream(e||"nodebuffer").toNodejsStream(t)},_compressWorker:function(e,t){if(this._data instanceof o&&this._data.compression.magic===e.magic)return this._data.getCompressedWorker();var r=this._decompressWorker();return this._dataBinary||(r=r.pipe(new a.Utf8EncodeWorker)),o.createWorkerFrom(r,e,t)},_decompressWorker:function(){return this._data instanceof o?this._data.getContentWorker():this._data instanceof h?this._data:new i(this._data)}};for(var u=["asText","asBinary","asNodeBuffer","asUint8Array","asArrayBuffer"],l=function(){throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.")},f=0;f<u.length;f++)n.prototype[u[f]]=l;t.exports=n},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(e,l,t){(function(t){"use strict";var r,n,e=t.MutationObserver||t.WebKitMutationObserver;if(e){var i=0,s=new e(u),a=t.document.createTextNode("");s.observe(a,{characterData:!0}),r=function(){a.data=i=++i%2}}else if(t.setImmediate||void 0===t.MessageChannel)r="document"in t&&"onreadystatechange"in t.document.createElement("script")?function(){var e=t.document.createElement("script");e.onreadystatechange=function(){u(),e.onreadystatechange=null,e.parentNode.removeChild(e),e=null},t.document.documentElement.appendChild(e)}:function(){setTimeout(u,0)};else{var o=new t.MessageChannel;o.port1.onmessage=u,r=function(){o.port2.postMessage(0)}}var h=[];function u(){var e,t;n=!0;for(var r=h.length;r;){for(t=h,h=[],e=-1;++e<r;)t[e]();r=h.length}n=!1}l.exports=function(e){1!==h.push(e)||n||r()}}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}],37:[function(e,t,r){"use strict";var i=e("immediate");function u(){}var l={},s=["REJECTED"],a=["FULFILLED"],n=["PENDING"];function o(e){if("function"!=typeof e)throw new TypeError("resolver must be a function");this.state=n,this.queue=[],this.outcome=void 0,e!==u&&d(this,e)}function h(e,t,r){this.promise=e,"function"==typeof t&&(this.onFulfilled=t,this.callFulfilled=this.otherCallFulfilled),"function"==typeof r&&(this.onRejected=r,this.callRejected=this.otherCallRejected)}function f(t,r,n){i(function(){var e;try{e=r(n)}catch(e){return l.reject(t,e)}e===t?l.reject(t,new TypeError("Cannot resolve promise with itself")):l.resolve(t,e)})}function c(e){var t=e&&e.then;if(e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof t)return function(){t.apply(e,arguments)}}function d(t,e){var r=!1;function n(e){r||(r=!0,l.reject(t,e))}function i(e){r||(r=!0,l.resolve(t,e))}var s=p(function(){e(i,n)});"error"===s.status&&n(s.value)}function p(e,t){var r={};try{r.value=e(t),r.status="success"}catch(e){r.status="error",r.value=e}return r}(t.exports=o).prototype.finally=function(t){if("function"!=typeof t)return this;var r=this.constructor;return this.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})})},o.prototype.catch=function(e){return this.then(null,e)},o.prototype.then=function(e,t){if("function"!=typeof e&&this.state===a||"function"!=typeof t&&this.state===s)return this;var r=new this.constructor(u);this.state!==n?f(r,this.state===a?e:t,this.outcome):this.queue.push(new h(r,e,t));return r},h.prototype.callFulfilled=function(e){l.resolve(this.promise,e)},h.prototype.otherCallFulfilled=function(e){f(this.promise,this.onFulfilled,e)},h.prototype.callRejected=function(e){l.reject(this.promise,e)},h.prototype.otherCallRejected=function(e){f(this.promise,this.onRejected,e)},l.resolve=function(e,t){var r=p(c,t);if("error"===r.status)return l.reject(e,r.value);var n=r.value;if(n)d(e,n);else{e.state=a,e.outcome=t;for(var i=-1,s=e.queue.length;++i<s;)e.queue[i].callFulfilled(t)}return e},l.reject=function(e,t){e.state=s,e.outcome=t;for(var r=-1,n=e.queue.length;++r<n;)e.queue[r].callRejected(t);return e},o.resolve=function(e){if(e instanceof this)return e;return l.resolve(new this(u),e)},o.reject=function(e){var t=new this(u);return l.reject(t,e)},o.all=function(e){var r=this;if("[object Array]"!==Object.prototype.toString.call(e))return this.reject(new TypeError("must be an array"));var n=e.length,i=!1;if(!n)return this.resolve([]);var s=new Array(n),a=0,t=-1,o=new this(u);for(;++t<n;)h(e[t],t);return o;function h(e,t){r.resolve(e).then(function(e){s[t]=e,++a!==n||i||(i=!0,l.resolve(o,s))},function(e){i||(i=!0,l.reject(o,e))})}},o.race=function(e){var t=this;if("[object Array]"!==Object.prototype.toString.call(e))return this.reject(new TypeError("must be an array"));var r=e.length,n=!1;if(!r)return this.resolve([]);var i=-1,s=new this(u);for(;++i<r;)a=e[i],t.resolve(a).then(function(e){n||(n=!0,l.resolve(s,e))},function(e){n||(n=!0,l.reject(s,e))});var a;return s}},{immediate:36}],38:[function(e,t,r){"use strict";var n={};(0,e("./lib/utils/common").assign)(n,e("./lib/deflate"),e("./lib/inflate"),e("./lib/zlib/constants")),t.exports=n},{"./lib/deflate":39,"./lib/inflate":40,"./lib/utils/common":41,"./lib/zlib/constants":44}],39:[function(e,t,r){"use strict";var a=e("./zlib/deflate"),o=e("./utils/common"),h=e("./utils/strings"),i=e("./zlib/messages"),s=e("./zlib/zstream"),u=Object.prototype.toString,l=0,f=-1,c=0,d=8;function p(e){if(!(this instanceof p))return new p(e);this.options=o.assign({level:f,method:d,chunkSize:16384,windowBits:15,memLevel:8,strategy:c,to:""},e||{});var t=this.options;t.raw&&0<t.windowBits?t.windowBits=-t.windowBits:t.gzip&&0<t.windowBits&&t.windowBits<16&&(t.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new s,this.strm.avail_out=0;var r=a.deflateInit2(this.strm,t.level,t.method,t.windowBits,t.memLevel,t.strategy);if(r!==l)throw new Error(i[r]);if(t.header&&a.deflateSetHeader(this.strm,t.header),t.dictionary){var n;if(n="string"==typeof t.dictionary?h.string2buf(t.dictionary):"[object ArrayBuffer]"===u.call(t.dictionary)?new Uint8Array(t.dictionary):t.dictionary,(r=a.deflateSetDictionary(this.strm,n))!==l)throw new Error(i[r]);this._dict_set=!0}}function n(e,t){var r=new p(t);if(r.push(e,!0),r.err)throw r.msg||i[r.err];return r.result}p.prototype.push=function(e,t){var r,n,i=this.strm,s=this.options.chunkSize;if(this.ended)return!1;n=t===~~t?t:!0===t?4:0,"string"==typeof e?i.input=h.string2buf(e):"[object ArrayBuffer]"===u.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;do{if(0===i.avail_out&&(i.output=new o.Buf8(s),i.next_out=0,i.avail_out=s),1!==(r=a.deflate(i,n))&&r!==l)return this.onEnd(r),!(this.ended=!0);0!==i.avail_out&&(0!==i.avail_in||4!==n&&2!==n)||("string"===this.options.to?this.onData(h.buf2binstring(o.shrinkBuf(i.output,i.next_out))):this.onData(o.shrinkBuf(i.output,i.next_out)))}while((0<i.avail_in||0===i.avail_out)&&1!==r);return 4===n?(r=a.deflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===l):2!==n||(this.onEnd(l),!(i.avail_out=0))},p.prototype.onData=function(e){this.chunks.push(e)},p.prototype.onEnd=function(e){e===l&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},r.Deflate=p,r.deflate=n,r.deflateRaw=function(e,t){return(t=t||{}).raw=!0,n(e,t)},r.gzip=function(e,t){return(t=t||{}).gzip=!0,n(e,t)}},{"./utils/common":41,"./utils/strings":42,"./zlib/deflate":46,"./zlib/messages":51,"./zlib/zstream":53}],40:[function(e,t,r){"use strict";var c=e("./zlib/inflate"),d=e("./utils/common"),p=e("./utils/strings"),m=e("./zlib/constants"),n=e("./zlib/messages"),i=e("./zlib/zstream"),s=e("./zlib/gzheader"),_=Object.prototype.toString;function a(e){if(!(this instanceof a))return new a(e);this.options=d.assign({chunkSize:16384,windowBits:0,to:""},e||{});var t=this.options;t.raw&&0<=t.windowBits&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(0<=t.windowBits&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),15<t.windowBits&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new i,this.strm.avail_out=0;var r=c.inflateInit2(this.strm,t.windowBits);if(r!==m.Z_OK)throw new Error(n[r]);this.header=new s,c.inflateGetHeader(this.strm,this.header)}function o(e,t){var r=new a(t);if(r.push(e,!0),r.err)throw r.msg||n[r.err];return r.result}a.prototype.push=function(e,t){var r,n,i,s,a,o,h=this.strm,u=this.options.chunkSize,l=this.options.dictionary,f=!1;if(this.ended)return!1;n=t===~~t?t:!0===t?m.Z_FINISH:m.Z_NO_FLUSH,"string"==typeof e?h.input=p.binstring2buf(e):"[object ArrayBuffer]"===_.call(e)?h.input=new Uint8Array(e):h.input=e,h.next_in=0,h.avail_in=h.input.length;do{if(0===h.avail_out&&(h.output=new d.Buf8(u),h.next_out=0,h.avail_out=u),(r=c.inflate(h,m.Z_NO_FLUSH))===m.Z_NEED_DICT&&l&&(o="string"==typeof l?p.string2buf(l):"[object ArrayBuffer]"===_.call(l)?new Uint8Array(l):l,r=c.inflateSetDictionary(this.strm,o)),r===m.Z_BUF_ERROR&&!0===f&&(r=m.Z_OK,f=!1),r!==m.Z_STREAM_END&&r!==m.Z_OK)return this.onEnd(r),!(this.ended=!0);h.next_out&&(0!==h.avail_out&&r!==m.Z_STREAM_END&&(0!==h.avail_in||n!==m.Z_FINISH&&n!==m.Z_SYNC_FLUSH)||("string"===this.options.to?(i=p.utf8border(h.output,h.next_out),s=h.next_out-i,a=p.buf2string(h.output,i),h.next_out=s,h.avail_out=u-s,s&&d.arraySet(h.output,h.output,i,s,0),this.onData(a)):this.onData(d.shrinkBuf(h.output,h.next_out)))),0===h.avail_in&&0===h.avail_out&&(f=!0)}while((0<h.avail_in||0===h.avail_out)&&r!==m.Z_STREAM_END);return r===m.Z_STREAM_END&&(n=m.Z_FINISH),n===m.Z_FINISH?(r=c.inflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===m.Z_OK):n!==m.Z_SYNC_FLUSH||(this.onEnd(m.Z_OK),!(h.avail_out=0))},a.prototype.onData=function(e){this.chunks.push(e)},a.prototype.onEnd=function(e){e===m.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=d.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},r.Inflate=a,r.inflate=o,r.inflateRaw=function(e,t){return(t=t||{}).raw=!0,o(e,t)},r.ungzip=o},{"./utils/common":41,"./utils/strings":42,"./zlib/constants":44,"./zlib/gzheader":47,"./zlib/inflate":49,"./zlib/messages":51,"./zlib/zstream":53}],41:[function(e,t,r){"use strict";var n="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;r.assign=function(e){for(var t=Array.prototype.slice.call(arguments,1);t.length;){var r=t.shift();if(r){if("object"!=typeof r)throw new TypeError(r+"must be non-object");for(var n in r)r.hasOwnProperty(n)&&(e[n]=r[n])}}return e},r.shrinkBuf=function(e,t){return e.length===t?e:e.subarray?e.subarray(0,t):(e.length=t,e)};var i={arraySet:function(e,t,r,n,i){if(t.subarray&&e.subarray)e.set(t.subarray(r,r+n),i);else for(var s=0;s<n;s++)e[i+s]=t[r+s]},flattenChunks:function(e){var t,r,n,i,s,a;for(t=n=0,r=e.length;t<r;t++)n+=e[t].length;for(a=new Uint8Array(n),t=i=0,r=e.length;t<r;t++)s=e[t],a.set(s,i),i+=s.length;return a}},s={arraySet:function(e,t,r,n,i){for(var s=0;s<n;s++)e[i+s]=t[r+s]},flattenChunks:function(e){return[].concat.apply([],e)}};r.setTyped=function(e){e?(r.Buf8=Uint8Array,r.Buf16=Uint16Array,r.Buf32=Int32Array,r.assign(r,i)):(r.Buf8=Array,r.Buf16=Array,r.Buf32=Array,r.assign(r,s))},r.setTyped(n)},{}],42:[function(e,t,r){"use strict";var h=e("./common"),i=!0,s=!0;try{String.fromCharCode.apply(null,[0])}catch(e){i=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(e){s=!1}for(var u=new h.Buf8(256),n=0;n<256;n++)u[n]=252<=n?6:248<=n?5:240<=n?4:224<=n?3:192<=n?2:1;function l(e,t){if(t<65537&&(e.subarray&&s||!e.subarray&&i))return String.fromCharCode.apply(null,h.shrinkBuf(e,t));for(var r="",n=0;n<t;n++)r+=String.fromCharCode(e[n]);return r}u[254]=u[254]=1,r.string2buf=function(e){var t,r,n,i,s,a=e.length,o=0;for(i=0;i<a;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),o+=r<128?1:r<2048?2:r<65536?3:4;for(t=new h.Buf8(o),i=s=0;s<o;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),r<128?t[s++]=r:(r<2048?t[s++]=192|r>>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r<n;r++)t[r]=e.charCodeAt(r);return t},r.buf2string=function(e,t){var r,n,i,s,a=t||e.length,o=new Array(2*a);for(r=n=0;r<a;)if((i=e[r++])<128)o[n++]=i;else if(4<(s=u[i]))o[n++]=65533,r+=s-1;else{for(i&=2===s?31:3===s?15:7;1<s&&r<a;)i=i<<6|63&e[r++],s--;1<s?o[n++]=65533:i<65536?o[n++]=i:(i-=65536,o[n++]=55296|i>>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{"./common":41}],43:[function(e,t,r){"use strict";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3<r?2e3:r;s=s+(i=i+t[n++]|0)|0,--a;);i%=65521,s%=65521}return i|s<<16|0}},{}],44:[function(e,t,r){"use strict";t.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],45:[function(e,t,r){"use strict";var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){"use strict";var h,c=e("../utils/common"),u=e("./trees"),d=e("./adler32"),p=e("./crc32"),n=e("./messages"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4<e?9:0)}function D(e){for(var t=e.length;0<=--t;)e[t]=0}function F(e){var t=e.state,r=t.pending;r>e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&s<c);if(n=S-(c-s),s=c-S,a<n){if(e.match_start=t,o<=(a=n))break;d=u[s+a-1],p=u[s+a]}}}while((t=f[t&l])>h&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u<l&&(l=u),r=0===l?0:(a.avail_in-=l,c.arraySet(o,a.input,a.next_in,l,h),1===a.state.wrap?a.adler=d(a.adler,o,l,h):2===a.state.wrap&&(a.adler=p(a.adler,o,l,h)),a.next_in+=l,a.total_in+=l,l),e.lookahead+=r,e.lookahead+e.insert>=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<<e.hash_shift^e.window[s+1])&e.hash_mask;e.insert&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[s+x-1])&e.hash_mask,e.prev[s&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=s,s++,e.insert--,!(e.lookahead+e.insert<x)););}while(e.lookahead<z&&0!==e.strm.avail_in)}function Z(e,t){for(var r,n;;){if(e.lookahead<z){if(j(e),e.lookahead<z&&t===l)return A;if(0===e.lookahead)break}if(r=0,e.lookahead>=x&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),0!==r&&e.strstart-r<=e.w_size-z&&(e.match_length=L(e,r)),e.match_length>=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart,0!=--e.match_length;);e.strstart++}else e.strstart+=e.match_length,e.match_length=0,e.ins_h=e.window[e.strstart],e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+1])&e.hash_mask;else n=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++;if(n&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=e.strstart<x-1?e.strstart:x-1,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}function W(e,t){for(var r,n,i;;){if(e.lookahead<z){if(j(e),e.lookahead<z&&t===l)return A;if(0===e.lookahead)break}if(r=0,e.lookahead>=x&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),e.prev_length=e.match_length,e.prev_match=e.match_start,e.match_length=x-1,0!==r&&e.prev_length<e.max_lazy_match&&e.strstart-r<=e.w_size-z&&(e.match_length=L(e,r),e.match_length<=5&&(1===e.strategy||e.match_length===x&&4096<e.strstart-e.match_start)&&(e.match_length=x-1)),e.prev_length>=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),0!=--e.prev_length;);if(e.match_available=0,e.match_length=x-1,e.strstart++,n&&(N(e,!1),0===e.strm.avail_out))return A}else if(e.match_available){if((n=u._tr_tally(e,0,e.window[e.strstart-1]))&&N(e,!1),e.strstart++,e.lookahead--,0===e.strm.avail_out)return A}else e.match_available=1,e.strstart++,e.lookahead--}return e.match_available&&(n=u._tr_tally(e,0,e.window[e.strstart-1]),e.match_available=0),e.insert=e.strstart<x-1?e.strstart:x-1,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}function M(e,t,r,n,i){this.good_length=e,this.max_lazy=t,this.nice_length=r,this.max_chain=n,this.func=i}function H(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=v,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new c.Buf16(2*w),this.dyn_dtree=new c.Buf16(2*(2*a+1)),this.bl_tree=new c.Buf16(2*(2*o+1)),D(this.dyn_ltree),D(this.dyn_dtree),D(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new c.Buf16(k+1),this.heap=new c.Buf16(2*s+1),D(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new c.Buf16(2*s+1),D(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function G(e){var t;return e&&e.state?(e.total_in=e.total_out=0,e.data_type=i,(t=e.state).pending=0,t.pending_out=0,t.wrap<0&&(t.wrap=-t.wrap),t.status=t.wrap?C:E,e.adler=2===t.wrap?0:1,t.last_flush=l,u._tr_init(t),m):R(e,_)}function K(e){var t=G(e);return t===m&&function(e){e.window_size=2*e.w_size,D(e.head),e.max_lazy_match=h[e.level].max_lazy,e.good_match=h[e.level].good_length,e.nice_match=h[e.level].nice_length,e.max_chain_length=h[e.level].max_chain,e.strstart=0,e.block_start=0,e.lookahead=0,e.insert=0,e.match_length=e.prev_length=x-1,e.match_available=0,e.ins_h=0}(e.state),t}function Y(e,t,r,n,i,s){if(!e)return _;var a=1;if(t===g&&(t=6),n<0?(a=0,n=-n):15<n&&(a=2,n-=16),i<1||y<i||r!==v||n<8||15<n||t<0||9<t||s<0||b<s)return R(e,_);8===n&&(n=9);var o=new H;return(e.state=o).strm=e,o.wrap=a,o.gzhead=null,o.w_bits=n,o.w_size=1<<o.w_bits,o.w_mask=o.w_size-1,o.hash_bits=i+7,o.hash_size=1<<o.hash_bits,o.hash_mask=o.hash_size-1,o.hash_shift=~~((o.hash_bits+x-1)/x),o.window=new c.Buf8(2*o.w_size),o.head=new c.Buf16(o.hash_size),o.prev=new c.Buf16(o.w_size),o.lit_bufsize=1<<i+6,o.pending_buf_size=4*o.lit_bufsize,o.pending_buf=new c.Buf8(o.pending_buf_size),o.d_buf=1*o.lit_bufsize,o.l_buf=3*o.lit_bufsize,o.level=t,o.strategy=s,o.method=r,K(e)}h=[new M(0,0,0,0,function(e,t){var r=65535;for(r>e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5<t||t<0)return e?R(e,_):_;if(n=e.state,!e.output||!e.input&&0!==e.avail_in||666===n.status&&t!==f)return R(e,0===e.avail_out?-5:_);if(n.strm=e,r=n.last_flush,n.last_flush=t,n.status===C)if(2===n.wrap)e.adler=0,U(n,31),U(n,139),U(n,8),n.gzhead?(U(n,(n.gzhead.text?1:0)+(n.gzhead.hcrc?2:0)+(n.gzhead.extra?4:0)+(n.gzhead.name?8:0)+(n.gzhead.comment?16:0)),U(n,255&n.gzhead.time),U(n,n.gzhead.time>>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindex<n.gzhead.name.length?255&n.gzhead.name.charCodeAt(n.gzindex++):0,U(n,s)}while(0!==s);n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindex<n.gzhead.comment.length?255&n.gzhead.comment.charCodeAt(n.gzindex++):0,U(n,s)}while(0!==s);n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0<e.strstart&&(n=a[i=e.strstart-1])===a[++i]&&n===a[++i]&&n===a[++i]){s=e.strstart+S;do{}while(n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&i<s);e.match_length=S-(s-i),e.match_length>e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0<n.wrap&&(n.wrap=-n.wrap),0!==n.pending?m:1)},r.deflateEnd=function(e){var t;return e&&e.state?(t=e.state.status)!==C&&69!==t&&73!==t&&91!==t&&103!==t&&t!==E&&666!==t?R(e,_):(e.state=null,t===E?R(e,-3):m):_},r.deflateSetDictionary=function(e,t){var r,n,i,s,a,o,h,u,l=t.length;if(!e||!e.state)return _;if(2===(s=(r=e.state).wrap)||1===s&&r.status!==C||r.lookahead)return _;for(1===s&&(e.adler=d(e.adler,t,l,0)),r.wrap=0,l>=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<<r.hash_shift^r.window[n+x-1])&r.hash_mask,r.prev[n&r.w_mask]=r.head[r.ins_h],r.head[r.ins_h]=n,n++,--i;);r.strstart=n,r.lookahead=x-1,j(r)}return r.strstart+=r.lookahead,r.block_start=r.strstart,r.insert=r.lookahead,r.lookahead=0,r.match_length=r.prev_length=x-1,r.match_available=0,e.next_in=o,e.input=h,e.avail_in=a,r.wrap=s,m},r.deflateInfo="pako deflate (from Nodeca project)"},{"../utils/common":41,"./adler32":43,"./crc32":45,"./messages":51,"./trees":52}],47:[function(e,t,r){"use strict";t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name="",this.comment="",this.hcrc=0,this.done=!1}},{}],48:[function(e,t,r){"use strict";t.exports=function(e,t){var r,n,i,s,a,o,h,u,l,f,c,d,p,m,_,g,b,v,y,w,k,x,S,z,C;r=e.state,n=e.next_in,z=e.input,i=n+(e.avail_in-5),s=e.next_out,C=e.output,a=s-(t-e.avail_out),o=s+(e.avail_out-257),h=r.dmax,u=r.wsize,l=r.whave,f=r.wnext,c=r.window,d=r.hold,p=r.bits,m=r.lencode,_=r.distcode,g=(1<<r.lenbits)-1,b=(1<<r.distbits)-1;e:do{p<15&&(d+=z[n++]<<p,p+=8,d+=z[n++]<<p,p+=8),v=m[d&g];t:for(;;){if(d>>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<<y)-1)];continue t}if(32&y){r.mode=12;break e}e.msg="invalid literal/length code",r.mode=30;break e}w=65535&v,(y&=15)&&(p<y&&(d+=z[n++]<<p,p+=8),w+=d&(1<<y)-1,d>>>=y,p-=y),p<15&&(d+=z[n++]<<p,p+=8,d+=z[n++]<<p,p+=8),v=_[d&b];r:for(;;){if(d>>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<<y)-1)];continue r}e.msg="invalid distance code",r.mode=30;break e}if(k=65535&v,p<(y&=15)&&(d+=z[n++]<<p,(p+=8)<y&&(d+=z[n++]<<p,p+=8)),h<(k+=d&(1<<y)-1)){e.msg="invalid distance too far back",r.mode=30;break e}if(d>>>=y,p-=y,(y=s-a)<k){if(l<(y=k-y)&&r.sane){e.msg="invalid distance too far back",r.mode=30;break e}if(S=c,(x=0)===f){if(x+=u-y,y<w){for(w-=y;C[s++]=c[x++],--y;);x=s-k,S=C}}else if(f<y){if(x+=u+f-y,(y-=f)<w){for(w-=y;C[s++]=c[x++],--y;);if(x=0,f<w){for(w-=y=f;C[s++]=c[x++],--y;);x=s-k,S=C}}}else if(x+=f-y,y<w){for(w-=y;C[s++]=c[x++],--y;);x=s-k,S=C}for(;2<w;)C[s++]=S[x++],C[s++]=S[x++],C[s++]=S[x++],w-=3;w&&(C[s++]=S[x++],1<w&&(C[s++]=S[x++]))}else{for(x=s-k;C[s++]=C[x++],C[s++]=C[x++],C[s++]=C[x++],2<(w-=3););w&&(C[s++]=C[x++],1<w&&(C[s++]=C[x++]))}break}}break}}while(n<i&&s<o);n-=w=p>>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n<i?i-n+5:5-(n-i),e.avail_out=s<o?o-s+257:257-(s-o),r.hold=d,r.bits=p}},{}],49:[function(e,t,r){"use strict";var I=e("../utils/common"),O=e("./adler32"),B=e("./crc32"),R=e("./inffast"),T=e("./inftrees"),D=1,F=2,N=0,U=-2,P=1,n=852,i=592;function L(e){return(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg="",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15<t)?U:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=r,n.wbits=t,o(e))):U}function u(e,t){var r,n;return e?(n=new s,(e.state=n).window=null,(r=h(e,t))!==N&&(e.state=null),r):U}var l,f,c=!0;function j(e){if(c){var t;for(l=new I.Buf32(512),f=new I.Buf32(32),t=0;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(T(D,e.lens,0,288,l,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;T(F,e.lens,0,32,f,0,e.work,{bits:5}),c=!1}e.lencode=l,e.lenbits=9,e.distcode=f,e.distbits=5}function Z(e,t,r,n){var i,s=e.state;return null===s.window&&(s.wsize=1<<s.wbits,s.wnext=0,s.whave=0,s.window=new I.Buf8(s.wsize)),n>=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave<s.wsize&&(s.whave+=i))),0}r.inflateReset=o,r.inflateReset2=h,r.inflateResetKeep=a,r.inflateInit=function(e){return u(e,15)},r.inflateInit2=u,r.inflate=function(e,t){var r,n,i,s,a,o,h,u,l,f,c,d,p,m,_,g,b,v,y,w,k,x,S,z,C=0,E=new I.Buf8(4),A=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!e||!e.state||!e.output||!e.input&&0!==e.avail_in)return U;12===(r=e.state).mode&&(r.mode=13),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,f=o,c=h,x=N;e:for(;;)switch(r.mode){case P:if(0===r.wrap){r.mode=13;break}for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(2&r.wrap&&35615===u){E[r.check=0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg="incorrect header check",r.mode=30;break}if(8!=(15&u)){e.msg="unknown compression method",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg="invalid window size",r.mode=30;break}r.dmax=1<<k,e.adler=r.check=1,r.mode=512&u?10:12,l=u=0;break;case 2:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(r.flags=u,8!=(255&r.flags)){e.msg="unknown compression method",r.mode=30;break}if(57344&r.flags){e.msg="unknown header flags set",r.mode=30;break}r.head&&(r.head.text=u>>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.head&&(r.head.time=u),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.head&&(r.head.xflags=255&u,r.head.os=u>>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.length=u,r.head&&(r.head.extra_len=u),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d<o;);if(512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,k)break e}else r.head&&(r.head.name=null);r.length=0,r.mode=8;case 8:if(4096&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.comment+=String.fromCharCode(k)),k&&d<o;);if(512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,k)break e}else r.head&&(r.head.comment=null);r.mode=9;case 9:if(512&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u!==(65535&r.check)){e.msg="header crc mismatch",r.mode=30;break}l=u=0}r.head&&(r.head.hcrc=r.flags>>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}e.adler=r.check=L(u),l=u=0,r.mode=11;case 11:if(0===r.havedict)return e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,2;e.adler=r.check=1,r.mode=12;case 12:if(5===t||6===t)break e;case 13:if(r.last){u>>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}switch(r.last=1&u,l-=1,3&(u>>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg="invalid block type",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if((65535&u)!=(u>>>16^65535)){e.msg="invalid stored block lengths",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o<d&&(d=o),h<d&&(d=h),0===d)break e;I.arraySet(i,n,s,d,a),o-=d,s+=d,h-=d,a+=d,r.length-=d;break}r.mode=12;break;case 17:for(;l<14;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(r.nlen=257+(31&u),u>>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286<r.nlen||30<r.ndist){e.msg="too many length or distance symbols",r.mode=30;break}r.have=0,r.mode=18;case 18:for(;r.have<r.ncode;){for(;l<3;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.lens[A[r.have++]]=7&u,u>>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid code lengths set",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have<r.nlen+r.ndist;){for(;g=(C=r.lencode[u&(1<<r.lenbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(b<16)u>>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u>>>=_,l-=_,0===r.have){e.msg="invalid bit length repeat",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}l-=_,k=0,d=3+(7&(u>>>=_)),u>>>=3,l-=3}else{for(z=_+7;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}l-=_,k=0,d=11+(127&(u>>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg="invalid bit length repeat",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg="invalid code -- missing end-of-block",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg="invalid literal/lengths set",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg="invalid distances set",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<<r.lenbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(g&&0==(240&g)){for(v=_,y=g,w=b;g=(C=r.lencode[w+((u&(1<<v+y)-1)>>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}u>>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg="invalid literal/length code",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.length+=u&(1<<r.extra)-1,u>>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<<r.distbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(0==(240&g)){for(v=_,y=g,w=b;g=(C=r.distcode[w+((u&(1<<v+y)-1)>>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}u>>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg="invalid distance code",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.offset+=u&(1<<r.extra)-1,u>>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg="invalid distance too far back",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg="invalid distance too far back",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(h<d&&(d=h),h-=d,r.length-=d;i[a++]=m[p++],--d;);0===r.length&&(r.mode=21);break;case 26:if(0===h)break e;i[a++]=r.length,h--,r.mode=21;break;case 27:if(r.wrap){for(;l<32;){if(0===o)break e;o--,u|=n[s++]<<l,l+=8}if(c-=h,e.total_out+=c,r.total+=c,c&&(e.adler=r.check=r.flags?B(r.check,i,c,a-c):O(r.check,i,c,a-c)),c=h,(r.flags?u:L(u))!==r.check){e.msg="incorrect data check",r.mode=30;break}l=u=0}r.mode=28;case 28:if(r.wrap&&r.flags){for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u!==(4294967295&r.total)){e.msg="incorrect length check",r.mode=30;break}l=u=0}r.mode=29;case 29:x=1;break e;case 30:x=-3;break e;case 31:return-4;case 32:default:return U}return e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,(r.wsize||c!==e.avail_out&&r.mode<30&&(r.mode<27||4!==t))&&Z(e,e.output,e.next_out,c-e.avail_out)?(r.mode=31,-4):(f-=e.avail_in,c-=e.avail_out,e.total_in+=f,e.total_out+=c,r.total+=c,r.wrap&&c&&(e.adler=r.check=r.flags?B(r.check,i,c,e.next_out-c):O(r.check,i,c,e.next_out-c)),e.data_type=r.bits+(r.last?64:0)+(12===r.mode?128:0)+(20===r.mode||15===r.mode?256:0),(0==f&&0===c||4===t)&&x===N&&(x=-5),x)},r.inflateEnd=function(e){if(!e||!e.state)return U;var t=e.state;return t.window&&(t.window=null),e.state=null,N},r.inflateGetHeader=function(e,t){var r;return e&&e.state?0==(2&(r=e.state).wrap)?U:((r.head=t).done=!1,N):U},r.inflateSetDictionary=function(e,t){var r,n=t.length;return e&&e.state?0!==(r=e.state).wrap&&11!==r.mode?U:11===r.mode&&O(1,t,n,0)!==r.check?-3:Z(e,t,n,n)?(r.mode=31,-4):(r.havedict=1,N):U},r.inflateInfo="pako inflate (from Nodeca project)"},{"../utils/common":41,"./adler32":43,"./crc32":45,"./inffast":48,"./inftrees":50}],50:[function(e,t,r){"use strict";var D=e("../utils/common"),F=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],N=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],U=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],P=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];t.exports=function(e,t,r,n,i,s,a,o){var h,u,l,f,c,d,p,m,_,g=o.bits,b=0,v=0,y=0,w=0,k=0,x=0,S=0,z=0,C=0,E=0,A=null,I=0,O=new D.Buf16(16),B=new D.Buf16(16),R=null,T=0;for(b=0;b<=15;b++)O[b]=0;for(v=0;v<n;v++)O[t[r+v]]++;for(k=g,w=15;1<=w&&0===O[w];w--);if(w<k&&(k=w),0===w)return i[s++]=20971520,i[s++]=20971520,o.bits=1,0;for(y=1;y<w&&0===O[y];y++);for(k<y&&(k=y),b=z=1;b<=15;b++)if(z<<=1,(z-=O[b])<0)return-1;if(0<z&&(0===e||1!==w))return-1;for(B[1]=0,b=1;b<15;b++)B[b+1]=B[b]+O[b];for(v=0;v<n;v++)0!==t[r+v]&&(a[B[t[r+v]]++]=v);if(d=0===e?(A=R=a,19):1===e?(A=F,I-=257,R=N,T-=257,256):(A=U,R=P,-1),b=y,c=s,S=v=E=0,l=-1,f=(C=1<<(x=k))-1,1===e&&852<C||2===e&&592<C)return 1;for(;;){for(p=b-S,_=a[v]<d?(m=0,a[v]):a[v]>d?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<<b-S,y=u=1<<x;i[c+(E>>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<<b-1;E&h;)h>>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k<b&&(E&f)!==l){for(0===S&&(S=k),c+=y,z=1<<(x=b-S);x+S<w&&!((z-=O[x+S])<=0);)x++,z<<=1;if(C+=1<<x,1===e&&852<C||2===e&&592<C)return 1;i[l=E&f]=k<<24|x<<16|c-s|0}}return 0!==E&&(i[c+E]=b-S<<24|64<<16|0),o.bits=k,0}},{"../utils/common":41}],51:[function(e,t,r){"use strict";t.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],52:[function(e,t,r){"use strict";var i=e("../utils/common"),o=0,h=1;function n(e){for(var t=e.length;0<=--t;)e[t]=0}var s=0,a=29,u=256,l=u+1+a,f=30,c=19,_=2*l+1,g=15,d=16,p=7,m=256,b=16,v=17,y=18,w=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],k=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],x=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],S=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],z=new Array(2*(l+2));n(z);var C=new Array(2*f);n(C);var E=new Array(512);n(E);var A=new Array(256);n(A);var I=new Array(a);n(I);var O,B,R,T=new Array(f);function D(e,t,r,n,i){this.static_tree=e,this.extra_bits=t,this.extra_base=r,this.elems=n,this.max_length=i,this.has_stree=e&&e.length}function F(e,t){this.dyn_tree=e,this.max_code=0,this.stat_desc=t}function N(e){return e<256?E[e]:E[256+(e>>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<<e.bi_valid&65535,U(e,e.bi_buf),e.bi_buf=t>>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<<e.bi_valid&65535,e.bi_valid+=r)}function L(e,t,r){P(e,r[2*t],r[2*t+1])}function j(e,t){for(var r=0;r|=1&e,e>>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t<l;t++)e.dyn_ltree[2*t]=0;for(t=0;t<f;t++)e.dyn_dtree[2*t]=0;for(t=0;t<c;t++)e.bl_tree[2*t]=0;e.dyn_ltree[2*m]=1,e.opt_len=e.static_len=0,e.last_lit=e.matches=0}function M(e){8<e.bi_valid?U(e,e.bi_buf):0<e.bi_valid&&(e.pending_buf[e.pending++]=e.bi_buf),e.bi_buf=0,e.bi_valid=0}function H(e,t,r,n){var i=2*t,s=2*r;return e[i]<e[s]||e[i]===e[s]&&n[t]<=n[r]}function G(e,t,r){for(var n=e.heap[r],i=r<<1;i<=e.heap_len&&(i<e.heap_len&&H(t,e.heap[i+1],e.heap[i],e.depth)&&i++,!H(t,n,e.heap[i],e.depth));)e.heap[r]=e.heap[i],r=i,i<<=1;e.heap[r]=n}function K(e,t,r){var n,i,s,a,o=0;if(0!==e.last_lit)for(;n=e.pending_buf[e.d_buf+2*o]<<8|e.pending_buf[e.d_buf+2*o+1],i=e.pending_buf[e.l_buf+o],o++,0===n?L(e,i,t):(L(e,(s=A[i])+u+1,t),0!==(a=w[s])&&P(e,i-=I[s],a),L(e,s=N(--n),r),0!==(a=k[s])&&P(e,n-=T[s],a)),o<e.last_lit;);L(e,m,t)}function Y(e,t){var r,n,i,s=t.dyn_tree,a=t.stat_desc.static_tree,o=t.stat_desc.has_stree,h=t.stat_desc.elems,u=-1;for(e.heap_len=0,e.heap_max=_,r=0;r<h;r++)0!==s[2*r]?(e.heap[++e.heap_len]=u=r,e.depth[r]=0):s[2*r+1]=0;for(;e.heap_len<2;)s[2*(i=e.heap[++e.heap_len]=u<2?++u:0)]=1,e.depth[i]=0,e.opt_len--,o&&(e.static_len-=a[2*i+1]);for(t.max_code=u,r=e.heap_len>>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u<n||(e.bl_count[s]++,a=0,d<=n&&(a=c[n-d]),o=h[2*n],e.opt_len+=o*(s+a),f&&(e.static_len+=o*(l[2*n+1]+a)));if(0!==m){do{for(s=p-1;0===e.bl_count[s];)s--;e.bl_count[s]--,e.bl_count[s+1]+=2,e.bl_count[p]--,m-=2}while(0<m);for(s=p;0!==s;s--)for(n=e.bl_count[s];0!==n;)u<(i=e.heap[--r])||(h[2*i+1]!==s&&(e.opt_len+=(s-h[2*i+1])*h[2*i],h[2*i+1]=s),n--)}}(e,t),Z(s,u,e.bl_count)}function X(e,t,r){var n,i,s=-1,a=t[1],o=0,h=7,u=4;for(0===a&&(h=138,u=3),t[2*(r+1)+1]=65535,n=0;n<=r;n++)i=a,a=t[2*(n+1)+1],++o<h&&i===a||(o<u?e.bl_tree[2*i]+=o:0!==i?(i!==s&&e.bl_tree[2*i]++,e.bl_tree[2*b]++):o<=10?e.bl_tree[2*v]++:e.bl_tree[2*y]++,s=i,u=(o=0)===a?(h=138,3):i===a?(h=6,3):(h=7,4))}function V(e,t,r){var n,i,s=-1,a=t[1],o=0,h=7,u=4;for(0===a&&(h=138,u=3),n=0;n<=r;n++)if(i=a,a=t[2*(n+1)+1],!(++o<h&&i===a)){if(o<u)for(;L(e,i,e.bl_tree),0!=--o;);else 0!==i?(i!==s&&(L(e,i,e.bl_tree),o--),L(e,b,e.bl_tree),P(e,o-3,2)):o<=10?(L(e,v,e.bl_tree),P(e,o-3,3)):(L(e,y,e.bl_tree),P(e,o-11,7));s=i,u=(o=0)===a?(h=138,3):i===a?(h=6,3):(h=7,4)}}n(T);var q=!1;function J(e,t,r,n){P(e,(s<<1)+(n?1:0),3),function(e,t,r,n){M(e),n&&(U(e,r),U(e,~r)),i.arraySet(e.pending_buf,e.window,t,r,e.pending),e.pending+=r}(e,t,r,!0)}r._tr_init=function(e){q||(function(){var e,t,r,n,i,s=new Array(g+1);for(n=r=0;n<a-1;n++)for(I[n]=r,e=0;e<1<<w[n];e++)A[r++]=n;for(A[r-1]=n,n=i=0;n<16;n++)for(T[n]=i,e=0;e<1<<k[n];e++)E[i++]=n;for(i>>=7;n<f;n++)for(T[n]=i<<7,e=0;e<1<<k[n]-7;e++)E[256+i++]=n;for(t=0;t<=g;t++)s[t]=0;for(e=0;e<=143;)z[2*e+1]=8,e++,s[8]++;for(;e<=255;)z[2*e+1]=9,e++,s[9]++;for(;e<=279;)z[2*e+1]=7,e++,s[7]++;for(;e<=287;)z[2*e+1]=8,e++,s[8]++;for(Z(z,l+1,s),e=0;e<f;e++)C[2*e+1]=5,C[2*e]=j(e,5);O=new D(z,w,u+1,l,g),B=new D(C,k,0,f,g),R=new D(new Array(0),x,0,c,p)}(),q=!0),e.l_desc=new F(e.dyn_ltree,O),e.d_desc=new F(e.dyn_dtree,B),e.bl_desc=new F(e.bl_tree,R),e.bi_buf=0,e.bi_valid=0,W(e)},r._tr_stored_block=J,r._tr_flush_block=function(e,t,r,n){var i,s,a=0;0<e.level?(2===e.strm.data_type&&(e.strm.data_type=function(e){var t,r=4093624447;for(t=0;t<=31;t++,r>>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t<u;t++)if(0!==e.dyn_ltree[2*t])return h;return o}(e)),Y(e,e.l_desc),Y(e,e.d_desc),a=function(e){var t;for(X(e,e.dyn_ltree,e.l_desc.max_code),X(e,e.dyn_dtree,e.d_desc.max_code),Y(e,e.bl_desc),t=c-1;3<=t&&0===e.bl_tree[2*S[t]+1];t--);return e.opt_len+=3*(t+1)+5+5+4,t}(e),i=e.opt_len+3+7>>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i<n;i++)P(e,e.bl_tree[2*S[i]+1],3);V(e,e.dyn_ltree,t-1),V(e,e.dyn_dtree,r-1)}(e,e.l_desc.max_code+1,e.d_desc.max_code+1,a+1),K(e,e.dyn_ltree,e.dyn_dtree)),W(e),n&&M(e)},r._tr_tally=function(e,t,r){return e.pending_buf[e.d_buf+2*e.last_lit]=t>>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{"../utils/common":41}],53:[function(e,t,r){"use strict";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){"use strict";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i="[object process]"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage("","*"),r.onmessage=t,e}}()?(a="setImmediate$"+Math.random()+"$",r.addEventListener?r.addEventListener("message",d,!1):r.attachEvent("onmessage",d),function(e){r.postMessage(a+e,"*")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&"onreadystatechange"in l.createElement("script")?(s=l.documentElement,function(e){var t=l.createElement("script");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){"function"!=typeof e&&(e=new Function(""+e));for(var t=new Array(arguments.length-1),r=0;r<t.length;r++)t[r]=arguments[r+1];var n={callback:e,args:t};return h[o]=n,i(o),o++},e.clearImmediate=f}function f(e){delete h[e]}function c(e){if(u)setTimeout(c,0,e);else{var t=h[e];if(t){u=!0;try{!function(e){var t=e.callback,r=e.args;switch(r.length){case 0:t();break;case 1:t(r[0]);break;case 2:t(r[0],r[1]);break;case 3:t(r[0],r[1],r[2]);break;default:t.apply(n,r)}}(t)}finally{f(e),u=!1}}}}function d(e){e.source===r&&"string"==typeof e.data&&0===e.data.indexOf(a)&&c(+e.data.slice(a.length))}}("undefined"==typeof self?void 0===e?this:e:self)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[10])(10)});
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer, __webpack_require__(/*! ./../../timers-browserify/main.js */ "./node_modules/timers-browserify/main.js").setImmediate, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"), __webpack_require__(/*! ./../../process/browser.js */ "./node_modules/process/browser.js")))

/***/ }),

/***/ "./node_modules/linebreak/src/classes.js":
/*!***********************************************!*\
  !*** ./node_modules/linebreak/src/classes.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Generated by CoffeeScript 1.7.1
(function() {
  var AI, AL, B2, BA, BB, BK, CB, CJ, CL, CM, CP, CR, EX, GL, H2, H3, HL, HY, ID, IN, IS, JL, JT, JV, LF, NL, NS, NU, OP, PO, PR, QU, RI, SA, SG, SP, SY, WJ, XX, ZW;

  exports.OP = OP = 0;

  exports.CL = CL = 1;

  exports.CP = CP = 2;

  exports.QU = QU = 3;

  exports.GL = GL = 4;

  exports.NS = NS = 5;

  exports.EX = EX = 6;

  exports.SY = SY = 7;

  exports.IS = IS = 8;

  exports.PR = PR = 9;

  exports.PO = PO = 10;

  exports.NU = NU = 11;

  exports.AL = AL = 12;

  exports.HL = HL = 13;

  exports.ID = ID = 14;

  exports.IN = IN = 15;

  exports.HY = HY = 16;

  exports.BA = BA = 17;

  exports.BB = BB = 18;

  exports.B2 = B2 = 19;

  exports.ZW = ZW = 20;

  exports.CM = CM = 21;

  exports.WJ = WJ = 22;

  exports.H2 = H2 = 23;

  exports.H3 = H3 = 24;

  exports.JL = JL = 25;

  exports.JV = JV = 26;

  exports.JT = JT = 27;

  exports.RI = RI = 28;

  exports.AI = AI = 29;

  exports.BK = BK = 30;

  exports.CB = CB = 31;

  exports.CJ = CJ = 32;

  exports.CR = CR = 33;

  exports.LF = LF = 34;

  exports.NL = NL = 35;

  exports.SA = SA = 36;

  exports.SG = SG = 37;

  exports.SP = SP = 38;

  exports.XX = XX = 39;

}).call(this);


/***/ }),

/***/ "./node_modules/linebreak/src/pairs.js":
/*!*********************************************!*\
  !*** ./node_modules/linebreak/src/pairs.js ***!
  \*********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Generated by CoffeeScript 1.7.1
(function() {
  var CI_BRK, CP_BRK, DI_BRK, IN_BRK, PR_BRK;

  exports.DI_BRK = DI_BRK = 0;

  exports.IN_BRK = IN_BRK = 1;

  exports.CI_BRK = CI_BRK = 2;

  exports.CP_BRK = CP_BRK = 3;

  exports.PR_BRK = PR_BRK = 4;

  exports.pairTable = [[PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, CP_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, DI_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, DI_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, PR_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK], [IN_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, IN_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, DI_BRK], [DI_BRK, PR_BRK, PR_BRK, IN_BRK, IN_BRK, IN_BRK, PR_BRK, PR_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK, IN_BRK, DI_BRK, DI_BRK, PR_BRK, CI_BRK, PR_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, DI_BRK, IN_BRK]];

}).call(this);


/***/ }),

/***/ "./node_modules/lookup-closest-locale/index.js":
/*!*****************************************************!*\
  !*** ./node_modules/lookup-closest-locale/index.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// @flow
// "lookup" algorithm http://tools.ietf.org/html/rfc4647#section-3.4
// assumes normalized language tags, and matches in a case sensitive manner
module.exports = function lookupClosestLocale (locale/*: string | string[] | void */, available/*: { [string]: any } */)/*: ?string */ {
  if (typeof locale === 'string' && available[locale]) return locale
  var locales = [].concat(locale || [])
  for (var l = 0, ll = locales.length; l < ll; ++l) {
    var current = locales[l].split('-')
    while (current.length) {
      var candidate = current.join('-')
      if (available[candidate]) return candidate
      current.pop()
    }
  }
}


/***/ }),

/***/ "./node_modules/microee/index.js":
/*!***************************************!*\
  !*** ./node_modules/microee/index.js ***!
  \***************************************/
/*! no static exports found */
/***/ (function(module, exports) {

function M() { this._events = {}; }
M.prototype = {
  on: function(ev, cb) {
    this._events || (this._events = {});
    var e = this._events;
    (e[ev] || (e[ev] = [])).push(cb);
    return this;
  },
  removeListener: function(ev, cb) {
    var e = this._events[ev] || [], i;
    for(i = e.length-1; i >= 0 && e[i]; i--){
      if(e[i] === cb || e[i].cb === cb) { e.splice(i, 1); }
    }
  },
  removeAllListeners: function(ev) {
    if(!ev) { this._events = {}; }
    else { this._events[ev] && (this._events[ev] = []); }
  },
  listeners: function(ev) {
    return (this._events ? this._events[ev] || [] : []);
  },
  emit: function(ev) {
    this._events || (this._events = {});
    var args = Array.prototype.slice.call(arguments, 1), i, e = this._events[ev] || [];
    for(i = e.length-1; i >= 0 && e[i]; i--){
      e[i].apply(this, args);
    }
    return this;
  },
  when: function(ev, cb) {
    return this.once(ev, cb, true);
  },
  once: function(ev, cb, when) {
    if(!cb) return this;
    function c() {
      if(!when) this.removeListener(ev, c);
      if(cb.apply(this, arguments) && when) this.removeListener(ev, c);
    }
    c.cb = cb;
    this.on(ev, c);
    return this;
  }
};
M.mixin = function(dest) {
  var o = M.prototype, k;
  for (k in o) {
    o.hasOwnProperty(k) && (dest.prototype[k] = o[k]);
  }
};
module.exports = M;


/***/ }),

/***/ "./node_modules/minilog/lib/common/filter.js":
/*!***************************************************!*\
  !*** ./node_modules/minilog/lib/common/filter.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// default filter
var Transform = __webpack_require__(/*! ./transform.js */ "./node_modules/minilog/lib/common/transform.js");

var levelMap = { debug: 1, info: 2, warn: 3, error: 4 };

function Filter() {
  this.enabled = true;
  this.defaultResult = true;
  this.clear();
}

Transform.mixin(Filter);

// allow all matching, with level >= given level
Filter.prototype.allow = function(name, level) {
  this._white.push({ n: name, l: levelMap[level] });
  return this;
};

// deny all matching, with level <= given level
Filter.prototype.deny = function(name, level) {
  this._black.push({ n: name, l: levelMap[level] });
  return this;
};

Filter.prototype.clear = function() {
  this._white = [];
  this._black = [];
  return this;
};

function test(rule, name) {
  // use .test for RegExps
  return (rule.n.test ? rule.n.test(name) : rule.n == name);
};

Filter.prototype.test = function(name, level) {
  var i, len = Math.max(this._white.length, this._black.length);
  for(i = 0; i < len; i++) {
    if(this._white[i] && test(this._white[i], name) && levelMap[level] >= this._white[i].l) {
      return true;
    }
    if(this._black[i] && test(this._black[i], name) && levelMap[level] <= this._black[i].l) {
      return false;
    }
  }
  return this.defaultResult;
};

Filter.prototype.write = function(name, level, args) {
  if(!this.enabled || this.test(name, level)) {
    return this.emit('item', name, level, args);
  }
};

module.exports = Filter;


/***/ }),

/***/ "./node_modules/minilog/lib/common/minilog.js":
/*!****************************************************!*\
  !*** ./node_modules/minilog/lib/common/minilog.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ./transform.js */ "./node_modules/minilog/lib/common/transform.js"),
    Filter = __webpack_require__(/*! ./filter.js */ "./node_modules/minilog/lib/common/filter.js");

var log = new Transform(),
    slice = Array.prototype.slice;

exports = module.exports = function create(name) {
  var o   = function() { log.write(name, undefined, slice.call(arguments)); return o; };
  o.debug = function() { log.write(name, 'debug', slice.call(arguments)); return o; };
  o.info  = function() { log.write(name, 'info',  slice.call(arguments)); return o; };
  o.warn  = function() { log.write(name, 'warn',  slice.call(arguments)); return o; };
  o.error = function() { log.write(name, 'error', slice.call(arguments)); return o; };
  o.log   = o.debug; // for interface compliance with Node and browser consoles
  o.suggest = exports.suggest;
  o.format = log.format;
  return o;
};

// filled in separately
exports.defaultBackend = exports.defaultFormatter = null;

exports.pipe = function(dest) {
  return log.pipe(dest);
};

exports.end = exports.unpipe = exports.disable = function(from) {
  return log.unpipe(from);
};

exports.Transform = Transform;
exports.Filter = Filter;
// this is the default filter that's applied when .enable() is called normally
// you can bypass it completely and set up your own pipes
exports.suggest = new Filter();

exports.enable = function() {
  if(exports.defaultFormatter) {
    return log.pipe(exports.suggest) // filter
              .pipe(exports.defaultFormatter) // formatter
              .pipe(exports.defaultBackend); // backend
  }
  return log.pipe(exports.suggest) // filter
            .pipe(exports.defaultBackend); // formatter
};



/***/ }),

/***/ "./node_modules/minilog/lib/common/transform.js":
/*!******************************************************!*\
  !*** ./node_modules/minilog/lib/common/transform.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var microee = __webpack_require__(/*! microee */ "./node_modules/microee/index.js");

// Implements a subset of Node's stream.Transform - in a cross-platform manner.
function Transform() {}

microee.mixin(Transform);

// The write() signature is different from Node's
// --> makes it much easier to work with objects in logs.
// One of the lessons from v1 was that it's better to target
// a good browser rather than the lowest common denominator
// internally.
// If you want to use external streams, pipe() to ./stringify.js first.
Transform.prototype.write = function(name, level, args) {
  this.emit('item', name, level, args);
};

Transform.prototype.end = function() {
  this.emit('end');
  this.removeAllListeners();
};

Transform.prototype.pipe = function(dest) {
  var s = this;
  // prevent double piping
  s.emit('unpipe', dest);
  // tell the dest that it's being piped to
  dest.emit('pipe', s);

  function onItem() {
    dest.write.apply(dest, Array.prototype.slice.call(arguments));
  }
  function onEnd() { !dest._isStdio && dest.end(); }

  s.on('item', onItem);
  s.on('end', onEnd);

  s.when('unpipe', function(from) {
    var match = (from === dest) || typeof from == 'undefined';
    if(match) {
      s.removeListener('item', onItem);
      s.removeListener('end', onEnd);
      dest.emit('unpipe');
    }
    return match;
  });

  return dest;
};

Transform.prototype.unpipe = function(from) {
  this.emit('unpipe', from);
  return this;
};

Transform.prototype.format = function(dest) {
  throw new Error([
    'Warning: .format() is deprecated in Minilog v2! Use .pipe() instead. For example:',
    'var Minilog = require(\'minilog\');',
    'Minilog',
    '  .pipe(Minilog.backends.console.formatClean)',
    '  .pipe(Minilog.backends.console);'].join('\n'));
};

Transform.mixin = function(dest) {
  var o = Transform.prototype, k;
  for (k in o) {
    o.hasOwnProperty(k) && (dest.prototype[k] = o[k]);
  }
};

module.exports = Transform;


/***/ }),

/***/ "./node_modules/minilog/lib/web/array.js":
/*!***********************************************!*\
  !*** ./node_modules/minilog/lib/web/array.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../common/transform.js */ "./node_modules/minilog/lib/common/transform.js"),
    cache = [ ];

var logger = new Transform();

logger.write = function(name, level, args) {
  cache.push([ name, level, args ]);
};

// utility functions
logger.get = function() { return cache; };
logger.empty = function() { cache = []; };

module.exports = logger;


/***/ }),

/***/ "./node_modules/minilog/lib/web/console.js":
/*!*************************************************!*\
  !*** ./node_modules/minilog/lib/web/console.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../common/transform.js */ "./node_modules/minilog/lib/common/transform.js");

var newlines = /\n+$/,
    logger = new Transform();

logger.write = function(name, level, args) {
  var i = args.length-1;
  if (typeof console === 'undefined' || !console.log) {
    return;
  }
  if(console.log.apply) {
    return console.log.apply(console, [name, level].concat(args));
  } else if(JSON && JSON.stringify) {
    // console.log.apply is undefined in IE8 and IE9
    // for IE8/9: make console.log at least a bit less awful
    if(args[i] && typeof args[i] == 'string') {
      args[i] = args[i].replace(newlines, '');
    }
    try {
      for(i = 0; i < args.length; i++) {
        args[i] = JSON.stringify(args[i]);
      }
    } catch(e) {}
    console.log(args.join(' '));
  }
};

logger.formatters = ['color', 'minilog'];
logger.color = __webpack_require__(/*! ./formatters/color.js */ "./node_modules/minilog/lib/web/formatters/color.js");
logger.minilog = __webpack_require__(/*! ./formatters/minilog.js */ "./node_modules/minilog/lib/web/formatters/minilog.js");

module.exports = logger;


/***/ }),

/***/ "./node_modules/minilog/lib/web/formatters/color.js":
/*!**********************************************************!*\
  !*** ./node_modules/minilog/lib/web/formatters/color.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../../common/transform.js */ "./node_modules/minilog/lib/common/transform.js"),
    color = __webpack_require__(/*! ./util.js */ "./node_modules/minilog/lib/web/formatters/util.js");

var colors = { debug: ['cyan'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] },
    logger = new Transform();

logger.write = function(name, level, args) {
  var fn = console.log;
  if(console[level] && console[level].apply) {
    fn = console[level];
    fn.apply(console, [ '%c'+name+' %c'+level, color('gray'), color.apply(color, colors[level])].concat(args));
  }
};

// NOP, because piping the formatted logs can only cause trouble.
logger.pipe = function() { };

module.exports = logger;


/***/ }),

/***/ "./node_modules/minilog/lib/web/formatters/minilog.js":
/*!************************************************************!*\
  !*** ./node_modules/minilog/lib/web/formatters/minilog.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../../common/transform.js */ "./node_modules/minilog/lib/common/transform.js"),
    color = __webpack_require__(/*! ./util.js */ "./node_modules/minilog/lib/web/formatters/util.js"),
    colors = { debug: ['gray'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] },
    logger = new Transform();

logger.write = function(name, level, args) {
  var fn = console.log;
  if(level != 'debug' && console[level]) {
    fn = console[level];
  }

  var subset = [], i = 0;
  if(level != 'info') {
    for(; i < args.length; i++) {
      if(typeof args[i] != 'string') break;
    }
    fn.apply(console, [ '%c'+name +' '+ args.slice(0, i).join(' '), color.apply(color, colors[level]) ].concat(args.slice(i)));
  } else {
    fn.apply(console, [ '%c'+name, color.apply(color, colors[level]) ].concat(args));
  }
};

// NOP, because piping the formatted logs can only cause trouble.
logger.pipe = function() { };

module.exports = logger;


/***/ }),

/***/ "./node_modules/minilog/lib/web/formatters/util.js":
/*!*********************************************************!*\
  !*** ./node_modules/minilog/lib/web/formatters/util.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

var hex = {
  black: '#000',
  red: '#c23621',
  green: '#25bc26',
  yellow: '#bbbb00',
  blue:  '#492ee1',
  magenta: '#d338d3',
  cyan: '#33bbc8',
  gray: '#808080',
  purple: '#708'
};
function color(fg, isInverse) {
  if(isInverse) {
    return 'color: #fff; background: '+hex[fg]+';';
  } else {
    return 'color: '+hex[fg]+';';
  }
}

module.exports = color;


/***/ }),

/***/ "./node_modules/minilog/lib/web/index.js":
/*!***********************************************!*\
  !*** ./node_modules/minilog/lib/web/index.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Minilog = __webpack_require__(/*! ../common/minilog.js */ "./node_modules/minilog/lib/common/minilog.js");

var oldEnable = Minilog.enable,
    oldDisable = Minilog.disable,
    isChrome = (typeof navigator != 'undefined' && /chrome/i.test(navigator.userAgent)),
    console = __webpack_require__(/*! ./console.js */ "./node_modules/minilog/lib/web/console.js");

// Use a more capable logging backend if on Chrome
Minilog.defaultBackend = (isChrome ? console.minilog : console);

// apply enable inputs from localStorage and from the URL
if(typeof window != 'undefined') {
  try {
    Minilog.enable(JSON.parse(window.localStorage['minilogSettings']));
  } catch(e) {}
  if(window.location && window.location.search) {
    var match = RegExp('[?&]minilog=([^&]*)').exec(window.location.search);
    match && Minilog.enable(decodeURIComponent(match[1]));
  }
}

// Make enable also add to localStorage
Minilog.enable = function() {
  oldEnable.call(Minilog, true);
  try { window.localStorage['minilogSettings'] = JSON.stringify(true); } catch(e) {}
  return this;
};

Minilog.disable = function() {
  oldDisable.call(Minilog);
  try { delete window.localStorage.minilogSettings; } catch(e) {}
  return this;
};

exports = module.exports = Minilog;

exports.backends = {
  array: __webpack_require__(/*! ./array.js */ "./node_modules/minilog/lib/web/array.js"),
  browser: Minilog.defaultBackend,
  localStorage: __webpack_require__(/*! ./localstorage.js */ "./node_modules/minilog/lib/web/localstorage.js"),
  jQuery: __webpack_require__(/*! ./jquery_simple.js */ "./node_modules/minilog/lib/web/jquery_simple.js")
};


/***/ }),

/***/ "./node_modules/minilog/lib/web/jquery_simple.js":
/*!*******************************************************!*\
  !*** ./node_modules/minilog/lib/web/jquery_simple.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../common/transform.js */ "./node_modules/minilog/lib/common/transform.js");

var cid = new Date().valueOf().toString(36);

function AjaxLogger(options) {
  this.url = options.url || '';
  this.cache = [];
  this.timer = null;
  this.interval = options.interval || 30*1000;
  this.enabled = true;
  this.jQuery = window.jQuery;
  this.extras = {};
}

Transform.mixin(AjaxLogger);

AjaxLogger.prototype.write = function(name, level, args) {
  if(!this.timer) { this.init(); }
  this.cache.push([name, level].concat(args));
};

AjaxLogger.prototype.init = function() {
  if(!this.enabled || !this.jQuery) return;
  var self = this;
  this.timer = setTimeout(function() {
    var i, logs = [], ajaxData, url = self.url;
    if(self.cache.length == 0) return self.init();
    // Test each log line and only log the ones that are valid (e.g. don't have circular references).
    // Slight performance hit but benefit is we log all valid lines.
    for(i = 0; i < self.cache.length; i++) {
      try {
        JSON.stringify(self.cache[i]);
        logs.push(self.cache[i]);
      } catch(e) { }
    }
    if(self.jQuery.isEmptyObject(self.extras)) {
        ajaxData = JSON.stringify({ logs: logs });
        url = self.url + '?client_id=' + cid;
    } else {
        ajaxData = JSON.stringify(self.jQuery.extend({logs: logs}, self.extras));
    }

    self.jQuery.ajax(url, {
      type: 'POST',
      cache: false,
      processData: false,
      data: ajaxData,
      contentType: 'application/json',
      timeout: 10000
    }).success(function(data, status, jqxhr) {
      if(data.interval) {
        self.interval = Math.max(1000, data.interval);
      }
    }).error(function() {
      self.interval = 30000;
    }).always(function() {
      self.init();
    });
    self.cache = [];
  }, this.interval);
};

AjaxLogger.prototype.end = function() {};

// wait until jQuery is defined. Useful if you don't control the load order.
AjaxLogger.jQueryWait = function(onDone) {
  if(typeof window !== 'undefined' && (window.jQuery || window.$)) {
    return onDone(window.jQuery || window.$);
  } else if (typeof window !== 'undefined') {
    setTimeout(function() { AjaxLogger.jQueryWait(onDone); }, 200);
  }
};

module.exports = AjaxLogger;


/***/ }),

/***/ "./node_modules/minilog/lib/web/localstorage.js":
/*!******************************************************!*\
  !*** ./node_modules/minilog/lib/web/localstorage.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var Transform = __webpack_require__(/*! ../common/transform.js */ "./node_modules/minilog/lib/common/transform.js"),
    cache = false;

var logger = new Transform();

logger.write = function(name, level, args) {
  if(typeof window == 'undefined' || typeof JSON == 'undefined' || !JSON.stringify || !JSON.parse) return;
  try {
    if(!cache) { cache = (window.localStorage.minilog ? JSON.parse(window.localStorage.minilog) : []); }
    cache.push([ new Date().toString(), name, level, args ]);
    window.localStorage.minilog = JSON.stringify(cache);
  } catch(e) {}
};

module.exports = logger;

/***/ }),

/***/ "./node_modules/process/browser.js":
/*!*****************************************!*\
  !*** ./node_modules/process/browser.js ***!
  \*****************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// shim for using process in browser
var process = module.exports = {};

// cached from whatever global is present so that test runners that stub it
// don't break things.  But we need to wrap it in a try catch in case it is
// wrapped in strict mode code which doesn't define any globals.  It's inside a
// function because try/catches deoptimize in certain engines.

var cachedSetTimeout;
var cachedClearTimeout;

function defaultSetTimout() {
    throw new Error('setTimeout has not been defined');
}
function defaultClearTimeout () {
    throw new Error('clearTimeout has not been defined');
}
(function () {
    try {
        if (typeof setTimeout === 'function') {
            cachedSetTimeout = setTimeout;
        } else {
            cachedSetTimeout = defaultSetTimout;
        }
    } catch (e) {
        cachedSetTimeout = defaultSetTimout;
    }
    try {
        if (typeof clearTimeout === 'function') {
            cachedClearTimeout = clearTimeout;
        } else {
            cachedClearTimeout = defaultClearTimeout;
        }
    } catch (e) {
        cachedClearTimeout = defaultClearTimeout;
    }
} ())
function runTimeout(fun) {
    if (cachedSetTimeout === setTimeout) {
        //normal enviroments in sane situations
        return setTimeout(fun, 0);
    }
    // if setTimeout wasn't available but was latter defined
    if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
        cachedSetTimeout = setTimeout;
        return setTimeout(fun, 0);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedSetTimeout(fun, 0);
    } catch(e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
            return cachedSetTimeout.call(null, fun, 0);
        } catch(e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
            return cachedSetTimeout.call(this, fun, 0);
        }
    }


}
function runClearTimeout(marker) {
    if (cachedClearTimeout === clearTimeout) {
        //normal enviroments in sane situations
        return clearTimeout(marker);
    }
    // if clearTimeout wasn't available but was latter defined
    if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
        cachedClearTimeout = clearTimeout;
        return clearTimeout(marker);
    }
    try {
        // when when somebody has screwed with setTimeout but no I.E. maddness
        return cachedClearTimeout(marker);
    } catch (e){
        try {
            // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
            return cachedClearTimeout.call(null, marker);
        } catch (e){
            // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
            // Some versions of I.E. have different rules for clearTimeout vs setTimeout
            return cachedClearTimeout.call(this, marker);
        }
    }



}
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;

function cleanUpNextTick() {
    if (!draining || !currentQueue) {
        return;
    }
    draining = false;
    if (currentQueue.length) {
        queue = currentQueue.concat(queue);
    } else {
        queueIndex = -1;
    }
    if (queue.length) {
        drainQueue();
    }
}

function drainQueue() {
    if (draining) {
        return;
    }
    var timeout = runTimeout(cleanUpNextTick);
    draining = true;

    var len = queue.length;
    while(len) {
        currentQueue = queue;
        queue = [];
        while (++queueIndex < len) {
            if (currentQueue) {
                currentQueue[queueIndex].run();
            }
        }
        queueIndex = -1;
        len = queue.length;
    }
    currentQueue = null;
    draining = false;
    runClearTimeout(timeout);
}

process.nextTick = function (fun) {
    var args = new Array(arguments.length - 1);
    if (arguments.length > 1) {
        for (var i = 1; i < arguments.length; i++) {
            args[i - 1] = arguments[i];
        }
    }
    queue.push(new Item(fun, args));
    if (queue.length === 1 && !draining) {
        runTimeout(drainQueue);
    }
};

// v8 likes predictible objects
function Item(fun, array) {
    this.fun = fun;
    this.array = array;
}
Item.prototype.run = function () {
    this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};

function noop() {}

process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.prependListener = noop;
process.prependOnceListener = noop;

process.listeners = function (name) { return [] }

process.binding = function (name) {
    throw new Error('process.binding is not supported');
};

process.cwd = function () { return '/' };
process.chdir = function (dir) {
    throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };


/***/ }),

/***/ "./node_modules/raw-loader/index.js!./node_modules/scratch-render/src/shaders/sprite.frag":
/*!***************************************************************************************!*\
  !*** ./node_modules/raw-loader!./node_modules/scratch-render/src/shaders/sprite.frag ***!
  \***************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = "precision mediump float;\n\n#ifdef DRAW_MODE_silhouette\nuniform vec4 u_silhouetteColor;\n#else // DRAW_MODE_silhouette\n# ifdef ENABLE_color\nuniform float u_color;\n# endif // ENABLE_color\n# ifdef ENABLE_brightness\nuniform float u_brightness;\n# endif // ENABLE_brightness\n#endif // DRAW_MODE_silhouette\n\n#ifdef DRAW_MODE_colorMask\nuniform vec3 u_colorMask;\nuniform float u_colorMaskTolerance;\n#endif // DRAW_MODE_colorMask\n\n#ifdef ENABLE_fisheye\nuniform float u_fisheye;\n#endif // ENABLE_fisheye\n#ifdef ENABLE_whirl\nuniform float u_whirl;\n#endif // ENABLE_whirl\n#ifdef ENABLE_pixelate\nuniform float u_pixelate;\nuniform vec2 u_skinSize;\n#endif // ENABLE_pixelate\n#ifdef ENABLE_mosaic\nuniform float u_mosaic;\n#endif // ENABLE_mosaic\n#ifdef ENABLE_ghost\nuniform float u_ghost;\n#endif // ENABLE_ghost\n\n#ifdef DRAW_MODE_line\nvarying vec4 v_lineColor;\nvarying float v_lineThickness;\nvarying float v_lineLength;\n#endif // DRAW_MODE_line\n\n#ifdef DRAW_MODE_background\nuniform vec4 u_backgroundColor;\n#endif // DRAW_MODE_background\n\nuniform sampler2D u_skin;\n\n#ifndef DRAW_MODE_background\nvarying vec2 v_texCoord;\n#endif\n\n// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations.\n// Smaller values can cause problems on some mobile devices.\nconst float epsilon = 1e-3;\n\n#if !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))\n// Branchless color conversions based on code from:\n// http://www.chilliant.com/rgb2hsv.html by Ian Taylor\n// Based in part on work by Sam Hocevar and Emil Persson\n// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Formal_derivation\n\n\n// Convert an RGB color to Hue, Saturation, and Value.\n// All components of input and output are expected to be in the [0,1] range.\nvec3 convertRGB2HSV(vec3 rgb)\n{\n\t// Hue calculation has 3 cases, depending on which RGB component is largest, and one of those cases involves a \"mod\"\n\t// operation. In order to avoid that \"mod\" we split the M==R case in two: one for G<B and one for B>G. The B>G case\n\t// will be calculated in the negative and fed through abs() in the hue calculation at the end.\n\t// See also: https://en.wikipedia.org/wiki/HSL_and_HSV#Hue_and_chroma\n\tconst vec4 hueOffsets = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n\n\t// temp1.xy = sort B & G (largest first)\n\t// temp1.z = the hue offset we'll use if it turns out that R is the largest component (M==R)\n\t// temp1.w = the hue offset we'll use if it turns out that R is not the largest component (M==G or M==B)\n\tvec4 temp1 = rgb.b > rgb.g ? vec4(rgb.bg, hueOffsets.wz) : vec4(rgb.gb, hueOffsets.xy);\n\n\t// temp2.x = the largest component of RGB (\"M\" / \"Max\")\n\t// temp2.yw = the smaller components of RGB, ordered for the hue calculation (not necessarily sorted by magnitude!)\n\t// temp2.z = the hue offset we'll use in the hue calculation\n\tvec4 temp2 = rgb.r > temp1.x ? vec4(rgb.r, temp1.yzx) : vec4(temp1.xyw, rgb.r);\n\n\t// m = the smallest component of RGB (\"min\")\n\tfloat m = min(temp2.y, temp2.w);\n\n\t// Chroma = M - m\n\tfloat C = temp2.x - m;\n\n\t// Value = M\n\tfloat V = temp2.x;\n\n\treturn vec3(\n\t\tabs(temp2.z + (temp2.w - temp2.y) / (6.0 * C + epsilon)), // Hue\n\t\tC / (temp2.x + epsilon), // Saturation\n\t\tV); // Value\n}\n\nvec3 convertHue2RGB(float hue)\n{\n\tfloat r = abs(hue * 6.0 - 3.0) - 1.0;\n\tfloat g = 2.0 - abs(hue * 6.0 - 2.0);\n\tfloat b = 2.0 - abs(hue * 6.0 - 4.0);\n\treturn clamp(vec3(r, g, b), 0.0, 1.0);\n}\n\nvec3 convertHSV2RGB(vec3 hsv)\n{\n\tvec3 rgb = convertHue2RGB(hsv.x);\n\tfloat c = hsv.z * hsv.y;\n\treturn rgb * c + hsv.z - c;\n}\n#endif // !defined(DRAW_MODE_silhouette) && (defined(ENABLE_color))\n\nconst vec2 kCenter = vec2(0.5, 0.5);\n\nvoid main()\n{\n\t#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))\n\tvec2 texcoord0 = v_texCoord;\n\n\t#ifdef ENABLE_mosaic\n\ttexcoord0 = fract(u_mosaic * texcoord0);\n\t#endif // ENABLE_mosaic\n\n\t#ifdef ENABLE_pixelate\n\t{\n\t\t// TODO: clean up \"pixel\" edges\n\t\tvec2 pixelTexelSize = u_skinSize / u_pixelate;\n\t\ttexcoord0 = (floor(texcoord0 * pixelTexelSize) + kCenter) / pixelTexelSize;\n\t}\n\t#endif // ENABLE_pixelate\n\n\t#ifdef ENABLE_whirl\n\t{\n\t\tconst float kRadius = 0.5;\n\t\tvec2 offset = texcoord0 - kCenter;\n\t\tfloat offsetMagnitude = length(offset);\n\t\tfloat whirlFactor = max(1.0 - (offsetMagnitude / kRadius), 0.0);\n\t\tfloat whirlActual = u_whirl * whirlFactor * whirlFactor;\n\t\tfloat sinWhirl = sin(whirlActual);\n\t\tfloat cosWhirl = cos(whirlActual);\n\t\tmat2 rotationMatrix = mat2(\n\t\t\tcosWhirl, -sinWhirl,\n\t\t\tsinWhirl, cosWhirl\n\t\t);\n\n\t\ttexcoord0 = rotationMatrix * offset + kCenter;\n\t}\n\t#endif // ENABLE_whirl\n\n\t#ifdef ENABLE_fisheye\n\t{\n\t\tvec2 vec = (texcoord0 - kCenter) / kCenter;\n\t\tfloat vecLength = length(vec);\n\t\tfloat r = pow(min(vecLength, 1.0), u_fisheye) * max(1.0, vecLength);\n\t\tvec2 unit = vec / vecLength;\n\n\t\ttexcoord0 = kCenter + r * unit * kCenter;\n\t}\n\t#endif // ENABLE_fisheye\n\n\tgl_FragColor = texture2D(u_skin, texcoord0);\n\n\t#if defined(ENABLE_color) || defined(ENABLE_brightness)\n\t// Divide premultiplied alpha values for proper color processing\n\t// Add epsilon to avoid dividing by 0 for fully transparent pixels\n\tgl_FragColor.rgb = clamp(gl_FragColor.rgb / (gl_FragColor.a + epsilon), 0.0, 1.0);\n\n\t#ifdef ENABLE_color\n\t{\n\t\tvec3 hsv = convertRGB2HSV(gl_FragColor.xyz);\n\n\t\t// this code forces grayscale values to be slightly saturated\n\t\t// so that some slight change of hue will be visible\n\t\tconst float minLightness = 0.11 / 2.0;\n\t\tconst float minSaturation = 0.09;\n\t\tif (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);\n\t\telse if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);\n\n\t\thsv.x = mod(hsv.x + u_color, 1.0);\n\t\tif (hsv.x < 0.0) hsv.x += 1.0;\n\n\t\tgl_FragColor.rgb = convertHSV2RGB(hsv);\n\t}\n\t#endif // ENABLE_color\n\n\t#ifdef ENABLE_brightness\n\tgl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1));\n\t#endif // ENABLE_brightness\n\n\t// Re-multiply color values\n\tgl_FragColor.rgb *= gl_FragColor.a + epsilon;\n\n\t#endif // defined(ENABLE_color) || defined(ENABLE_brightness)\n\n\t#ifdef ENABLE_ghost\n\tgl_FragColor *= u_ghost;\n\t#endif // ENABLE_ghost\n\n\t#ifdef DRAW_MODE_silhouette\n\t// Discard fully transparent pixels for stencil test\n\tif (gl_FragColor.a == 0.0) {\n\t\tdiscard;\n\t}\n\t// switch to u_silhouetteColor only AFTER the alpha test\n\tgl_FragColor = u_silhouetteColor;\n\t#else // DRAW_MODE_silhouette\n\n\t#ifdef DRAW_MODE_colorMask\n\tvec3 maskDistance = abs(gl_FragColor.rgb - u_colorMask);\n\tvec3 colorMaskTolerance = vec3(u_colorMaskTolerance, u_colorMaskTolerance, u_colorMaskTolerance);\n\tif (any(greaterThan(maskDistance, colorMaskTolerance)))\n\t{\n\t\tdiscard;\n\t}\n\t#endif // DRAW_MODE_colorMask\n\t#endif // DRAW_MODE_silhouette\n\n\t#ifdef DRAW_MODE_straightAlpha\n\t// Un-premultiply alpha.\n\tgl_FragColor.rgb /= gl_FragColor.a + epsilon;\n\t#endif\n\n\t#endif // !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))\n\n\t#ifdef DRAW_MODE_line\n\t// Maaaaagic antialiased-line-with-round-caps shader.\n\n\t// \"along-the-lineness\". This increases parallel to the line.\n\t// It goes from negative before the start point, to 0.5 through the start to the end, then ramps up again\n\t// past the end point.\n\tfloat d = ((v_texCoord.x - clamp(v_texCoord.x, 0.0, v_lineLength)) * 0.5) + 0.5;\n\n\t// Distance from (0.5, 0.5) to (d, the perpendicular coordinate). When we're in the middle of the line,\n\t// d will be 0.5, so the distance will be 0 at points close to the line and will grow at points further from it.\n\t// For the \"caps\", d will ramp down/up, giving us rounding.\n\t// See https://www.youtube.com/watch?v=PMltMdi1Wzg for a rough outline of the technique used to round the lines.\n\tfloat line = distance(vec2(0.5), vec2(d, v_texCoord.y)) * 2.0;\n\t// Expand out the line by its thickness.\n\tline -= ((v_lineThickness - 1.0) * 0.5);\n\t// Because \"distance to the center of the line\" decreases the closer we get to the line, but we want more opacity\n\t// the closer we are to the line, invert it.\n\tgl_FragColor = v_lineColor * clamp(1.0 - line, 0.0, 1.0);\n\t#endif // DRAW_MODE_line\n\n\t#ifdef DRAW_MODE_background\n\tgl_FragColor = u_backgroundColor;\n\t#endif\n}\n"

/***/ }),

/***/ "./node_modules/raw-loader/index.js!./node_modules/scratch-render/src/shaders/sprite.vert":
/*!***************************************************************************************!*\
  !*** ./node_modules/raw-loader!./node_modules/scratch-render/src/shaders/sprite.vert ***!
  \***************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = "precision mediump float;\n\n#ifdef DRAW_MODE_line\nuniform vec2 u_stageSize;\nattribute vec2 a_lineThicknessAndLength;\nattribute vec4 a_penPoints;\nattribute vec4 a_lineColor;\n\nvarying vec4 v_lineColor;\nvarying float v_lineThickness;\nvarying float v_lineLength;\nvarying vec4 v_penPoints;\n\n// Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations.\n// Smaller values can cause problems on some mobile devices.\nconst float epsilon = 1e-3;\n#endif\n\n#if !(defined(DRAW_MODE_line) || defined(DRAW_MODE_background))\nuniform mat4 u_projectionMatrix;\nuniform mat4 u_modelMatrix;\nattribute vec2 a_texCoord;\n#endif\n\nattribute vec2 a_position;\n\nvarying vec2 v_texCoord;\n\nvoid main() {\n\t#ifdef DRAW_MODE_line\n\t// Calculate a rotated (\"tight\") bounding box around the two pen points.\n\t// Yes, we're doing this 6 times (once per vertex), but on actual GPU hardware,\n\t// it's still faster than doing it in JS combined with the cost of uniformMatrix4fv.\n\n\t// Expand line bounds by sqrt(2) / 2 each side-- this ensures that all antialiased pixels\n\t// fall within the quad, even at a 45-degree diagonal\n\tvec2 position = a_position;\n\tfloat expandedRadius = (a_lineThicknessAndLength.x * 0.5) + 1.4142135623730951;\n\n\t// The X coordinate increases along the length of the line. It's 0 at the center of the origin point\n\t// and is in pixel-space (so at n pixels along the line, its value is n).\n\tv_texCoord.x = mix(0.0, a_lineThicknessAndLength.y + (expandedRadius * 2.0), a_position.x) - expandedRadius;\n\t// The Y coordinate is perpendicular to the line. It's also in pixel-space.\n\tv_texCoord.y = ((a_position.y - 0.5) * expandedRadius) + 0.5;\n\n\tposition.x *= a_lineThicknessAndLength.y + (2.0 * expandedRadius);\n\tposition.y *= 2.0 * expandedRadius;\n\n\t// 1. Center around first pen point\n\tposition -= expandedRadius;\n\n\t// 2. Rotate quad to line angle\n\tvec2 pointDiff = a_penPoints.zw;\n\t// Ensure line has a nonzero length so it's rendered properly\n\t// As long as either component is nonzero, the line length will be nonzero\n\t// If the line is zero-length, give it a bit of horizontal length\n\tpointDiff.x = (abs(pointDiff.x) < epsilon && abs(pointDiff.y) < epsilon) ? epsilon : pointDiff.x;\n\t// The `normalized` vector holds rotational values equivalent to sine/cosine\n\t// We're applying the standard rotation matrix formula to the position to rotate the quad to the line angle\n\t// pointDiff can hold large values so we must divide by u_lineLength instead of calling GLSL's normalize function:\n\t// https://asawicki.info/news_1596_watch_out_for_reduced_precision_normalizelength_in_opengl_es\n\tvec2 normalized = pointDiff / max(a_lineThicknessAndLength.y, epsilon);\n\tposition = mat2(normalized.x, normalized.y, -normalized.y, normalized.x) * position;\n\n\t// 3. Translate quad\n\tposition += a_penPoints.xy;\n\n\t// 4. Apply view transform\n\tposition *= 2.0 / u_stageSize;\n\tgl_Position = vec4(position, 0, 1);\n\n\tv_lineColor = a_lineColor;\n\tv_lineThickness = a_lineThicknessAndLength.x;\n\tv_lineLength = a_lineThicknessAndLength.y;\n\tv_penPoints = a_penPoints;\n\t#elif defined(DRAW_MODE_background)\n\tgl_Position = vec4(a_position * 2.0, 0, 1);\n\t#else\n\tgl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1);\n\tv_texCoord = a_texCoord;\n\t#endif\n}\n"

/***/ }),

/***/ "./node_modules/scratch-audio/src/ADPCMSoundDecoder.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-audio/src/ADPCMSoundDecoder.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArrayBufferStream = __webpack_require__(/*! ./ArrayBufferStream */ "./node_modules/scratch-audio/src/ArrayBufferStream.js");

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-audio/src/log.js");
/**
 * Data used by the decompression algorithm
 * @type {Array}
 */


const STEP_TABLE = [7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767];
/**
 * Data used by the decompression algorithm
 * @type {Array}
 */

const INDEX_TABLE = [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8];
let _deltaTable = null;
/**
 * Build a table of deltas from the 89 possible steps and 16 codes.
 * @return {Array<number>} computed delta values
 */

const deltaTable = function deltaTable() {
  if (_deltaTable === null) {
    const NUM_STEPS = STEP_TABLE.length;
    const NUM_INDICES = INDEX_TABLE.length;
    _deltaTable = new Array(NUM_STEPS * NUM_INDICES).fill(0);
    let i = 0;

    for (let index = 0; index < NUM_STEPS; index++) {
      for (let code = 0; code < NUM_INDICES; code++) {
        const step = STEP_TABLE[index];
        let delta = 0;
        if (code & 4) delta += step;
        if (code & 2) delta += step >> 1;
        if (code & 1) delta += step >> 2;
        delta += step >> 3;
        _deltaTable[i++] = code & 8 ? -delta : delta;
      }
    }
  }

  return _deltaTable;
};
/**
 * Decode wav audio files that have been compressed with the ADPCM format.
 * This is necessary because, while web browsers have native decoders for many audio
 * formats, ADPCM is a non-standard format used by Scratch since its early days.
 * This decoder is based on code from Scratch-Flash:
 * https://github.com/LLK/scratch-flash/blob/master/src/sound/WAVFile.as
 */


class ADPCMSoundDecoder {
  /**
   * @param {AudioContext} audioContext - a webAudio context
   * @constructor
   */
  constructor(audioContext) {
    this.audioContext = audioContext;
  }
  /**
   * Data used by the decompression algorithm
   * @type {Array}
   */


  static get STEP_TABLE() {
    return STEP_TABLE;
  }
  /**
   * Data used by the decompression algorithm
   * @type {Array}
   */


  static get INDEX_TABLE() {
    return INDEX_TABLE;
  }
  /**
   * Decode an ADPCM sound stored in an ArrayBuffer and return a promise
   * with the decoded audio buffer.
   * @param  {ArrayBuffer} audioData - containing ADPCM encoded wav audio
   * @return {AudioBuffer} the decoded audio buffer
   */


  decode(audioData) {
    return new Promise((resolve, reject) => {
      const stream = new ArrayBufferStream(audioData);
      const riffStr = stream.readUint8String(4);

      if (riffStr !== 'RIFF') {
        log.warn('incorrect adpcm wav header');
        reject();
      }

      const lengthInHeader = stream.readInt32();

      if (lengthInHeader + 8 !== audioData.byteLength) {
        log.warn("adpcm wav length in header: ".concat(lengthInHeader, " is incorrect"));
      }

      const wavStr = stream.readUint8String(4);

      if (wavStr !== 'WAVE') {
        log.warn('incorrect adpcm wav header');
        reject();
      }

      const formatChunk = this.extractChunk('fmt ', stream);
      this.encoding = formatChunk.readUint16();
      this.channels = formatChunk.readUint16();
      this.samplesPerSecond = formatChunk.readUint32();
      this.bytesPerSecond = formatChunk.readUint32();
      this.blockAlignment = formatChunk.readUint16();
      this.bitsPerSample = formatChunk.readUint16();
      formatChunk.position += 2; // skip extra header byte count

      this.samplesPerBlock = formatChunk.readUint16();
      this.adpcmBlockSize = (this.samplesPerBlock - 1) / 2 + 4; // block size in bytes

      const compressedData = this.extractChunk('data', stream);
      const sampleCount = this.numberOfSamples(compressedData, this.adpcmBlockSize);
      const buffer = this.audioContext.createBuffer(1, sampleCount, this.samplesPerSecond);
      this.imaDecompress(compressedData, this.adpcmBlockSize, buffer.getChannelData(0));
      resolve(buffer);
    });
  }
  /**
   * Extract a chunk of audio data from the stream, consisting of a set of audio data bytes
   * @param  {string} chunkType - the type of chunk to extract. 'data' or 'fmt' (format)
   * @param  {ArrayBufferStream} stream - an stream containing the audio data
   * @return {ArrayBufferStream} a stream containing the desired chunk
   */


  extractChunk(chunkType, stream) {
    stream.position = 12;

    while (stream.position < stream.getLength() - 8) {
      const typeStr = stream.readUint8String(4);
      const chunkSize = stream.readInt32();

      if (typeStr === chunkType) {
        const chunk = stream.extract(chunkSize);
        return chunk;
      }

      stream.position += chunkSize;
    }
  }
  /**
   * Count the exact number of samples in the compressed data.
   * @param {ArrayBufferStream} compressedData - the compressed data
   * @param {number} blockSize - size of each block in the data in bytes
   * @return {number} number of samples in the compressed data
   */


  numberOfSamples(compressedData, blockSize) {
    if (!compressedData) return 0;
    compressedData.position = 0;
    const available = compressedData.getBytesAvailable();
    const blocks = available / blockSize | 0; // Number of samples in full blocks.

    const fullBlocks = blocks * (2 * (blockSize - 4)) + 1; // Number of samples in the last incomplete block. 0 if the last block
    // is full.

    const subBlock = Math.max(available % blockSize - 4, 0) * 2; // 1 if the last block is incomplete. 0 if it is complete.

    const incompleteBlock = Math.min(available % blockSize, 1);
    return fullBlocks + subBlock + incompleteBlock;
  }
  /**
   * Decompress sample data using the IMA ADPCM algorithm.
   * Note: Handles only one channel, 4-bits per sample.
   * @param  {ArrayBufferStream} compressedData - a stream of compressed audio samples
   * @param  {number} blockSize - the number of bytes in the stream
   * @param  {Float32Array} out - the uncompressed audio samples
   */


  imaDecompress(compressedData, blockSize, out) {
    let sample;
    let code;
    let delta;
    let index = 0;
    let lastByte = -1; // -1 indicates that there is no saved lastByte
    // Bail and return no samples if we have no data

    if (!compressedData) return;
    compressedData.position = 0;
    const size = out.length;
    const samplesAfterBlockHeader = (blockSize - 4) * 2;
    const DELTA_TABLE = deltaTable();
    let i = 0;

    while (i < size) {
      // read block header
      sample = compressedData.readInt16();
      index = compressedData.readUint8();
      compressedData.position++; // skip extra header byte

      if (index > 88) index = 88;
      out[i++] = sample / 32768;
      const blockLength = Math.min(samplesAfterBlockHeader, size - i);
      const blockStart = i;

      while (i - blockStart < blockLength) {
        // read 4-bit code and compute delta from previous sample
        lastByte = compressedData.readUint8();
        code = lastByte & 0xF;
        delta = DELTA_TABLE[index * 16 + code]; // compute next index

        index += INDEX_TABLE[code];
        if (index > 88) index = 88;else if (index < 0) index = 0; // compute and output sample

        sample += delta;
        if (sample > 32767) sample = 32767;else if (sample < -32768) sample = -32768;
        out[i++] = sample / 32768; // use 4-bit code from lastByte and compute delta from previous
        // sample

        code = lastByte >> 4 & 0xF;
        delta = DELTA_TABLE[index * 16 + code]; // compute next index

        index += INDEX_TABLE[code];
        if (index > 88) index = 88;else if (index < 0) index = 0; // compute and output sample

        sample += delta;
        if (sample > 32767) sample = 32767;else if (sample < -32768) sample = -32768;
        out[i++] = sample / 32768;
      }
    }
  }

}

module.exports = ADPCMSoundDecoder;

/***/ }),

/***/ "./node_modules/scratch-audio/src/ArrayBufferStream.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-audio/src/ArrayBufferStream.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class ArrayBufferStream {
  /**
   * ArrayBufferStream wraps the built-in javascript ArrayBuffer, adding the ability to access
   * data in it like a stream, tracking its position.
   * You can request to read a value from the front of the array, and it will keep track of the position
   * within the byte array, so that successive reads are consecutive.
   * The available types to read include:
   * Uint8, Uint8String, Int16, Uint16, Int32, Uint32
   * @param {ArrayBuffer} arrayBuffer - array to use as a stream
   * @param {number} start - the start position in the raw buffer. position
   * will be relative to the start value.
   * @param {number} end - the end position in the raw buffer. length and
   * bytes available will be relative to the end value.
   * @param {ArrayBufferStream} parent - if passed reuses the parent's
   * internal objects
   * @constructor
   */
  constructor(arrayBuffer) {
    let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arrayBuffer.byteLength;
    let {
      _uint8View = new Uint8Array(arrayBuffer)
    } = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};

    /**
     * Raw data buffer for stream to read.
     * @type {ArrayBufferStream}
     */
    this.arrayBuffer = arrayBuffer;
    /**
     * Start position in arrayBuffer. Read values are relative to the start
     * in the arrayBuffer.
     * @type {number}
     */

    this.start = start;
    /**
     * End position in arrayBuffer. Length and bytes available are relative
     * to the start, end, and _position in the arrayBuffer;
     * @type {number};
     */

    this.end = end;
    /**
     * Cached Uint8Array view of the arrayBuffer. Heavily used for reading
     * Uint8 values and Strings from the stream.
     * @type {Uint8Array}
     */

    this._uint8View = _uint8View;
    /**
     * Raw position in the arrayBuffer relative to the beginning of the
     * arrayBuffer.
     * @type {number}
     */

    this._position = start;
  }
  /**
   * Return a new ArrayBufferStream that is a slice of the existing one
   * @param  {number} length - the number of bytes of extract
   * @return {ArrayBufferStream} the extracted stream
   */


  extract(length) {
    return new ArrayBufferStream(this.arrayBuffer, this._position, this._position + length, this);
  }
  /**
   * @return {number} the length of the stream in bytes
   */


  getLength() {
    return this.end - this.start;
  }
  /**
   * @return {number} the number of bytes available after the current position in the stream
   */


  getBytesAvailable() {
    return this.end - this._position;
  }
  /**
   * Position relative to the start value in the arrayBuffer of this
   * ArrayBufferStream.
   * @type {number}
   */


  get position() {
    return this._position - this.start;
  }
  /**
   * Set the position to read from in the arrayBuffer.
   * @type {number}
   * @param {number} value - new value to set position to
   */


  set position(value) {
    this._position = value + this.start;
    return value;
  }
  /**
   * Read an unsigned 8 bit integer from the stream
   * @return {number} the next 8 bit integer in the stream
   */


  readUint8() {
    const val = this._uint8View[this._position];
    this._position += 1;
    return val;
  }
  /**
   * Read a sequence of bytes of the given length and convert to a string.
   * This is a convenience method for use with short strings.
   * @param {number} length - the number of bytes to convert
   * @return {string} a String made by concatenating the chars in the input
   */


  readUint8String(length) {
    const arr = this._uint8View;
    let str = '';
    const end = this._position + length;

    for (let i = this._position; i < end; i++) {
      str += String.fromCharCode(arr[i]);
    }

    this._position += length;
    return str;
  }
  /**
   * Read a 16 bit integer from the stream
   * @return {number} the next 16 bit integer in the stream
   */


  readInt16() {
    const val = new Int16Array(this.arrayBuffer, this._position, 1)[0];
    this._position += 2; // one 16 bit int is 2 bytes

    return val;
  }
  /**
   * Read an unsigned 16 bit integer from the stream
   * @return {number} the next unsigned 16 bit integer in the stream
   */


  readUint16() {
    const val = new Uint16Array(this.arrayBuffer, this._position, 1)[0];
    this._position += 2; // one 16 bit int is 2 bytes

    return val;
  }
  /**
   * Read a 32 bit integer from the stream
   * @return {number} the next 32 bit integer in the stream
   */


  readInt32() {
    let val;

    if (this._position % 4 === 0) {
      val = new Int32Array(this.arrayBuffer, this._position, 1)[0];
    } else {
      // Cannot read Int32 directly out because offset is not multiple of 4
      // Need to slice out the values first
      val = new Int32Array(this.arrayBuffer.slice(this._position, this._position + 4))[0];
    }

    this._position += 4; // one 32 bit int is 4 bytes

    return val;
  }
  /**
   * Read an unsigned 32 bit integer from the stream
   * @return {number} the next unsigned 32 bit integer in the stream
   */


  readUint32() {
    const val = new Uint32Array(this.arrayBuffer, this._position, 1)[0];
    this._position += 4; // one 32 bit int is 4 bytes

    return val;
  }

}

module.exports = ArrayBufferStream;

/***/ }),

/***/ "./node_modules/scratch-audio/src/AudioEngine.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-audio/src/AudioEngine.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const StartAudioContext = __webpack_require__(/*! ./StartAudioContext */ "./node_modules/scratch-audio/src/StartAudioContext.js");

const AudioContext = __webpack_require__(/*! audio-context */ "./node_modules/audio-context/index.js");

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-audio/src/log.js");

const uid = __webpack_require__(/*! ./uid */ "./node_modules/scratch-audio/src/uid.js");

const ADPCMSoundDecoder = __webpack_require__(/*! ./ADPCMSoundDecoder */ "./node_modules/scratch-audio/src/ADPCMSoundDecoder.js");

const Loudness = __webpack_require__(/*! ./Loudness */ "./node_modules/scratch-audio/src/Loudness.js");

const SoundPlayer = __webpack_require__(/*! ./SoundPlayer */ "./node_modules/scratch-audio/src/SoundPlayer.js");

const EffectChain = __webpack_require__(/*! ./effects/EffectChain */ "./node_modules/scratch-audio/src/effects/EffectChain.js");

const PanEffect = __webpack_require__(/*! ./effects/PanEffect */ "./node_modules/scratch-audio/src/effects/PanEffect.js");

const PitchEffect = __webpack_require__(/*! ./effects/PitchEffect */ "./node_modules/scratch-audio/src/effects/PitchEffect.js");

const VolumeEffect = __webpack_require__(/*! ./effects/VolumeEffect */ "./node_modules/scratch-audio/src/effects/VolumeEffect.js");

const SoundBank = __webpack_require__(/*! ./SoundBank */ "./node_modules/scratch-audio/src/SoundBank.js");
/**
 * Wrapper to ensure that audioContext.decodeAudioData is a promise
 * @param {object} audioContext The current AudioContext
 * @param {ArrayBuffer} buffer Audio data buffer to decode
 * @return {Promise} A promise that resolves to the decoded audio
 */


const decodeAudioData = function decodeAudioData(audioContext, buffer) {
  // Check for newer promise-based API
  if (audioContext.decodeAudioData.length === 1) {
    return audioContext.decodeAudioData(buffer);
  } // Fall back to callback API


  return new Promise((resolve, reject) => {
    audioContext.decodeAudioData(buffer, decodedAudio => resolve(decodedAudio), error => reject(error));
  });
};
/**
 * There is a single instance of the AudioEngine. It handles global audio
 * properties and effects, loads all the audio buffers for sounds belonging to
 * sprites.
 */


class AudioEngine {
  constructor() {
    let audioContext = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new AudioContext();

    /**
     * AudioContext to play and manipulate sounds with a graph of source
     * and effect nodes.
     * @type {AudioContext}
     */
    this.audioContext = audioContext;
    StartAudioContext(this.audioContext);
    /**
     * Master GainNode that all sounds plays through. Changing this node
     * will change the volume for all sounds.
     * @type {GainNode}
     */

    this.inputNode = this.audioContext.createGain();
    this.inputNode.connect(this.audioContext.destination);
    /**
     * a map of soundIds to audio buffers, holding sounds for all sprites
     * @type {Object<String, ArrayBuffer>}
     */

    this.audioBuffers = {};
    /**
     * A Loudness detector.
     * @type {Loudness}
     */

    this.loudness = null;
    /**
     * Array of effects applied in order, left to right,
     * Left is closest to input, Right is closest to output
     */

    this.effects = [PanEffect, PitchEffect, VolumeEffect];
  }
  /**
   * Current time in the AudioEngine.
   * @type {number}
   */


  get currentTime() {
    return this.audioContext.currentTime;
  }
  /**
   * Names of the audio effects.
   * @enum {string}
   */


  get EFFECT_NAMES() {
    return {
      pitch: 'pitch',
      pan: 'pan'
    };
  }
  /**
   * A short duration to transition audio prarameters.
   *
   * Used as a time constant for exponential transitions. A general value
   * must be large enough that it does not cute off lower frequency, or bass,
   * sounds. Human hearing lower limit is ~20Hz making a safe value 25
   * milliseconds or 0.025 seconds, where half of a 20Hz wave will play along
   * with the DECAY. Higher frequencies will play multiple waves during the
   * same amount of time and avoid clipping.
   *
   * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/AudioParam/setTargetAtTime}
   * @const {number}
   */


  get DECAY_DURATION() {
    return 0.025;
  }
  /**
   * Some environments cannot smoothly change parameters immediately, provide
   * a small delay before decaying.
   *
   * @see {@link https://bugzilla.mozilla.org/show_bug.cgi?id=1228207}
   * @const {number}
   */


  get DECAY_WAIT() {
    return 0.05;
  }
  /**
   * Get the input node.
   * @return {AudioNode} - audio node that is the input for this effect
   */


  getInputNode() {
    return this.inputNode;
  }
  /**
   * Decode a sound, decompressing it into audio samples.
   * @param {object} sound - an object containing audio data and metadata for
   *     a sound
   * @param {Buffer} sound.data - sound data loaded from scratch-storage
   * @returns {?Promise} - a promise which will resolve to the sound id and
   *     buffer if decoded
   */


  _decodeSound(sound) {
    // Make a copy of the buffer because decoding detaches the original
    // buffer
    const bufferCopy1 = sound.data.buffer.slice(0); // todo: multiple decodings of the same buffer create duplicate decoded
    // copies in audioBuffers. Create a hash id of the buffer or deprecate
    // audioBuffers to avoid memory issues for large audio buffers.

    const soundId = uid(); // Attempt to decode the sound using the browser's native audio data
    // decoder If that fails, attempt to decode as ADPCM

    const decoding = decodeAudioData(this.audioContext, bufferCopy1).catch(() => {
      // If the file is empty, create an empty sound
      if (sound.data.length === 0) {
        return this._emptySound();
      } // The audio context failed to parse the sound data
      // we gave it, so try to decode as 'adpcm'
      // First we need to create another copy of our original data


      const bufferCopy2 = sound.data.buffer.slice(0); // Try decoding as adpcm

      return new ADPCMSoundDecoder(this.audioContext).decode(bufferCopy2).catch(() => this._emptySound());
    }).then(buffer => [soundId, buffer], error => {
      log.warn('audio data could not be decoded', error);
    });
    return decoding;
  }
  /**
   * An empty sound buffer, for use when we are unable to decode a sound file.
   * @returns {AudioBuffer} - an empty audio buffer.
   */


  _emptySound() {
    return this.audioContext.createBuffer(1, 1, this.audioContext.sampleRate);
  }
  /**
   * Decode a sound, decompressing it into audio samples.
   *
   * Store a reference to it the sound in the audioBuffers dictionary,
   * indexed by soundId.
   *
   * @param {object} sound - an object containing audio data and metadata for
   *     a sound
   * @param {Buffer} sound.data - sound data loaded from scratch-storage
   * @returns {?Promise} - a promise which will resolve to the sound id
   */


  decodeSound(sound) {
    return this._decodeSound(sound).then(_ref => {
      let [id, buffer] = _ref;
      this.audioBuffers[id] = buffer;
      return id;
    });
  }
  /**
   * Decode a sound, decompressing it into audio samples.
   *
   * Create a SoundPlayer instance that can be used to play the sound and
   * stop and fade out playback.
   *
   * @param {object} sound - an object containing audio data and metadata for
   *     a sound
   * @param {Buffer} sound.data - sound data loaded from scratch-storage
   * @returns {?Promise} - a promise which will resolve to the buffer
   */


  decodeSoundPlayer(sound) {
    return this._decodeSound(sound).then(_ref2 => {
      let [id, buffer] = _ref2;
      return new SoundPlayer(this, {
        id,
        buffer
      });
    });
  }
  /**
   * Get the current loudness of sound received by the microphone.
   * Sound is measured in RMS and smoothed.
   * @return {number} loudness scaled 0 to 100
   */


  getLoudness() {
    // The microphone has not been set up, so try to connect to it
    if (!this.loudness) {
      this.loudness = new Loudness(this.audioContext);
    }

    return this.loudness.getLoudness();
  }
  /**
   * Create an effect chain.
   * @returns {EffectChain} chain of effects defined by this AudioEngine
   */


  createEffectChain() {
    const effects = new EffectChain(this, this.effects);
    effects.connect(this);
    return effects;
  }
  /**
   * Create a sound bank and effect chain.
   * @returns {SoundBank} a sound bank configured with an effect chain
   *     defined by this AudioEngine
   */


  createBank() {
    return new SoundBank(this, this.createEffectChain());
  }

}

module.exports = AudioEngine;

/***/ }),

/***/ "./node_modules/scratch-audio/src/Loudness.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-audio/src/Loudness.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-audio/src/log.js");

class Loudness {
  /**
   * Instrument and detect a loudness value from a local microphone.
   * @param {AudioContext} audioContext - context to create nodes from for
   *     detecting loudness
   * @constructor
   */
  constructor(audioContext) {
    /**
     * AudioContext the mic will connect to and provide analysis of
     * @type {AudioContext}
     */
    this.audioContext = audioContext;
    /**
     * Are we connecting to the mic yet?
     * @type {Boolean}
     */

    this.connectingToMic = false;
    /**
     * microphone, for measuring loudness, with a level meter analyzer
     * @type {MediaStreamSourceNode}
     */

    this.mic = null;
  }
  /**
   * Get the current loudness of sound received by the microphone.
   * Sound is measured in RMS and smoothed.
   * Some code adapted from Tone.js: https://github.com/Tonejs/Tone.js
   * @return {number} loudness scaled 0 to 100
   */


  getLoudness() {
    // The microphone has not been set up, so try to connect to it
    if (!this.mic && !this.connectingToMic) {
      this.connectingToMic = true; // prevent multiple connection attempts

      navigator.mediaDevices.getUserMedia({
        audio: true
      }).then(stream => {
        this.audioStream = stream;
        this.mic = this.audioContext.createMediaStreamSource(stream);
        this.analyser = this.audioContext.createAnalyser();
        this.mic.connect(this.analyser);
        this.micDataArray = new Float32Array(this.analyser.fftSize);
      }).catch(err => {
        log.warn(err);
      });
    } // If the microphone is set up and active, measure the loudness


    if (this.mic && this.audioStream.active) {
      this.analyser.getFloatTimeDomainData(this.micDataArray);
      let sum = 0; // compute the RMS of the sound

      for (let i = 0; i < this.micDataArray.length; i++) {
        sum += Math.pow(this.micDataArray[i], 2);
      }

      let rms = Math.sqrt(sum / this.micDataArray.length); // smooth the value, if it is descending

      if (this._lastValue) {
        rms = Math.max(rms, this._lastValue * 0.6);
      }

      this._lastValue = rms; // Scale the measurement so it's more sensitive to quieter sounds

      rms *= 1.63;
      rms = Math.sqrt(rms); // Scale it up to 0-100 and round

      rms = Math.round(rms * 100); // Prevent it from going above 100

      rms = Math.min(rms, 100);
      return rms;
    } // if there is no microphone input, return -1


    return -1;
  }

}

module.exports = Loudness;

/***/ }),

/***/ "./node_modules/scratch-audio/src/SoundBank.js":
/*!*****************************************************!*\
  !*** ./node_modules/scratch-audio/src/SoundBank.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-audio/src/log.js");
/**
 * A symbol indicating all targets are to be effected.
 * @const {string}
 */


const ALL_TARGETS = '*';

class SoundBank {
  /**
   * A bank of sounds that can be played.
   * @constructor
   * @param {AudioEngine} audioEngine - related AudioEngine
   * @param {EffectChain} effectChainPrime - original EffectChain cloned for
   *     playing sounds
   */
  constructor(audioEngine, effectChainPrime) {
    /**
     * AudioEngine this SoundBank is related to.
     * @type {AudioEngine}
     */
    this.audioEngine = audioEngine;
    /**
     * Map of ids to soundPlayers.
     * @type {object<SoundPlayer>}
     */

    this.soundPlayers = {};
    /**
     * Map of targets by sound id.
     * @type {Map<string, Target>}
     */

    this.playerTargets = new Map();
    /**
     * Map of effect chains by sound id.
     * @type {Map<string, EffectChain}
     */

    this.soundEffects = new Map();
    /**
     * Original EffectChain cloned for every playing sound.
     * @type {EffectChain}
     */

    this.effectChainPrime = effectChainPrime;
  }
  /**
   * Add a sound player instance likely from AudioEngine.decodeSoundPlayer
   * @param {SoundPlayer} soundPlayer - SoundPlayer to add
   */


  addSoundPlayer(soundPlayer) {
    this.soundPlayers[soundPlayer.id] = soundPlayer;
  }
  /**
   * Get a sound player by id.
   * @param {string} soundId - sound to look for
   * @returns {SoundPlayer} instance of sound player for the id
   */


  getSoundPlayer(soundId) {
    if (!this.soundPlayers[soundId]) {
      log.error("SoundBank.getSoundPlayer(".concat(soundId, "): called missing sound in bank"));
    }

    return this.soundPlayers[soundId];
  }
  /**
   * Get a sound EffectChain by id.
   * @param {string} sound - sound to look for an EffectChain
   * @returns {EffectChain} available EffectChain for this id
   */


  getSoundEffects(sound) {
    if (!this.soundEffects.has(sound)) {
      this.soundEffects.set(sound, this.effectChainPrime.clone());
    }

    return this.soundEffects.get(sound);
  }
  /**
   * Play a sound.
   * @param {Target} target - Target to play for
   * @param {string} soundId - id of sound to play
   * @returns {Promise} promise that resolves when the sound finishes playback
   */


  playSound(target, soundId) {
    const effects = this.getSoundEffects(soundId);
    const player = this.getSoundPlayer(soundId);

    if (this.playerTargets.get(soundId) !== target) {
      // make sure to stop the old sound, effectively "forking" the output
      // when the target switches before we adjust it's effects
      player.stop();
    }

    this.playerTargets.set(soundId, target);
    effects.addSoundPlayer(player);
    effects.setEffectsFromTarget(target);
    player.connect(effects);
    player.play();
    return player.finished();
  }
  /**
   * Set the effects (pan, pitch, and volume) from values on the given target.
   * @param {Target} target - target to set values from
   */


  setEffects(target) {
    this.playerTargets.forEach((playerTarget, key) => {
      if (playerTarget === target) {
        this.getSoundEffects(key).setEffectsFromTarget(target);
      }
    });
  }
  /**
   * Stop playback of sound by id if was lasted played by the target.
   * @param {Target} target - target to check if it last played the sound
   * @param {string} soundId - id of the sound to stop
   */


  stop(target, soundId) {
    if (this.playerTargets.get(soundId) === target) {
      this.soundPlayers[soundId].stop();
    }
  }
  /**
   * Stop all sounds for all targets or a specific target.
   * @param {Target|string} target - a symbol for all targets or the target
   *     to stop sounds for
   */


  stopAllSounds() {
    let target = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ALL_TARGETS;
    this.playerTargets.forEach((playerTarget, key) => {
      if (target === ALL_TARGETS || playerTarget === target) {
        this.getSoundPlayer(key).stop();
      }
    });
  }
  /**
   * Dispose of all EffectChains and SoundPlayers.
   */


  dispose() {
    this.playerTargets.clear();
    this.soundEffects.forEach(effects => effects.dispose());
    this.soundEffects.clear();

    for (const soundId in this.soundPlayers) {
      if (this.soundPlayers.hasOwnProperty(soundId)) {
        this.soundPlayers[soundId].dispose();
      }
    }

    this.soundPlayers = {};
  }

}

module.exports = SoundBank;

/***/ }),

/***/ "./node_modules/scratch-audio/src/SoundPlayer.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-audio/src/SoundPlayer.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const {
  EventEmitter
} = __webpack_require__(/*! events */ "./node_modules/events/events.js");

const VolumeEffect = __webpack_require__(/*! ./effects/VolumeEffect */ "./node_modules/scratch-audio/src/effects/VolumeEffect.js");
/**
 * Name of event that indicates playback has ended.
 * @const {string}
 */


const ON_ENDED = 'ended';

class SoundPlayer extends EventEmitter {
  /**
   * Play sounds that stop without audible clipping.
   *
   * @param {AudioEngine} audioEngine - engine to play sounds on
   * @param {object} data - required data for sound playback
   * @param {string} data.id - a unique id for this sound
   * @param {ArrayBuffer} data.buffer - buffer of the sound's waveform to play
   * @constructor
   */
  constructor(audioEngine, _ref) {
    let {
      id,
      buffer
    } = _ref;
    super();
    /**
     * Unique sound identifier set by AudioEngine.
     * @type {string}
     */

    this.id = id;
    /**
     * AudioEngine creating this sound player.
     * @type {AudioEngine}
     */

    this.audioEngine = audioEngine;
    /**
     * Decoded audio buffer from audio engine for playback.
     * @type {AudioBuffer}
     */

    this.buffer = buffer;
    /**
     * Output audio node.
     * @type {AudioNode}
     */

    this.outputNode = null;
    /**
     * VolumeEffect used to fade out playing sounds when stopping them.
     * @type {VolumeEffect}
     */

    this.volumeEffect = null;
    /**
     * Target engine, effect, or chain this player directly connects to.
     * @type {AudioEngine|Effect|EffectChain}
     */

    this.target = null;
    /**
     * Internally is the SoundPlayer initialized with at least its buffer
     * source node and output node.
     * @type {boolean}
     */

    this.initialized = false;
    /**
     * Is the sound playing or starting to play?
     * @type {boolean}
     */

    this.isPlaying = false;
    /**
     * Timestamp sound is expected to be starting playback until. Once the
     * future timestamp is reached the sound is considered to be playing
     * through the audio hardware and stopping should fade out instead of
     * cutting off playback.
     * @type {number}
     */

    this.startingUntil = 0;
    /**
     * Rate to play back the audio at.
     * @type {number}
     */

    this.playbackRate = 1; // handleEvent is a EventTarget api for the DOM, however the
    // web-audio-test-api we use uses an addEventListener that isn't
    // compatable with object and requires us to pass this bound function
    // instead

    this.handleEvent = this.handleEvent.bind(this);
  }
  /**
   * Is plaback currently starting?
   * @type {boolean}
   */


  get isStarting() {
    return this.isPlaying && this.startingUntil > this.audioEngine.currentTime;
  }
  /**
   * Handle any event we have told the output node to listen for.
   * @param {Event} event - dom event to handle
   */


  handleEvent(event) {
    if (event.type === ON_ENDED) {
      this.onEnded();
    }
  }
  /**
   * Event listener for when playback ends.
   */


  onEnded() {
    this.emit('stop');
    this.isPlaying = false;
  }
  /**
   * Create the buffer source node during initialization or secondary
   * playback.
   */


  _createSource() {
    if (this.outputNode !== null) {
      this.outputNode.removeEventListener(ON_ENDED, this.handleEvent);
      this.outputNode.disconnect();
    }

    this.outputNode = this.audioEngine.audioContext.createBufferSource();
    this.outputNode.playbackRate.value = this.playbackRate;
    this.outputNode.buffer = this.buffer;
    this.outputNode.addEventListener(ON_ENDED, this.handleEvent);

    if (this.target !== null) {
      this.connect(this.target);
    }
  }
  /**
   * Initialize the player for first playback.
   */


  initialize() {
    this.initialized = true;

    this._createSource();
  }
  /**
   * Connect the player to the engine or an effect chain.
   * @param {object} target - object to connect to
   * @returns {object} - return this sound player
   */


  connect(target) {
    if (target === this.volumeEffect) {
      this.outputNode.disconnect();
      this.outputNode.connect(this.volumeEffect.getInputNode());
      return;
    }

    this.target = target;

    if (!this.initialized) {
      return;
    }

    if (this.volumeEffect === null) {
      this.outputNode.disconnect();
      this.outputNode.connect(target.getInputNode());
    } else {
      this.volumeEffect.connect(target);
    }

    return this;
  }
  /**
   * Teardown the player.
   */


  dispose() {
    if (!this.initialized) {
      return;
    }

    this.stopImmediately();

    if (this.volumeEffect !== null) {
      this.volumeEffect.dispose();
      this.volumeEffect = null;
    }

    this.outputNode.disconnect();
    this.outputNode = null;
    this.target = null;
    this.initialized = false;
  }
  /**
   * Take the internal state of this player and create a new player from
   * that. Restore the state of this player to that before its first playback.
   *
   * The returned player can be used to stop the original playback or
   * continue it without manipulation from the original player.
   *
   * @returns {SoundPlayer} - new SoundPlayer with old state
   */


  take() {
    if (this.outputNode) {
      this.outputNode.removeEventListener(ON_ENDED, this.handleEvent);
    }

    const taken = new SoundPlayer(this.audioEngine, this);
    taken.playbackRate = this.playbackRate;

    if (this.isPlaying) {
      taken.startingUntil = this.startingUntil;
      taken.isPlaying = this.isPlaying;
      taken.initialized = this.initialized;
      taken.outputNode = this.outputNode;
      taken.outputNode.addEventListener(ON_ENDED, taken.handleEvent);
      taken.volumeEffect = this.volumeEffect;

      if (taken.volumeEffect) {
        taken.volumeEffect.audioPlayer = taken;
      }

      if (this.target !== null) {
        taken.connect(this.target);
      }

      this.emit('stop');
      taken.emit('play');
    }

    this.outputNode = null;
    this.volumeEffect = null;
    this.initialized = false;
    this.startingUntil = 0;
    this.isPlaying = false;
    return taken;
  }
  /**
   * Start playback for this sound.
   *
   * If the sound is already playing it will stop playback with a quick fade
   * out.
   */


  play() {
    if (this.isStarting) {
      this.emit('stop');
      this.emit('play');
      return;
    }

    if (this.isPlaying) {
      this.stop();
    }

    if (this.initialized) {
      this._createSource();
    } else {
      this.initialize();
    }

    this.outputNode.start();
    this.isPlaying = true;
    const {
      currentTime,
      DECAY_DURATION
    } = this.audioEngine;
    this.startingUntil = currentTime + DECAY_DURATION;
    this.emit('play');
  }
  /**
   * Stop playback after quickly fading out.
   */


  stop() {
    if (!this.isPlaying) {
      return;
    } // always do a manual stop on a taken / volume effect fade out sound
    // player take will emit "stop" as well as reset all of our playing
    // statuses / remove our nodes / etc


    const taken = this.take();
    taken.volumeEffect = new VolumeEffect(taken.audioEngine, taken, null);
    taken.volumeEffect.connect(taken.target); // volumeEffect will recursively connect to us if it needs to, so this
    // happens too:
    // taken.connect(taken.volumeEffect);

    taken.finished().then(() => taken.dispose());
    taken.volumeEffect.set(0);
    const {
      currentTime,
      DECAY_DURATION
    } = this.audioEngine;
    taken.outputNode.stop(currentTime + DECAY_DURATION);
  }
  /**
   * Stop immediately without fading out. May cause audible clipping.
   */


  stopImmediately() {
    if (!this.isPlaying) {
      return;
    }

    this.outputNode.stop();
    this.isPlaying = false;
    this.startingUntil = 0;
    this.emit('stop');
  }
  /**
   * Return a promise that resolves when the sound next finishes.
   * @returns {Promise} - resolves when the sound finishes
   */


  finished() {
    return new Promise(resolve => {
      this.once('stop', resolve);
    });
  }
  /**
   * Set the sound's playback rate.
   * @param {number} value - playback rate. Default is 1.
   */


  setPlaybackRate(value) {
    this.playbackRate = value;

    if (this.initialized) {
      this.outputNode.playbackRate.value = value;
    }
  }

}

module.exports = SoundPlayer;

/***/ }),

/***/ "./node_modules/scratch-audio/src/StartAudioContext.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-audio/src/StartAudioContext.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// StartAudioContext assumes that we are in a window/document setting and messes with the unit
// tests, this is our own version just checking to see if we have a global document to listen
// to before we even try to "start" it.  Our test api audio context is started by default.
const StartAudioContext = __webpack_require__(/*! startaudiocontext */ "./node_modules/startaudiocontext/StartAudioContext.js");

module.exports = function (context) {
  if (typeof document !== 'undefined') {
    return StartAudioContext(context);
  }
};

/***/ }),

/***/ "./node_modules/scratch-audio/src/effects/Effect.js":
/*!**********************************************************!*\
  !*** ./node_modules/scratch-audio/src/effects/Effect.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * An effect on an AudioPlayer and all its SoundPlayers.
 */
class Effect {
  /**
   * @param {AudioEngine} audioEngine - audio engine this runs with
   * @param {AudioPlayer} audioPlayer - audio player this affects
   * @param {Effect} lastEffect - effect in the chain before this one
   * @constructor
   */
  constructor(audioEngine, audioPlayer, lastEffect) {
    this.audioEngine = audioEngine;
    this.audioPlayer = audioPlayer;
    this.lastEffect = lastEffect;
    this.value = this.DEFAULT_VALUE;
    this.initialized = false;
    this.inputNode = null;
    this.outputNode = null;
    this.target = null;
  }
  /**
   * Return the name of the effect.
   * @type {string}
   */


  get name() {
    throw new Error("".concat(this.constructor.name, ".name is not implemented"));
  }
  /**
   * Default value to set the Effect to when constructed and when clear'ed.
   * @const {number}
   */


  get DEFAULT_VALUE() {
    return 0;
  }
  /**
   * Should the effect be connected to the audio graph?
   * The pitch effect is an example that does not need to be patched in.
   * Instead of affecting the graph it affects the player directly.
   * @return {boolean} is the effect affecting the graph?
   */


  get _isPatch() {
    return this.initialized && (this.value !== this.DEFAULT_VALUE || this.audioPlayer === null);
  }
  /**
   * Get the input node.
   * @return {AudioNode} - audio node that is the input for this effect
   */


  getInputNode() {
    if (this._isPatch) {
      return this.inputNode;
    }

    return this.target.getInputNode();
  }
  /**
   * Initialize the Effect.
   * Effects start out uninitialized. Then initialize when they are first set
   * with some value.
   * @throws {Error} throws when left unimplemented
   */


  initialize() {
    throw new Error("".concat(this.constructor.name, ".initialize is not implemented."));
  }
  /**
   * Set the effects value.
   * @private
   * @param {number} value - new value to set effect to
   */


  _set() {
    throw new Error("".concat(this.constructor.name, "._set is not implemented."));
  }
  /**
   * Set the effects value.
   * @param {number} value - new value to set effect to
   */


  set(value) {
    // Initialize the node on first set.
    if (!this.initialized) {
      this.initialize();
    } // Store whether the graph should currently affected by this effect.


    const wasPatch = this._isPatch;

    if (wasPatch) {
      this._lastPatch = this.audioEngine.currentTime;
    } // Call the internal implementation per this Effect.


    if (value !== this.value) {
      this._set(value);
    } // Connect or disconnect from the graph if this now applies or no longer
    // applies an effect.


    if (this._isPatch !== wasPatch && this.target !== null) {
      this.connect(this.target);
    }
  }
  /**
   * Update the effect for changes in the audioPlayer.
   */


  update() {}
  /**
   * Clear the value back to the default.
   */


  clear() {
    this.set(this.DEFAULT_VALUE);
  }
  /**
   * Connnect this effect's output to another audio node
   * @param {object} target - target whose node to should be connected
   */


  connect(target) {
    if (target === null) {
      throw new Error('target may not be null');
    }

    const checkForCircularReference = subtarget => {
      if (subtarget) {
        if (subtarget === this) {
          return true;
        }

        return checkForCircularReference(subtarget.target);
      }
    };

    if (checkForCircularReference(target)) {
      throw new Error('Effect cannot connect to itself');
    }

    this.target = target;

    if (this.outputNode !== null) {
      this.outputNode.disconnect();
    }

    if (this._isPatch || this._lastPatch + this.audioEngine.DECAY_DURATION < this.audioEngine.currentTime) {
      this.outputNode.connect(target.getInputNode());
    }

    if (this.lastEffect === null) {
      if (this.audioPlayer !== null) {
        this.audioPlayer.connect(this);
      }
    } else {
      this.lastEffect.connect(this);
    }
  }
  /**
   * Clean up and disconnect audio nodes.
   */


  dispose() {
    this.inputNode = null;
    this.outputNode = null;
    this.target = null;
    this.initialized = false;
  }

}

module.exports = Effect;

/***/ }),

/***/ "./node_modules/scratch-audio/src/effects/EffectChain.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-audio/src/effects/EffectChain.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class EffectChain {
  /**
   * Chain of effects that can be applied to a group of SoundPlayers.
   * @param {AudioEngine} audioEngine - engine whose effects these belong to
   * @param {Array<Effect>} effects - array of Effect classes to construct
   */
  constructor(audioEngine, effects) {
    /**
     * AudioEngine whose effects these belong to.
     * @type {AudioEngine}
     */
    this.audioEngine = audioEngine;
    /**
     * Node incoming connections will attach to. This node than connects to
     * the items in the chain which finally connect to some output.
     * @type {AudioNode}
     */

    this.inputNode = this.audioEngine.audioContext.createGain();
    /**
     * List of Effect types to create.
     * @type {Array<Effect>}
     */

    this.effects = effects; // Effects are instantiated in reverse so that the first refers to the
    // second, the second refers to the third, etc and the last refers to
    // null.

    let lastEffect = null;
    /**
     * List of instantiated Effects.
     * @type {Array<Effect>}
     */

    this._effects = effects.reverse().map(Effect => {
      const effect = new Effect(audioEngine, this, lastEffect);
      this[effect.name] = effect;
      lastEffect = effect;
      return effect;
    }).reverse();
    /**
     * First effect of this chain.
     * @type {Effect}
     */

    this.firstEffect = this._effects[0];
    /**
     * Last effect of this chain.
     * @type {Effect}
     */

    this.lastEffect = this._effects[this._effects.length - 1];
    /**
     * A set of players this chain is managing.
     */

    this._soundPlayers = new Set();
  }
  /**
   * Create a clone of the EffectChain.
   * @returns {EffectChain} a clone of this EffectChain
   */


  clone() {
    const chain = new EffectChain(this.audioEngine, this.effects);

    if (this.target) {
      chain.connect(this.target);
    }

    return chain;
  }
  /**
   * Add a sound player.
   * @param {SoundPlayer} soundPlayer - a sound player to manage
   */


  addSoundPlayer(soundPlayer) {
    if (!this._soundPlayers.has(soundPlayer)) {
      this._soundPlayers.add(soundPlayer);

      this.update();
    }
  }
  /**
   * Remove a sound player.
   * @param {SoundPlayer} soundPlayer - a sound player to stop managing
   */


  removeSoundPlayer(soundPlayer) {
    this._soundPlayers.remove(soundPlayer);
  }
  /**
   * Get the audio input node.
   * @returns {AudioNode} audio node the upstream can connect to
   */


  getInputNode() {
    return this.inputNode;
  }
  /**
   * Connnect this player's output to another audio node.
   * @param {object} target - target whose node to should be connected
   */


  connect(target) {
    const {
      firstEffect,
      lastEffect
    } = this;

    if (target === lastEffect) {
      this.inputNode.disconnect();
      this.inputNode.connect(lastEffect.getInputNode());
      return;
    } else if (target === firstEffect) {
      return;
    }

    this.target = target;
    firstEffect.connect(target);
  }
  /**
   * Array of SoundPlayers managed by this EffectChain.
   * @returns {Array<SoundPlayer>} sound players managed by this chain
   */


  getSoundPlayers() {
    return [...this._soundPlayers];
  }
  /**
   * Set Effect values with named values on target.soundEffects if it exist
   * and then from target itself.
   * @param {Target} target - target to set values from
   */


  setEffectsFromTarget(target) {
    this._effects.forEach(effect => {
      if ('soundEffects' in target && effect.name in target.soundEffects) {
        effect.set(target.soundEffects[effect.name]);
      } else if (effect.name in target) {
        effect.set(target[effect.name]);
      }
    });
  }
  /**
   * Set an effect value by its name.
   * @param {string} effect - effect name to change
   * @param {number} value - value to set effect to
   */


  set(effect, value) {
    if (effect in this) {
      this[effect].set(value);
    }
  }
  /**
   * Update managed sound players with the effects on this chain.
   */


  update() {
    this._effects.forEach(effect => effect.update());
  }
  /**
   * Clear all effects to their default values.
   */


  clear() {
    this._effects.forEach(effect => effect.clear());
  }
  /**
   * Dispose of all effects in this chain. Nothing is done to managed
   * SoundPlayers.
   */


  dispose() {
    this._soundPlayers = null;

    this._effects.forEach(effect => effect.dispose());

    this._effects = null;
  }

}

module.exports = EffectChain;

/***/ }),

/***/ "./node_modules/scratch-audio/src/effects/PanEffect.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-audio/src/effects/PanEffect.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Effect = __webpack_require__(/*! ./Effect */ "./node_modules/scratch-audio/src/effects/Effect.js");
/**
 * A pan effect, which moves the sound to the left or right between the speakers
 * Effect value of -100 puts the audio entirely on the left channel,
 * 0 centers it, 100 puts it on the right.
 */


class PanEffect extends Effect {
  /**
   * @param {AudioEngine} audioEngine - audio engine this runs with
   * @param {AudioPlayer} audioPlayer - audio player this affects
   * @param {Effect} lastEffect - effect in the chain before this one
   * @constructor
   */
  constructor(audioEngine, audioPlayer, lastEffect) {
    super(audioEngine, audioPlayer, lastEffect);
    this.leftGain = null;
    this.rightGain = null;
    this.channelMerger = null;
  }
  /**
   * Return the name of the effect.
   * @type {string}
   */


  get name() {
    return 'pan';
  }
  /**
   * Initialize the Effect.
   * Effects start out uninitialized. Then initialize when they are first set
   * with some value.
   * @throws {Error} throws when left unimplemented
   */


  initialize() {
    const audioContext = this.audioEngine.audioContext;
    this.inputNode = audioContext.createGain();
    this.leftGain = audioContext.createGain();
    this.rightGain = audioContext.createGain();
    this.channelMerger = audioContext.createChannelMerger(2);
    this.outputNode = this.channelMerger;
    this.inputNode.connect(this.leftGain);
    this.inputNode.connect(this.rightGain);
    this.leftGain.connect(this.channelMerger, 0, 0);
    this.rightGain.connect(this.channelMerger, 0, 1);
    this.initialized = true;
  }
  /**
   * Set the effect value
   * @param {number} value - the new value to set the effect to
   */


  _set(value) {
    this.value = value; // Map the scratch effect value (-100 to 100) to (0 to 1)

    const p = (value + 100) / 200; // Use trig functions for equal-loudness panning
    // See e.g. https://docs.cycling74.com/max7/tutorials/13_panningchapter01

    const leftVal = Math.cos(p * Math.PI / 2);
    const rightVal = Math.sin(p * Math.PI / 2);
    const {
      currentTime,
      DECAY_WAIT,
      DECAY_DURATION
    } = this.audioEngine;
    this.leftGain.gain.setTargetAtTime(leftVal, currentTime + DECAY_WAIT, DECAY_DURATION);
    this.rightGain.gain.setTargetAtTime(rightVal, currentTime + DECAY_WAIT, DECAY_DURATION);
  }
  /**
   * Clean up and disconnect audio nodes.
   */


  dispose() {
    if (!this.initialized) {
      return;
    }

    this.inputNode.disconnect();
    this.leftGain.disconnect();
    this.rightGain.disconnect();
    this.channelMerger.disconnect();
    this.inputNode = null;
    this.leftGain = null;
    this.rightGain = null;
    this.channelMerger = null;
    this.outputNode = null;
    this.target = null;
    this.initialized = false;
  }

}

module.exports = PanEffect;

/***/ }),

/***/ "./node_modules/scratch-audio/src/effects/PitchEffect.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-audio/src/effects/PitchEffect.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Effect = __webpack_require__(/*! ./Effect */ "./node_modules/scratch-audio/src/effects/Effect.js");
/**
 * A pitch change effect, which changes the playback rate of the sound in order
 * to change its pitch: reducing the playback rate lowers the pitch, increasing
 * the rate raises the pitch. The duration of the sound is also changed.
 *
 * Changing the value of the pitch effect by 10 causes a change in pitch by 1
 * semitone (i.e. a musical half-step, such as the difference between C and C#)
 * Changing the pitch effect by 120 changes the pitch by one octave (12
 * semitones)
 *
 * The value of this effect is not clamped (i.e. it is typically between -120
 * and 120, but can be set much higher or much lower, with weird and fun
 * results). We should consider what extreme values to use for clamping it.
 *
 * Note that this effect functions differently from the other audio effects. It
 * is not part of a chain of audio nodes. Instead, it provides a way to set the
 * playback on one SoundPlayer or a group of them.
 */


class PitchEffect extends Effect {
  /**
   * @param {AudioEngine} audioEngine - audio engine this runs with
   * @param {AudioPlayer} audioPlayer - audio player this affects
   * @param {Effect} lastEffect - effect in the chain before this one
   * @constructor
   */
  constructor(audioEngine, audioPlayer, lastEffect) {
    super(audioEngine, audioPlayer, lastEffect);
    /**
     * The playback rate ratio
     * @type {Number}
     */

    this.ratio = 1;
  }
  /**
   * Return the name of the effect.
   * @type {string}
   */


  get name() {
    return 'pitch';
  }
  /**
   * Should the effect be connected to the audio graph?
   * @return {boolean} is the effect affecting the graph?
   */


  get _isPatch() {
    return false;
  }
  /**
   * Get the input node.
   * @return {AudioNode} - audio node that is the input for this effect
   */


  getInputNode() {
    return this.target.getInputNode();
  }
  /**
   * Initialize the Effect.
   * Effects start out uninitialized. Then initialize when they are first set
   * with some value.
   * @throws {Error} throws when left unimplemented
   */


  initialize() {
    this.initialized = true;
  }
  /**
   * Set the effect value.
   * @param {number} value - the new value to set the effect to
   */


  _set(value) {
    this.value = value;
    this.ratio = this.getRatio(this.value);
    this.updatePlayers(this.audioPlayer.getSoundPlayers());
  }
  /**
   * Update the effect for changes in the audioPlayer.
   */


  update() {
    this.updatePlayers(this.audioPlayer.getSoundPlayers());
  }
  /**
   * Compute the playback ratio for an effect value.
   * The playback ratio is scaled so that a change of 10 in the effect value
   * gives a change of 1 semitone in the ratio.
   * @param {number} val - an effect value
   * @returns {number} a playback ratio
   */


  getRatio(val) {
    const interval = val / 10; // Convert the musical interval in semitones to a frequency ratio

    return Math.pow(2, interval / 12);
  }
  /**
   * Update a sound player's playback rate using the current ratio for the
   * effect
   * @param {object} player - a SoundPlayer object
   */


  updatePlayer(player) {
    player.setPlaybackRate(this.ratio);
  }
  /**
   * Update a sound player's playback rate using the current ratio for the
   * effect
   * @param {object} players - a dictionary of SoundPlayer objects to update,
   *     indexed by md5
   */


  updatePlayers(players) {
    if (!players) return;

    for (const id in players) {
      if (players.hasOwnProperty(id)) {
        this.updatePlayer(players[id]);
      }
    }
  }

}

module.exports = PitchEffect;

/***/ }),

/***/ "./node_modules/scratch-audio/src/effects/VolumeEffect.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-audio/src/effects/VolumeEffect.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Effect = __webpack_require__(/*! ./Effect */ "./node_modules/scratch-audio/src/effects/Effect.js");
/**
 * Affect the volume of an effect chain.
 */


class VolumeEffect extends Effect {
  /**
   * Default value to set the Effect to when constructed and when clear'ed.
   * @const {number}
   */
  get DEFAULT_VALUE() {
    return 100;
  }
  /**
   * Return the name of the effect.
   * @type {string}
   */


  get name() {
    return 'volume';
  }
  /**
   * Initialize the Effect.
   * Effects start out uninitialized. Then initialize when they are first set
   * with some value.
   * @throws {Error} throws when left unimplemented
   */


  initialize() {
    this.inputNode = this.audioEngine.audioContext.createGain();
    this.outputNode = this.inputNode;
    this.initialized = true;
  }
  /**
   * Set the effects value.
   * @private
   * @param {number} value - new value to set effect to
   */


  _set(value) {
    this.value = value;
    const {
      gain
    } = this.outputNode;
    const {
      currentTime,
      DECAY_DURATION
    } = this.audioEngine;
    gain.linearRampToValueAtTime(value / 100, currentTime + DECAY_DURATION);
  }
  /**
   * Clean up and disconnect audio nodes.
   */


  dispose() {
    if (!this.initialized) {
      return;
    }

    this.outputNode.disconnect();
    this.inputNode = null;
    this.outputNode = null;
    this.target = null;
    this.initialized = false;
  }

}

module.exports = VolumeEffect;

/***/ }),

/***/ "./node_modules/scratch-audio/src/index.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-audio/src/index.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileOverview Scratch Audio is divided into a single AudioEngine, that
 * handles global functionality, and AudioPlayers, belonging to individual
 * sprites and clones.
 */
const AudioEngine = __webpack_require__(/*! ./AudioEngine */ "./node_modules/scratch-audio/src/AudioEngine.js");

module.exports = AudioEngine;

/***/ }),

/***/ "./node_modules/scratch-audio/src/log.js":
/*!***********************************************!*\
  !*** ./node_modules/scratch-audio/src/log.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const minilog = __webpack_require__(/*! minilog */ "./node_modules/minilog/lib/web/index.js");

minilog.enable();
module.exports = minilog('scratch-audioengine');

/***/ }),

/***/ "./node_modules/scratch-audio/src/uid.js":
/*!***********************************************!*\
  !*** ./node_modules/scratch-audio/src/uid.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview UID generator, from Blockly.
 */

/**
 * Legal characters for the unique ID.
 * Should be all on a US keyboard.  No XML special characters or control codes.
 * Removed $ due to issue 251.
 * @private
 */
const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/**
 * Generate a unique ID, from Blockly.  This should be globally unique.
 * 87 characters ^ 20 length > 128 bits (better than a UUID).
 * @return {string} A globally unique ID string.
 */

const uid = function uid() {
  const length = 20;
  const soupLength = soup_.length;
  const id = [];

  for (let i = 0; i < length; i++) {
    id[i] = soup_.charAt(Math.random() * soupLength);
  }

  return id.join('');
};

module.exports = uid;

/***/ }),

/***/ "./node_modules/scratch-render-fonts/src/index.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-render-fonts/src/index.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Synchronously load TTF fonts.
// First, have Webpack load their data as Base 64 strings.
let FONTS;

const getFonts = function getFonts() {
  if (FONTS) return FONTS;
  /* eslint-disable global-require */

  FONTS = {
    'Sans Serif': __webpack_require__(/*! base64-loader!./NotoSans-Medium.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/NotoSans-Medium.ttf"),
    'Serif': __webpack_require__(/*! base64-loader!./SourceSerifPro-Regular.otf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/SourceSerifPro-Regular.otf"),
    'Handwriting': __webpack_require__(/*! base64-loader!./handlee-regular.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/handlee-regular.ttf"),
    'Marker': __webpack_require__(/*! base64-loader!./Knewave.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Knewave.ttf"),
    'Curly': __webpack_require__(/*! base64-loader!./Griffy-Regular.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Griffy-Regular.ttf"),
    'Pixel': __webpack_require__(/*! base64-loader!./Grand9K-Pixel.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Grand9K-Pixel.ttf"),
    'Scratch': __webpack_require__(/*! base64-loader!./Scratch.ttf */ "./node_modules/base64-loader/index.js!./node_modules/scratch-render-fonts/src/Scratch.ttf")
  };
  /* eslint-enable global-require */
  // For each Base 64 string,
  // 1. Replace each with a usable @font-face tag that points to a Data URI.
  // 2. Inject the font into a style on `document.body`, so measurements
  //    can be accurately taken in SvgRenderer._transformMeasurements.

  for (const fontName in FONTS) {
    const fontData = FONTS[fontName];
    FONTS[fontName] = '@font-face {' + "font-family: \"".concat(fontName, "\";src: url(\"data:application/x-font-ttf;charset=utf-8;base64,").concat(fontData, "\");}");
  }

  if (!document.getElementById('scratch-font-styles')) {
    const documentStyleTag = document.createElement('style');
    documentStyleTag.id = 'scratch-font-styles';

    for (const fontName in FONTS) {
      documentStyleTag.textContent += FONTS[fontName];
    }

    document.body.insertBefore(documentStyleTag, document.body.firstChild);
  }

  return FONTS;
};

module.exports = getFonts;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/base64-js/index.js":
/*!*********************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/base64-js/index.js ***!
  \*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function placeHoldersCount (b64) {
  var len = b64.length
  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // the number of equal signs (place holders)
  // if there are two placeholders, than the two characters before it
  // represent one byte
  // if there is only one, then the three characters before it represent 2 bytes
  // this is just a cheap hack to not do indexOf twice
  return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
}

function byteLength (b64) {
  // base64 is 4/3 + up to two characters of the original data
  return (b64.length * 3 / 4) - placeHoldersCount(b64)
}

function toByteArray (b64) {
  var i, l, tmp, placeHolders, arr
  var len = b64.length
  placeHolders = placeHoldersCount(b64)

  arr = new Arr((len * 3 / 4) - placeHolders)

  // if there are placeholders, only get up to the last complete 4 chars
  l = placeHolders > 0 ? len - 4 : len

  var L = 0

  for (i = 0; i < l; i += 4) {
    tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
    arr[L++] = (tmp >> 16) & 0xFF
    arr[L++] = (tmp >> 8) & 0xFF
    arr[L++] = tmp & 0xFF
  }

  if (placeHolders === 2) {
    tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[L++] = tmp & 0xFF
  } else if (placeHolders === 1) {
    tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[L++] = (tmp >> 8) & 0xFF
    arr[L++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var output = ''
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    output += lookup[tmp >> 2]
    output += lookup[(tmp << 4) & 0x3F]
    output += '=='
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
    output += lookup[tmp >> 10]
    output += lookup[(tmp >> 4) & 0x3F]
    output += lookup[(tmp << 2) & 0x3F]
    output += '='
  }

  parts.push(output)

  return parts.join('')
}


/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/bitmap-adapter.js":
/*!*********************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/bitmap-adapter.js ***!
  \*********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const base64js = __webpack_require__(/*! base64-js */ "./node_modules/scratch-render/node_modules/base64-js/index.js");
/**
 * Adapts Scratch 2.0 bitmaps for use in scratch 3.0
 */


class BitmapAdapter {
  /**
   * @param {?function} makeImage HTML image constructor. Tests can provide this.
   * @param {?function} makeCanvas HTML canvas constructor. Tests can provide this.
   */
  constructor(makeImage, makeCanvas) {
    this._makeImage = makeImage ? makeImage : () => new Image();
    this._makeCanvas = makeCanvas ? makeCanvas : () => document.createElement('canvas');
  }
  /**
   * Return a canvas with the resized version of the given image, done using nearest-neighbor interpolation
   * @param {CanvasImageSource} image The image to resize
   * @param {int} newWidth The desired post-resize width of the image
   * @param {int} newHeight The desired post-resize height of the image
   * @returns {HTMLCanvasElement} A canvas with the resized image drawn on it.
   */


  resize(image, newWidth, newHeight) {
    // We want to always resize using nearest-neighbor interpolation. However, canvas implementations are free to
    // use linear interpolation (or other "smooth" interpolation methods) when downscaling:
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1360415
    // It seems we can get around this by resizing in two steps: first width, then height. This will always result
    // in nearest-neighbor interpolation, even when downscaling.
    const stretchWidthCanvas = this._makeCanvas();

    stretchWidthCanvas.width = newWidth;
    stretchWidthCanvas.height = image.height;
    let context = stretchWidthCanvas.getContext('2d');
    context.imageSmoothingEnabled = false;
    context.drawImage(image, 0, 0, stretchWidthCanvas.width, stretchWidthCanvas.height);

    const stretchHeightCanvas = this._makeCanvas();

    stretchHeightCanvas.width = newWidth;
    stretchHeightCanvas.height = newHeight;
    context = stretchHeightCanvas.getContext('2d');
    context.imageSmoothingEnabled = false;
    context.drawImage(stretchWidthCanvas, 0, 0, stretchHeightCanvas.width, stretchHeightCanvas.height);
    return stretchHeightCanvas;
  }
  /**
   * Scratch 2.0 had resolution 1 and 2 bitmaps. All bitmaps in Scratch 3.0 are equivalent
   * to resolution 2 bitmaps. Therefore, converting a resolution 1 bitmap means doubling
   * it in width and height.
   * @param {!string} dataURI Base 64 encoded image data of the bitmap
   * @param {!function} callback Node-style callback that returns updated dataURI if conversion succeeded
   */


  convertResolution1Bitmap(dataURI, callback) {
    const image = this._makeImage();

    image.src = dataURI;

    image.onload = () => {
      callback(null, this.resize(image, image.width * 2, image.height * 2).toDataURL());
    };

    image.onerror = () => {
      callback('Image load failed');
    };
  }
  /**
   * Given width/height of an uploaded item, return width/height the image will be resized
   * to in Scratch 3.0
   * @param {!number} oldWidth original width
   * @param {!number} oldHeight original height
   * @return {object} Array of new width, new height
   */


  getResizedWidthHeight(oldWidth, oldHeight) {
    const STAGE_WIDTH = 480;
    const STAGE_HEIGHT = 360;
    const STAGE_RATIO = STAGE_WIDTH / STAGE_HEIGHT; // If both dimensions are smaller than or equal to corresponding stage dimension,
    // double both dimensions

    if (oldWidth <= STAGE_WIDTH && oldHeight <= STAGE_HEIGHT) {
      return {
        width: oldWidth * 2,
        height: oldHeight * 2
      };
    } // If neither dimension is larger than 2x corresponding stage dimension,
    // this is an in-between image, return it as is


    if (oldWidth <= STAGE_WIDTH * 2 && oldHeight <= STAGE_HEIGHT * 2) {
      return {
        width: oldWidth,
        height: oldHeight
      };
    }

    const imageRatio = oldWidth / oldHeight; // Otherwise, figure out how to resize

    if (imageRatio >= STAGE_RATIO) {
      // Wide Image
      return {
        width: STAGE_WIDTH * 2,
        height: STAGE_WIDTH * 2 / imageRatio
      };
    } // In this case we have either:
    // - A wide image, but not with as big a ratio between width and height,
    // making it so that fitting the width to double stage size would leave
    // the height too big to fit in double the stage height
    // - A square image that's still larger than the double at least
    // one of the stage dimensions, so pick the smaller of the two dimensions (to fit)
    // - A tall image
    // In any of these cases, resize the image to fit the height to double the stage height


    return {
      width: STAGE_HEIGHT * 2 * imageRatio,
      height: STAGE_HEIGHT * 2
    };
  }
  /**
   * Given bitmap data, resize as necessary.
   * @param {ArrayBuffer | string} fileData Base 64 encoded image data of the bitmap
   * @param {string} fileType The MIME type of this file
   * @returns {Promise} Resolves to resized image data Uint8Array
   */


  importBitmap(fileData, fileType) {
    let dataURI = fileData;

    if (fileData instanceof ArrayBuffer) {
      dataURI = this.convertBinaryToDataURI(fileData, fileType);
    }

    return new Promise((resolve, reject) => {
      const image = this._makeImage();

      image.src = dataURI;

      image.onload = () => {
        const newSize = this.getResizedWidthHeight(image.width, image.height);

        if (newSize.width === image.width && newSize.height === image.height) {
          // No change
          resolve(this.convertDataURIToBinary(dataURI));
        } else {
          const resizedDataURI = this.resize(image, newSize.width, newSize.height).toDataURL();
          resolve(this.convertDataURIToBinary(resizedDataURI));
        }
      };

      image.onerror = () => {
        reject('Image load failed');
      };
    });
  } // TODO consolidate with scratch-vm/src/util/base64-util.js
  // From https://gist.github.com/borismus/1032746


  convertDataURIToBinary(dataURI) {
    const BASE64_MARKER = ';base64,';
    const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
    const base64 = dataURI.substring(base64Index);
    const raw = window.atob(base64);
    const rawLength = raw.length;
    const array = new Uint8Array(new ArrayBuffer(rawLength));

    for (let i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    return array;
  }

  convertBinaryToDataURI(arrayBuffer, contentType) {
    return "data:".concat(contentType, ";base64,").concat(base64js.fromByteArray(new Uint8Array(arrayBuffer)));
  }

}

module.exports = BitmapAdapter;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/fixup-svg-string.js":
/*!***********************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/fixup-svg-string.js ***!
  \***********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Fixup svg string prior to parsing.
 * @param {!string} svgString String of the svg to fix.
 * @returns {!string} fixed svg that should be parseable.
 */
module.exports = function (svgString) {
  // Add root svg namespace if it does not exist.
  const svgAttrs = svgString.match(/<svg [^>]*>/);

  if (svgAttrs && svgAttrs[0].indexOf('xmlns=') === -1) {
    svgString = svgString.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
  } // There are some SVGs from Illustrator that use undeclared entities.
  // Just replace those entities with fake namespace references to prevent
  // DOMParser from crashing


  if (svgAttrs && svgAttrs[0].indexOf('&ns_') !== -1 && svgString.indexOf('<!DOCTYPE') === -1) {
    svgString = svgString.replace(svgAttrs[0], svgAttrs[0].replace(/&ns_[^;]+;/g, 'http://ns.adobe.com/Extensibility/1.0/'));
  } // Some SVGs exported from Photoshop have been found to have an invalid mime type
  // Chrome and Safari won't render these SVGs, so we correct it here


  if (svgString.includes('data:img/png')) {
    svgString = svgString.replace( // capture entire image tag with xlink:href=and the quote - dont capture data: bit
    /(<image[^>]+?xlink:href=["'])data:img\/png/g, // use the captured <image ..... xlink:href=" then append the right data uri mime type
    ($0, $1) => "".concat($1, "data:image/png"));
  } // Some SVGs from Inkscape attempt to bind a prefix to a reserved namespace name.
  // This will cause SVG parsing to fail, so replace these with a dummy namespace name.
  // This namespace name is only valid for "xml", and if we bind "xmlns:xml" to the dummy namespace,
  // parsing will fail yet again, so exclude "xmlns:xml" declarations.


  const xmlnsRegex = /(<[^>]+?xmlns:(?!xml=)[^ ]+=)"http:\/\/www.w3.org\/XML\/1998\/namespace"/g;

  if (svgString.match(xmlnsRegex) !== null) {
    svgString = svgString.replace( // capture the entire attribute
    xmlnsRegex, // use the captured attribute name; replace only the URL
    ($0, $1) => "".concat($1, "\"http://dummy.namespace\""));
  } // Strip `svg:` prefix (sometimes added by Inkscape) from all tags. They interfere with DOMPurify (prefixed tag
  // names are not recognized) and the paint editor.
  // This matches opening and closing tags--the capture group captures the slash if it exists, and it is reinserted
  // in the replacement text.


  svgString = svgString.replace(/<(\/?)\s*svg:/g, '<$1'); // The <metadata> element is not needed for rendering and sometimes contains
  // unparseable garbage from Illustrator :( Empty out the contents.
  // Note: [\s\S] matches everything including newlines, which .* does not

  svgString = svgString.replace(/<metadata>[\s\S]*<\/metadata>/, '<metadata></metadata>'); // Empty script tags and javascript executing

  svgString = svgString.replace(/<script[\s\S]*>[\s\S]*<\/script>/, '<script></scri'+'pt>');
  return svgString;
};

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-converter.js":
/*!*********************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-converter.js ***!
  \*********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileOverview Convert 2.0 fonts to 3.0 fonts.
 */

/**
 * Given an SVG, replace Scratch 2.0 fonts with new 3.0 fonts. Add defaults where there are none.
 * @param {SVGElement} svgTag The SVG dom object
 * @return {void}
 */
const convertFonts = function convertFonts(svgTag) {
  // Collect all text elements into a list.
  const textElements = [];

  const collectText = domElement => {
    if (domElement.localName === 'text') {
      textElements.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectText(domElement.childNodes[i]);
    }
  };

  collectText(svgTag); // If there's an old font-family, switch to the new one.

  for (const textElement of textElements) {
    // If there's no font-family provided, provide one.
    if (!textElement.getAttribute('font-family') || textElement.getAttribute('font-family') === 'Helvetica') {
      textElement.setAttribute('font-family', 'Sans Serif');
    } else if (textElement.getAttribute('font-family') === 'Mystery') {
      textElement.setAttribute('font-family', 'Curly');
    } else if (textElement.getAttribute('font-family') === 'Gloria') {
      textElement.setAttribute('font-family', 'Handwriting');
    } else if (textElement.getAttribute('font-family') === 'Donegal') {
      textElement.setAttribute('font-family', 'Serif');
    }
  }
};

module.exports = convertFonts;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-inliner.js":
/*!*******************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-inliner.js ***!
  \*******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileOverview Import bitmap data into Scratch 3.0, resizing image as necessary.
 */
const getFonts = __webpack_require__(/*! scratch-render-fonts */ "./node_modules/scratch-render-fonts/src/index.js");
/**
 * Given SVG data, inline the fonts. This allows them to be rendered correctly when set
 * as the source of an HTMLImageElement. Here is a note from tmickel:
 *   // Inject fonts that are needed.
 *   // It would be nice if there were another way to get the SVG-in-canvas
 *   // to render the correct font family, but I couldn't find any other way.
 *   // Other things I tried:
 *   // Just injecting the font-family into the document: no effect.
 *   // External stylesheet linked to by SVG: no effect.
 *   // Using a <link> or <style>@import</style> to link to font-family
 *   // injected into the document: no effect.
 * @param {string} svgString The string representation of the svg to modify
 * @return {string} The svg with any needed fonts inlined
 */


const inlineSvgFonts = function inlineSvgFonts(svgString) {
  const FONTS = getFonts(); // Make it clear that this function only operates on strings.
  // If we don't explicitly throw this here, the function silently fails.

  if (typeof svgString !== 'string') {
    throw new Error('SVG to be inlined is not a string');
  } // Collect fonts that need injection.


  const fontsNeeded = new Set();
  const fontRegex = /font-family="([^"]*)"/g;
  let matches = fontRegex.exec(svgString);

  while (matches) {
    fontsNeeded.add(matches[1]);
    matches = fontRegex.exec(svgString);
  }

  if (fontsNeeded.size > 0) {
    let str = '<defs><style>';

    for (const font of fontsNeeded) {
      if (Object.prototype.hasOwnProperty.call(FONTS, font)) {
        str += "".concat(FONTS[font]);
      }
    }

    str += '</style></defs>';
    svgString = svgString.replace(/<svg[^>]*>/, "$&".concat(str));
    return svgString;
  }

  return svgString;
};

module.exports = inlineSvgFonts;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/index.js":
/*!************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/index.js ***!
  \************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const SVGRenderer = __webpack_require__(/*! ./svg-renderer */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-renderer.js");

const BitmapAdapter = __webpack_require__(/*! ./bitmap-adapter */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/bitmap-adapter.js");

const inlineSvgFonts = __webpack_require__(/*! ./font-inliner */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-inliner.js");

const loadSvgString = __webpack_require__(/*! ./load-svg-string */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/load-svg-string.js");

const serializeSvgToString = __webpack_require__(/*! ./serialize-svg-to-string */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-element.js");

const convertFonts = __webpack_require__(/*! ./font-converter */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-converter.js"); // /**
//  * Export for NPM & Node.js
//  * @type {RenderWebGL}
//  */


module.exports = {
  BitmapAdapter: BitmapAdapter,
  convertFonts: convertFonts,
  inlineSvgFonts: inlineSvgFonts,
  loadSvgString: loadSvgString,
  serializeSvgToString: serializeSvgToString,
  SvgElement: SvgElement,
  SVGRenderer: SVGRenderer
};

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/load-svg-string.js":
/*!**********************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/load-svg-string.js ***!
  \**********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const DOMPurify = __webpack_require__(/*! dompurify */ "./node_modules/dompurify/dist/purify.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-element.js");

const convertFonts = __webpack_require__(/*! ./font-converter */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-converter.js");

const fixupSvgString = __webpack_require__(/*! ./fixup-svg-string */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/fixup-svg-string.js");

const transformStrokeWidths = __webpack_require__(/*! ./transform-applier */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/transform-applier.js");
/**
 * @param {SVGElement} svgTag the tag to search within
 * @param {string} [tagName] svg tag to search for (or collect all elements if not given)
 * @return {Array} a list of elements with the given tagname
 */


const collectElements = (svgTag, tagName) => {
  const elts = [];

  const collectElementsInner = domElement => {
    if ((domElement.localName === tagName || typeof tagName === 'undefined') && domElement.getAttribute) {
      elts.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectElementsInner(domElement.childNodes[i]);
    }
  };

  collectElementsInner(svgTag);
  return elts;
};
/**
 * Fix SVGs to comply with SVG spec. Scratch 2 defaults to x2 = 0 when x2 is missing, but
 * SVG defaults to x2 = 1 when missing.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformGradients = svgTag => {
  const linearGradientElements = collectElements(svgTag, 'linearGradient'); // For each gradient element, supply x2 if necessary.

  for (const gradientElement of linearGradientElements) {
    if (!gradientElement.getAttribute('x2')) {
      gradientElement.setAttribute('x2', '0');
    }
  }
};
/**
 * Fix SVGs to match appearance in Scratch 2, which used nearest neighbor scaling for bitmaps
 * within SVGs.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformImages = svgTag => {
  const imageElements = collectElements(svgTag, 'image'); // For each image element, set image rendering to pixelated

  const pixelatedImages = 'image-rendering: optimizespeed; image-rendering: pixelated;';

  for (const elt of imageElements) {
    if (elt.getAttribute('style')) {
      elt.setAttribute('style', "".concat(pixelatedImages, " ").concat(elt.getAttribute('style')));
    } else {
      elt.setAttribute('style', pixelatedImages);
    }
  }
};
/**
 * Transforms an SVG's text elements for Scratch 2.0 quirks.
 * These quirks include:
 * 1. `x` and `y` properties are removed/ignored.
 * 2. Alignment is set to `text-before-edge`.
 * 3. Line-breaks are converted to explicit <tspan> elements.
 * 4. Any required fonts are injected.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformText = svgTag => {
  // Collect all text elements into a list.
  const textElements = [];

  const collectText = domElement => {
    if (domElement.localName === 'text') {
      textElements.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectText(domElement.childNodes[i]);
    }
  };

  collectText(svgTag);
  convertFonts(svgTag); // For each text element, apply quirks.

  for (const textElement of textElements) {
    // Remove x and y attributes - they are not used in Scratch.
    textElement.removeAttribute('x');
    textElement.removeAttribute('y'); // Set text-before-edge alignment:
    // Scratch renders all text like this.

    textElement.setAttribute('alignment-baseline', 'text-before-edge');
    textElement.setAttribute('xml:space', 'preserve'); // If there's no font size provided, provide one.

    if (!textElement.getAttribute('font-size')) {
      textElement.setAttribute('font-size', '18');
    }

    let text = textElement.textContent; // Fix line breaks in text, which are not natively supported by SVG.
    // Only fix if text does not have child tspans.
    // @todo this will not work for font sizes with units such as em, percent
    // However, text made in scratch 2 should only ever export size 22 font.

    const fontSize = parseFloat(textElement.getAttribute('font-size'));
    const tx = 2;
    let ty = 0;
    let spacing = 1.2; // Try to match the position and spacing of Scratch 2.0's fonts.
    // Different fonts seem to use different line spacing.
    // Scratch 2 always uses alignment-baseline=text-before-edge
    // However, most SVG readers don't support this attribute
    // or don't support it alongside use of tspan, so the translations
    // here are to make up for that.

    if (textElement.getAttribute('font-family') === 'Handwriting') {
      spacing = 2;
      ty = -11 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Scratch') {
      spacing = 0.89;
      ty = -3 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Curly') {
      spacing = 1.38;
      ty = -6 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Marker') {
      spacing = 1.45;
      ty = -6 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Sans Serif') {
      spacing = 1.13;
      ty = -3 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Serif') {
      spacing = 1.25;
      ty = -4 * fontSize / 22;
    }

    if (textElement.transform.baseVal.numberOfItems === 0) {
      const transform = svgTag.createSVGTransform();
      textElement.transform.baseVal.appendItem(transform);
    } // Right multiply matrix by a translation of (tx, ty)


    const mtx = textElement.transform.baseVal.getItem(0).matrix;
    mtx.e += mtx.a * tx + mtx.c * ty;
    mtx.f += mtx.b * tx + mtx.d * ty;

    if (text && textElement.childElementCount === 0) {
      textElement.textContent = '';
      const lines = text.split('\n');
      text = '';

      for (const line of lines) {
        const tspanNode = SvgElement.create('tspan');
        tspanNode.setAttribute('x', '0');
        tspanNode.setAttribute('style', 'white-space: pre');
        tspanNode.setAttribute('dy', "".concat(spacing, "em"));
        tspanNode.textContent = line ? line : ' ';
        textElement.appendChild(tspanNode);
      }
    }
  }
};
/**
 * Find the largest stroke width in the svg. If a shape has no
 * `stroke` property, it has a stroke-width of 0. If it has a `stroke`,
 * it is by default a stroke-width of 1.
 * This is used to enlarge the computed bounding box, which doesn't take
 * stroke width into account.
 * @param {SVGSVGElement} rootNode The root SVG node to traverse.
 * @return {number} The largest stroke width in the SVG.
 */


const findLargestStrokeWidth = rootNode => {
  let largestStrokeWidth = 0;

  const collectStrokeWidths = domElement => {
    if (domElement.getAttribute) {
      if (domElement.getAttribute('stroke')) {
        largestStrokeWidth = Math.max(largestStrokeWidth, 1);
      }

      if (domElement.getAttribute('stroke-width')) {
        largestStrokeWidth = Math.max(largestStrokeWidth, Number(domElement.getAttribute('stroke-width')) || 0);
      }
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectStrokeWidths(domElement.childNodes[i]);
    }
  };

  collectStrokeWidths(rootNode);
  return largestStrokeWidth;
};
/**
 * Transform the measurements of the SVG.
 * In Scratch 2.0, SVGs are drawn without respect to the width,
 * height, and viewBox attribute on the tag. The exporter
 * does output these properties - but they appear to be incorrect often.
 * To address the incorrect measurements, we append the DOM to the
 * document, and then use SVG's native `getBBox` to find the real
 * drawn dimensions. This ensures things drawn in negative dimensions,
 * outside the given viewBox, etc., are all eventually drawn to the canvas.
 * I tried to do this several other ways: stripping the width/height/viewBox
 * attributes and then drawing (Firefox won't draw anything),
 * or inflating them and then measuring a canvas. But this seems to be
 * a natural and performant way.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformMeasurements = svgTag => {
  // Append the SVG dom to the document.
  // This allows us to use `getBBox` on the page,
  // which returns the full bounding-box of all drawn SVG
  // elements, similar to how Scratch 2.0 did measurement.
  const svgSpot = document.createElement('span'); // Since we're adding user-provided SVG to document.body,
  // sanitizing is required. This should not affect bounding box calculation.
  // outerHTML is attribute of Element (and not HTMLElement), so use it instead of
  // calling serializer or toString()
  // NOTE: svgTag remains untouched!

  const rawValue = svgTag.outerHTML;
  const sanitizedValue = DOMPurify.sanitize(rawValue, {
    // Use SVG profile (no HTML elements)
    USE_PROFILES: {
      svg: true
    },
    // Remove some tags that Scratch does not use.
    FORBID_TAGS: ['a', 'audio', 'canvas', 'video'],
    // Allow data URI in image tags (e.g. SVGs converted from bitmap)
    ADD_DATA_URI_TAGS: ['image']
  });
  let bbox;

  try {
    // Insert sanitized value.
    svgSpot.innerHTML = sanitizedValue;
    document.body.appendChild(svgSpot); // Take the bounding box. We have to get elements via svgSpot
    // because we added it via innerHTML.

    bbox = svgSpot.children[0].getBBox();
  } finally {
    // Always destroy the element, even if, for example, getBBox throws.
    document.body.removeChild(svgSpot);
  } // Enlarge the bbox from the largest found stroke width
  // This may have false-positives, but at least the bbox will always
  // contain the full graphic including strokes.
  // If the width or height is zero however, don't enlarge since
  // they won't have a stroke width that needs to be enlarged.


  let halfStrokeWidth;

  if (bbox.width === 0 || bbox.height === 0) {
    halfStrokeWidth = 0;
  } else {
    halfStrokeWidth = findLargestStrokeWidth(svgTag) / 2;
  }

  const width = bbox.width + halfStrokeWidth * 2;
  const height = bbox.height + halfStrokeWidth * 2;
  const x = bbox.x - halfStrokeWidth;
  const y = bbox.y - halfStrokeWidth; // Set the correct measurements on the SVG tag

  svgTag.setAttribute('width', width);
  svgTag.setAttribute('height', height);
  svgTag.setAttribute('viewBox', "".concat(x, " ").concat(y, " ").concat(width, " ").concat(height));
};
/**
 * Find all instances of a URL-referenced `stroke` in the svg. In 2.0, all gradient strokes
 * have a round `stroke-linejoin` and `stroke-linecap`... for some reason.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const setGradientStrokeRoundedness = svgTag => {
  const elements = collectElements(svgTag);

  for (const elt of elements) {
    if (!elt.style) continue;
    const stroke = elt.style.stroke || elt.getAttribute('stroke');

    if (stroke && stroke.match(/^url\(#.*\)$/)) {
      elt.style['stroke-linejoin'] = 'round';
      elt.style['stroke-linecap'] = 'round';
    }
  }
};
/**
 * In-place, convert passed SVG to something consistent that will be rendered the way we want them to be.
 * @param {SVGSvgElement} svgTag root SVG node to operate upon
 * @param {boolean} [fromVersion2] True if we should perform conversion from version 2 to version 3 svg.
 */


const normalizeSvg = (svgTag, fromVersion2) => {
  if (fromVersion2) {
    // Fix gradients. Scratch 2 exports no x2 when x2 = 0, but
    // SVG default is that x2 is 1. This must be done before
    // transformStrokeWidths since transformStrokeWidths affects
    // gradients.
    transformGradients(svgTag);
  }

  transformStrokeWidths(svgTag, window);
  transformImages(svgTag);

  if (fromVersion2) {
    // Transform all text elements.
    transformText(svgTag); // Transform measurements.

    transformMeasurements(svgTag); // Fix stroke roundedness.

    setGradientStrokeRoundedness(svgTag);
  } else if (!svgTag.getAttribute('viewBox')) {
    // Renderer expects a view box.
    transformMeasurements(svgTag);
  } else if (!svgTag.getAttribute('width') || !svgTag.getAttribute('height')) {
    svgTag.setAttribute('width', svgTag.viewBox.baseVal.width);
    svgTag.setAttribute('height', svgTag.viewBox.baseVal.height);
  }
};
/**
 * Load an SVG string and normalize it. All the steps before drawing/measuring.
 * Currently, this will normalize stroke widths (see transform-applier.js) and render all embedded images pixelated.
 * The returned SVG will be guaranteed to always have a `width`, `height` and `viewBox`.
 * In addition, if the `fromVersion2` parameter is `true`, several "quirks-mode" transformations will be applied which
 * mimic Scratch 2.0's SVG rendering.
 * @param {!string} svgString String of SVG data to draw in quirks-mode.
 * @param {boolean} [fromVersion2] True if we should perform conversion from version 2 to version 3 svg.
 * @return {SVGSVGElement} The normalized SVG element.
 */


const loadSvgString = (svgString, fromVersion2) => {
  // Parse string into SVG XML.
  const parser = new DOMParser();
  svgString = fixupSvgString(svgString);
  const svgDom = parser.parseFromString(svgString, 'text/xml');

  if (svgDom.childNodes.length < 1 || svgDom.documentElement.localName !== 'svg') {
    throw new Error('Document does not appear to be SVG.');
  }

  const svgTag = svgDom.documentElement;
  normalizeSvg(svgTag, fromVersion2);
  return svgTag;
};

module.exports = loadSvgString;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const inlineSvgFonts = __webpack_require__(/*! ./font-inliner */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/font-inliner.js");
/**
 * Serialize a given SVG DOM to a string.
 * @param {SVGSVGElement} svgTag The SVG element to serialize.
 * @param {?boolean} shouldInjectFonts True if fonts should be included in the SVG as
 *     base64 data.
 * @returns {string} String representing current SVG data.
 */


const serializeSvgToString = (svgTag, shouldInjectFonts) => {
  const serializer = new XMLSerializer();
  let string = serializer.serializeToString(svgTag);

  if (shouldInjectFonts) {
    string = inlineSvgFonts(string);
  }

  return string;
};

module.exports = serializeSvgToString;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-element.js":
/*!******************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-element.js ***!
  \******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* Adapted from
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name SvgElement
 * @namespace
 * @private
 */
class SvgElement {
  // SVG related namespaces
  static get svg() {
    return 'http://www.w3.org/2000/svg';
  }

  static get xmlns() {
    return 'http://www.w3.org/2000/xmlns';
  }

  static get xlink() {
    return 'http://www.w3.org/1999/xlink';
  } // Mapping of attribute names to required namespaces:


  static attributeNamespace() {
    return {
      'href': SvgElement.xlink,
      'xlink': SvgElement.xmlns,
      // Only the xmlns attribute needs the trailing slash. See #984
      'xmlns': "".concat(SvgElement.xmlns, "/"),
      // IE needs the xmlns namespace when setting 'xmlns:xlink'. See #984
      'xmlns:xlink': "".concat(SvgElement.xmlns, "/")
    };
  }

  static create(tag, attributes, formatter) {
    return SvgElement.set(document.createElementNS(SvgElement.svg, tag), attributes, formatter);
  }

  static get(node, name) {
    const namespace = SvgElement.attributeNamespace[name];
    const value = namespace ? node.getAttributeNS(namespace, name) : node.getAttribute(name);
    return value === 'null' ? null : value;
  }

  static set(node, attributes, formatter) {
    for (const name in attributes) {
      let value = attributes[name];
      const namespace = SvgElement.attributeNamespace[name];

      if (typeof value === 'number' && formatter) {
        value = formatter.number(value);
      }

      if (namespace) {
        node.setAttributeNS(namespace, name, value);
      } else {
        node.setAttribute(name, value);
      }
    }

    return node;
  }

}

module.exports = SvgElement;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-renderer.js":
/*!*******************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-renderer.js ***!
  \*******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const loadSvgString = __webpack_require__(/*! ./load-svg-string */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/load-svg-string.js");

const serializeSvgToString = __webpack_require__(/*! ./serialize-svg-to-string */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js");
/**
 * Main quirks-mode SVG rendering code.
 * @deprecated Call into individual methods exported from this library instead.
 */


class SvgRenderer {
  /**
   * Create a quirks-mode SVG renderer for a particular canvas.
   * @param {HTMLCanvasElement} [canvas] An optional canvas element to draw to. If this is not provided, the renderer
   * will create a new canvas.
   * @constructor
   */
  constructor(canvas) {
    /**
     * The canvas that this SVG renderer will render to.
     * @type {HTMLCanvasElement}
     * @private
     */
    this._canvas = canvas || document.createElement('canvas');
    this._context = this._canvas.getContext('2d');
    /**
     * A measured SVG "viewbox"
     * @typedef {object} SvgRenderer#SvgMeasurements
     * @property {number} x - The left edge of the SVG viewbox.
     * @property {number} y - The top edge of the SVG viewbox.
     * @property {number} width - The width of the SVG viewbox.
     * @property {number} height - The height of the SVG viewbox.
     */

    /**
     * The measurement box of the currently loaded SVG.
     * @type {SvgRenderer#SvgMeasurements}
     * @private
     */

    this._measurements = {
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
    /**
     * The `<img>` element with the contents of the currently loaded SVG.
     * @type {?HTMLImageElement}
     * @private
     */

    this._cachedImage = null;
    /**
     * True if this renderer's current SVG is loaded and can be rendered to the canvas.
     * @type {boolean}
     */

    this.loaded = false;
  }
  /**
   * @returns {!HTMLCanvasElement} this renderer's target canvas.
   */


  get canvas() {
    return this._canvas;
  }
  /**
   * @return {Array<number>} the natural size, in Scratch units, of this SVG.
   */


  get size() {
    return [this._measurements.width, this._measurements.height];
  }
  /**
   * @return {Array<number>} the offset (upper left corner) of the SVG's view box.
   */


  get viewOffset() {
    return [this._measurements.x, this._measurements.y];
  }
  /**
   * Load an SVG string and normalize it. All the steps before drawing/measuring.
   * @param {!string} svgString String of SVG data to draw in quirks-mode.
   * @param {?boolean} fromVersion2 True if we should perform conversion from
   *     version 2 to version 3 svg.
   */


  loadString(svgString, fromVersion2) {
    // New svg string invalidates the cached image
    this._cachedImage = null;
    const svgTag = loadSvgString(svgString, fromVersion2);
    this._svgTag = svgTag;
    this._measurements = {
      width: svgTag.viewBox.baseVal.width,
      height: svgTag.viewBox.baseVal.height,
      x: svgTag.viewBox.baseVal.x,
      y: svgTag.viewBox.baseVal.y
    };
  }
  /**
   * Load an SVG string, normalize it, and prepare it for (synchronous) rendering.
   * @param {!string} svgString String of SVG data to draw in quirks-mode.
   * @param {?boolean} fromVersion2 True if we should perform conversion from version 2 to version 3 svg.
   * @param {Function} [onFinish] - An optional callback to call when the SVG is loaded and can be rendered.
   */


  loadSVG(svgString, fromVersion2, onFinish) {
    this.loadString(svgString, fromVersion2);

    this._createSVGImage(onFinish);
  }
  /**
   * Creates an <img> element for the currently loaded SVG string, then calls the callback once it's loaded.
   * @param {Function} [onFinish] - An optional callback to call when the <img> has loaded.
   */


  _createSVGImage(onFinish) {
    if (this._cachedImage === null) this._cachedImage = new Image();
    const img = this._cachedImage;

    img.onload = () => {
      this.loaded = true;
      if (onFinish) onFinish();
    };

    const svgText = this.toString(true
    /* shouldInjectFonts */
    );
    img.src = "data:image/svg+xml;utf8,".concat(encodeURIComponent(svgText));
    this.loaded = false;
  }
  /**
   * Serialize the active SVG DOM to a string.
   * @param {?boolean} shouldInjectFonts True if fonts should be included in the SVG as
   *     base64 data.
   * @returns {string} String representing current SVG data.
   * @deprecated Use the standalone `serializeSvgToString` export instead.
   */


  toString(shouldInjectFonts) {
    return serializeSvgToString(this._svgTag, shouldInjectFonts);
  }
  /**
   * Synchronously draw the loaded SVG to this renderer's `canvas`.
   * @param {number} [scale] - Optionally, also scale the image by this factor.
   */


  draw(scale) {
    if (!this.loaded) throw new Error('SVG image has not finished loading');

    this._drawFromImage(scale);
  }
  /**
   * Draw to the canvas from a loaded image element.
   * @param {number} [scale] - Optionally, also scale the image by this factor.
   **/


  _drawFromImage(scale) {
    if (this._cachedImage === null) return;
    const ratio = Number.isFinite(scale) ? scale : 1;
    const bbox = this._measurements;
    this._canvas.width = bbox.width * ratio;
    this._canvas.height = bbox.height * ratio; // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored pre-scaling.
    // e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have a width of 1, but
    // the image's width will be rounded down to 0 on some browsers (Firefox) prior to being drawn at that scale.

    if (this._canvas.width <= 0 || this._canvas.height <= 0 || this._cachedImage.naturalWidth <= 0 || this._cachedImage.naturalHeight <= 0) return;

    this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);

    this._context.setTransform(ratio, 0, 0, ratio, 0, 0);

    this._context.drawImage(this._cachedImage, 0, 0);
  }

}

module.exports = SvgRenderer;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/transform-applier.js":
/*!************************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/transform-applier.js ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Matrix = __webpack_require__(/*! transformation-matrix */ "./node_modules/transformation-matrix/build-umd/transformation-matrix.min.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/svg-element.js");

const log = __webpack_require__(/*! ./util/log */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/util/log.js");
/**
 * @fileOverview Apply transforms to match stroke width appearance in 2.0 and 3.0
 */
// Adapted from paper.js's Path.applyTransform


const _parseTransform = function _parseTransform(domElement) {
  let matrix = Matrix.identity();
  const string = domElement.attributes && domElement.attributes.transform && domElement.attributes.transform.value;
  if (!string) return matrix; // https://www.w3.org/TR/SVG/types.html#DataTypeTransformList
  // Parse SVG transform string. First we split at /)\s*/, to separate
  // commands

  const transforms = string.split(/\)\s*/g);

  for (const transform of transforms) {
    if (!transform) break; // Command come before the '(', values after

    const parts = transform.split(/\(\s*/);
    const command = parts[0].trim();
    const v = parts[1].split(/[\s,]+/g); // Convert values to floats

    for (let j = 0; j < v.length; j++) {
      v[j] = parseFloat(v[j]);
    }

    switch (command) {
      case 'matrix':
        matrix = Matrix.compose(matrix, {
          a: v[0],
          b: v[1],
          c: v[2],
          d: v[3],
          e: v[4],
          f: v[5]
        });
        break;

      case 'rotate':
        matrix = Matrix.compose(matrix, Matrix.rotateDEG(v[0], v[1] || 0, v[2] || 0));
        break;

      case 'translate':
        matrix = Matrix.compose(matrix, Matrix.translate(v[0], v[1] || 0));
        break;

      case 'scale':
        matrix = Matrix.compose(matrix, Matrix.scale(v[0], v[1] || v[0]));
        break;

      case 'skewX':
        matrix = Matrix.compose(matrix, Matrix.skewDEG(v[0], 0));
        break;

      case 'skewY':
        matrix = Matrix.compose(matrix, Matrix.skewDEG(0, v[0]));
        break;

      default:
        log.error("Couldn't parse: ".concat(command));
    }
  }

  return matrix;
}; // Adapted from paper.js's Matrix.decompose
// Given a matrix, return the x and y scale factors of the matrix


const _getScaleFactor = function _getScaleFactor(matrix) {
  const a = matrix.a;
  const b = matrix.b;
  const c = matrix.c;
  const d = matrix.d;
  const det = a * d - b * c;

  if (a !== 0 || b !== 0) {
    const r = Math.sqrt(a * a + b * b);
    return {
      x: r,
      y: det / r
    };
  }

  if (c !== 0 || d !== 0) {
    const s = Math.sqrt(c * c + d * d);
    return {
      x: det / s,
      y: s
    };
  } // a = b = c = d = 0


  return {
    x: 0,
    y: 0
  };
}; // Returns null if matrix is not invertible. Otherwise returns given ellipse
// transformed by transform, an object {radiusX, radiusY, rotation}.


const _calculateTransformedEllipse = function _calculateTransformedEllipse(radiusX, radiusY, theta, transform) {
  theta = -theta * Math.PI / 180;
  const a = transform.a;
  const b = -transform.c;
  const c = -transform.b;
  const d = transform.d; // Since other parameters determine the translation of the ellipse in SVG, we do not need to worry
  // about what e and f are.

  const det = a * d - b * c; // Non-invertible matrix

  if (det === 0) return null; // rotA, rotB, and rotC represent Ax^2 + Bxy + Cy^2 = 1 coefficients for a rotated ellipse formula

  const sinT = Math.sin(theta);
  const cosT = Math.cos(theta);
  const sin2T = Math.sin(2 * theta);
  const rotA = cosT * cosT / radiusX / radiusX + sinT * sinT / radiusY / radiusY;
  const rotB = sin2T / radiusX / radiusX - sin2T / radiusY / radiusY;
  const rotC = sinT * sinT / radiusX / radiusX + cosT * cosT / radiusY / radiusY; // Calculate the ellipse formula of the transformed ellipse
  // A, B, and C represent Ax^2 + Bxy + Cy^2 = 1 / det / det coefficients in a transformed ellipse formula
  // scaled by inverse det squared (to preserve accuracy)

  const A = rotA * d * d - rotB * d * c + rotC * c * c;
  const B = -2 * rotA * b * d + rotB * a * d + rotB * b * c - 2 * rotC * a * c;
  const C = rotA * b * b - rotB * a * b + rotC * a * a; // Derive new radii and theta from the transformed ellipse formula

  const newRadiusXOverDet = Math.sqrt(2) * Math.sqrt((A + C - Math.sqrt(A * A + B * B - 2 * A * C + C * C)) / (-B * B + 4 * A * C));
  const newRadiusYOverDet = 1 / Math.sqrt(A + C - 1 / newRadiusXOverDet / newRadiusXOverDet);
  let temp = (A - 1 / newRadiusXOverDet / newRadiusXOverDet) / (1 / newRadiusYOverDet / newRadiusYOverDet - 1 / newRadiusXOverDet / newRadiusXOverDet);
  if (temp < 0 && Math.abs(temp) < 1e-8) temp = 0; // Fix floating point issue

  temp = Math.sqrt(temp);
  if (Math.abs(1 - temp) < 1e-8) temp = 1; // Fix floating point issue
  // Solve for which of the two possible thetas is correct

  let newTheta = Math.asin(temp);
  temp = B / (1 / newRadiusXOverDet / newRadiusXOverDet - 1 / newRadiusYOverDet / newRadiusYOverDet);
  const newTheta2 = -newTheta;

  if (Math.abs(Math.sin(2 * newTheta2) - temp) < Math.abs(Math.sin(2 * newTheta) - temp)) {
    newTheta = newTheta2;
  }

  return {
    radiusX: newRadiusXOverDet * det,
    radiusY: newRadiusYOverDet * det,
    rotation: -newTheta * 180 / Math.PI
  };
}; // Adapted from paper.js's PathItem.setPathData


const _transformPath = function _transformPath(pathString, transform) {
  if (!transform || Matrix.toString(transform) === Matrix.toString(Matrix.identity())) return pathString; // First split the path data into parts of command-coordinates pairs
  // Commands are any of these characters: mzlhvcsqta

  const parts = pathString && pathString.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig);
  let coords;
  let relative = false;
  let previous;
  let control;
  let current = {
    x: 0,
    y: 0
  };
  let start = {
    x: 0,
    y: 0
  };
  let result = '';

  const getCoord = function getCoord(index, coord) {
    let val = +coords[index];

    if (relative) {
      val += current[coord];
    }

    return val;
  };

  const getPoint = function getPoint(index) {
    return {
      x: getCoord(index, 'x'),
      y: getCoord(index + 1, 'y')
    };
  };

  const roundTo4Places = function roundTo4Places(num) {
    return Number(num.toFixed(4));
  }; // Returns the transformed point as a string


  const getString = function getString(point) {
    const transformed = Matrix.applyToPoint(transform, point);
    return "".concat(roundTo4Places(transformed.x), " ").concat(roundTo4Places(transformed.y), " ");
  };

  for (let i = 0, l = parts && parts.length; i < l; i++) {
    const part = parts[i];
    const command = part[0];
    const lower = command.toLowerCase(); // Match all coordinate values

    coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
    const length = coords && coords.length;
    relative = command === lower; // Fix issues with z in the middle of SVG path data, not followed by
    // a m command, see paper.js#413:

    if (previous === 'z' && !/[mz]/.test(lower)) {
      result += "M ".concat(current.x, " ").concat(current.y, " ");
    }

    switch (lower) {
      case 'm': // Move to

      case 'l':
        // Line to
        {
          let move = lower === 'm';

          for (let j = 0; j < length; j += 2) {
            result += move ? 'M ' : 'L ';
            current = getPoint(j);
            result += getString(current);

            if (move) {
              start = current;
              move = false;
            }
          }

          control = current;
          break;
        }

      case 'h': // Horizontal line

      case 'v':
        // Vertical line
        {
          const coord = lower === 'h' ? 'x' : 'y';
          current = {
            x: current.x,
            y: current.y
          }; // Clone as we're going to modify it.

          for (let j = 0; j < length; j++) {
            current[coord] = getCoord(j, coord);
            result += "L ".concat(getString(current));
          }

          control = current;
          break;
        }

      case 'c':
        // Cubic Bezier curve
        for (let j = 0; j < length; j += 6) {
          const handle1 = getPoint(j);
          control = getPoint(j + 2);
          current = getPoint(j + 4);
          result += "C ".concat(getString(handle1)).concat(getString(control)).concat(getString(current));
        }

        break;

      case 's':
        // Smooth cubic Bezier curve
        for (let j = 0; j < length; j += 4) {
          const handle1 = /[cs]/.test(previous) ? {
            x: current.x * 2 - control.x,
            y: current.y * 2 - control.y
          } : current;
          control = getPoint(j);
          current = getPoint(j + 2);
          result += "C ".concat(getString(handle1)).concat(getString(control)).concat(getString(current));
          previous = lower;
        }

        break;

      case 'q':
        // Quadratic Bezier curve
        for (let j = 0; j < length; j += 4) {
          control = getPoint(j);
          current = getPoint(j + 2);
          result += "Q ".concat(getString(control)).concat(getString(current));
        }

        break;

      case 't':
        // Smooth quadratic Bezier curve
        for (let j = 0; j < length; j += 2) {
          control = /[qt]/.test(previous) ? {
            x: current.x * 2 - control.x,
            y: current.y * 2 - control.y
          } : current;
          current = getPoint(j);
          result += "Q ".concat(getString(control)).concat(getString(current));
          previous = lower;
        }

        break;

      case 'a':
        // Elliptical arc curve
        for (let j = 0; j < length; j += 7) {
          current = getPoint(j + 5);
          const rx = +coords[j];
          const ry = +coords[j + 1];
          const rotation = +coords[j + 2];
          const largeArcFlag = +coords[j + 3];
          let clockwiseFlag = +coords[j + 4];

          const newEllipse = _calculateTransformedEllipse(rx, ry, rotation, transform);

          const matrixScale = _getScaleFactor(transform);

          if (newEllipse) {
            if (matrixScale.x > 0 && matrixScale.y < 0 || matrixScale.x < 0 && matrixScale.y > 0) {
              clockwiseFlag = clockwiseFlag ^ 1;
            }

            result += "A ".concat(roundTo4Places(Math.abs(newEllipse.radiusX)), " ") + "".concat(roundTo4Places(Math.abs(newEllipse.radiusY)), " ") + "".concat(roundTo4Places(newEllipse.rotation), " ").concat(largeArcFlag, " ") + "".concat(clockwiseFlag, " ").concat(getString(current));
          } else {
            result += "L ".concat(getString(current));
          }
        }

        break;

      case 'z':
        // Close path
        result += "Z "; // Correctly handle relative m commands, see paper.js#1101:

        current = start;
        break;
    }

    previous = lower;
  }

  return result;
};

const GRAPHICS_ELEMENTS = ['circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text', 'use'];
const CONTAINER_ELEMENTS = ['a', 'defs', 'g', 'marker', 'glyph', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol'];

const _isContainerElement = function _isContainerElement(element) {
  return element.tagName && CONTAINER_ELEMENTS.includes(element.tagName.toLowerCase());
};

const _isGraphicsElement = function _isGraphicsElement(element) {
  return element.tagName && GRAPHICS_ELEMENTS.includes(element.tagName.toLowerCase());
};

const _isPathWithTransformAndStroke = function _isPathWithTransformAndStroke(element, strokeWidth) {
  if (!element.attributes) return false;
  strokeWidth = element.attributes['stroke-width'] ? Number(element.attributes['stroke-width'].value) : Number(strokeWidth);
  return strokeWidth && element.tagName && element.tagName.toLowerCase() === 'path' && element.attributes.d && element.attributes.d.value;
};

const _quadraticMean = function _quadraticMean(a, b) {
  return Math.sqrt((a * a + b * b) / 2);
};

const _createGradient = function _createGradient(gradientId, svgTag, bbox, matrix) {
  // Adapted from Paper.js's SvgImport.getValue
  const getValue = function getValue(node, name, isString, allowNull, allowPercent, defaultValue) {
    // Interpret value as number. Never return NaN, but 0 instead.
    // If the value is a sequence of numbers, parseFloat will
    // return the first occurring number, which is enough for now.
    let value = SvgElement.get(node, name);
    let res;

    if (value === null) {
      if (defaultValue) {
        res = defaultValue;

        if (/%\s*$/.test(res)) {
          value = defaultValue;
          res = parseFloat(value);
        }
      } else if (allowNull) {
        res = null;
      } else if (isString) {
        res = '';
      } else {
        res = 0;
      }
    } else if (isString) {
      res = value;
    } else {
      res = parseFloat(value);
    } // Support for dimensions in percentage of the root size. If root-size
    // is not set (e.g. during <defs>), just scale the percentage value to
    // 0..1, as required by gradients with gradientUnits="objectBoundingBox"


    if (/%\s*$/.test(value)) {
      const size = allowPercent ? 1 : bbox[/x|^width/.test(name) ? 'width' : 'height'];
      return res / 100 * size;
    }

    return res;
  };

  const getPoint = function getPoint(node, x, y, allowNull, allowPercent, defaultX, defaultY) {
    x = getValue(node, x || 'x', false, allowNull, allowPercent, defaultX);
    y = getValue(node, y || 'y', false, allowNull, allowPercent, defaultY);
    return allowNull && (x === null || y === null) ? null : {
      x,
      y
    };
  };

  let defs = svgTag.getElementsByTagName('defs');

  if (defs.length === 0) {
    defs = SvgElement.create('defs');
    svgTag.appendChild(defs);
  } else {
    defs = defs[0];
  } // Clone the old gradient. We'll make a new one, since the gradient might be reused elsewhere
  // with different transform matrix


  const oldGradient = svgTag.getElementById(gradientId);
  if (!oldGradient) return;
  const radial = oldGradient.tagName.toLowerCase() === 'radialgradient';
  const newGradient = svgTag.getElementById(gradientId).cloneNode(true
  /* deep */
  ); // Give the new gradient a new ID

  let matrixString = Matrix.toString(matrix);
  matrixString = matrixString.substring(8, matrixString.length - 1);
  const newGradientId = "".concat(gradientId, "-").concat(matrixString);
  newGradient.setAttribute('id', newGradientId); // This gradient already exists and was transformed before. Just reuse the already-transformed one.

  if (svgTag.getElementById(newGradientId)) {
    // This is the same code as in the end of the function, but I don't feel like wrapping the next 80 lines
    // in an `if (!svgTag.getElementById(newGradientId))` block
    return "url(#".concat(newGradientId, ")");
  }

  const scaleToBounds = getValue(newGradient, 'gradientUnits', true) !== 'userSpaceOnUse';
  let origin;
  let destination;
  let radius;
  let focal;

  if (radial) {
    origin = getPoint(newGradient, 'cx', 'cy', false, scaleToBounds, '50%', '50%');
    radius = getValue(newGradient, 'r', false, false, scaleToBounds, '50%');
    focal = getPoint(newGradient, 'fx', 'fy', true, scaleToBounds);
  } else {
    origin = getPoint(newGradient, 'x1', 'y1', false, scaleToBounds);
    destination = getPoint(newGradient, 'x2', 'y2', false, scaleToBounds, '1');

    if (origin.x === destination.x && origin.y === destination.y) {
      // If it's degenerate, use the color of the last stop, as described by
      // https://www.w3.org/TR/SVG/pservers.html#LinearGradientNotes
      const stops = newGradient.getElementsByTagName('stop');

      if (!stops.length || !stops[stops.length - 1].attributes || !stops[stops.length - 1].attributes['stop-color']) {
        return null;
      }

      return stops[stops.length - 1].attributes['stop-color'].value;
    }
  } // Transform points
  // Emulate SVG's gradientUnits="objectBoundingBox"


  if (scaleToBounds) {
    const boundsMatrix = Matrix.compose(Matrix.translate(bbox.x, bbox.y), Matrix.scale(bbox.width, bbox.height));
    origin = Matrix.applyToPoint(boundsMatrix, origin);
    if (destination) destination = Matrix.applyToPoint(boundsMatrix, destination);

    if (radius) {
      radius = _quadraticMean(bbox.width, bbox.height) * radius;
    }

    if (focal) focal = Matrix.applyToPoint(boundsMatrix, focal);
  }

  if (radial) {
    origin = Matrix.applyToPoint(matrix, origin);

    const matrixScale = _getScaleFactor(matrix);

    radius = _quadraticMean(matrixScale.x, matrixScale.y) * radius;
    if (focal) focal = Matrix.applyToPoint(matrix, focal);
  } else {
    const dot = (a, b) => a.x * b.x + a.y * b.y;

    const multiply = (coefficient, v) => ({
      x: coefficient * v.x,
      y: coefficient * v.y
    });

    const add = (a, b) => ({
      x: a.x + b.x,
      y: a.y + b.y
    });

    const subtract = (a, b) => ({
      x: a.x - b.x,
      y: a.y - b.y
    }); // The line through origin and gradientPerpendicular is the line at which the gradient starts


    let gradientPerpendicular = Math.abs(origin.x - destination.x) < 1e-8 ? add(origin, {
      x: 1,
      y: (origin.x - destination.x) / (destination.y - origin.y)
    }) : add(origin, {
      x: (destination.y - origin.y) / (origin.x - destination.x),
      y: 1
    }); // Transform points

    gradientPerpendicular = Matrix.applyToPoint(matrix, gradientPerpendicular);
    origin = Matrix.applyToPoint(matrix, origin);
    destination = Matrix.applyToPoint(matrix, destination); // Calculate the direction that the gradient has changed to

    const originToPerpendicular = subtract(gradientPerpendicular, origin);
    const originToDestination = subtract(destination, origin);
    const gradientDirection = Math.abs(originToPerpendicular.x) < 1e-8 ? {
      x: 1,
      y: -originToPerpendicular.x / originToPerpendicular.y
    } : {
      x: -originToPerpendicular.y / originToPerpendicular.x,
      y: 1
    }; // Set the destination so that the gradient moves in the correct direction, by projecting the destination vector
    // onto the gradient direction vector

    const projectionCoeff = dot(originToDestination, gradientDirection) / dot(gradientDirection, gradientDirection);
    const projection = multiply(projectionCoeff, gradientDirection);
    destination = {
      x: origin.x + projection.x,
      y: origin.y + projection.y
    };
  } // Put values back into svg


  if (radial) {
    newGradient.setAttribute('cx', Number(origin.x.toFixed(4)));
    newGradient.setAttribute('cy', Number(origin.y.toFixed(4)));
    newGradient.setAttribute('r', Number(radius.toFixed(4)));

    if (focal) {
      newGradient.setAttribute('fx', Number(focal.x.toFixed(4)));
      newGradient.setAttribute('fy', Number(focal.y.toFixed(4)));
    }
  } else {
    newGradient.setAttribute('x1', Number(origin.x.toFixed(4)));
    newGradient.setAttribute('y1', Number(origin.y.toFixed(4)));
    newGradient.setAttribute('x2', Number(destination.x.toFixed(4)));
    newGradient.setAttribute('y2', Number(destination.y.toFixed(4)));
  }

  newGradient.setAttribute('gradientUnits', 'userSpaceOnUse');
  defs.appendChild(newGradient);
  return "url(#".concat(newGradientId, ")");
}; // Adapted from paper.js's SvgImport.getDefinition


const _parseUrl = (value, windowRef) => {
  // When url() comes from a style property, '#'' seems to be missing on
  // WebKit. We also get variations of quotes or no quotes, single or
  // double, so handle it all with one regular expression:
  const match = value && value.match(/\((?:["'#]*)([^"')]+)/);
  const name = match && match[1];
  const res = name && windowRef ? // This is required by Firefox, which can produce absolute
  // urls for local gradients, see paperjs#1001:
  name.replace("".concat(windowRef.location.href.split('#')[0], "#"), '') : name;
  return res;
};
/**
 * Scratch 2.0 displays stroke widths in a "normalized" way, that is,
 * if a shape with a stroke width has a transform applied, it will be
 * rendered with a stroke that is the same width all the way around,
 * instead of stretched looking.
 *
 * The vector paint editor also prefers to normalize the stroke width,
 * rather than keep track of transforms at the group level, as this
 * simplifies editing (e.g. stroke width 3 always means the same thickness)
 *
 * This function performs that normalization process, pushing transforms
 * on groups down to the leaf level and averaging out the stroke width
 * around the shapes. Note that this doens't just change stroke widths, it
 * changes path data and attributes throughout the SVG.
 *
 * @param {SVGElement} svgTag The SVG dom object
 * @param {Window} windowRef The window to use. Need to pass in for
 *     tests to work, as they get angry at even the mention of window.
 * @param {object} bboxForTesting The bounds to use. Need to pass in for
 *     tests only, because getBBox doesn't work in Node. This should
 *     be the bounds of the svgTag without including stroke width or transforms.
 * @return {void}
 */


const transformStrokeWidths = function transformStrokeWidths(svgTag, windowRef, bboxForTesting) {
  const inherited = Matrix.identity();

  const applyTransforms = (element, matrix, strokeWidth, fill, stroke) => {
    if (_isContainerElement(element)) {
      // Push fills and stroke width down to leaves
      if (element.attributes['stroke-width']) {
        strokeWidth = element.attributes['stroke-width'].value;
      }

      if (element.attributes) {
        if (element.attributes.fill) fill = element.attributes.fill.value;
        if (element.attributes.stroke) stroke = element.attributes.stroke.value;
      } // If any child nodes don't take attributes, leave the attributes
      // at the parent level.


      for (let i = 0; i < element.childNodes.length; i++) {
        applyTransforms(element.childNodes[i], Matrix.compose(matrix, _parseTransform(element)), strokeWidth, fill, stroke);
      }

      element.removeAttribute('transform');
      element.removeAttribute('stroke-width');
      element.removeAttribute('fill');
      element.removeAttribute('stroke');
    } else if (_isPathWithTransformAndStroke(element, strokeWidth)) {
      if (element.attributes['stroke-width']) {
        strokeWidth = element.attributes['stroke-width'].value;
      }

      if (element.attributes.fill) fill = element.attributes.fill.value;
      if (element.attributes.stroke) stroke = element.attributes.stroke.value;
      matrix = Matrix.compose(matrix, _parseTransform(element));

      if (Matrix.toString(matrix) === Matrix.toString(Matrix.identity())) {
        element.removeAttribute('transform');
        element.setAttribute('stroke-width', strokeWidth);
        if (fill) element.setAttribute('fill', fill);
        if (stroke) element.setAttribute('stroke', stroke);
        return;
      } // Transform gradient


      const fillGradientId = _parseUrl(fill, windowRef);

      const strokeGradientId = _parseUrl(stroke, windowRef);

      if (fillGradientId || strokeGradientId) {
        const doc = windowRef.document; // Need path bounds to transform gradient

        const svgSpot = doc.createElement('span');
        let bbox;

        if (bboxForTesting) {
          bbox = bboxForTesting;
        } else {
          try {
            doc.body.appendChild(svgSpot);
            const svg = SvgElement.set(doc.createElementNS(SvgElement.svg, 'svg'));
            const path = SvgElement.set(doc.createElementNS(SvgElement.svg, 'path'));
            path.setAttribute('d', element.attributes.d.value);
            svg.appendChild(path);
            svgSpot.appendChild(svg); // Take the bounding box.

            bbox = svg.getBBox();
          } finally {
            // Always destroy the element, even if, for example, getBBox throws.
            doc.body.removeChild(svgSpot);
          }
        }

        if (fillGradientId) {
          const newFillRef = _createGradient(fillGradientId, svgTag, bbox, matrix);

          if (newFillRef) fill = newFillRef;
        }

        if (strokeGradientId) {
          const newStrokeRef = _createGradient(strokeGradientId, svgTag, bbox, matrix);

          if (newStrokeRef) stroke = newStrokeRef;
        }
      } // Transform path data


      element.setAttribute('d', _transformPath(element.attributes.d.value, matrix));
      element.removeAttribute('transform'); // Transform stroke width

      const matrixScale = _getScaleFactor(matrix);

      element.setAttribute('stroke-width', _quadraticMean(matrixScale.x, matrixScale.y) * strokeWidth);
      if (fill) element.setAttribute('fill', fill);
      if (stroke) element.setAttribute('stroke', stroke);
    } else if (_isGraphicsElement(element)) {
      // Push stroke width, fill, and stroke down to leaves
      if (strokeWidth && !element.attributes['stroke-width']) {
        element.setAttribute('stroke-width', strokeWidth);
      }

      if (fill && !element.attributes.fill) {
        element.setAttribute('fill', fill);
      }

      if (stroke && !element.attributes.stroke) {
        element.setAttribute('stroke', stroke);
      } // Push transform down to leaves


      matrix = Matrix.compose(matrix, _parseTransform(element));

      if (Matrix.toString(matrix) === Matrix.toString(Matrix.identity())) {
        element.removeAttribute('transform');
      } else {
        element.setAttribute('transform', Matrix.toString(matrix));
      }
    }
  };

  applyTransforms(svgTag, inherited, 1
  /* default SVG stroke width */
  );
};

module.exports = transformStrokeWidths;

/***/ }),

/***/ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/util/log.js":
/*!***************************************************************************************!*\
  !*** ./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/util/log.js ***!
  \***************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const minilog = __webpack_require__(/*! minilog */ "./node_modules/minilog/lib/web/index.js");

minilog.enable();
module.exports = minilog('scratch-svg-render');

/***/ }),

/***/ "./node_modules/scratch-render/src/BitmapSkin.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-render/src/BitmapSkin.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const Skin = __webpack_require__(/*! ./Skin */ "./node_modules/scratch-render/src/Skin.js");

class BitmapSkin extends Skin {
  /**
   * Create a new Bitmap Skin.
   * @extends Skin
   * @param {!int} id - The ID for this Skin.
   * @param {!RenderWebGL} renderer - The renderer which will use this skin.
   */
  constructor(id, renderer) {
    super(id, renderer);
    /** @type {!int} */

    this._costumeResolution = 1;
    /** @type {Array<int>} */

    this._textureSize = [0, 0];
  }
  /**
   * Dispose of this object. Do not use it after calling this method.
   */


  dispose() {
    if (this._texture) {
      this._renderer.gl.deleteTexture(this._texture);

      this._texture = null;
    }

    super.dispose();
  }
  /**
   * @return {Array<number>} the "native" size, in texels, of this skin.
   */


  get size() {
    return [this._textureSize[0] / this._costumeResolution, this._textureSize[1] / this._costumeResolution];
  }
  /**
   * @param {Array<number>} scale - The scaling factors to be used.
   * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale.
   */
  // eslint-disable-next-line no-unused-vars


  getTexture(scale) {
    return this._texture || super.getTexture();
  }
  /**
   * Set the contents of this skin to a snapshot of the provided bitmap data.
   * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
   * @param {int} [costumeResolution=1] - The resolution to use for this bitmap.
   * @param {Array<number>} [rotationCenter] - Optional rotation center for the bitmap. If not supplied, it will be
   * calculated from the bounding box
   * @fires Skin.event:WasAltered
   */


  setBitmap(bitmapData, costumeResolution, rotationCenter) {
    if (!bitmapData.width || !bitmapData.height) {
      super.setEmptyImageData();
      return;
    }

    const gl = this._renderer.gl; // TW: We want to use <canvas> as-is because reading ImageData wastes memory.
    // However, vanilla LLK/scratch-vm will reuse any canvas that we get here for other costumes,
    // which will cause bugs when Silhouette lazily reads the canvas data.
    // TurboWarp/scratch-vm does not reuse canvases and will set canvas.reusable = false.

    let textureData = bitmapData;

    if (bitmapData instanceof HTMLCanvasElement && bitmapData.reusable !== false) {
      const context = bitmapData.getContext('2d');
      textureData = context.getImageData(0, 0, bitmapData.width, bitmapData.height);
    }

    if (this._texture === null) {
      const textureOptions = {
        auto: false,
        wrap: gl.CLAMP_TO_EDGE
      };
      this._texture = twgl.createTexture(gl, textureOptions);
    }

    this._setTexture(textureData); // Do these last in case any of the above throws an exception


    this._costumeResolution = costumeResolution || 2;
    this._textureSize = BitmapSkin._getBitmapSize(bitmapData);
    if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter();
    this._rotationCenter[0] = rotationCenter[0];
    this._rotationCenter[1] = rotationCenter[1];
    this.emitWasAltered();
  }
  /**
   * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - bitmap data to inspect.
   * @returns {Array<int>} the width and height of the bitmap data, in pixels.
   * @private
   */


  static _getBitmapSize(bitmapData) {
    if (bitmapData instanceof HTMLImageElement) {
      return [bitmapData.naturalWidth || bitmapData.width, bitmapData.naturalHeight || bitmapData.height];
    }

    if (bitmapData instanceof HTMLVideoElement) {
      return [bitmapData.videoWidth || bitmapData.width, bitmapData.videoHeight || bitmapData.height];
    } // ImageData or HTMLCanvasElement


    return [bitmapData.width, bitmapData.height];
  }

}

module.exports = BitmapSkin;

/***/ }),

/***/ "./node_modules/scratch-render/src/Drawable.js":
/*!*****************************************************!*\
  !*** ./node_modules/scratch-render/src/Drawable.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const Rectangle = __webpack_require__(/*! ./Rectangle */ "./node_modules/scratch-render/src/Rectangle.js");

const RenderConstants = __webpack_require__(/*! ./RenderConstants */ "./node_modules/scratch-render/src/RenderConstants.js");

const ShaderManager = __webpack_require__(/*! ./ShaderManager */ "./node_modules/scratch-render/src/ShaderManager.js");

const EffectTransform = __webpack_require__(/*! ./EffectTransform */ "./node_modules/scratch-render/src/EffectTransform.js");

const log = __webpack_require__(/*! ./util/log */ "./node_modules/scratch-render/src/util/log.js");
/**
 * An internal workspace for calculating texture locations from world vectors
 * this is REUSED for memory conservation reasons
 * @type {twgl.v3}
 */


const __isTouchingPosition = twgl.v3.create();

const FLOATING_POINT_ERROR_ALLOWANCE = 1e-6;
/**
 * Convert a scratch space location into a texture space float.  Uses the
 * internal __isTouchingPosition as a return value, so this should be copied
 * if you ever need to get two local positions and store both.  Requires that
 * the drawable inverseMatrix is up to date.
 *
 * @param {Drawable} drawable The drawable to get the inverse matrix and uniforms from
 * @param {twgl.v3} vec [x,y] scratch space vector
 * @return {twgl.v3} [x,y] texture space float vector - transformed by effects and matrix
 */

const getLocalPosition = (drawable, vec) => {
  // Transfrom from world coordinates to Drawable coordinates.
  const localPosition = __isTouchingPosition;
  const v0 = vec[0];
  const v1 = vec[1];
  const m = drawable._inverseMatrix; // var v2 = v[2];

  const d = v0 * m[3] + v1 * m[7] + m[15]; // The RenderWebGL quad flips the texture's X axis. So rendered bottom
  // left is 1, 0 and the top right is 0, 1. Flip the X axis so
  // localPosition matches that transformation.

  localPosition[0] = 0.5 - (v0 * m[0] + v1 * m[4] + m[12]) / d;
  localPosition[1] = (v0 * m[1] + v1 * m[5] + m[13]) / d + 0.5; // Fix floating point issues near 0. Filed https://github.com/LLK/scratch-render/issues/688 that
  // they're happening in the first place.
  // TODO: Check if this can be removed after render pull 479 is merged

  if (Math.abs(localPosition[0]) < FLOATING_POINT_ERROR_ALLOWANCE) localPosition[0] = 0;
  if (Math.abs(localPosition[1]) < FLOATING_POINT_ERROR_ALLOWANCE) localPosition[1] = 0; // Apply texture effect transform if the localPosition is within the drawable's space,
  // and any effects are currently active.

  if (drawable.enabledEffects !== 0 && localPosition[0] >= 0 && localPosition[0] < 1 && localPosition[1] >= 0 && localPosition[1] < 1) {
    EffectTransform.transformPoint(drawable, localPosition, localPosition);
  }

  return localPosition;
};

class Drawable {
  /**
   * An object which can be drawn by the renderer.
   * @todo double-buffer all rendering state (position, skin, effects, etc.)
   * @param {!int} id - This Drawable's unique ID.
   * @param {!RenderWebGL} renderer - The renderer that created this Drawable
   * @constructor
   */
  constructor(id, renderer) {
    /** @type {!int} */
    this._id = id;
    this._renderer = renderer;
    /**
     * The uniforms to be used by the vertex and pixel shaders.
     * Some of these are used by other parts of the renderer as well.
     * @type {Object.<string,*>}
     * @private
     */

    this._uniforms = {
      /**
       * The model matrix, to concat with projection at draw time.
       * @type {module:twgl/m4.Mat4}
       */
      u_modelMatrix: twgl.m4.identity(),

      /**
       * The color to use in the silhouette draw mode.
       * @type {Array<number>}
       */
      u_silhouetteColor: Drawable.color4fFromID(this._id)
    }; // Effect values are uniforms too

    const numEffects = ShaderManager.EFFECTS.length;

    for (let index = 0; index < numEffects; ++index) {
      const effectName = ShaderManager.EFFECTS[index];
      const effectInfo = ShaderManager.EFFECT_INFO[effectName];
      const converter = effectInfo.converter;
      this._uniforms[effectInfo.uniformName] = converter(0);
    }

    this._position = twgl.v3.create(0, 0);
    this._scale = twgl.v3.create(100, 100);
    this._direction = 90;
    this._transformDirty = true;
    this._rotationMatrix = twgl.m4.identity();
    this._rotationTransformDirty = true;
    this._rotationAdjusted = twgl.v3.create();
    this._rotationCenterDirty = true;
    this._skinScale = twgl.v3.create(0, 0, 0);
    this._skinScaleDirty = true;
    this._inverseMatrix = twgl.m4.identity();
    this._inverseTransformDirty = true;
    this._visible = true;
    /** A bitmask identifying which effects are currently in use.
     * @readonly
     * @type {int} */

    this.enabledEffects = 0;
    /** @todo move convex hull functionality, maybe bounds functionality overall, to Skin classes */

    this._convexHullPoints = null;
    this._convexHullDirty = true; // The precise bounding box will be from the transformed convex hull points,
    // so initialize the array of transformed hull points in setConvexHullPoints.
    // Initializing it once per convex hull recalculation avoids unnecessary creation of twgl.v3 objects.

    this._transformedHullPoints = null;
    this._transformedHullDirty = true;
    this._skinWasAltered = this._skinWasAltered.bind(this);
    this.isTouching = this._isTouchingNever;
    this._highQuality = false;
  }

  setHighQuality(highQuality) {
    this._highQuality = highQuality;
  }
  /**
   * Dispose of this Drawable. Do not use it after calling this method.
   */


  dispose() {
    // Use the setter: disconnect events
    this.skin = null;
  }
  /**
   * Mark this Drawable's transform as dirty.
   * It will be recalculated next time it's needed.
   */


  setTransformDirty() {
    this._transformDirty = true;
    this._inverseTransformDirty = true;
    this._transformedHullDirty = true;
  }
  /**
   * @returns {number} The ID for this Drawable.
   */


  get id() {
    return this._id;
  }
  /**
   * @returns {Skin} the current skin for this Drawable.
   */


  get skin() {
    return this._skin;
  }
  /**
   * @param {Skin} newSkin - A new Skin for this Drawable.
   */


  set skin(newSkin) {
    if (this._skin !== newSkin) {
      this._skin = newSkin;

      this._skinWasAltered();
    }
  }
  /**
   * @returns {Array<number>} the current scaling percentages applied to this Drawable. [100,100] is normal size.
   */


  get scale() {
    return [this._scale[0], this._scale[1]];
  }
  /**
   * @returns {object.<string, *>} the shader uniforms to be used when rendering this Drawable.
   */


  getUniforms() {
    if (this._transformDirty) {
      this._calculateTransform();
    }

    return this._uniforms;
  }
  /**
   * @returns {boolean} whether this Drawable is visible.
   */


  getVisible() {
    return this._visible;
  }
  /**
   * Update the position if it is different. Marks the transform as dirty.
   * @param {Array.<number>} position A new position.
   */


  updatePosition(position) {
    if (this._position[0] !== position[0] || this._position[1] !== position[1]) {
      if (this._highQuality) {
        this._position[0] = position[0];
        this._position[1] = position[1];
      } else {
        this._position[0] = Math.round(position[0]);
        this._position[1] = Math.round(position[1]);
      }

      this._renderer.dirty = true;
      this.setTransformDirty();
    }
  }
  /**
   * Update the direction if it is different. Marks the transform as dirty.
   * @param {number} direction A new direction.
   */


  updateDirection(direction) {
    if (this._direction !== direction) {
      this._direction = direction;
      this._renderer.dirty = true;
      this._rotationTransformDirty = true;
      this.setTransformDirty();
    }
  }
  /**
   * Update the scale if it is different. Marks the transform as dirty.
   * @param {Array.<number>} scale A new scale.
   */


  updateScale(scale) {
    if (this._scale[0] !== scale[0] || this._scale[1] !== scale[1]) {
      this._scale[0] = scale[0];
      this._scale[1] = scale[1];
      this._renderer.dirty = true;
      this._rotationCenterDirty = true;
      this._skinScaleDirty = true;
      this.setTransformDirty();
    }
  }
  /**
   * Update visibility if it is different. Marks the convex hull as dirty.
   * @param {boolean} visible A new visibility state.
   */


  updateVisible(visible) {
    if (this._visible !== visible) {
      this._visible = visible;
      this._renderer.dirty = true;
      this.setConvexHullDirty();
    }
  }
  /**
   * Update an effect. Marks the convex hull as dirty if the effect changes shape.
   * @param {string} effectName The name of the effect.
   * @param {number} rawValue A new effect value.
   */


  updateEffect(effectName, rawValue) {
    this._renderer.dirty = true;
    const effectInfo = ShaderManager.EFFECT_INFO[effectName];

    if (rawValue) {
      this.enabledEffects |= effectInfo.mask;
    } else {
      this.enabledEffects &= ~effectInfo.mask;
    }

    const converter = effectInfo.converter;
    this._uniforms[effectInfo.uniformName] = converter(rawValue);

    if (effectInfo.shapeChanges) {
      this.setConvexHullDirty();
    }
  }
  /**
   * Update the position, direction, scale, or effect properties of this Drawable.
   * @deprecated Use specific update* methods instead.
   * @param {object.<string,*>} properties The new property values to set.
   */


  updateProperties(properties) {
    if ('position' in properties) {
      this.updatePosition(properties.position);
    }

    if ('direction' in properties) {
      this.updateDirection(properties.direction);
    }

    if ('scale' in properties) {
      this.updateScale(properties.scale);
    }

    if ('visible' in properties) {
      this.updateVisible(properties.visible);
    }

    const numEffects = ShaderManager.EFFECTS.length;

    for (let index = 0; index < numEffects; ++index) {
      const effectName = ShaderManager.EFFECTS[index];

      if (effectName in properties) {
        this.updateEffect(effectName, properties[effectName]);
      }
    }
  }
  /**
   * Calculate the transform to use when rendering this Drawable.
   * @private
   */


  _calculateTransform() {
    if (this._rotationTransformDirty) {
      const rotation = (270 - this._direction) * Math.PI / 180; // Calling rotationZ sets the destination matrix to a rotation
      // around the Z axis setting matrix components 0, 1, 4 and 5 with
      // cosine and sine values of the rotation.
      // twgl.m4.rotationZ(rotation, this._rotationMatrix);
      // twgl assumes the last value set to the matrix was anything.
      // Drawable knows, it was another rotationZ matrix, so we can skip
      // assigning the values that will never change.

      const c = Math.cos(rotation);
      const s = Math.sin(rotation);
      this._rotationMatrix[0] = c;
      this._rotationMatrix[1] = s; // this._rotationMatrix[2] = 0;
      // this._rotationMatrix[3] = 0;

      this._rotationMatrix[4] = -s;
      this._rotationMatrix[5] = c; // this._rotationMatrix[6] = 0;
      // this._rotationMatrix[7] = 0;
      // this._rotationMatrix[8] = 0;
      // this._rotationMatrix[9] = 0;
      // this._rotationMatrix[10] = 1;
      // this._rotationMatrix[11] = 0;
      // this._rotationMatrix[12] = 0;
      // this._rotationMatrix[13] = 0;
      // this._rotationMatrix[14] = 0;
      // this._rotationMatrix[15] = 1;

      this._rotationTransformDirty = false;
    } // Adjust rotation center relative to the skin.


    if (this._rotationCenterDirty && this.skin !== null) {
      // twgl version of the following in function work.
      // let rotationAdjusted = twgl.v3.subtract(
      //     this.skin.rotationCenter,
      //     twgl.v3.divScalar(this.skin.size, 2, this._rotationAdjusted),
      //     this._rotationAdjusted
      // );
      // rotationAdjusted = twgl.v3.multiply(
      //     rotationAdjusted, this._scale, rotationAdjusted
      // );
      // rotationAdjusted = twgl.v3.divScalar(
      //     rotationAdjusted, 100, rotationAdjusted
      // );
      // rotationAdjusted[1] *= -1; // Y flipped to Scratch coordinate.
      // rotationAdjusted[2] = 0; // Z coordinate is 0.
      // Locally assign rotationCenter and skinSize to keep from having
      // the Skin getter properties called twice while locally assigning
      // their components for readability.
      const rotationCenter = this.skin.rotationCenter;
      const skinSize = this.skin.size;
      const center0 = rotationCenter[0];
      const center1 = rotationCenter[1];
      const skinSize0 = skinSize[0];
      const skinSize1 = skinSize[1];
      const scale0 = this._scale[0];
      const scale1 = this._scale[1];
      const rotationAdjusted = this._rotationAdjusted;
      rotationAdjusted[0] = (center0 - skinSize0 / 2) * scale0 / 100;
      rotationAdjusted[1] = (center1 - skinSize1 / 2) * scale1 / 100 * -1; // rotationAdjusted[2] = 0;

      this._rotationCenterDirty = false;
    }

    if (this._skinScaleDirty && this.skin !== null) {
      // twgl version of the following in function work.
      // const scaledSize = twgl.v3.divScalar(
      //     twgl.v3.multiply(this.skin.size, this._scale),
      //     100
      // );
      // // was NaN because the vectors have only 2 components.
      // scaledSize[2] = 0;
      // Locally assign skinSize to keep from having the Skin getter
      // properties called twice.
      const skinSize = this.skin.size;
      const scaledSize = this._skinScale;
      scaledSize[0] = skinSize[0] * this._scale[0] / 100;
      scaledSize[1] = skinSize[1] * this._scale[1] / 100; // scaledSize[2] = 0;

      this._skinScaleDirty = false;
    }

    const modelMatrix = this._uniforms.u_modelMatrix; // twgl version of the following in function work.
    // twgl.m4.identity(modelMatrix);
    // twgl.m4.translate(modelMatrix, this._position, modelMatrix);
    // twgl.m4.multiply(modelMatrix, this._rotationMatrix, modelMatrix);
    // twgl.m4.translate(modelMatrix, this._rotationAdjusted, modelMatrix);
    // twgl.m4.scale(modelMatrix, scaledSize, modelMatrix);
    // Drawable configures a 3D matrix for drawing in WebGL, but most values
    // will never be set because the inputs are on the X and Y position axis
    // and the Z rotation axis. Drawable can bring the work inside
    // _calculateTransform and greatly reduce the ammount of math and array
    // assignments needed.

    const scale0 = this._skinScale[0];
    const scale1 = this._skinScale[1];
    const rotation00 = this._rotationMatrix[0];
    const rotation01 = this._rotationMatrix[1];
    const rotation10 = this._rotationMatrix[4];
    const rotation11 = this._rotationMatrix[5];
    const adjusted0 = this._rotationAdjusted[0];
    const adjusted1 = this._rotationAdjusted[1];
    const position0 = this._position[0];
    const position1 = this._position[1]; // Commented assignments show what the values are when the matrix was
    // instantiated. Those values will never change so they do not need to
    // be reassigned.

    modelMatrix[0] = scale0 * rotation00;
    modelMatrix[1] = scale0 * rotation01; // modelMatrix[2] = 0;
    // modelMatrix[3] = 0;

    modelMatrix[4] = scale1 * rotation10;
    modelMatrix[5] = scale1 * rotation11; // modelMatrix[6] = 0;
    // modelMatrix[7] = 0;
    // modelMatrix[8] = 0;
    // modelMatrix[9] = 0;
    // modelMatrix[10] = 1;
    // modelMatrix[11] = 0;

    modelMatrix[12] = rotation00 * adjusted0 + rotation10 * adjusted1 + position0;
    modelMatrix[13] = rotation01 * adjusted0 + rotation11 * adjusted1 + position1; // modelMatrix[14] = 0;
    // modelMatrix[15] = 1;

    this._transformDirty = false;
  }
  /**
   * Whether the Drawable needs convex hull points provided by the renderer.
   * @return {boolean} True when no convex hull known, or it's dirty.
   */


  needsConvexHullPoints() {
    return !this._convexHullPoints || this._convexHullDirty || this._convexHullPoints.length === 0;
  }
  /**
   * Set the convex hull to be dirty.
   * Do this whenever the Drawable's shape has possibly changed.
   */


  setConvexHullDirty() {
    this._convexHullDirty = true;
  }
  /**
   * Set the convex hull points for the Drawable.
   * @param {Array<Array<number>>} points Convex hull points, as [[x, y], ...]
   */


  setConvexHullPoints(points) {
    this._convexHullPoints = points;
    this._convexHullDirty = false; // Re-create the "transformed hull points" array.
    // We only do this when the hull points change to avoid unnecessary allocations and GC.

    this._transformedHullPoints = [];

    for (let i = 0; i < points.length; i++) {
      this._transformedHullPoints.push(twgl.v3.create());
    }

    this._transformedHullDirty = true;
  }
  /**
   * @function
   * @name isTouching
   * Check if the world position touches the skin.
   * The caller is responsible for ensuring this drawable's inverse matrix & its skin's silhouette are up-to-date.
   * @see updateCPURenderAttributes
   * @param {twgl.v3} vec World coordinate vector.
   * @return {boolean} True if the world position touches the skin.
   */
  // `updateCPURenderAttributes` sets this Drawable instance's `isTouching` method
  // to one of the following three functions:
  // If this drawable has no skin, set it to `_isTouchingNever`.
  // Otherwise, if this drawable uses nearest-neighbor scaling at its current scale, set it to `_isTouchingNearest`.
  // Otherwise, set it to `_isTouchingLinear`.
  // This allows several checks to be moved from the `isTouching` function to `updateCPURenderAttributes`.
  // eslint-disable-next-line no-unused-vars


  _isTouchingNever(vec) {
    return false;
  }

  _isTouchingNearest(vec) {
    return this.skin.isTouchingNearest(getLocalPosition(this, vec));
  }

  _isTouchingLinear(vec) {
    return this.skin.isTouchingLinear(getLocalPosition(this, vec));
  }
  /**
   * Get the precise bounds for a Drawable.
   * This function applies the transform matrix to the known convex hull,
   * and then finds the minimum box along the axes.
   * Before calling this, ensure the renderer has updated convex hull points.
   * @param {?Rectangle} result optional destination for bounds calculation
   * @return {!Rectangle} Bounds for a tight box around the Drawable.
   */


  getBounds(result) {
    if (this.needsConvexHullPoints()) {
      throw new Error('Needs updated convex hull points before bounds calculation.');
    }

    if (this._transformDirty) {
      this._calculateTransform();
    }

    const transformedHullPoints = this._getTransformedHullPoints(); // Search through transformed points to generate box on axes.


    result = result || new Rectangle();
    result.initFromPointsAABB(transformedHullPoints);
    return result;
  }
  /**
   * Get the precise bounds for the upper 8px slice of the Drawable.
   * Used for calculating where to position a text bubble.
   * Before calling this, ensure the renderer has updated convex hull points.
   * @param {?Rectangle} result optional destination for bounds calculation
   * @return {!Rectangle} Bounds for a tight box around a slice of the Drawable.
   */


  getBoundsForBubble(result) {
    if (this.needsConvexHullPoints()) {
      throw new Error('Needs updated convex hull points before bubble bounds calculation.');
    }

    if (this._transformDirty) {
      this._calculateTransform();
    }

    const slice = 8; // px, how tall the top slice to measure should be.

    const transformedHullPoints = this._getTransformedHullPoints();

    const maxY = Math.max.apply(null, transformedHullPoints.map(p => p[1]));
    const filteredHullPoints = transformedHullPoints.filter(p => p[1] > maxY - slice); // Search through filtered points to generate box on axes.

    result = result || new Rectangle();
    result.initFromPointsAABB(filteredHullPoints);
    return result;
  }
  /**
   * Get the rough axis-aligned bounding box for the Drawable.
   * Calculated by transforming the skin's bounds.
   * Note that this is less precise than the box returned by `getBounds`,
   * which is tightly snapped to account for a Drawable's transparent regions.
   * `getAABB` returns a much less accurate bounding box, but will be much
   * faster to calculate so may be desired for quick checks/optimizations.
   * @param {?Rectangle} result optional destination for bounds calculation
   * @return {!Rectangle} Rough axis-aligned bounding box for Drawable.
   */


  getAABB(result) {
    if (this._transformDirty) {
      this._calculateTransform();
    }

    const tm = this._uniforms.u_modelMatrix;
    result = result || new Rectangle();
    result.initFromModelMatrix(tm);
    return result;
  }
  /**
   * Return the best Drawable bounds possible without performing graphics queries.
   * I.e., returns the tight bounding box when the convex hull points are already
   * known, but otherwise return the rough AABB of the Drawable.
   * @param {?Rectangle} result optional destination for bounds calculation
   * @return {!Rectangle} Bounds for the Drawable.
   */


  getFastBounds(result) {
    if (!this.needsConvexHullPoints()) {
      return this.getBounds(result);
    }

    return this.getAABB(result);
  }
  /**
   * Transform all the convex hull points by the current Drawable's
   * transform. This allows us to skip recalculating the convex hull
   * for many Drawable updates, including translation, rotation, scaling.
   * @return {!Array.<!Array.number>} Array of glPoints which are Array<x, y>
   * @private
   */


  _getTransformedHullPoints() {
    if (!this._transformedHullDirty) {
      return this._transformedHullPoints;
    }

    const projection = twgl.m4.ortho(-1, 1, -1, 1, -1, 1);
    const skinSize = this.skin.size;
    const halfXPixel = 1 / skinSize[0] / 2;
    const halfYPixel = 1 / skinSize[1] / 2;
    const tm = twgl.m4.multiply(this._uniforms.u_modelMatrix, projection);

    for (let i = 0; i < this._convexHullPoints.length; i++) {
      const point = this._convexHullPoints[i];
      const dstPoint = this._transformedHullPoints[i];
      dstPoint[0] = 0.5 + -point[0] / skinSize[0] - halfXPixel;
      dstPoint[1] = point[1] / skinSize[1] - 0.5 + halfYPixel;
      twgl.m4.transformPoint(tm, dstPoint, dstPoint);
    }

    this._transformedHullDirty = false;
    return this._transformedHullPoints;
  }
  /**
   * Update the transform matrix and calculate it's inverse for collision
   * and local texture position purposes.
   */


  updateMatrix() {
    if (this._transformDirty) {
      this._calculateTransform();
    } // Get the inverse of the model matrix or update it.


    if (this._inverseTransformDirty) {
      const inverse = this._inverseMatrix;
      twgl.m4.copy(this._uniforms.u_modelMatrix, inverse); // The normal matrix uses a z scaling of 0 causing model[10] to be
      // 0. Getting a 4x4 inverse is impossible without a scaling in x, y,
      // and z.

      inverse[10] = 1;
      twgl.m4.inverse(inverse, inverse);
      this._inverseTransformDirty = false;
    }
  }
  /**
   * Update everything necessary to render this drawable on the CPU.
   */


  updateCPURenderAttributes() {
    this.updateMatrix(); // CPU rendering always occurs at the "native" size, so no need to scale up this._scale

    if (this.skin) {
      this.skin.updateSilhouette(this._scale);

      if (this.skin.useNearest(this._scale, this)) {
        this.isTouching = this._isTouchingNearest;
      } else {
        this.isTouching = this._isTouchingLinear;
      }
    } else {
      log.warn("Could not find skin for drawable with id: ".concat(this._id));
      this.isTouching = this._isTouchingNever;
    }
  }
  /**
   * Respond to an internal change in the current Skin.
   */


  _skinWasAltered() {
    this._renderer.dirty = true;
    this._rotationCenterDirty = true;
    this._skinScaleDirty = true;
    this.setConvexHullDirty();
    this.setTransformDirty();
  }
  /**
   * Calculate a color to represent the given ID number. At least one component of
   * the resulting color will be non-zero if the ID is not RenderConstants.ID_NONE.
   * @param {int} id The ID to convert.
   * @returns {Array<number>} An array of [r,g,b,a], each component in the range [0,1].
   */


  static color4fFromID(id) {
    id -= RenderConstants.ID_NONE;
    const r = (id >> 0 & 255) / 255.0;
    const g = (id >> 8 & 255) / 255.0;
    const b = (id >> 16 & 255) / 255.0;
    return [r, g, b, 1.0];
  }
  /**
   * Calculate the ID number represented by the given color. If all components of
   * the color are zero, the result will be RenderConstants.ID_NONE; otherwise the result
   * will be a valid ID.
   * @param {int} r The red value of the color, in the range [0,255].
   * @param {int} g The green value of the color, in the range [0,255].
   * @param {int} b The blue value of the color, in the range [0,255].
   * @returns {int} The ID represented by that color.
   */


  static color3bToID(r, g, b) {
    let id;
    id = (r & 255) << 0;
    id |= (g & 255) << 8;
    id |= (b & 255) << 16;
    return id + RenderConstants.ID_NONE;
  }
  /**
   * Sample a color from a drawable's texture.
   * The caller is responsible for ensuring this drawable's inverse matrix & its skin's silhouette are up-to-date.
   * @see updateCPURenderAttributes
   * @param {twgl.v3} vec The scratch space [x,y] vector
   * @param {Drawable} drawable The drawable to sample the texture from
   * @param {Uint8ClampedArray} dst The "color4b" representation of the texture at point.
   * @param {number} [effectMask] A bitmask for which effects to use. Optional.
   * @returns {Uint8ClampedArray} The dst object filled with the color4b
   */


  static sampleColor4b(vec, drawable, dst, effectMask) {
    const localPosition = getLocalPosition(drawable, vec);

    if (localPosition[0] < 0 || localPosition[1] < 0 || localPosition[0] > 1 || localPosition[1] > 1) {
      dst[0] = 0;
      dst[1] = 0;
      dst[2] = 0;
      dst[3] = 0;
      return dst;
    }

    const textColor = // commenting out to only use nearest for now
    // drawable.skin.useNearest(drawable._scale, drawable) ?
    drawable.skin._silhouette.colorAtNearest(localPosition, dst); // : drawable.skin._silhouette.colorAtLinear(localPosition, dst);


    if (drawable.enabledEffects === 0) return textColor;
    return EffectTransform.transformColor(drawable, textColor, effectMask);
  }

}

module.exports = Drawable;

/***/ }),

/***/ "./node_modules/scratch-render/src/EffectTransform.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-render/src/EffectTransform.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * A utility to transform a texture coordinate to another texture coordinate
 * representing how the shaders apply effects.
 */
const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const {
  rgbToHsv,
  hsvToRgb
} = __webpack_require__(/*! ./util/color-conversions */ "./node_modules/scratch-render/src/util/color-conversions.js");

const ShaderManager = __webpack_require__(/*! ./ShaderManager */ "./node_modules/scratch-render/src/ShaderManager.js");
/**
 * A texture coordinate is between 0 and 1. 0.5 is the center position.
 * @const {number}
 */


const CENTER_X = 0.5;
/**
 * A texture coordinate is between 0 and 1. 0.5 is the center position.
 * @const {number}
 */

const CENTER_Y = 0.5;
/**
 * Reused memory location for storing an HSV color value.
 * @type {Array<number>}
 */

const __hsv = [0, 0, 0];

class EffectTransform {
  /**
   * Transform a color in-place given the drawable's effect uniforms.  Will apply
   * Ghost and Color and Brightness effects.
   * @param {Drawable} drawable The drawable to get uniforms from.
   * @param {Uint8ClampedArray} inOutColor The color to transform.
   * @param {number} [effectMask] A bitmask for which effects to use. Optional.
   * @returns {Uint8ClampedArray} dst filled with the transformed color
   */
  static transformColor(drawable, inOutColor, effectMask) {
    // If the color is fully transparent, don't bother attempting any transformations.
    if (inOutColor[3] === 0) {
      return inOutColor;
    }

    let effects = drawable.enabledEffects;
    if (typeof effectMask === 'number') effects &= effectMask;
    const uniforms = drawable.getUniforms();
    const enableColor = (effects & ShaderManager.EFFECT_INFO.color.mask) !== 0;
    const enableBrightness = (effects & ShaderManager.EFFECT_INFO.brightness.mask) !== 0;

    if (enableColor || enableBrightness) {
      // gl_FragColor.rgb /= gl_FragColor.a + epsilon;
      // Here, we're dividing by the (previously pre-multiplied) alpha to ensure HSV is properly calculated
      // for partially transparent pixels.
      // epsilon is present in the shader because dividing by 0 (fully transparent pixels) messes up calculations.
      // We're doing this with a Uint8ClampedArray here, so dividing by 0 just gives 255. We're later multiplying
      // by 0 again, so it won't affect results.
      const alpha = inOutColor[3] / 255;
      inOutColor[0] /= alpha;
      inOutColor[1] /= alpha;
      inOutColor[2] /= alpha;

      if (enableColor) {
        // vec3 hsv = convertRGB2HSV(gl_FragColor.xyz);
        const hsv = rgbToHsv(inOutColor, __hsv); // this code forces grayscale values to be slightly saturated
        // so that some slight change of hue will be visible
        // const float minLightness = 0.11 / 2.0;

        const minV = 0.11 / 2.0; // const float minSaturation = 0.09;

        const minS = 0.09; // if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness);

        if (hsv[2] < minV) {
          hsv[0] = 0;
          hsv[1] = 1;
          hsv[2] = minV; // else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z);
        } else if (hsv[1] < minS) {
          hsv[0] = 0;
          hsv[1] = minS;
        } // hsv.x = mod(hsv.x + u_color, 1.0);
        // if (hsv.x < 0.0) hsv.x += 1.0;


        hsv[0] = uniforms.u_color + hsv[0] + 1; // gl_FragColor.rgb = convertHSV2RGB(hsl);

        hsvToRgb(hsv, inOutColor);
      }

      if (enableBrightness) {
        const brightness = uniforms.u_brightness * 255; // gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1));
        // We don't need to clamp because the Uint8ClampedArray does that for us

        inOutColor[0] += brightness;
        inOutColor[1] += brightness;
        inOutColor[2] += brightness;
      } // gl_FragColor.rgb *= gl_FragColor.a + epsilon;
      // Now we're doing the reverse, premultiplying by the alpha once again.


      inOutColor[0] *= alpha;
      inOutColor[1] *= alpha;
      inOutColor[2] *= alpha;
    }

    if ((effects & ShaderManager.EFFECT_INFO.ghost.mask) !== 0) {
      // gl_FragColor *= u_ghost
      inOutColor[0] *= uniforms.u_ghost;
      inOutColor[1] *= uniforms.u_ghost;
      inOutColor[2] *= uniforms.u_ghost;
      inOutColor[3] *= uniforms.u_ghost;
    }

    return inOutColor;
  }
  /**
   * Transform a texture coordinate to one that would be select after applying shader effects.
   * @param {Drawable} drawable The drawable whose effects to emulate.
   * @param {twgl.v3} vec The texture coordinate to transform.
   * @param {twgl.v3} dst A place to store the output coordinate.
   * @return {twgl.v3} dst - The coordinate after being transform by effects.
   */


  static transformPoint(drawable, vec, dst) {
    twgl.v3.copy(vec, dst);
    const effects = drawable.enabledEffects;
    const uniforms = drawable.getUniforms();

    if ((effects & ShaderManager.EFFECT_INFO.mosaic.mask) !== 0) {
      // texcoord0 = fract(u_mosaic * texcoord0);
      dst[0] = uniforms.u_mosaic * dst[0] % 1;
      dst[1] = uniforms.u_mosaic * dst[1] % 1;
    }

    if ((effects & ShaderManager.EFFECT_INFO.pixelate.mask) !== 0) {
      const skinUniforms = drawable.skin.getUniforms(); // vec2 pixelTexelSize = u_skinSize / u_pixelate;

      const texelX = skinUniforms.u_skinSize[0] / uniforms.u_pixelate;
      const texelY = skinUniforms.u_skinSize[1] / uniforms.u_pixelate; // texcoord0 = (floor(texcoord0 * pixelTexelSize) + kCenter) /
      //   pixelTexelSize;

      dst[0] = (Math.floor(dst[0] * texelX) + CENTER_X) / texelX;
      dst[1] = (Math.floor(dst[1] * texelY) + CENTER_Y) / texelY;
    }

    if ((effects & ShaderManager.EFFECT_INFO.whirl.mask) !== 0) {
      // const float kRadius = 0.5;
      const RADIUS = 0.5; // vec2 offset = texcoord0 - kCenter;

      const offsetX = dst[0] - CENTER_X;
      const offsetY = dst[1] - CENTER_Y; // float offsetMagnitude = length(offset);

      const offsetMagnitude = Math.sqrt(Math.pow(offsetX, 2) + Math.pow(offsetY, 2)); // float whirlFactor = max(1.0 - (offsetMagnitude / kRadius), 0.0);

      const whirlFactor = Math.max(1.0 - offsetMagnitude / RADIUS, 0.0); // float whirlActual = u_whirl * whirlFactor * whirlFactor;

      const whirlActual = uniforms.u_whirl * whirlFactor * whirlFactor; // float sinWhirl = sin(whirlActual);

      const sinWhirl = Math.sin(whirlActual); // float cosWhirl = cos(whirlActual);

      const cosWhirl = Math.cos(whirlActual); // mat2 rotationMatrix = mat2(
      //     cosWhirl, -sinWhirl,
      //     sinWhirl, cosWhirl
      // );

      const rot1 = cosWhirl;
      const rot2 = -sinWhirl;
      const rot3 = sinWhirl;
      const rot4 = cosWhirl; // texcoord0 = rotationMatrix * offset + kCenter;

      dst[0] = rot1 * offsetX + rot3 * offsetY + CENTER_X;
      dst[1] = rot2 * offsetX + rot4 * offsetY + CENTER_Y;
    }

    if ((effects & ShaderManager.EFFECT_INFO.fisheye.mask) !== 0) {
      // vec2 vec = (texcoord0 - kCenter) / kCenter;
      const vX = (dst[0] - CENTER_X) / CENTER_X;
      const vY = (dst[1] - CENTER_Y) / CENTER_Y; // float vecLength = length(vec);

      const vLength = Math.sqrt(vX * vX + vY * vY); // float r = pow(min(vecLength, 1.0), u_fisheye) * max(1.0, vecLength);

      const r = Math.pow(Math.min(vLength, 1), uniforms.u_fisheye) * Math.max(1, vLength); // vec2 unit = vec / vecLength;

      const unitX = vX / vLength;
      const unitY = vY / vLength; // texcoord0 = kCenter + r * unit * kCenter;

      dst[0] = CENTER_X + r * unitX * CENTER_X;
      dst[1] = CENTER_Y + r * unitY * CENTER_Y;
    }

    return dst;
  }

}

module.exports = EffectTransform;

/***/ }),

/***/ "./node_modules/scratch-render/src/PenSkin.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-render/src/PenSkin.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const RenderConstants = __webpack_require__(/*! ./RenderConstants */ "./node_modules/scratch-render/src/RenderConstants.js");

const Skin = __webpack_require__(/*! ./Skin */ "./node_modules/scratch-render/src/Skin.js");

const ShaderManager = __webpack_require__(/*! ./ShaderManager */ "./node_modules/scratch-render/src/ShaderManager.js");
/**
 * Attributes to use when drawing with the pen
 * @typedef {object} PenSkin#PenAttributes
 * @property {number} [diameter] - The size (diameter) of the pen.
 * @property {Array<number>} [color4f] - The pen color as an array of [r,g,b,a], each component in the range [0,1].
 */

/**
 * The pen attributes to use when unspecified.
 * @type {PenSkin#PenAttributes}
 * @memberof PenSkin
 * @private
 * @const
 */


const DefaultPenAttributes = {
  color4f: [0, 0, 1, 1],
  diameter: 1
};
/**
 * Reused memory location for storing a premultiplied pen color.
 * @type {FloatArray}
 */

const __premultipliedColor = [0, 0, 0, 0];
const PEN_BUFFER_SIZE_LARGER = 65520;
const PEN_BUFFER_SIZE_SMALLER = 32760;

class PenSkin extends Skin {
  /**
   * Create a Skin which implements a Scratch pen layer.
   * @param {int} id - The unique ID for this Skin.
   * @param {RenderWebGL} renderer - The renderer which will use this Skin.
   * @extends Skin
   * @listens RenderWebGL#event:NativeSizeChanged
   */
  constructor(id, renderer) {
    super(id, renderer);
    /** @type {Array<number>} */

    this._size = null;
    /** @type {WebGLFramebuffer} */

    this._framebuffer = null;
    /** @type {boolean} */

    this._silhouetteDirty = false;
    /** @type {Uint8Array} */

    this._silhouettePixels = null;
    /** @type {ImageData} */

    this._silhouetteImageData = null;
    /** @type {object} */

    this._lineOnBufferDrawRegionId = {
      enter: () => this._enterDrawLineOnBuffer(),
      exit: () => this._exitDrawLineOnBuffer()
    };
    /** @type {object} */

    this._usePenBufferDrawRegionId = {
      enter: () => this._enterUsePenBuffer(),
      exit: () => this._exitUsePenBuffer()
    }; // tw: renderQuality attribute

    this.renderQuality = 1; // tw: keep track of native size

    this._nativeSize = renderer.getNativeSize(); // tw: create the extra data structures needed to buffer pen

    this._resetAttributeIndexes();

    this.a_lineColor = new Float32Array(PEN_BUFFER_SIZE_LARGER);
    this.a_lineThicknessAndLength = new Float32Array(PEN_BUFFER_SIZE_SMALLER);
    this.a_penPoints = new Float32Array(PEN_BUFFER_SIZE_LARGER);
    this.a_position = new Float32Array(PEN_BUFFER_SIZE_SMALLER);

    for (let i = 0; i < this.a_position.length; i += 12) {
      this.a_position[i + 0] = 1;
      this.a_position[i + 1] = 0;
      this.a_position[i + 2] = 0;
      this.a_position[i + 3] = 0;
      this.a_position[i + 4] = 1;
      this.a_position[i + 5] = 1;
      this.a_position[i + 6] = 1;
      this.a_position[i + 7] = 1;
      this.a_position[i + 8] = 0;
      this.a_position[i + 9] = 0;
      this.a_position[i + 10] = 0;
      this.a_position[i + 11] = 1;
    }
    /** @type {twgl.BufferInfo} */


    this._lineBufferInfo = twgl.createBufferInfoFromArrays(this._renderer.gl, {
      a_position: {
        numComponents: 2,
        data: this.a_position
      },
      a_lineColor: {
        numComponents: 4,
        drawType: this._renderer.gl.STREAM_DRAW,
        data: this.a_lineColor
      },
      a_lineThicknessAndLength: {
        numComponents: 2,
        drawType: this._renderer.gl.STREAM_DRAW,
        data: this.a_lineThicknessAndLength
      },
      a_penPoints: {
        numComponents: 4,
        drawType: this._renderer.gl.STREAM_DRAW,
        data: this.a_penPoints
      }
    });
    const NO_EFFECTS = 0;
    /** @type {twgl.ProgramInfo} */

    this._lineShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.line, NO_EFFECTS); // tw: draw region used to preserve texture when resizing

    this._drawTextureShader = this._renderer._shaderManager.getShader(ShaderManager.DRAW_MODE.default, NO_EFFECTS);
    /** @type {object} */

    this._drawTextureRegionId = {
      enter: () => this._enterDrawTexture(),
      exit: () => this._exitDrawTexture()
    };
    this.onNativeSizeChanged = this.onNativeSizeChanged.bind(this);

    this._renderer.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged);

    this._setCanvasSize(renderer.getNativeSize());
  }
  /**
   * Dispose of this object. Do not use it after calling this method.
   */


  dispose() {
    this._renderer.removeListener(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged);

    this._renderer.gl.deleteTexture(this._texture);

    this._texture = null;
    super.dispose();
  }
  /**
   * @return {Array<number>} the "native" size, in texels, of this skin. [width, height]
   */


  get size() {
    // tw: use native size for Drawable positioning logic
    return this._nativeSize;
  }

  useNearest(scale) {
    // Use nearest-neighbor interpolation when scaling up the pen skin-- this matches Scratch 2.0.
    // When scaling it down, use linear interpolation to avoid giving pen lines a "dashed" appearance.
    return Math.max(scale[0], scale[1]) >= 100;
  }
  /**
   * @param {Array<number>} scale The X and Y scaling factors to be used, as percentages of this skin's "native" size.
   * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given size.
   */
  // eslint-disable-next-line no-unused-vars


  getTexture(scale) {
    return this._texture;
  }
  /**
   * Clear the pen layer.
   */


  clear() {
    this._renderer.enterDrawRegion(this._usePenBufferDrawRegionId);
    /* Reset framebuffer to transparent black */


    const gl = this._renderer.gl;
    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT);
    this._silhouetteDirty = true;
  }
  /**
   * Draw a point on the pen layer.
   * @param {PenAttributes} penAttributes - how the point should be drawn.
   * @param {number} x - the X coordinate of the point to draw.
   * @param {number} y - the Y coordinate of the point to draw.
   */


  drawPoint(penAttributes, x, y) {
    this.drawLine(penAttributes, x, y, x, y);
  }
  /**
   * Draw a line on the pen layer.
   * @param {PenAttributes} penAttributes - how the line should be drawn.
   * @param {number} x0 - the X coordinate of the beginning of the line.
   * @param {number} y0 - the Y coordinate of the beginning of the line.
   * @param {number} x1 - the X coordinate of the end of the line.
   * @param {number} y1 - the Y coordinate of the end of the line.
   */


  drawLine(penAttributes, x0, y0, x1, y1) {
    // For compatibility with Scratch 2.0, offset pen lines of width 1 and 3 so they're pixel-aligned.
    // See https://github.com/LLK/scratch-render/pull/314
    const diameter = penAttributes.diameter || DefaultPenAttributes.diameter;
    const offset = diameter === 1 || diameter === 3 ? 0.5 : 0;

    this._drawLineOnBuffer(penAttributes, x0 + offset, y0 + offset, x1 + offset, y1 + offset);

    this._silhouetteDirty = true;
  }
  /**
   * Prepare to draw lines in the _lineOnBufferDrawRegionId region.
   */


  _enterDrawLineOnBuffer() {
    // tw: reset attributes when starting pen drawing
    this._resetAttributeIndexes();

    const gl = this._renderer.gl;
    twgl.bindFramebufferInfo(gl, this._framebuffer);
    gl.viewport(0, 0, this._size[0], this._size[1]);
    const currentShader = this._lineShader;
    gl.useProgram(currentShader.program);
    twgl.setBuffersAndAttributes(gl, currentShader, this._lineBufferInfo);
    const uniforms = {
      u_skin: this._texture,
      u_stageSize: this._size
    };
    twgl.setUniforms(currentShader, uniforms);
  }
  /**
   * Return to a base state from _lineOnBufferDrawRegionId.
   */


  _exitDrawLineOnBuffer() {
    // tw: flush when exiting pen rendering
    if (this.a_lineColorIndex) {
      this._flushLines();
    }

    const gl = this._renderer.gl;
    twgl.bindFramebufferInfo(gl, null);
  }
  /**
   * Prepare to do things with this PenSkin's framebuffer
   */


  _enterUsePenBuffer() {
    twgl.bindFramebufferInfo(this._renderer.gl, this._framebuffer);
  }
  /**
   * Return to a base state
   */


  _exitUsePenBuffer() {
    twgl.bindFramebufferInfo(this._renderer.gl, null);
  } // tw: draw region used to preserve texture when resizing


  _enterDrawTexture() {
    this._enterUsePenBuffer();

    const gl = this._renderer.gl;
    gl.viewport(0, 0, this._size[0], this._size[1]);
    gl.useProgram(this._drawTextureShader.program);
    twgl.setBuffersAndAttributes(gl, this._drawTextureShader, this._renderer._bufferInfo);
  }

  _exitDrawTexture() {
    this._exitUsePenBuffer();
  }

  _drawPenTexture(texture) {
    this._renderer.enterDrawRegion(this._drawTextureRegionId);

    const gl = this._renderer.gl;
    const width = this._size[0];
    const height = this._size[1];
    const uniforms = {
      u_skin: texture,
      u_projectionMatrix: twgl.m4.ortho(width / 2, width / -2, height / -2, height / 2, -1, 1, twgl.m4.identity()),
      u_modelMatrix: twgl.m4.scaling(twgl.v3.create(width, height, 0), twgl.m4.identity())
    };
    twgl.setTextureParameters(gl, texture, {
      // Always use NEAREST because this most closely matches Scratch behavior
      minMag: gl.NEAREST
    });
    twgl.setUniforms(this._drawTextureShader, uniforms);
    twgl.drawBufferInfo(gl, this._renderer._bufferInfo, gl.TRIANGLES);
  }
  /**
   * Draw a line on the framebuffer.
   * Note that the point coordinates are in the following coordinate space:
   * +y is down, (0, 0) is the center, and the coords range from (-width / 2, -height / 2) to (height / 2, width / 2).
   * @param {PenAttributes} penAttributes - how the line should be drawn.
   * @param {number} x0 - the X coordinate of the beginning of the line.
   * @param {number} y0 - the Y coordinate of the beginning of the line.
   * @param {number} x1 - the X coordinate of the end of the line.
   * @param {number} y1 - the Y coordinate of the end of the line.
   */


  _drawLineOnBuffer(penAttributes, x0, y0, x1, y1) {
    this._renderer.enterDrawRegion(this._lineOnBufferDrawRegionId); // tw: flush if this line would overflow buffers
    // For some reason, looking up the size of a_lineColor with .length is very slow in some browsers.
    // We see measurable performance improvements by comparing to a constant instead.


    if (this.a_lineColorIndex + 24 > PEN_BUFFER_SIZE_LARGER) {
      this._flushLines();
    } // Premultiply pen color by pen transparency


    const penColor = penAttributes.color4f || DefaultPenAttributes.color4f;
    __premultipliedColor[0] = penColor[0] * penColor[3];
    __premultipliedColor[1] = penColor[1] * penColor[3];
    __premultipliedColor[2] = penColor[2] * penColor[3];
    __premultipliedColor[3] = penColor[3]; // tw: apply renderQuality

    x0 *= this.renderQuality;
    y0 *= this.renderQuality;
    x1 *= this.renderQuality;
    y1 *= this.renderQuality; // Fun fact: Doing this calculation in the shader has the potential to overflow the floating-point range.
    // 'mediump' precision is only required to have a range up to 2^14 (16384), so any lines longer than 2^7 (128)
    // can overflow that, because you're squaring the operands, and they could end up as "infinity".
    // Even GLSL's `length` function won't save us here:
    // https://asawicki.info/news_1596_watch_out_for_reduced_precision_normalizelength_in_opengl_es

    const lineDiffX = x1 - x0;
    const lineDiffY = y1 - y0;
    const lineLength = Math.sqrt(lineDiffX * lineDiffX + lineDiffY * lineDiffY); // tw: apply renderQuality

    const lineThickness = (penAttributes.diameter || DefaultPenAttributes.diameter) * this.renderQuality; // tw: write pen draws to buffers where they will be flushed later

    for (let i = 0; i < 6; i++) {
      this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[0];
      this.a_lineColorIndex++;
      this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[1];
      this.a_lineColorIndex++;
      this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[2];
      this.a_lineColorIndex++;
      this.a_lineColor[this.a_lineColorIndex] = __premultipliedColor[3];
      this.a_lineColorIndex++;
      this.a_lineThicknessAndLength[this.a_lineThicknessAndLengthIndex] = lineThickness;
      this.a_lineThicknessAndLengthIndex++;
      this.a_lineThicknessAndLength[this.a_lineThicknessAndLengthIndex] = lineLength;
      this.a_lineThicknessAndLengthIndex++;
      this.a_penPoints[this.a_penPointsIndex] = x0;
      this.a_penPointsIndex++;
      this.a_penPoints[this.a_penPointsIndex] = -y0;
      this.a_penPointsIndex++;
      this.a_penPoints[this.a_penPointsIndex] = lineDiffX;
      this.a_penPointsIndex++;
      this.a_penPoints[this.a_penPointsIndex] = -lineDiffY;
      this.a_penPointsIndex++;
    }
  } // tw: resets indexes in the pen drawing buffers


  _resetAttributeIndexes() {
    this.a_lineColorIndex = 0;
    this.a_lineThicknessAndLengthIndex = 0;
    this.a_penPointsIndex = 0;
  } // tw: flushes buffered pen lines to the GPU


  _flushLines() {
    const gl = this._renderer.gl;
    const currentShader = this._lineShader; // If only a small amount of data needs to be uploaded, only upload part of the data.
    // todo: need to see if this helps and fine tune this number

    if (this.a_lineColorIndex < 1000) {
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_lineColor, new Float32Array(this.a_lineColor.buffer, 0, this.a_lineColorIndex), 0);
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_penPoints, new Float32Array(this.a_penPoints.buffer, 0, this.a_penPointsIndex), 0);
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_lineThicknessAndLength, new Float32Array(this.a_lineThicknessAndLength.buffer, 0, this.a_lineThicknessAndLengthIndex), 0);
    } else {
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_lineColor, this.a_lineColor);
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_penPoints, this.a_penPoints);
      twgl.setAttribInfoBufferFromArray(gl, this._lineBufferInfo.attribs.a_lineThicknessAndLength, this.a_lineThicknessAndLength);
    } // todo: if we skip twgl and do all this buffer stuff ourselves, we can skip some unneeded gl calls


    twgl.setBuffersAndAttributes(gl, currentShader, this._lineBufferInfo);
    twgl.drawBufferInfo(gl, this._lineBufferInfo, gl.TRIANGLES, this.a_lineThicknessAndLengthIndex / 2);

    this._resetAttributeIndexes();

    this._silhouetteDirty = true;
  }
  /**
   * React to a change in the renderer's native size.
   * @param {object} event - The change event.
   */


  onNativeSizeChanged(event) {
    // tw: keep track of native size
    this._nativeSize = event.newSize;

    this._setCanvasSize([event.newSize[0] * this.renderQuality, event.newSize[1] * this.renderQuality]);

    this.emitWasAltered();
  }
  /**
   * Set the size of the pen canvas.
   * @param {Array<int>} canvasSize - the new width and height for the canvas.
   * @private
   */


  _setCanvasSize(canvasSize) {
    const [width, height] = canvasSize; // tw: do not resize if new size === old size

    if (this._size && this._size[0] === width && this._size[1] === height) {
      return;
    }

    this._size = canvasSize; // tw: use native size for Drawable positioning logic

    this._rotationCenter[0] = this._nativeSize[0] / 2;
    this._rotationCenter[1] = this._nativeSize[1] / 2;
    const gl = this._renderer.gl; // tw: store current texture to redraw it later

    const oldTexture = this._texture;
    this._texture = twgl.createTexture(gl, {
      mag: gl.NEAREST,
      min: gl.NEAREST,
      wrap: gl.CLAMP_TO_EDGE,
      width,
      height
    });
    const attachments = [{
      format: gl.RGBA,
      attachment: this._texture
    }];

    if (this._framebuffer) {
      // tw: resize framebuffer info doesn't work here, so always make a new framebuffer
      // twgl.resizeFramebufferInfo(gl, this._framebuffer, attachments, width, height);
      this._framebuffer = twgl.createFramebufferInfo(gl, attachments, width, height);
    } else {
      this._framebuffer = twgl.createFramebufferInfo(gl, attachments, width, height);
    }

    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT); // tw: preserve old texture when resizing

    if (oldTexture) {
      this._drawPenTexture(oldTexture);
    }

    this._silhouettePixels = new Uint8Array(Math.floor(width * height * 4));
    this._silhouetteImageData = new ImageData(width, height);
    this._silhouetteDirty = true;
  } // tw: sets the "quality" of the pen skin


  setRenderQuality(quality) {
    if (this.renderQuality === quality) {
      return;
    }

    this.renderQuality = quality;

    this._setCanvasSize([Math.round(this._nativeSize[0] * quality), Math.round(this._nativeSize[1] * quality)]);
  }
  /**
   * If there have been pen operations that have dirtied the canvas, update
   * now before someone wants to use our silhouette.
   */


  updateSilhouette() {
    if (this._silhouetteDirty) {
      this._renderer.enterDrawRegion(this._usePenBufferDrawRegionId); // Sample the framebuffer's pixels into the silhouette instance


      const gl = this._renderer.gl;
      gl.readPixels(0, 0, this._size[0], this._size[1], gl.RGBA, gl.UNSIGNED_BYTE, this._silhouettePixels);

      this._silhouetteImageData.data.set(this._silhouettePixels);

      this._silhouette.update(this._silhouetteImageData, true
      /* isPremultiplied */
      );

      this._silhouetteDirty = false;
    }
  }

}

module.exports = PenSkin;

/***/ }),

/***/ "./node_modules/scratch-render/src/Rectangle.js":
/*!******************************************************!*\
  !*** ./node_modules/scratch-render/src/Rectangle.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class Rectangle {
  /**
   * A utility for creating and comparing axis-aligned rectangles.
   * Rectangles are always initialized to the "largest possible rectangle";
   * use one of the init* methods below to set up a particular rectangle.
   * @constructor
   */
  constructor() {
    this.left = -Infinity;
    this.right = Infinity;
    this.bottom = -Infinity;
    this.top = Infinity;
  }
  /**
   * Initialize a Rectangle from given Scratch-coordinate bounds.
   * @param {number} left Left bound of the rectangle.
   * @param {number} right Right bound of the rectangle.
   * @param {number} bottom Bottom bound of the rectangle.
   * @param {number} top Top bound of the rectangle.
   */


  initFromBounds(left, right, bottom, top) {
    this.left = left;
    this.right = right;
    this.bottom = bottom;
    this.top = top;
  }
  /**
   * Initialize a Rectangle to the minimum AABB around a set of points.
   * @param {Array<Array<number>>} points Array of [x, y] points.
   */


  initFromPointsAABB(points) {
    this.left = Infinity;
    this.right = -Infinity;
    this.top = -Infinity;
    this.bottom = Infinity;

    for (let i = 0; i < points.length; i++) {
      const x = points[i][0];
      const y = points[i][1];

      if (x < this.left) {
        this.left = x;
      }

      if (x > this.right) {
        this.right = x;
      }

      if (y > this.top) {
        this.top = y;
      }

      if (y < this.bottom) {
        this.bottom = y;
      }
    }
  }
  /**
   * Initialize a Rectangle to a 1 unit square centered at 0 x 0 transformed
   * by a model matrix.
   * @param {Array.<number>} m A 4x4 matrix to transform the rectangle by.
   * @tutorial Rectangle-AABB-Matrix
   */


  initFromModelMatrix(m) {
    // In 2D space, we will soon use the 2x2 "top left" scale and rotation
    // submatrix, while we store and the 1x2 "top right" that position
    // vector.
    const m30 = m[3 * 4 + 0];
    const m31 = m[3 * 4 + 1]; // "Transform" a (0.5, 0.5) vector by the scale and rotation matrix but
    // sum the absolute of each component instead of use the signed values.

    const x = Math.abs(0.5 * m[0 * 4 + 0]) + Math.abs(0.5 * m[1 * 4 + 0]);
    const y = Math.abs(0.5 * m[0 * 4 + 1]) + Math.abs(0.5 * m[1 * 4 + 1]); // And adding them to the position components initializes our Rectangle.

    this.left = -x + m30;
    this.right = x + m30;
    this.top = y + m31;
    this.bottom = -y + m31;
  }
  /**
   * Determine if this Rectangle intersects some other.
   * Note that this is a comparison assuming the Rectangle was
   * initialized with Scratch-space bounds or points.
   * @param {!Rectangle} other Rectangle to check if intersecting.
   * @return {boolean} True if this Rectangle intersects other.
   */


  intersects(other) {
    return this.left <= other.right && other.left <= this.right && this.top >= other.bottom && other.top >= this.bottom;
  }
  /**
   * Determine if this Rectangle fully contains some other.
   * Note that this is a comparison assuming the Rectangle was
   * initialized with Scratch-space bounds or points.
   * @param {!Rectangle} other Rectangle to check if fully contained.
   * @return {boolean} True if this Rectangle fully contains other.
   */


  contains(other) {
    return other.left > this.left && other.right < this.right && other.top < this.top && other.bottom > this.bottom;
  }
  /**
   * Clamp a Rectangle to bounds.
   * @param {number} left Left clamp.
   * @param {number} right Right clamp.
   * @param {number} bottom Bottom clamp.
   * @param {number} top Top clamp.
   */


  clamp(left, right, bottom, top) {
    this.left = Math.max(this.left, left);
    this.right = Math.min(this.right, right);
    this.bottom = Math.max(this.bottom, bottom);
    this.top = Math.min(this.top, top);
    this.left = Math.min(this.left, right);
    this.right = Math.max(this.right, left);
    this.bottom = Math.min(this.bottom, top);
    this.top = Math.max(this.top, bottom);
  }
  /**
   * Push out the Rectangle to integer bounds.
   */


  snapToInt() {
    this.left = Math.floor(this.left);
    this.right = Math.ceil(this.right);
    this.bottom = Math.floor(this.bottom);
    this.top = Math.ceil(this.top);
  }
  /**
   * Compute the intersection of two bounding Rectangles.
   * Could be an impossible box if they don't intersect.
   * @param {Rectangle} a One rectangle
   * @param {Rectangle} b Other rectangle
   * @param {?Rectangle} result A resulting storage rectangle  (safe to pass
   *                            a or b if you want to overwrite one)
   * @returns {Rectangle} resulting rectangle
   */


  static intersect(a, b) {
    let result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Rectangle();
    result.left = Math.max(a.left, b.left);
    result.right = Math.min(a.right, b.right);
    result.top = Math.min(a.top, b.top);
    result.bottom = Math.max(a.bottom, b.bottom);
    return result;
  }
  /**
   * Compute the union of two bounding Rectangles.
   * @param {Rectangle} a One rectangle
   * @param {Rectangle} b Other rectangle
   * @param {?Rectangle} result A resulting storage rectangle  (safe to pass
   *                            a or b if you want to overwrite one)
   * @returns {Rectangle} resulting rectangle
   */


  static union(a, b) {
    let result = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Rectangle();
    result.left = Math.min(a.left, b.left);
    result.right = Math.max(a.right, b.right); // Scratch Space - +y is up

    result.top = Math.max(a.top, b.top);
    result.bottom = Math.min(a.bottom, b.bottom);
    return result;
  }
  /**
   * Width of the Rectangle.
   * @return {number} Width of rectangle.
   */


  get width() {
    return Math.abs(this.left - this.right);
  }
  /**
   * Height of the Rectangle.
   * @return {number} Height of rectangle.
   */


  get height() {
    return Math.abs(this.top - this.bottom);
  }

}

module.exports = Rectangle;

/***/ }),

/***/ "./node_modules/scratch-render/src/RenderConstants.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-render/src/RenderConstants.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/** @module RenderConstants */

/**
 * Various constants meant for use throughout the renderer.
 * @enum
 */
module.exports = {
  /**
   * The ID value to use for "no item" or when an object has been disposed.
   * @const {int}
   */
  ID_NONE: -1,

  /**
   * @enum {string}
   */
  Events: {
    /**
     * Event emitted when the high quality render option changes.
     */
    UseHighQualityRenderChanged: 'UseHighQualityRenderChanged',

    /**
     * Event emitted when the private skin access option changes.
     */
    AllowPrivateSkinAccessChanged: 'AllowPrivateSkinAccessChanged',

    /**
     * NativeSizeChanged event
     *
     * @event RenderWebGL#event:NativeSizeChanged
     * @type {object}
     * @property {Array<int>} newSize - the new size of the renderer
     */
    NativeSizeChanged: 'NativeSizeChanged'
  }
};

/***/ }),

/***/ "./node_modules/scratch-render/src/RenderWebGL.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-render/src/RenderWebGL.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");

const hull = __webpack_require__(/*! hull.js */ "./node_modules/hull.js/src/hull.js");

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const BitmapSkin = __webpack_require__(/*! ./BitmapSkin */ "./node_modules/scratch-render/src/BitmapSkin.js");

const Drawable = __webpack_require__(/*! ./Drawable */ "./node_modules/scratch-render/src/Drawable.js");

const Rectangle = __webpack_require__(/*! ./Rectangle */ "./node_modules/scratch-render/src/Rectangle.js");

const PenSkin = __webpack_require__(/*! ./PenSkin */ "./node_modules/scratch-render/src/PenSkin.js");

const RenderConstants = __webpack_require__(/*! ./RenderConstants */ "./node_modules/scratch-render/src/RenderConstants.js");

const ShaderManager = __webpack_require__(/*! ./ShaderManager */ "./node_modules/scratch-render/src/ShaderManager.js");

const SVGSkin = __webpack_require__(/*! ./SVGSkin */ "./node_modules/scratch-render/src/SVGSkin.js");

const TextBubbleSkin = __webpack_require__(/*! ./TextBubbleSkin */ "./node_modules/scratch-render/src/TextBubbleSkin.js");

const EffectTransform = __webpack_require__(/*! ./EffectTransform */ "./node_modules/scratch-render/src/EffectTransform.js");

const log = __webpack_require__(/*! ./util/log */ "./node_modules/scratch-render/src/util/log.js");

const __isTouchingDrawablesPoint = twgl.v3.create();

const __candidatesBounds = new Rectangle();

const __fenceBounds = new Rectangle();

const __touchingColor = new Uint8ClampedArray(4);

const __blendColor = new Uint8ClampedArray(4); // More pixels than this and we give up to the GPU and take the cost of readPixels
// Width * Height * Number of drawables at location


const __cpuTouchingColorPixelCount = 4e4;
/**
 * @callback RenderWebGL#idFilterFunc
 * @param {int} drawableID The ID to filter.
 * @return {bool} True if the ID passes the filter, otherwise false.
 */

/**
 * Maximum touch size for a picking check.
 * @todo Figure out a reasonable max size. Maybe this should be configurable?
 * @type {Array<int>}
 * @memberof RenderWebGL
 */

const MAX_TOUCH_SIZE = [3, 3];
/**
 * Passed to the uniforms for mask in touching color
 */

const MASK_TOUCHING_COLOR_TOLERANCE = 2;
/**
 * Maximum number of pixels in either dimension of "extracted drawable" data
 * @type {int}
 */

const MAX_EXTRACTED_DRAWABLE_DIMENSION = 2048;
/**
 * Determines if the mask color is "close enough" (only test the 6 top bits for
 * each color).  These bit masks are what scratch 2 used to use, so we do the same.
 * @param {Uint8Array} a A color3b or color4b value.
 * @param {Uint8Array} b A color3b or color4b value.
 * @returns {boolean} If the colors match within the parameters.
 */

const maskMatches = (a, b) => // has some non-alpha component to test against
a[3] > 0 && (a[0] & 0b11111100) === (b[0] & 0b11111100) && (a[1] & 0b11111100) === (b[1] & 0b11111100) && (a[2] & 0b11111100) === (b[2] & 0b11111100);
/**
 * Determines if the given color is "close enough" (only test the 5 top bits for
 * red and green, 4 bits for blue).  These bit masks are what scratch 2 used to use,
 * so we do the same.
 * @param {Uint8Array} a A color3b or color4b value.
 * @param {Uint8Array} b A color3b or color4b value / or a larger array when used with offsets
 * @param {number} offset An offset into the `b` array, which lets you use a larger array to test
 *                  multiple values at the same time.
 * @returns {boolean} If the colors match within the parameters.
 */


const colorMatches = (a, b, offset) => (a[0] & 0b11111000) === (b[offset + 0] & 0b11111000) && (a[1] & 0b11111000) === (b[offset + 1] & 0b11111000) && (a[2] & 0b11110000) === (b[offset + 2] & 0b11110000);
/**
 * Sprite Fencing - The number of pixels a sprite is required to leave remaining
 * onscreen around the edge of the staging area.
 * @type {number}
 */


const FENCE_WIDTH = 15;

class RenderWebGL extends EventEmitter {
  /**
   * Check if this environment appears to support this renderer before attempting to create an instance.
   * Catching an exception from the constructor is also a valid way to test for (lack of) support.
   * @param {canvas} [optCanvas] - An optional canvas to use for the test. Otherwise a temporary canvas will be used.
   * @returns {boolean} - True if this environment appears to support this renderer, false otherwise.
   */
  static isSupported(optCanvas) {
    try {
      optCanvas = optCanvas || document.createElement('canvas');
      const options = {
        alpha: false,
        stencil: true,
        antialias: false
      };
      return !!(optCanvas.getContext('webgl', options) || optCanvas.getContext('experimental-webgl', options) || optCanvas.getContext('webgl2', options));
    } catch (e) {
      return false;
    }
  }
  /**
   * Ask TWGL to create a rendering context with the attributes used by this renderer.
   * @param {canvas} canvas - attach the context to this canvas.
   * @returns {WebGLRenderingContext} - a TWGL rendering context (backed by either WebGL 1.0 or 2.0).
   * @private
   */


  static _getContext(canvas) {
    const contextAttribs = {
      alpha: false,
      stencil: true,
      antialias: false,
      powerPreference: RenderWebGL.powerPreference
    }; // getWebGLContext = try WebGL 1.0 only
    // getContext = try WebGL 2.0 and if that doesn't work, try WebGL 1.0
    // getWebGLContext || getContext = try WebGL 1.0 and if that doesn't work, try WebGL 2.0

    return twgl.getWebGLContext(canvas, contextAttribs) || twgl.getContext(canvas, contextAttribs);
  }
  /**
   * Create a renderer for drawing Scratch sprites to a canvas using WebGL.
   * Coordinates will default to Scratch 2.0 values if unspecified.
   * The stage's "native" size will be calculated from the these coordinates.
   * For example, the defaults result in a native size of 480x360.
   * Queries such as "touching color?" will always execute at the native size.
   * @see RenderWebGL#setStageSize
   * @see RenderWebGL#resize
   * @param {canvas} canvas The canvas to draw onto.
   * @param {int} [xLeft=-240] The x-coordinate of the left edge.
   * @param {int} [xRight=240] The x-coordinate of the right edge.
   * @param {int} [yBottom=-180] The y-coordinate of the bottom edge.
   * @param {int} [yTop=180] The y-coordinate of the top edge.
   * @constructor
   * @listens RenderWebGL#event:NativeSizeChanged
   */


  constructor(canvas, xLeft, xRight, yBottom, yTop) {
    super();
    /** @type {WebGLRenderingContext} */

    const gl = this._gl = RenderWebGL._getContext(canvas);

    if (!gl) {
      throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.');
    }
    /** @type {RenderWebGL.UseGpuModes} */


    this._useGpuMode = RenderWebGL.UseGpuModes.Automatic;
    /** @type {Drawable[]} */

    this._allDrawables = [];
    /** @type {Skin[]} */

    this._allSkins = [];
    /** @type {Array<int>} */

    this._drawList = []; // A list of layer group names in the order they should appear
    // from furthest back to furthest in front.

    /** @type {Array<String>} */

    this._groupOrdering = [];
    /**
     * @typedef LayerGroup
     * @property {int} groupIndex The relative position of this layer group in the group ordering
     * @property {int} drawListOffset The absolute position of this layer group in the draw list
     * This number gets updated as drawables get added to or deleted from the draw list.
     */
    // Map of group name to layer group

    /** @type {Object.<string, LayerGroup>} */

    this._layerGroups = {};
    /** @type {int} */

    this._nextDrawableId = RenderConstants.ID_NONE + 1;
    /** @type {int} */

    this._nextSkinId = RenderConstants.ID_NONE + 1;
    /** @type {module:twgl/m4.Mat4} */

    this._projection = twgl.m4.identity();
    /** @type {ShaderManager} */

    this._shaderManager = new ShaderManager(gl);
    /** @type {any} */

    this._regionId = null;
    /** @type {function} */

    this._exitRegion = null;
    /** @type {object} */

    this._backgroundDrawRegionId = {
      enter: () => this._enterDrawBackground(),
      exit: () => this._exitDrawBackground()
    };
    /** @type {Array.<snapshotCallback>} */

    this._snapshotCallbacks = [];
    /** @type {Array<number>} */
    // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor3b

    this._backgroundColor4f = [0, 0, 0, 1];
    /** @type {Uint8ClampedArray} */
    // Don't set this directly-- use setBackgroundColor so it stays in sync with _backgroundColor4f

    this._backgroundColor3b = new Uint8ClampedArray(3); // tw: track id of pen skin

    this._penSkinId = null;
    this.useHighQualityRender = false;
    this.offscreenTouching = false;
    this.dirty = true;

    this._createGeometry();

    this.on(RenderConstants.Events.NativeSizeChanged, this.onNativeSizeChanged);
    this.setBackgroundColor(1, 1, 1);
    this.setStageSize(xLeft || -240, xRight || 240, yBottom || -180, yTop || 180);
    this.resize(this._nativeSize[0], this._nativeSize[1]);
    gl.disable(gl.DEPTH_TEST);
    /** @todo disable when no partial transparency? */

    gl.enable(gl.BLEND);
    gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    /**
     * Whether projects should be able to access the contents of private skins such as webcams.
     * If set to false, routines such as isTouchingColor will ignore private skins.
     * Private skins will still be rendered on the canvas regardless of this setting.
     * This is set to true by default for compatibility with vanilla Scratch.
     * @type {boolean}
     */

    this.allowPrivateSkinAccess = true;
  } // tw: implement high quality pen option


  setUseHighQualityRender(enabled) {
    this.dirty = true;
    this.useHighQualityRender = enabled;
    this.emit(RenderConstants.Events.UseHighQualityRenderChanged, enabled);

    this._updateRenderQuality();
  }

  _updateRenderQuality() {
    if (this._penSkinId !== null) {
      const skin = this._allSkins[this._penSkinId];

      if (skin) {
        if (this.useHighQualityRender) {
          skin.setRenderQuality(this.canvas.width / this._nativeSize[0]);
        } else {
          skin.setRenderQuality(1);
        }
      }
    }

    for (const drawable of this._allDrawables) {
      if (drawable) {
        drawable.setHighQuality(this.useHighQualityRender);
      }
    }
  }
  /**
   * Configure whether the renderer should let projects access private skins.
   * @param {boolean} allowPrivateSkinAccess Whether projects can access private skins or not.
   */


  setPrivateSkinAccess(allowPrivateSkinAccess) {
    this.allowPrivateSkinAccess = allowPrivateSkinAccess;
    this.emit(RenderConstants.Events.AllowPrivateSkinAccessChanged, allowPrivateSkinAccess);
  }
  /**
   * @returns {WebGLRenderingContext} the WebGL rendering context associated with this renderer.
   */


  get gl() {
    return this._gl;
  }
  /**
   * @returns {HTMLCanvasElement} the canvas of the WebGL rendering context associated with this renderer.
   */


  get canvas() {
    return this._gl && this._gl.canvas;
  }
  /**
   * Set the physical size of the stage in device-independent pixels.
   * This will be multiplied by the device's pixel ratio on high-DPI displays.
   * @param {int} pixelsWide The desired width in device-independent pixels.
   * @param {int} pixelsTall The desired height in device-independent pixels.
   */


  resize(pixelsWide, pixelsTall) {
    const {
      canvas
    } = this._gl;
    const pixelRatio = window.devicePixelRatio || 1;
    const newWidth = pixelsWide * pixelRatio;
    const newHeight = pixelsTall * pixelRatio; // Certain operations, such as moving the color picker, call `resize` once per frame, even though the canvas
    // size doesn't change. To avoid unnecessary canvas updates, check that we *really* need to resize the canvas.

    if (canvas.width !== newWidth || canvas.height !== newHeight) {
      canvas.width = newWidth;
      canvas.height = newHeight; // Resizing the canvas causes it to be cleared, so redraw it.

      this.dirty = true;
      this.draw();

      this._updateRenderQuality();
    }
  }
  /**
   * Set the background color for the stage. The stage will be cleared with this
   * color each frame.
   * @param {number} red The red component for the background.
   * @param {number} green The green component for the background.
   * @param {number} blue The blue component for the background.
   */


  setBackgroundColor(red, green, blue) {
    this.dirty = true;
    this._backgroundColor4f[0] = red;
    this._backgroundColor4f[1] = green;
    this._backgroundColor4f[2] = blue;
    this._backgroundColor3b[0] = red * 255;
    this._backgroundColor3b[1] = green * 255;
    this._backgroundColor3b[2] = blue * 255;
  }
  /**
   * Tell the renderer to draw various debug information to the provided canvas
   * during certain operations.
   * @param {canvas} canvas The canvas to use for debug output.
   */


  setDebugCanvas(canvas) {
    this._debugCanvas = canvas;
  }
  /**
   * Control the use of the GPU or CPU paths in `isTouchingColor`.
   * @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU.
   */


  setUseGpuMode(useGpuMode) {
    this._useGpuMode = useGpuMode;
  }
  /**
   * Set logical size of the stage in Scratch units.
   * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240.
   * @param {int} xRight The right edge's x-coordinate. Scratch 2 uses 240.
   * @param {int} yBottom The bottom edge's y-coordinate. Scratch 2 uses -180.
   * @param {int} yTop The top edge's y-coordinate. Scratch 2 uses 180.
   */


  setStageSize(xLeft, xRight, yBottom, yTop) {
    this._xLeft = xLeft;
    this._xRight = xRight;
    this._yBottom = yBottom;
    this._yTop = yTop; // swap yBottom & yTop to fit Scratch convention of +y=up

    this._projection = twgl.m4.ortho(xLeft, xRight, yBottom, yTop, -1, 1);

    this._setNativeSize(Math.abs(xRight - xLeft), Math.abs(yBottom - yTop));
  }
  /**
   * @return {Array<int>} the "native" size of the stage, which is used for pen, query renders, etc.
   */


  getNativeSize() {
    return [this._nativeSize[0], this._nativeSize[1]];
  }
  /**
   * Set the "native" size of the stage, which is used for pen, query renders, etc.
   * @param {int} width - the new width to set.
   * @param {int} height - the new height to set.
   * @private
   * @fires RenderWebGL#event:NativeSizeChanged
   */


  _setNativeSize(width, height) {
    this._nativeSize = [width, height];
    this.emit(RenderConstants.Events.NativeSizeChanged, {
      newSize: this._nativeSize
    });
  }
  /**
   * Create a new bitmap skin from a snapshot of the provided bitmap data.
   * @param {ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} bitmapData - new contents for this skin.
   * @param {!int} [costumeResolution=1] - The resolution to use for this bitmap.
   * @param {?Array<number>} [rotationCenter] Optional: rotation center of the skin. If not supplied, the center of
   * the skin will be used.
   * @returns {!int} the ID for the new skin.
   */


  createBitmapSkin(bitmapData, costumeResolution, rotationCenter) {
    const skinId = this._nextSkinId++;
    const newSkin = new BitmapSkin(skinId, this);
    newSkin.setBitmap(bitmapData, costumeResolution, rotationCenter);
    this._allSkins[skinId] = newSkin;
    return skinId;
  }
  /**
   * Create a new SVG skin.
   * @param {!string} svgData - new SVG to use.
   * @param {?Array<number>} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the
   * skin will be used
   * @returns {!int} the ID for the new skin.
   */


  createSVGSkin(svgData, rotationCenter) {
    const skinId = this._nextSkinId++;
    const newSkin = new SVGSkin(skinId, this);
    newSkin.setSVG(svgData, rotationCenter);
    this._allSkins[skinId] = newSkin;
    return skinId;
  }
  /**
   * Create a new PenSkin - a skin which implements a Scratch pen layer.
   * @returns {!int} the ID for the new skin.
   */


  createPenSkin() {
    const skinId = this._nextSkinId++;
    const newSkin = new PenSkin(skinId, this);
    this._allSkins[skinId] = newSkin; // tw: track id of pen skin

    this._penSkinId = skinId; // tw: high quality pen may have been enabled before the pen skin was created

    this._updateRenderQuality();

    return skinId;
  }
  /**
   * Create a new SVG skin using the text bubble svg creator. The rotation center
   * is always placed at the top left.
   * @param {!string} type - either "say" or "think".
   * @param {!string} text - the text for the bubble.
   * @param {!boolean} pointsLeft - which side the bubble is pointing.
   * @returns {!int} the ID for the new skin.
   */


  createTextSkin(type, text, pointsLeft) {
    const skinId = this._nextSkinId++;
    const newSkin = new TextBubbleSkin(skinId, this);
    newSkin.setTextBubble(type, text, pointsLeft);
    this._allSkins[skinId] = newSkin;
    return skinId;
  }
  /**
   * Update an existing SVG skin, or create an SVG skin if the previous skin was not SVG.
   * @param {!int} skinId the ID for the skin to change.
   * @param {!string} svgData - new SVG to use.
   * @param {?Array<number>} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the
   * skin will be used
   */


  updateSVGSkin(skinId, svgData, rotationCenter) {
    if (this._allSkins[skinId] instanceof SVGSkin) {
      this._allSkins[skinId].setSVG(svgData, rotationCenter);

      return;
    }

    const newSkin = new SVGSkin(skinId, this);
    newSkin.setSVG(svgData, rotationCenter);

    this._reskin(skinId, newSkin);
  }
  /**
   * Update an existing bitmap skin, or create a bitmap skin if the previous skin was not bitmap.
   * @param {!int} skinId the ID for the skin to change.
   * @param {!ImageData|HTMLImageElement|HTMLCanvasElement|HTMLVideoElement} imgData - new contents for this skin.
   * @param {!number} bitmapResolution - the resolution scale for a bitmap costume.
   * @param {?Array<number>} rotationCenter Optional: rotation center of the skin. If not supplied, the center of the
   * skin will be used
   */


  updateBitmapSkin(skinId, imgData, bitmapResolution, rotationCenter) {
    if (this._allSkins[skinId] instanceof BitmapSkin) {
      this._allSkins[skinId].setBitmap(imgData, bitmapResolution, rotationCenter);

      return;
    }

    const newSkin = new BitmapSkin(skinId, this);
    newSkin.setBitmap(imgData, bitmapResolution, rotationCenter);

    this._reskin(skinId, newSkin);
  }

  _reskin(skinId, newSkin) {
    const oldSkin = this._allSkins[skinId];
    this._allSkins[skinId] = newSkin; // Tell drawables to update

    for (const drawable of this._allDrawables) {
      if (drawable && drawable.skin === oldSkin) {
        drawable.skin = newSkin;
      }
    }

    oldSkin.dispose();
  }
  /**
   * Update a skin using the text bubble svg creator.
   * @param {!int} skinId the ID for the skin to change.
   * @param {!string} type - either "say" or "think".
   * @param {!string} text - the text for the bubble.
   * @param {!boolean} pointsLeft - which side the bubble is pointing.
   */


  updateTextSkin(skinId, type, text, pointsLeft) {
    if (this._allSkins[skinId] instanceof TextBubbleSkin) {
      this._allSkins[skinId].setTextBubble(type, text, pointsLeft);

      return;
    }

    const newSkin = new TextBubbleSkin(skinId, this);
    newSkin.setTextBubble(type, text, pointsLeft);

    this._reskin(skinId, newSkin);
  }
  /**
   * Destroy an existing skin. Do not use the skin or its ID after calling this.
   * @param {!int} skinId - The ID of the skin to destroy.
   */


  destroySkin(skinId) {
    const oldSkin = this._allSkins[skinId];
    oldSkin.dispose();
    delete this._allSkins[skinId];
  }
  /**
   * Create a new Drawable and add it to the scene.
   * @param {string} group Layer group to add the drawable to
   * @returns {int} The ID of the new Drawable.
   */


  createDrawable(group) {
    if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) {
      log.warn('Cannot create a drawable without a known layer group');
      return;
    }

    const drawableID = this._nextDrawableId++;
    const drawable = new Drawable(drawableID, this);
    this._allDrawables[drawableID] = drawable;

    this._addToDrawList(drawableID, group); // tw: implement high quality render


    drawable.setHighQuality(this.useHighQualityRender);
    drawable.skin = null;
    return drawableID;
  }
  /**
   * Mark a skin as containing private information.
   * @param {number} skinID The skin's ID
   */


  markSkinAsPrivate(skinID) {
    const skin = this._allSkins[skinID];

    if (!skin) {
      return;
    }

    skin.private = true;
  }
  /**
   * Set the layer group ordering for the renderer.
   * @param {Array<string>} groupOrdering The ordered array of layer group
   * names
   */


  setLayerGroupOrdering(groupOrdering) {
    this._groupOrdering = groupOrdering;

    for (let i = 0; i < this._groupOrdering.length; i++) {
      this._layerGroups[this._groupOrdering[i]] = {
        groupIndex: i,
        drawListOffset: 0
      };
    }
  }

  _addToDrawList(drawableID, group) {
    const currentLayerGroup = this._layerGroups[group];
    const currentGroupOrderingIndex = currentLayerGroup.groupIndex;

    const drawListOffset = this._endIndexForKnownLayerGroup(currentLayerGroup);

    this._drawList.splice(drawListOffset, 0, drawableID);

    this._updateOffsets('add', currentGroupOrderingIndex);
  }

  _updateOffsets(updateType, currentGroupOrderingIndex) {
    for (let i = currentGroupOrderingIndex + 1; i < this._groupOrdering.length; i++) {
      const laterGroupName = this._groupOrdering[i];

      if (updateType === 'add') {
        this._layerGroups[laterGroupName].drawListOffset++;
      } else if (updateType === 'delete') {
        this._layerGroups[laterGroupName].drawListOffset--;
      }
    }
  }

  get _visibleDrawList() {
    return this._drawList.filter(id => this._allDrawables[id]._visible);
  } // Given a layer group, return the index where it ends (non-inclusive),
  // e.g. the returned index does not have a drawable from this layer group in it)


  _endIndexForKnownLayerGroup(layerGroup) {
    const groupIndex = layerGroup.groupIndex;

    if (groupIndex === this._groupOrdering.length - 1) {
      return this._drawList.length;
    }

    return this._layerGroups[this._groupOrdering[groupIndex + 1]].drawListOffset;
  }
  /**
   * Destroy a Drawable, removing it from the scene.
   * @param {int} drawableID The ID of the Drawable to remove.
   * @param {string} group Group name that the drawable belongs to
   */


  destroyDrawable(drawableID, group) {
    if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) {
      log.warn('Cannot destroy drawable without known layer group.');
      return;
    }

    this.dirty = true;
    const drawable = this._allDrawables[drawableID];
    drawable.dispose();
    delete this._allDrawables[drawableID];
    const currentLayerGroup = this._layerGroups[group];

    const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup);

    let index = currentLayerGroup.drawListOffset;

    while (index < endIndex) {
      if (this._drawList[index] === drawableID) {
        break;
      }

      index++;
    }

    if (index < endIndex) {
      this._drawList.splice(index, 1);

      this._updateOffsets('delete', currentLayerGroup.groupIndex);
    } else {
      log.warn('Could not destroy drawable that could not be found in layer group.');
      return;
    }
  }
  /**
   * Returns the position of the given drawableID in the draw list. This is
   * the absolute position irrespective of layer group.
   * @param {number} drawableID The drawable ID to find.
   * @return {number} The postion of the given drawable ID.
   */


  getDrawableOrder(drawableID) {
    return this._drawList.indexOf(drawableID);
  }
  /**
   * Set a drawable's order in the drawable list (effectively, z/layer).
   * Can be used to move drawables to absolute positions in the list,
   * or relative to their current positions.
   * "go back N layers": setDrawableOrder(id, -N, true, 1); (assuming stage at 0).
   * "go to back": setDrawableOrder(id, 1); (assuming stage at 0).
   * "go to front": setDrawableOrder(id, Infinity);
   * @param {int} drawableID ID of Drawable to reorder.
   * @param {number} order New absolute order or relative order adjusment.
   * @param {string=} group Name of layer group drawable belongs to.
   * Reordering will not take place if drawable cannot be found within the bounds
   * of the layer group.
   * @param {boolean=} optIsRelative If set, `order` refers to a relative change.
   * @param {number=} optMin If set, order constrained to be at least `optMin`.
   * @return {?number} New order if changed, or null.
   */


  setDrawableOrder(drawableID, order, group, optIsRelative, optMin) {
    if (!group || !Object.prototype.hasOwnProperty.call(this._layerGroups, group)) {
      log.warn('Cannot set the order of a drawable without a known layer group.');
      return;
    }

    this.dirty = true;
    const currentLayerGroup = this._layerGroups[group];
    const startIndex = currentLayerGroup.drawListOffset;

    const endIndex = this._endIndexForKnownLayerGroup(currentLayerGroup);

    let oldIndex = startIndex;

    while (oldIndex < endIndex) {
      if (this._drawList[oldIndex] === drawableID) {
        break;
      }

      oldIndex++;
    }

    if (oldIndex < endIndex) {
      // Remove drawable from the list.
      if (order === 0) {
        return oldIndex;
      }

      const _ = this._drawList.splice(oldIndex, 1)[0]; // Determine new index.


      let newIndex = order;

      if (optIsRelative) {
        newIndex += oldIndex;
      }

      const possibleMin = (optMin || 0) + startIndex;
      const min = possibleMin >= startIndex && possibleMin < endIndex ? possibleMin : startIndex;
      newIndex = Math.max(newIndex, min);
      newIndex = Math.min(newIndex, endIndex); // Insert at new index.

      this._drawList.splice(newIndex, 0, drawableID);

      return newIndex;
    }

    return null;
  }

  skinWasAltered(skin) {
    // This is very hot function.
    for (let i = 0; i < this._allDrawables.length; i++) {
      const drawable = this._allDrawables[i];

      if (drawable && drawable._skin === skin) {
        drawable._skinWasAltered();
      }
    }
  }
  /**
   * Draw all current drawables and present the frame on the canvas.
   */


  draw() {
    if (!this.dirty) {
      return;
    }

    this.dirty = false;

    this._doExitDrawRegion();

    const gl = this._gl;
    twgl.bindFramebufferInfo(gl, null);
    gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
    gl.clearColor(...this._backgroundColor4f);
    gl.clear(gl.COLOR_BUFFER_BIT);

    this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, this._projection, {
      framebufferWidth: gl.canvas.width,
      framebufferHeight: gl.canvas.height
    });

    if (this._snapshotCallbacks.length > 0) {
      const snapshot = gl.canvas.toDataURL();

      this._snapshotCallbacks.forEach(cb => cb(snapshot));

      this._snapshotCallbacks = [];
    }
  }
  /**
   * Get the precise bounds for a Drawable.
   * @param {int} drawableID ID of Drawable to get bounds for.
   * @return {object} Bounds for a tight box around the Drawable.
   */


  getBounds(drawableID) {
    const drawable = this._allDrawables[drawableID]; // Tell the Drawable about its updated convex hull, if necessary.

    if (drawable.needsConvexHullPoints()) {
      const points = this._getConvexHullPointsForDrawable(drawableID);

      drawable.setConvexHullPoints(points);
    }

    const bounds = drawable.getFastBounds(); // In debug mode, draw the bounds.

    if (this._debugCanvas) {
      const gl = this._gl;
      this._debugCanvas.width = gl.canvas.width;
      this._debugCanvas.height = gl.canvas.height;

      const context = this._debugCanvas.getContext('2d');

      context.drawImage(gl.canvas, 0, 0);
      context.strokeStyle = '#FF0000';
      const pr = window.devicePixelRatio;
      context.strokeRect(pr * (bounds.left + this._nativeSize[0] / 2), pr * (-bounds.top + this._nativeSize[1] / 2), pr * (bounds.right - bounds.left), pr * (-bounds.bottom + bounds.top));
    }

    return bounds;
  }
  /**
   * Get the precise bounds for a Drawable around the top slice.
   * Used for positioning speech bubbles more closely to the sprite.
   * @param {int} drawableID ID of Drawable to get bubble bounds for.
   * @return {object} Bounds for a tight box around the Drawable top slice.
   */


  getBoundsForBubble(drawableID) {
    const drawable = this._allDrawables[drawableID]; // Tell the Drawable about its updated convex hull, if necessary.

    if (drawable.needsConvexHullPoints()) {
      const points = this._getConvexHullPointsForDrawable(drawableID);

      drawable.setConvexHullPoints(points);
    }

    const bounds = drawable.getBoundsForBubble(); // In debug mode, draw the bounds.

    if (this._debugCanvas) {
      const gl = this._gl;
      this._debugCanvas.width = gl.canvas.width;
      this._debugCanvas.height = gl.canvas.height;

      const context = this._debugCanvas.getContext('2d');

      context.drawImage(gl.canvas, 0, 0);
      context.strokeStyle = '#FF0000';
      const pr = window.devicePixelRatio;
      context.strokeRect(pr * (bounds.left + this._nativeSize[0] / 2), pr * (-bounds.top + this._nativeSize[1] / 2), pr * (bounds.right - bounds.left), pr * (-bounds.bottom + bounds.top));
    }

    return bounds;
  }
  /**
   * Get the current skin (costume) size of a Drawable.
   * @param {int} drawableID The ID of the Drawable to measure.
   * @return {Array<number>} Skin size, width and height.
   */


  getCurrentSkinSize(drawableID) {
    const drawable = this._allDrawables[drawableID];
    return this.getSkinSize(drawable.skin.id);
  }
  /**
   * Get the size of a skin by ID.
   * @param {int} skinID The ID of the Skin to measure.
   * @return {Array<number>} Skin size, width and height.
   */


  getSkinSize(skinID) {
    const skin = this._allSkins[skinID];
    return skin.size;
  }
  /**
   * Get the rotation center of a skin by ID.
   * @param {int} skinID The ID of the Skin
   * @return {Array<number>} The rotationCenterX and rotationCenterY
   */


  getSkinRotationCenter(skinID) {
    const skin = this._allSkins[skinID];
    return skin.calculateRotationCenter();
  }
  /**
   * Check if a particular Drawable is touching a particular color.
   * Unlike touching drawable, if the "tester" is invisble, we will still test.
   * @param {int} drawableID The ID of the Drawable to check.
   * @param {Array<int>} color3b Test if the Drawable is touching this color.
   * @param {Array<int>} [mask3b] Optionally mask the check to this part of Drawable.
   * @returns {boolean} True iff the Drawable is touching the color.
   */


  isTouchingColor(drawableID, color3b, mask3b) {
    const candidates = this._candidatesTouching(drawableID, this._visibleDrawList);

    let bounds;

    if (colorMatches(color3b, this._backgroundColor3b, 0)) {
      // If the color we're checking for is the background color, don't confine the check to
      // candidate drawables' bounds--since the background spans the entire stage, we must check
      // everything that lies inside the drawable.
      bounds = this._touchingBounds(drawableID); // e.g. empty costume, or off the stage

      if (bounds === null) return false;
    } else if (candidates.length === 0) {
      // If not checking for the background color, we can return early if there are no candidate drawables.
      return false;
    } else {
      bounds = this._candidatesBounds(candidates);
    }

    const maxPixelsForCPU = this._getMaxPixelsForCPU();

    const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d');

    if (debugCanvasContext) {
      this._debugCanvas.width = bounds.width;
      this._debugCanvas.height = bounds.height;
    } // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen


    if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) {
      this._isTouchingColorGpuStart(drawableID, candidates.map(_ref => {
        let {
          id
        } = _ref;
        return id;
      }).reverse(), bounds, color3b, mask3b);
    }

    const drawable = this._allDrawables[drawableID];
    const point = __isTouchingDrawablesPoint;
    const color = __touchingColor;
    const hasMask = Boolean(mask3b);
    drawable.updateCPURenderAttributes(); // Masked drawable ignores ghost effect

    const effectMask = ~ShaderManager.EFFECT_INFO.ghost.mask; // Scratch Space - +y is top

    for (let y = bounds.bottom; y <= bounds.top; y++) {
      if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) {
        return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom);
      }

      for (let x = bounds.left; x <= bounds.right; x++) {
        point[1] = y;
        point[0] = x; // if we use a mask, check our sample color...

        if (hasMask ? maskMatches(Drawable.sampleColor4b(point, drawable, color, effectMask), mask3b) : drawable.isTouching(point)) {
          RenderWebGL.sampleColor3b(point, candidates, color);

          if (debugCanvasContext) {
            debugCanvasContext.fillStyle = "rgb(".concat(color[0], ",").concat(color[1], ",").concat(color[2], ")");
            debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1);
          } // ...and the target color is drawn at this pixel


          if (colorMatches(color, color3b, 0)) {
            return true;
          }
        }
      }
    }

    return false;
  }

  _getMaxPixelsForCPU() {
    switch (this._useGpuMode) {
      case RenderWebGL.UseGpuModes.ForceCPU:
        return Infinity;

      case RenderWebGL.UseGpuModes.ForceGPU:
        return 0;

      case RenderWebGL.UseGpuModes.Automatic:
      default:
        return __cpuTouchingColorPixelCount;
    }
  }

  _enterDrawBackground() {
    const gl = this.gl;

    const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0);

    gl.disable(gl.BLEND);
    gl.useProgram(currentShader.program);
    twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo);
  }

  _exitDrawBackground() {
    const gl = this.gl;
    gl.enable(gl.BLEND);
  }

  _isTouchingColorGpuStart(drawableID, candidateIDs, bounds, color3b, mask3b) {
    this._doExitDrawRegion();

    const gl = this._gl;
    twgl.bindFramebufferInfo(gl, this._queryBufferInfo); // Limit size of viewport to the bounds around the target Drawable,
    // and create the projection matrix for the draw.

    gl.viewport(0, 0, bounds.width, bounds.height);
    const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1); // Clear the query buffer to fully transparent. This will be the color of pixels that fail the stencil test.

    gl.clearColor(0, 0, 0, 0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);
    let extraUniforms;

    if (mask3b) {
      extraUniforms = {
        u_colorMask: [mask3b[0] / 255, mask3b[1] / 255, mask3b[2] / 255],
        u_colorMaskTolerance: MASK_TOUCHING_COLOR_TOLERANCE / 255
      };
    }

    try {
      // Using the stencil buffer, mask out the drawing to either the drawable's alpha channel
      // or pixels of the drawable which match the mask color, depending on whether a mask color is given.
      // Masked-out pixels will not be checked.
      gl.enable(gl.STENCIL_TEST);
      gl.stencilFunc(gl.ALWAYS, 1, 1);
      gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
      gl.colorMask(false, false, false, false);

      this._drawThese([drawableID], mask3b ? ShaderManager.DRAW_MODE.colorMask : ShaderManager.DRAW_MODE.silhouette, projection, {
        extraUniforms,
        ignoreVisibility: true,
        // Touching color ignores sprite visibility,
        effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask
      });

      gl.stencilFunc(gl.EQUAL, 1, 1);
      gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
      gl.colorMask(true, true, true, true); // Draw the background as a quad. Drawing a background with gl.clear will not mask to the stenciled area.

      this.enterDrawRegion(this._backgroundDrawRegionId);
      const uniforms = {
        u_backgroundColor: this._backgroundColor4f
      };

      const currentShader = this._shaderManager.getShader(ShaderManager.DRAW_MODE.background, 0);

      twgl.setUniforms(currentShader, uniforms);
      twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES); // Draw the candidate drawables on top of the background.

      this._drawThese(candidateIDs, ShaderManager.DRAW_MODE.default, projection, {
        idFilterFunc: testID => testID !== drawableID
      });
    } finally {
      gl.colorMask(true, true, true, true);
      gl.disable(gl.STENCIL_TEST);

      this._doExitDrawRegion();
    }
  }

  _isTouchingColorGpuFin(bounds, color3b, stop) {
    const gl = this._gl;
    const pixels = new Uint8Array(Math.floor(bounds.width * (bounds.height - stop) * 4));
    gl.readPixels(0, 0, bounds.width, bounds.height - stop, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

    if (this._debugCanvas) {
      this._debugCanvas.width = bounds.width;
      this._debugCanvas.height = bounds.height;

      const context = this._debugCanvas.getContext('2d');

      const imageData = context.getImageData(0, 0, bounds.width, bounds.height - stop);
      imageData.data.set(pixels);
      context.putImageData(imageData, 0, 0);
    }

    for (let pixelBase = 0; pixelBase < pixels.length; pixelBase += 4) {
      // Transparent pixels are masked (either by the drawable's alpha channel or color mask).
      if (pixels[pixelBase + 3] !== 0 && colorMatches(color3b, pixels, pixelBase)) {
        return true;
      }
    }

    return false;
  }
  /**
   * Check if a particular Drawable is touching any in a set of Drawables.
   * @param {int} drawableID The ID of the Drawable to check.
   * @param {?Array<int>} candidateIDs The Drawable IDs to check, otherwise all visible drawables in the renderer
   * @returns {boolean} True if the Drawable is touching one of candidateIDs.
   */


  isTouchingDrawables(drawableID) {
    let candidateIDs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this._drawList;

    const candidates = this._candidatesTouching(drawableID, // even if passed an invisible drawable, we will NEVER touch it!
    candidateIDs.filter(id => this._allDrawables[id]._visible)); // if we are invisble we don't touch anything.


    if (candidates.length === 0 || !this._allDrawables[drawableID]._visible) {
      return false;
    } // Get the union of all the candidates intersections.


    const bounds = this._candidatesBounds(candidates);

    const drawable = this._allDrawables[drawableID];
    const point = __isTouchingDrawablesPoint;
    drawable.updateCPURenderAttributes(); // This is an EXTREMELY brute force collision detector, but it is
    // still faster than asking the GPU to give us the pixels.

    for (let x = bounds.left; x <= bounds.right; x++) {
      // Scratch Space - +y is top
      point[0] = x;

      for (let y = bounds.bottom; y <= bounds.top; y++) {
        point[1] = y;

        if (drawable.isTouching(point)) {
          for (let index = 0; index < candidates.length; index++) {
            if (candidates[index].drawable.isTouching(point)) {
              return true;
            }
          }
        }
      }
    }

    return false;
  }
  /**
   * Convert a client based x/y position on the canvas to a Scratch 3 world space
   * Rectangle.  This creates recangles with a radius to cover selecting multiple
   * scratch pixels with touch / small render areas.
   *
   * @param {int} centerX The client x coordinate of the picking location.
   * @param {int} centerY The client y coordinate of the picking location.
   * @param {int} [width] The client width of the touch event (optional).
   * @param {int} [height] The client width of the touch event (optional).
   * @returns {Rectangle} Scratch world space rectangle, iterate bottom <= top,
   *                      left <= right.
   */


  clientSpaceToScratchBounds(centerX, centerY) {
    let width = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
    let height = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
    const gl = this._gl;
    const clientToScratchX = this._nativeSize[0] / gl.canvas.clientWidth;
    const clientToScratchY = this._nativeSize[1] / gl.canvas.clientHeight;
    width *= clientToScratchX;
    height *= clientToScratchY;
    width = Math.max(1, Math.min(Math.round(width), MAX_TOUCH_SIZE[0]));
    height = Math.max(1, Math.min(Math.round(height), MAX_TOUCH_SIZE[1]));
    const x = centerX * clientToScratchX - (width - 1) / 2; // + because scratch y is inverted

    const y = centerY * clientToScratchY + (height - 1) / 2;
    const xOfs = width % 2 ? 0 : -0.5; // y is offset +0.5

    const yOfs = height % 2 ? 0 : -0.5;
    const bounds = new Rectangle();
    bounds.initFromBounds(Math.floor(this._xLeft + x + xOfs), Math.floor(this._xLeft + x + xOfs + width - 1), Math.ceil(this._yTop - y + yOfs), Math.ceil(this._yTop - y + yOfs + height - 1));
    return bounds;
  }
  /**
   * Determine if the drawable is touching a client based x/y.  Helper method for sensing
   * touching mouse-pointer.  Ignores visibility.
   *
   * @param {int} drawableID The ID of the drawable to check.
   * @param {int} centerX The client x coordinate of the picking location.
   * @param {int} centerY The client y coordinate of the picking location.
   * @param {int} [touchWidth] The client width of the touch event (optional).
   * @param {int} [touchHeight] The client height of the touch event (optional).
   * @returns {boolean} If the drawable has any pixels that would draw in the touch area
   */


  drawableTouching(drawableID, centerX, centerY, touchWidth, touchHeight) {
    const drawable = this._allDrawables[drawableID];

    if (!drawable) {
      return false;
    }

    const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight);
    const drawableBounds = drawable.getFastBounds();
    drawableBounds.snapToInt();

    if (!drawableBounds.intersects(bounds)) {
      return false;
    }

    drawable.updateCPURenderAttributes();
    const worldPos = twgl.v3.create();

    for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) {
      for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) {
        if (drawable.isTouching(worldPos)) {
          return true;
        }
      }
    }

    return false;
  }
  /**
   * Detect which sprite, if any, is at the given location.
   * This function will pick all drawables that are visible, unless specific
   * candidate drawable IDs are provided.  Used for determining what is clicked
   * or dragged.  Will not select hidden / ghosted sprites.
   *
   * @param {int} centerX The client x coordinate of the picking location.
   * @param {int} centerY The client y coordinate of the picking location.
   * @param {int} [touchWidth] The client width of the touch event (optional).
   * @param {int} [touchHeight] The client height of the touch event (optional).
   * @param {Array<int>} [candidateIDs] The Drawable IDs to pick from, otherwise all visible drawables.
   * @returns {int} The ID of the topmost Drawable under the picking location, or
   * RenderConstants.ID_NONE if there is no Drawable at that location.
   */


  pick(centerX, centerY, touchWidth, touchHeight, candidateIDs) {
    const bounds = this.clientSpaceToScratchBounds(centerX, centerY, touchWidth, touchHeight);

    if (bounds.left === -Infinity || bounds.bottom === -Infinity) {
      return false;
    }

    candidateIDs = (candidateIDs || this._drawList).filter(id => {
      const drawable = this._allDrawables[id]; // default pick list ignores visible and ghosted sprites.

      if (drawable.getVisible() && drawable.getUniforms().u_ghost !== 0) {
        const drawableBounds = drawable.getFastBounds();
        const inRange = bounds.intersects(drawableBounds);
        if (!inRange) return false;
        if (drawable.skin instanceof PenSkin) return false;
        drawable.updateCPURenderAttributes();
        return true;
      }

      return false;
    });

    if (candidateIDs.length === 0) {
      return false;
    }

    const hits = [];
    const worldPos = twgl.v3.create(0, 0, 0); // Iterate over the scratch pixels and check if any candidate can be
    // touched at that point.

    for (worldPos[1] = bounds.bottom; worldPos[1] <= bounds.top; worldPos[1]++) {
      for (worldPos[0] = bounds.left; worldPos[0] <= bounds.right; worldPos[0]++) {
        // Check candidates in the reverse order they would have been
        // drawn. This will determine what candiate's silhouette pixel
        // would have been drawn at the point.
        for (let d = candidateIDs.length - 1; d >= 0; d--) {
          const id = candidateIDs[d];
          const drawable = this._allDrawables[id];

          if (drawable.isTouching(worldPos)) {
            hits[id] = (hits[id] || 0) + 1;
            break;
          }
        }
      }
    } // Bias toward selecting anything over nothing


    hits[RenderConstants.ID_NONE] = 0;
    let hit = RenderConstants.ID_NONE;

    for (const hitID in hits) {
      if (Object.prototype.hasOwnProperty.call(hits, hitID) && hits[hitID] > hits[hit]) {
        hit = hitID;
      }
    }

    return Number(hit);
  }
  /**
   * @typedef DrawableExtraction
   * @property {ImageData} data Raw pixel data for the drawable
   * @property {number} x The x coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels'
   * @property {number} y The y coordinate of the drawable's bounding box's top-left corner, in 'CSS pixels'
   * @property {number} width The drawable's bounding box width, in 'CSS pixels'
   * @property {number} height The drawable's bounding box height, in 'CSS pixels'
   */

  /**
   * Return a drawable's pixel data and bounds in screen space.
   * @param {int} drawableID The ID of the drawable to get pixel data for
   * @return {DrawableExtraction} Data about the picked drawable
   */


  extractDrawableScreenSpace(drawableID) {
    const drawable = this._allDrawables[drawableID];
    if (!drawable) throw new Error("Could not extract drawable with ID ".concat(drawableID, "; it does not exist"));

    this._doExitDrawRegion();

    const nativeCenterX = this._nativeSize[0] * 0.5;
    const nativeCenterY = this._nativeSize[1] * 0.5;
    const scratchBounds = drawable.getFastBounds();
    const canvas = this.canvas; // Ratio of the screen-space scale of the stage's canvas to the "native size" of the stage

    const scaleFactor = canvas.width / this._nativeSize[0]; // Bounds of the extracted drawable, in "canvas pixel space"
    // (origin is 0, 0, destination is the canvas width, height).

    const canvasSpaceBounds = new Rectangle();
    canvasSpaceBounds.initFromBounds((scratchBounds.left + nativeCenterX) * scaleFactor, (scratchBounds.right + nativeCenterX) * scaleFactor, // in "canvas space", +y is down, but Rectangle methods assume bottom < top, so swap them
    (nativeCenterY - scratchBounds.top) * scaleFactor, (nativeCenterY - scratchBounds.bottom) * scaleFactor);
    canvasSpaceBounds.snapToInt(); // undo the transformation to transform the bounds, snapped to "canvas-pixel space", back to "Scratch space"
    // We have to transform -> snap -> invert transform so that the "Scratch-space" bounds are snapped in
    // "canvas-pixel space".

    scratchBounds.initFromBounds(canvasSpaceBounds.left / scaleFactor - nativeCenterX, canvasSpaceBounds.right / scaleFactor - nativeCenterX, nativeCenterY - canvasSpaceBounds.top / scaleFactor, nativeCenterY - canvasSpaceBounds.bottom / scaleFactor);
    const gl = this._gl; // Set a reasonable max limit width and height for the bufferInfo bounds

    const maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
    const clampedWidth = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.width, maxTextureSize);
    const clampedHeight = Math.min(MAX_EXTRACTED_DRAWABLE_DIMENSION, canvasSpaceBounds.height, maxTextureSize); // Make a new bufferInfo since this._queryBufferInfo is limited to 480x360

    const bufferInfo = twgl.createFramebufferInfo(gl, [{
      format: gl.RGBA
    }], clampedWidth, clampedHeight);

    try {
      twgl.bindFramebufferInfo(gl, bufferInfo); // Limit size of viewport to the bounds around the target Drawable,
      // and create the projection matrix for the draw.

      gl.viewport(0, 0, clampedWidth, clampedHeight);
      const projection = twgl.m4.ortho(scratchBounds.left, scratchBounds.right, scratchBounds.top, scratchBounds.bottom, -1, 1);
      gl.clearColor(0, 0, 0, 0);
      gl.clear(gl.COLOR_BUFFER_BIT);

      this._drawThese([drawableID], ShaderManager.DRAW_MODE.straightAlpha, projection, {
        // Don't apply the ghost effect. TODO: is this an intentional design decision?
        effectMask: ~ShaderManager.EFFECT_INFO.ghost.mask,
        // We're doing this in screen-space, so the framebuffer dimensions should be those of the canvas in
        // screen-space. This is used to ensure SVG skins are rendered at the proper resolution.
        framebufferWidth: canvas.width,
        framebufferHeight: canvas.height
      });

      const data = new Uint8Array(Math.floor(clampedWidth * clampedHeight * 4));
      gl.readPixels(0, 0, clampedWidth, clampedHeight, gl.RGBA, gl.UNSIGNED_BYTE, data); // readPixels can only read into a Uint8Array, but ImageData has to take a Uint8ClampedArray.
      // We can share the same underlying buffer between them to avoid having to copy any data.

      const imageData = new ImageData(new Uint8ClampedArray(data.buffer), clampedWidth, clampedHeight); // On high-DPI devices, the canvas' width (in canvas pixels) will be larger than its width in CSS pixels.
      // We want to return the CSS-space bounds,
      // so take into account the ratio between the canvas' pixel dimensions and its layout dimensions.
      // This is usually the same as 1 / window.devicePixelRatio, but if e.g. you zoom your browser window without
      // the canvas resizing, then it'll differ.

      const ratio = canvas.getBoundingClientRect().width / canvas.width;
      return {
        imageData,
        x: canvasSpaceBounds.left * ratio,
        y: canvasSpaceBounds.bottom * ratio,
        width: canvasSpaceBounds.width * ratio,
        height: canvasSpaceBounds.height * ratio
      };
    } finally {
      gl.deleteFramebuffer(bufferInfo.framebuffer);
    }
  }
  /**
   * @typedef ColorExtraction
   * @property {Uint8Array} data Raw pixel data for the drawable
   * @property {int} width Drawable bounding box width
   * @property {int} height Drawable bounding box height
   * @property {object} color Color object with RGBA properties at picked location
   */

  /**
   * Return drawable pixel data and color at a given position
   * @param {int} x The client x coordinate of the picking location.
   * @param {int} y The client y coordinate of the picking location.
   * @param {int} radius The client radius to extract pixels with.
   * @return {?ColorExtraction} Data about the picked color
   */


  extractColor(x, y, radius) {
    this._doExitDrawRegion();

    const scratchX = Math.round(this._nativeSize[0] * (x / this._gl.canvas.clientWidth - 0.5));
    const scratchY = Math.round(-this._nativeSize[1] * (y / this._gl.canvas.clientHeight - 0.5));
    const gl = this._gl;
    twgl.bindFramebufferInfo(gl, this._queryBufferInfo);
    const bounds = new Rectangle();
    bounds.initFromBounds(scratchX - radius, scratchX + radius, scratchY - radius, scratchY + radius);
    const pickX = scratchX - bounds.left;
    const pickY = bounds.top - scratchY;
    gl.viewport(0, 0, bounds.width, bounds.height);
    const projection = twgl.m4.ortho(bounds.left, bounds.right, bounds.top, bounds.bottom, -1, 1);
    gl.clearColor(...this._backgroundColor4f);
    gl.clear(gl.COLOR_BUFFER_BIT);

    this._drawThese(this._drawList, ShaderManager.DRAW_MODE.default, projection);

    const data = new Uint8Array(Math.floor(bounds.width * bounds.height * 4));
    gl.readPixels(0, 0, bounds.width, bounds.height, gl.RGBA, gl.UNSIGNED_BYTE, data);
    const pixelBase = Math.floor(4 * (pickY * bounds.width + pickX));
    const color = {
      r: data[pixelBase],
      g: data[pixelBase + 1],
      b: data[pixelBase + 2],
      a: data[pixelBase + 3]
    };

    if (this._debugCanvas) {
      this._debugCanvas.width = bounds.width;
      this._debugCanvas.height = bounds.height;

      const ctx = this._debugCanvas.getContext('2d');

      const imageData = ctx.createImageData(bounds.width, bounds.height);
      imageData.data.set(data);
      ctx.putImageData(imageData, 0, 0);
      ctx.strokeStyle = 'black';
      ctx.fillStyle = "rgba(".concat(color.r, ", ").concat(color.g, ", ").concat(color.b, ", ").concat(color.a, ")");
      ctx.rect(pickX - 4, pickY - 4, 8, 8);
      ctx.fill();
      ctx.stroke();
    }

    return {
      data: data,
      width: bounds.width,
      height: bounds.height,
      color: color
    };
  }
  /**
   * Get the candidate bounding box for a touching query.
   * @param {int} drawableID ID for drawable of query.
   * @return {?Rectangle} Rectangle bounds for touching query, or null.
   */


  _touchingBounds(drawableID) {
    const drawable = this._allDrawables[drawableID];
    /** @todo remove this once URL-based skin setting is removed. */

    if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null;
    const bounds = drawable.getFastBounds(); // Limit queries to the stage size.

    if (!this.offscreenTouching) {
      bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop);
    } // Use integer coordinates for queries - weird things happen
    // when you provide float width/heights to gl.viewport and projection.


    bounds.snapToInt();

    if (bounds.width === 0 || bounds.height === 0) {
      // No space to query.
      return null;
    }

    return bounds;
  }

  _unsnappedTouchingBounds(drawableID) {
    // _touchingBounds with the snapToint call removed.
    const drawable = this._allDrawables[drawableID];
    if (!drawable.skin || !drawable.skin.getTexture([100, 100])) return null;
    const bounds = drawable.getFastBounds();

    if (!this.offscreenTouching) {
      bounds.clamp(this._xLeft, this._xRight, this._yBottom, this._yTop);
    }

    if (bounds.width === 0 || bounds.height === 0) {
      return null;
    }

    return bounds;
  }
  /**
   * Filter a list of candidates for a touching query into only those that
   * could possibly intersect the given bounds.
   * @param {int} drawableID - ID for drawable of query.
   * @param {Array<int>} candidateIDs - Candidates for touching query.
   * @return {?Array< {id, drawable, intersection} >} Filtered candidates with useful data.
   */


  _candidatesTouching(drawableID, candidateIDs) {
    const bounds = this._touchingBounds(drawableID);

    const result = [];

    if (bounds === null) {
      return result;
    } // iterate through the drawables list BACKWARDS - we want the top most item to be the first we check


    for (let index = candidateIDs.length - 1; index >= 0; index--) {
      const id = candidateIDs[index];

      if (id !== drawableID) {
        const drawable = this._allDrawables[id]; // Text bubbles aren't considered in "touching" queries

        if (drawable.skin instanceof TextBubbleSkin) continue;

        if (drawable.skin && drawable._visible) {
          // If private skin access is disabled, do not allow projects to use touching blocks to guess the
          // contents of a private skin.
          if (!this.allowPrivateSkinAccess && drawable.skin.private) continue; // Update the CPU position data

          drawable.updateCPURenderAttributes();
          const candidateBounds = drawable.getFastBounds(); // Push bounds out to integers. If a drawable extends out into half a pixel, that half-pixel still
          // needs to be tested. Plus, in some areas we construct another rectangle from the union of these,
          // and iterate over its pixels (width * height). Turns out that doesn't work so well when the
          // width/height aren't integers.

          candidateBounds.snapToInt();

          if (bounds.intersects(candidateBounds)) {
            result.push({
              id,
              drawable,
              intersection: Rectangle.intersect(bounds, candidateBounds)
            });
          }
        }
      }
    }

    return result;
  }
  /**
   * Helper to get the union bounds from a set of candidates returned from the above method
   * @private
   * @param {Array<object>} candidates info from _candidatesTouching
   * @return {Rectangle} the outer bounding box union
   */


  _candidatesBounds(candidates) {
    return candidates.reduce((memo, _ref2) => {
      let {
        intersection
      } = _ref2;

      if (!memo) {
        return intersection;
      } // store the union of the two rectangles in our static rectangle instance


      return Rectangle.union(memo, intersection, __candidatesBounds);
    }, null);
  }
  /**
   * Update a drawable's skin.
   * @param {number} drawableID The drawable's id.
   * @param {number} skinId The skin to update to.
   */


  updateDrawableSkinId(drawableID, skinId) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.skin = this._allSkins[skinId];
  }
  /**
   * Update a drawable's position.
   * @param {number} drawableID The drawable's id.
   * @param {Array.<number>} position The new position.
   */


  updateDrawablePosition(drawableID, position) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updatePosition(position);
  }
  /**
   * Update a drawable's direction.
   * @param {number} drawableID The drawable's id.
   * @param {number} direction A new direction.
   */


  updateDrawableDirection(drawableID, direction) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updateDirection(direction);
  }
  /**
   * Update a drawable's scale.
   * @param {number} drawableID The drawable's id.
   * @param {Array.<number>} scale A new scale.
   */


  updateDrawableScale(drawableID, scale) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updateScale(scale);
  }
  /**
   * Update a drawable's direction and scale together.
   * @param {number} drawableID The drawable's id.
   * @param {number} direction A new direction.
   * @param {Array.<number>} scale A new scale.
   */


  updateDrawableDirectionScale(drawableID, direction, scale) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updateDirection(direction);
    drawable.updateScale(scale);
  }
  /**
   * Update a drawable's visibility.
   * @param {number} drawableID The drawable's id.
   * @param {boolean} visible Will the drawable be visible?
   */


  updateDrawableVisible(drawableID, visible) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updateVisible(visible);
  }
  /**
   * Update a drawable's visual effect.
   * @param {number} drawableID The drawable's id.
   * @param {string} effectName The effect to change.
   * @param {number} value A new effect value.
   */


  updateDrawableEffect(drawableID, effectName, value) {
    const drawable = this._allDrawables[drawableID]; // TODO: https://github.com/LLK/scratch-vm/issues/2288

    if (!drawable) return;
    drawable.updateEffect(effectName, value);
  }
  /**
   * Update the position, direction, scale, or effect properties of this Drawable.
   * @deprecated Use specific updateDrawable* methods instead.
   * @param {int} drawableID The ID of the Drawable to update.
   * @param {object.<string,*>} properties The new property values to set.
   */


  updateDrawableProperties(drawableID, properties) {
    const drawable = this._allDrawables[drawableID];

    if (!drawable) {
      /**
       * @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here.
       * Right now this happens so much on some projects that a warning or exception here can hang the browser.
       */
      return;
    }

    if ('skinId' in properties) {
      this.updateDrawableSkinId(drawableID, properties.skinId);
    }

    drawable.updateProperties(properties);
  }
  /**
   * Update the position object's x & y members to keep the drawable fenced in view.
   * @param {int} drawableID - The ID of the Drawable to update.
   * @param {Array.<number, number>} position to be fenced - An array of type [x, y]
   * @return {Array.<number, number>} The fenced position as an array [x, y]
   */


  getFencedPositionOfDrawable(drawableID, position) {
    let x = position[0];
    let y = position[1];
    const drawable = this._allDrawables[drawableID];

    if (!drawable) {
      // @todo(https://github.com/LLK/scratch-vm/issues/2288) fix whatever's wrong in the VM which causes this, then add a warning or throw here.
      // Right now this happens so much on some projects that a warning or exception here can hang the browser.
      return [x, y];
    }

    const dx = x - drawable._position[0];
    const dy = y - drawable._position[1];

    const aabb = drawable._skin.getFenceBounds(drawable, __fenceBounds);

    const inset = Math.floor(Math.min(aabb.width, aabb.height) / 2);
    const sx = this._xRight - Math.min(FENCE_WIDTH, inset);

    if (aabb.right + dx < -sx) {
      x = Math.ceil(drawable._position[0] - (sx + aabb.right));
    } else if (aabb.left + dx > sx) {
      x = Math.floor(drawable._position[0] + (sx - aabb.left));
    }

    const sy = this._yTop - Math.min(FENCE_WIDTH, inset);

    if (aabb.top + dy < -sy) {
      y = Math.ceil(drawable._position[1] - (sy + aabb.top));
    } else if (aabb.bottom + dy > sy) {
      y = Math.floor(drawable._position[1] + (sy - aabb.bottom));
    }

    return [x, y];
  }
  /**
   * Clear a pen layer.
   * @param {int} penSkinID - the unique ID of a Pen Skin.
   */


  penClear(penSkinID) {
    this.dirty = true;
    const skin =
    /** @type {PenSkin} */
    this._allSkins[penSkinID];
    skin.clear();
  }
  /**
   * Draw a point on a pen layer.
   * @param {int} penSkinID - the unique ID of a Pen Skin.
   * @param {PenAttributes} penAttributes - how the point should be drawn.
   * @param {number} x - the X coordinate of the point to draw.
   * @param {number} y - the Y coordinate of the point to draw.
   */


  penPoint(penSkinID, penAttributes, x, y) {
    this.dirty = true;
    const skin =
    /** @type {PenSkin} */
    this._allSkins[penSkinID];
    skin.drawPoint(penAttributes, x, y);
  }
  /**
   * Draw a line on a pen layer.
   * @param {int} penSkinID - the unique ID of a Pen Skin.
   * @param {PenAttributes} penAttributes - how the line should be drawn.
   * @param {number} x0 - the X coordinate of the beginning of the line.
   * @param {number} y0 - the Y coordinate of the beginning of the line.
   * @param {number} x1 - the X coordinate of the end of the line.
   * @param {number} y1 - the Y coordinate of the end of the line.
   */


  penLine(penSkinID, penAttributes, x0, y0, x1, y1) {
    this.dirty = true;
    const skin =
    /** @type {PenSkin} */
    this._allSkins[penSkinID];
    skin.drawLine(penAttributes, x0, y0, x1, y1);
  }
  /**
   * Stamp a Drawable onto a pen layer.
   * @param {int} penSkinID - the unique ID of a Pen Skin.
   * @param {int} stampID - the unique ID of the Drawable to use as the stamp.
   */


  penStamp(penSkinID, stampID) {
    this.dirty = true;
    const stampDrawable = this._allDrawables[stampID];

    if (!stampDrawable) {
      return;
    } // TW: The bounds will be snapped later


    const bounds = this._unsnappedTouchingBounds(stampID);

    if (!bounds) {
      return;
    }

    this._doExitDrawRegion();

    const skin =
    /** @type {PenSkin} */
    this._allSkins[penSkinID];
    const gl = this._gl;
    twgl.bindFramebufferInfo(gl, skin._framebuffer); // Limit size of viewport to the bounds around the stamp Drawable and create the projection matrix for the draw.
    // TW: We upscale the "stage space" to "screen space" and then snap the coordinates so that tiled projects
    // don't have seems between sprites.

    const quality = skin.renderQuality;
    bounds.left *= quality;
    bounds.right *= quality;
    bounds.top *= quality;
    bounds.bottom *= quality;
    bounds.snapToInt();
    gl.viewport(this._nativeSize[0] * 0.5 * quality + bounds.left, this._nativeSize[1] * 0.5 * quality - bounds.top, bounds.width, bounds.height);
    const projection = twgl.m4.ortho( // TW: We have to convert the snapped "screen-space" back to "stage-space" for rendering.
    bounds.left / quality, bounds.right / quality, bounds.top / quality, bounds.bottom / quality, -1, 1); // Draw the stamped sprite onto the PenSkin's framebuffer.

    this._drawThese([stampID], ShaderManager.DRAW_MODE.default, projection, {
      ignoreVisibility: true,
      framebufferWidth: this._nativeSize[0] * quality,
      framebufferHeight: this._nativeSize[1] * quality
    });

    skin._silhouetteDirty = true;
  }
  /* ******
   * Truly internal functions: these support the functions above.
   ********/

  /**
   * Build geometry (vertex and index) buffers.
   * @private
   */


  _createGeometry() {
    const quad = {
      a_position: {
        numComponents: 2,
        data: [-0.5, -0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5, -0.5, 0.5, 0.5]
      },
      a_texCoord: {
        numComponents: 2,
        data: [1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1]
      }
    };
    this._bufferInfo = twgl.createBufferInfoFromArrays(this._gl, quad);
  }
  /**
   * Respond to a change in the "native" rendering size. The native size is used by buffers which are fixed in size
   * regardless of the size of the main render target. This includes the buffers used for queries such as picking and
   * color-touching. The fixed size allows (more) consistent behavior across devices and presentation modes.
   * @param {object} event - The change event.
   * @private
   */


  onNativeSizeChanged(event) {
    this.dirty = true;
    const [width, height] = event.newSize;
    const gl = this._gl;
    const attachments = [{
      format: gl.RGBA
    }, {
      format: gl.DEPTH_STENCIL
    }];

    if (!this._pickBufferInfo) {
      this._pickBufferInfo = twgl.createFramebufferInfo(gl, attachments, MAX_TOUCH_SIZE[0], MAX_TOUCH_SIZE[1]);
    }
    /** @todo should we create this on demand to save memory? */
    // A 480x360 32-bpp buffer is 675 KiB.


    if (this._queryBufferInfo) {
      twgl.resizeFramebufferInfo(gl, this._queryBufferInfo, attachments, width, height);
    } else {
      this._queryBufferInfo = twgl.createFramebufferInfo(gl, attachments, width, height);
    }
  }
  /**
   * Enter a draw region.
   *
   * A draw region is where multiple draw operations are performed with the
   * same GL state. WebGL performs poorly when it changes state like blend
   * mode. Marking a collection of state values as a "region" the renderer
   * can skip superfluous extra state calls when it is already in that
   * region. Since one region may be entered from within another a exit
   * handle can also be registered that is called when a new region is about
   * to be entered to restore a common inbetween state.
   *
   * @param {any} regionId - id of the region to enter
   * @param {function} enter - handle to call when first entering a region
   * @param {function} exit - handle to call when leaving a region
   */


  enterDrawRegion(regionId) {
    let enter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : regionId.enter;
    let exit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : regionId.exit;

    if (this._regionId !== regionId) {
      this._doExitDrawRegion();

      this._regionId = regionId;
      enter();
      this._exitRegion = exit;
    }
  }
  /**
   * Forcefully exit the current region returning to a common inbetween GL
   * state.
   */


  _doExitDrawRegion() {
    if (this._exitRegion !== null) {
      this._exitRegion();
    }

    this._exitRegion = null;
    this._regionId = null;
  }
  /**
   * Draw a set of Drawables, by drawable ID
   * @param {Array<int>} drawables The Drawable IDs to draw, possibly this._drawList.
   * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc.
   * @param {module:twgl/m4.Mat4} projection The projection matrix to use.
   * @param {object} [opts] Options for drawing
   * @param {idFilterFunc} opts.filter An optional filter function.
   * @param {object.<string,*>} opts.extraUniforms Extra uniforms for the shaders.
   * @param {int} opts.effectMask Bitmask for effects to allow
   * @param {boolean} opts.ignoreVisibility Draw all, despite visibility (e.g. stamping, touching color)
   * @param {int} opts.framebufferWidth The width of the framebuffer being drawn onto. Defaults to "native" width
   * @param {int} opts.framebufferHeight The height of the framebuffer being drawn onto. Defaults to "native" height
   * @private
   */


  _drawThese(drawables, drawMode, projection) {
    let opts = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
    const gl = this._gl;
    let currentShader = null;
    const framebufferSpaceScaleDiffers = 'framebufferWidth' in opts && 'framebufferHeight' in opts && opts.framebufferWidth !== this._nativeSize[0] && opts.framebufferHeight !== this._nativeSize[1];
    const numDrawables = drawables.length;

    for (let drawableIndex = 0; drawableIndex < numDrawables; ++drawableIndex) {
      const drawableID = drawables[drawableIndex]; // If we have a filter, check whether the ID fails

      if (opts.filter && !opts.filter(drawableID)) continue;
      const drawable = this._allDrawables[drawableID];
      /** @todo check if drawable is inside the viewport before anything else */
      // Hidden drawables (e.g., by a "hide" block) are not drawn unless
      // the ignoreVisibility flag is used (e.g. for stamping or touchingColor).

      if (!drawable.getVisible() && !opts.ignoreVisibility) continue; // drawableScale is the "framebuffer-pixel-space" scale of the drawable, as percentages of the drawable's
      // "native size" (so 100 = same as skin's "native size", 200 = twice "native size").
      // If the framebuffer dimensions are the same as the stage's "native" size, there's no need to calculate it.

      const drawableScale = framebufferSpaceScaleDiffers ? [drawable.scale[0] * opts.framebufferWidth / this._nativeSize[0], drawable.scale[1] * opts.framebufferHeight / this._nativeSize[1]] : drawable.scale; // If the skin or texture isn't ready yet, skip it.

      if (!drawable.skin || !drawable.skin.getTexture(drawableScale)) continue;
      const uniforms = {};
      let effectBits = drawable.enabledEffects;
      effectBits &= Object.prototype.hasOwnProperty.call(opts, 'effectMask') ? opts.effectMask : effectBits;

      const newShader = this._shaderManager.getShader(drawMode, effectBits); // Manually perform region check. Do not create functions inside a
      // loop.


      if (this._regionId !== newShader) {
        this._doExitDrawRegion();

        this._regionId = newShader;
        currentShader = newShader;
        gl.useProgram(currentShader.program);
        twgl.setBuffersAndAttributes(gl, currentShader, this._bufferInfo);
        Object.assign(uniforms, {
          u_projectionMatrix: projection
        });
      }

      Object.assign(uniforms, drawable.skin.getUniforms(drawableScale), drawable.getUniforms()); // Apply extra uniforms after the Drawable's, to allow overwriting.

      if (opts.extraUniforms) {
        Object.assign(uniforms, opts.extraUniforms);
      }

      if (uniforms.u_skin) {
        twgl.setTextureParameters(gl, uniforms.u_skin, {
          minMag: drawable.skin.useNearest(drawableScale, drawable) ? gl.NEAREST : gl.LINEAR
        });
      }

      twgl.setUniforms(currentShader, uniforms);
      twgl.drawBufferInfo(gl, this._bufferInfo, gl.TRIANGLES);
    }

    this._regionId = null;
  }
  /**
   * Get the convex hull points for a particular Drawable.
   * To do this, calculate it based on the drawable's Silhouette.
   * @param {int} drawableID The Drawable IDs calculate convex hull for.
   * @return {Array<Array<number>>} points Convex hull points, as [[x, y], ...]
   */


  _getConvexHullPointsForDrawable(drawableID) {
    const drawable = this._allDrawables[drawableID];
    const [width, height] = drawable.skin.size; // No points in the hull if invisible or size is 0.

    if (!drawable.getVisible() || width === 0 || height === 0) {
      return [];
    }

    drawable.updateCPURenderAttributes();
    /**
     * Return the determinant of two vectors, the vector from A to B and the vector from A to C.
     *
     * The determinant is useful in this case to know if AC is counter-clockwise from AB.
     * A positive value means that AC is counter-clockwise from AB. A negative value means AC is clockwise from AB.
     *
     * @param {Float32Array} A A 2d vector in space.
     * @param {Float32Array} B A 2d vector in space.
     * @param {Float32Array} C A 2d vector in space.
     * @return {number} Greater than 0 if counter clockwise, less than if clockwise, 0 if all points are on a line.
     */

    const determinant = function determinant(A, B, C) {
      // AB = B - A
      // AC = C - A
      // det (AB BC) = AB0 * AC1 - AB1 * AC0
      return (B[0] - A[0]) * (C[1] - A[1]) - (B[1] - A[1]) * (C[0] - A[0]);
    }; // This algorithm for calculating the convex hull somewhat resembles the monotone chain algorithm.
    // The main difference is that instead of sorting the points by x-coordinate, and y-coordinate in case of ties,
    // it goes through them by y-coordinate in the outer loop and x-coordinate in the inner loop.
    // This gives us "left" and "right" hulls, whereas the monotone chain algorithm gives "top" and "bottom" hulls.
    // Adapted from https://github.com/LLK/scratch-flash/blob/dcbeeb59d44c3be911545dfe54d46a32404f8e69/src/scratch/ScratchCostume.as#L369-L413


    const leftHull = [];
    const rightHull = []; // While convex hull algorithms usually push and pop values from the list of hull points,
    // here, we keep indices for the "last" point in each array. Any points past these indices are ignored.
    // This is functionally equivalent to pushing and popping from a "stack" of hull points.

    let leftEndPointIndex = -1;
    let rightEndPointIndex = -1;

    const _pixelPos = twgl.v3.create();

    const _effectPos = twgl.v3.create();

    let currentPoint; // *Not* Scratch Space-- +y is bottom
    // Loop over all rows of pixels, starting at the top

    for (let y = 0; y < height; y++) {
      _pixelPos[1] = y / height; // We start at the leftmost point, then go rightwards until we hit an opaque pixel

      let x = 0;

      for (; x < width; x++) {
        _pixelPos[0] = x / width;
        EffectTransform.transformPoint(drawable, _pixelPos, _effectPos);

        if (drawable.skin.isTouchingLinear(_effectPos)) {
          currentPoint = [x, y];
          break;
        }
      } // If we managed to loop all the way through, there are no opaque pixels on this row. Go to the next one


      if (x >= width) {
        continue;
      } // Because leftEndPointIndex is initialized to -1, this is skipped for the first two rows.
      // It runs only when there are enough points in the left hull to make at least one line.
      // If appending the current point to the left hull makes a counter-clockwise turn,
      // we want to append the current point. Otherwise, we decrement the index of the "last" hull point until the
      // current point makes a counter-clockwise turn.
      // This decrementing has the same effect as popping from the point list, but is hopefully faster.


      while (leftEndPointIndex > 0) {
        if (determinant(leftHull[leftEndPointIndex], leftHull[leftEndPointIndex - 1], currentPoint) > 0) {
          break;
        } else {
          // leftHull.pop();
          --leftEndPointIndex;
        }
      } // This has the same effect as pushing to the point list.
      // This "list head pointer" coding style leaves excess points dangling at the end of the list,
      // but that doesn't matter; we simply won't copy them over to the final hull.
      // leftHull.push(currentPoint);


      leftHull[++leftEndPointIndex] = currentPoint; // Now we repeat the process for the right side, looking leftwards for a pixel.

      for (x = width - 1; x >= 0; x--) {
        _pixelPos[0] = x / width;
        EffectTransform.transformPoint(drawable, _pixelPos, _effectPos);

        if (drawable.skin.isTouchingLinear(_effectPos)) {
          currentPoint = [x, y];
          break;
        }
      } // Because we're coming at this from the right, it goes clockwise this time.


      while (rightEndPointIndex > 0) {
        if (determinant(rightHull[rightEndPointIndex], rightHull[rightEndPointIndex - 1], currentPoint) < 0) {
          break;
        } else {
          --rightEndPointIndex;
        }
      }

      rightHull[++rightEndPointIndex] = currentPoint;
    } // Start off "hullPoints" with the left hull points.


    const hullPoints = leftHull; // This is where we get rid of those dangling extra points.

    hullPoints.length = leftEndPointIndex + 1; // Add points from the right side in reverse order so all points are ordered clockwise.

    for (let j = rightEndPointIndex; j >= 0; --j) {
      hullPoints.push(rightHull[j]);
    } // Simplify boundary points using hull.js.
    // TODO: Remove this; this algorithm already generates convex hulls.


    return hull(hullPoints, Infinity);
  }
  /**
   * Sample a "final" color from an array of drawables at a given scratch space.
   * Will blend any alpha values with the drawables "below" it.
   * @param {twgl.v3} vec Scratch Vector Space to sample
   * @param {Array<Drawables>} drawables A list of drawables with the "top most"
   *              drawable at index 0
   * @param {Uint8ClampedArray} dst The color3b space to store the answer in.
   * @return {Uint8ClampedArray} The dst vector with everything blended down.
   */


  static sampleColor3b(vec, drawables, dst) {
    dst = dst || new Uint8ClampedArray(3);
    dst.fill(0);
    let blendAlpha = 1;

    for (let index = 0; blendAlpha !== 0 && index < drawables.length; index++) {
      /*
      if (left > vec[0] || right < vec[0] ||
          bottom > vec[1] || top < vec[0]) {
          continue;
      }
      */
      Drawable.sampleColor4b(vec, drawables[index].drawable, __blendColor); // Equivalent to gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA)

      dst[0] += __blendColor[0] * blendAlpha;
      dst[1] += __blendColor[1] * blendAlpha;
      dst[2] += __blendColor[2] * blendAlpha;
      blendAlpha *= 1 - __blendColor[3] / 255;
    } // Backdrop could be transparent, so we need to go to the "clear color" of the
    // draw scene (white) as a fallback if everything was alpha


    dst[0] += blendAlpha * 255;
    dst[1] += blendAlpha * 255;
    dst[2] += blendAlpha * 255;
    return dst;
  }
  /**
   * @callback RenderWebGL#snapshotCallback
   * @param {string} dataURI Data URI of the snapshot of the renderer
   */

  /**
   * @param {snapshotCallback} callback Function called in the next frame with the snapshot data
   */


  requestSnapshot(callback) {
    this.dirty = true;

    this._snapshotCallbacks.push(callback);
  }

} // :3


RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawableScreenSpace;
/**
 * Values for setUseGPU()
 * @enum {string}
 */

RenderWebGL.UseGpuModes = {
  /**
   * Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two.
   */
  Automatic: 'Automatic',

  /**
   * Always use the GPU path.
   */
  ForceGPU: 'ForceGPU',

  /**
   * Always use the CPU path.
   */
  ForceCPU: 'ForceCPU'
};
/**
 * WebGL powerPreference used for future RenderWebGL instances.
 * The power preference of a renderer cannot be changed after instantiation.
 * @type {'default'|'high-performance'|'low-power'}
 */

RenderWebGL.powerPreference = 'default';
module.exports = RenderWebGL;

/***/ }),

/***/ "./node_modules/scratch-render/src/SVGSkin.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-render/src/SVGSkin.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const Skin = __webpack_require__(/*! ./Skin */ "./node_modules/scratch-render/src/Skin.js");

const {
  loadSvgString,
  serializeSvgToString
} = __webpack_require__(/*! scratch-svg-renderer */ "./node_modules/scratch-render/node_modules/scratch-svg-renderer/src/index.js");

const ShaderManager = __webpack_require__(/*! ./ShaderManager */ "./node_modules/scratch-render/src/ShaderManager.js");

const MAX_TEXTURE_DIMENSION = 2048;
/**
 * All scaled renderings of the SVG are stored in an array. The 1.0 scale of
 * the SVG is stored at the 8th index. The smallest possible 1 / 256 scale
 * rendering is stored at the 0th index.
 * @const {number}
 */

const INDEX_OFFSET = 8;

class SVGSkin extends Skin {
  /**
   * Create a new SVG skin.
   * @param {!int} id - The ID for this Skin.
   * @param {!RenderWebGL} renderer - The renderer which will use this skin.
   * @constructor
   * @extends Skin
   */
  constructor(id, renderer) {
    super(id, renderer);
    /** @type {HTMLImageElement} */

    this._svgImage = document.createElement('img');
    /** @type {boolean} */

    this._svgImageLoaded = false;
    /** @type {Array<number>} */

    this._size = [0, 0];
    /** @type {HTMLCanvasElement} */

    this._canvas = document.createElement('canvas');
    /** @type {CanvasRenderingContext2D} */

    this._context = this._canvas.getContext('2d');
    /** @type {Array<WebGLTexture>} */

    this._scaledMIPs = [];
    /** @type {number} */

    this._largestMIPScale = 0;
    /**
    * Ratio of the size of the SVG and the max size of the WebGL texture
    * @type {Number}
    */

    this._maxTextureScale = 1;
  }
  /**
   * Dispose of this object. Do not use it after calling this method.
   */


  dispose() {
    this.resetMIPs();
    super.dispose();
  }
  /**
   * @return {Array<number>} the natural size, in Scratch units, of this skin.
   */


  get size() {
    return [this._size[0], this._size[1]];
  }

  useNearest(scale, drawable) {
    // If the effect bits for mosaic, pixelate, whirl, or fisheye are set, use linear
    if ((drawable.enabledEffects & (ShaderManager.EFFECT_INFO.fisheye.mask | ShaderManager.EFFECT_INFO.whirl.mask | ShaderManager.EFFECT_INFO.pixelate.mask | ShaderManager.EFFECT_INFO.mosaic.mask)) !== 0) {
      return false;
    } // We can't use nearest neighbor unless we are a multiple of 90 rotation


    if (drawable._direction % 90 !== 0) {
      return false;
    } // Because SVG skins' bounding boxes are currently not pixel-aligned, the idea here is to hide blurriness
    // by using nearest-neighbor scaling if one screen-space pixel is "close enough" to one texture pixel.
    // If the scale of the skin is very close to 100 (0.99999 variance is okay I guess)
    // TODO: Make this check more precise. We should use nearest if there's less than one pixel's difference
    // between the screen-space and texture-space sizes of the skin. Mipmaps make this harder because there are
    // multiple textures (and hence multiple texture spaces) and we need to know which one to choose.


    if (Math.abs(scale[0]) > 99 && Math.abs(scale[0]) < 101 && Math.abs(scale[1]) > 99 && Math.abs(scale[1]) < 101) {
      return true;
    }

    return false;
  }
  /**
   * Create a MIP for a given scale.
   * @param {number} scale - The relative size of the MIP
   * @return {SVGMIP} An object that handles creating and updating SVG textures.
   */


  createMIP(scale) {
    const isLargestMIP = this._largestMIPScale < scale; // TW: Silhouette will lazily read image data from our <canvas>. However, this canvas is shared
    // between the Skin and Silhouette so changing it here can mess up Silhouette. To prevent that,
    // we will force the silhouette to synchronously read the image data before we mutate the
    // canvas, unless the new MIP is the largest MIP, in which case doing so is unnecessary as we
    // will update the silhouette later anyways.

    if (!isLargestMIP) {
      this._silhouette.unlazy();
    }

    const [width, height] = this._size;
    this._canvas.width = width * scale;
    this._canvas.height = height * scale;
    if (this._canvas.width <= 0 || this._canvas.height <= 0 || // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored
    // pre-scaling; e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have
    // a width of 1, but the image's width will be rounded down to 0 on some browsers (Firefox) prior to being
    // drawn at that scale, resulting in an IndexSizeError if we attempt to draw it.
    this._svgImage.naturalWidth <= 0 || this._svgImage.naturalHeight <= 0) return super.getTexture();

    this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);

    this._context.setTransform(scale, 0, 0, scale, 0, 0);

    this._context.drawImage(this._svgImage, 0, 0); // TW: Reading image data from <canvas> is very slow and causes animations to stutter,
    // so we just use the canvas directly instead.


    const textureData = this._canvas;
    const textureOptions = {
      auto: false,
      wrap: this._renderer.gl.CLAMP_TO_EDGE,
      src: textureData,
      premultiplyAlpha: true
    };
    const mip = twgl.createTexture(this._renderer.gl, textureOptions); // Check if this is the largest MIP created so far. Currently, silhouettes only get scaled up.

    if (isLargestMIP) {
      this._silhouette.update(textureData);

      this._largestMIPScale = scale;
    }

    return mip;
  }

  updateSilhouette() {
    let scale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [100, 100];
    // Ensure a silhouette exists.
    this.getTexture(scale);

    this._silhouette.unlazy();
  }
  /**
   * @param {Array<number>} scale - The scaling factors to be used, each in the [0,100] range.
   * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale.
   */


  getTexture(scale) {
    // The texture only ever gets uniform scale. Take the larger of the two axes.
    const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100;
    const requestedScale = Math.min(scaleMax / 100, this._maxTextureScale); // Math.ceil(Math.log2(scale)) means we use the "1x" texture at (0.5, 1] scale,
    // the "2x" texture at (1, 2] scale, the "4x" texture at (2, 4] scale, etc.
    // This means that one texture pixel will always be between 0.5x and 1x the size of one rendered pixel,
    // but never bigger than one rendered pixel--this prevents blurriness from blowing up the texture too much.

    const mipLevel = Math.max(Math.ceil(Math.log2(requestedScale)) + INDEX_OFFSET, 0); // Can't use bitwise stuff here because we need to handle negative exponents

    const mipScale = Math.pow(2, mipLevel - INDEX_OFFSET);

    if (this._svgImageLoaded && !this._scaledMIPs[mipLevel]) {
      this._scaledMIPs[mipLevel] = this.createMIP(mipScale);
    }

    return this._scaledMIPs[mipLevel] || super.getTexture();
  }
  /**
   * Do a hard reset of the existing MIPs by deleting them.
   */


  resetMIPs() {
    this._scaledMIPs.forEach(oldMIP => this._renderer.gl.deleteTexture(oldMIP));

    this._scaledMIPs.length = 0;
    this._largestMIPScale = 0;
  }
  /**
   * Set the contents of this skin to a snapshot of the provided SVG data.
   * @param {string} svgData - new SVG to use.
   * @param {Array<number>} [rotationCenter] - Optional rotation center for the SVG. If not supplied, it will be
   * calculated from the bounding box
   * @fires Skin.event:WasAltered
   */


  setSVG(svgData, rotationCenter) {
    const svgTag = loadSvgString(svgData);
    const svgText = serializeSvgToString(svgTag, true
    /* shouldInjectFonts */
    );
    this._svgImageLoaded = false;
    const {
      x,
      y,
      width,
      height
    } = svgTag.viewBox.baseVal; // While we're setting the size before the image is loaded, this doesn't cause the skin to appear with the wrong
    // size for a few frames while the new image is loading, because we don't emit the `WasAltered` event, telling
    // drawables using this skin to update, until the image is loaded.
    // We need to do this because the VM reads the skin's `size` directly after calling `setSVG`.
    // TODO: return a Promise so that the VM can read the skin's `size` after the image is loaded.

    this._size[0] = width;
    this._size[1] = height; // If there is another load already in progress, replace the old onload to effectively cancel the old load

    this._svgImage.onload = () => {
      if (width === 0 || height === 0) {
        super.setEmptyImageData();
        return;
      }

      const maxDimension = Math.ceil(Math.max(width, height));
      let testScale = 2;

      for (testScale; maxDimension * testScale <= MAX_TEXTURE_DIMENSION; testScale *= 2) {
        this._maxTextureScale = testScale;
      }

      this.resetMIPs();
      if (typeof rotationCenter === 'undefined') rotationCenter = this.calculateRotationCenter(); // Compensate for viewbox offset.
      // See https://github.com/LLK/scratch-render/pull/90.

      this._rotationCenter[0] = rotationCenter[0] - x;
      this._rotationCenter[1] = rotationCenter[1] - y;
      this._svgImageLoaded = true;
      this.emitWasAltered();
    };

    this._svgImage.src = "data:image/svg+xml;utf8,".concat(encodeURIComponent(svgText));
  }

}

module.exports = SVGSkin;

/***/ }),

/***/ "./node_modules/scratch-render/src/ShaderManager.js":
/*!**********************************************************!*\
  !*** ./node_modules/scratch-render/src/ShaderManager.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

class ShaderManager {
  /**
   * @param {WebGLRenderingContext} gl WebGL rendering context to create shaders for
   * @constructor
   */
  constructor(gl) {
    this._gl = gl;
    /**
     * The cache of all shaders compiled so far, filled on demand.
     * @type {Object<ShaderManager.DRAW_MODE, Array<ProgramInfo>>}
     * @private
     */

    this._shaderCache = {};

    for (const modeName in ShaderManager.DRAW_MODE) {
      if (Object.prototype.hasOwnProperty.call(ShaderManager.DRAW_MODE, modeName)) {
        this._shaderCache[modeName] = [];
      }
    }
  }
  /**
   * Fetch the shader for a particular set of active effects.
   * Build the shader if necessary.
   * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc.
   * @param {int} effectBits Bitmask representing the enabled effects.
   * @returns {ProgramInfo} The shader's program info.
   */


  getShader(drawMode, effectBits) {
    const cache = this._shaderCache[drawMode];

    if (drawMode === ShaderManager.DRAW_MODE.silhouette) {
      // Silhouette mode isn't affected by these effects.
      effectBits &= ~(ShaderManager.EFFECT_INFO.color.mask | ShaderManager.EFFECT_INFO.brightness.mask);
    }

    let shader = cache[effectBits];

    if (!shader) {
      shader = cache[effectBits] = this._buildShader(drawMode, effectBits);
    }

    return shader;
  }
  /**
   * Build the shader for a particular set of active effects.
   * @param {ShaderManager.DRAW_MODE} drawMode Draw normally, silhouette, etc.
   * @param {int} effectBits Bitmask representing the enabled effects.
   * @returns {ProgramInfo} The new shader's program info.
   * @private
   */


  _buildShader(drawMode, effectBits) {
    const numEffects = ShaderManager.EFFECTS.length;
    const defines = ["#define DRAW_MODE_".concat(drawMode)];

    for (let index = 0; index < numEffects; ++index) {
      if ((effectBits & 1 << index) !== 0) {
        defines.push("#define ENABLE_".concat(ShaderManager.EFFECTS[index]));
      }
    }

    const definesText = "".concat(defines.join('\n'), "\n");
    /* eslint-disable global-require */

    const vsFullText = definesText + __webpack_require__(/*! raw-loader!./shaders/sprite.vert */ "./node_modules/raw-loader/index.js!./node_modules/scratch-render/src/shaders/sprite.vert");

    const fsFullText = definesText + __webpack_require__(/*! raw-loader!./shaders/sprite.frag */ "./node_modules/raw-loader/index.js!./node_modules/scratch-render/src/shaders/sprite.frag");
    /* eslint-enable global-require */


    return twgl.createProgramInfo(this._gl, [vsFullText, fsFullText]);
  }

}
/**
 * @typedef {object} ShaderManager.Effect
 * @prop {int} mask - The bit in 'effectBits' representing the effect.
 * @prop {function} converter - A conversion function which takes a Scratch value (generally in the range
 *   0..100 or -100..100) and maps it to a value useful to the shader. This
 *   mapping may not be reversible.
 * @prop {boolean} shapeChanges - Whether the effect could change the drawn shape.
 */

/**
 * Mapping of each effect name to info about that effect.
 * @enum {ShaderManager.Effect}
 */


ShaderManager.EFFECT_INFO = {
  /** Color effect */
  color: {
    uniformName: 'u_color',
    mask: 1 << 0,
    converter: x => x / 200 % 1,
    shapeChanges: false
  },

  /** Fisheye effect */
  fisheye: {
    uniformName: 'u_fisheye',
    mask: 1 << 1,
    converter: x => Math.max(0, (x + 100) / 100),
    shapeChanges: true
  },

  /** Whirl effect */
  whirl: {
    uniformName: 'u_whirl',
    mask: 1 << 2,
    converter: x => -x * Math.PI / 180,
    shapeChanges: true
  },

  /** Pixelate effect */
  pixelate: {
    uniformName: 'u_pixelate',
    mask: 1 << 3,
    converter: x => Math.abs(x) / 10,
    shapeChanges: true
  },

  /** Mosaic effect */
  mosaic: {
    uniformName: 'u_mosaic',
    mask: 1 << 4,
    converter: x => {
      x = Math.round((Math.abs(x) + 10) / 10);
      /** @todo cap by Math.min(srcWidth, srcHeight) */

      return Math.max(1, Math.min(x, 512));
    },
    shapeChanges: true
  },

  /** Brightness effect */
  brightness: {
    uniformName: 'u_brightness',
    mask: 1 << 5,
    converter: x => Math.max(-100, Math.min(x, 100)) / 100,
    shapeChanges: false
  },

  /** Ghost effect */
  ghost: {
    uniformName: 'u_ghost',
    mask: 1 << 6,
    converter: x => 1 - Math.max(0, Math.min(x, 100)) / 100,
    shapeChanges: false
  }
};
/**
 * The name of each supported effect.
 * @type {Array}
 */

ShaderManager.EFFECTS = Object.keys(ShaderManager.EFFECT_INFO);
/**
 * The available draw modes.
 * @readonly
 * @enum {string}
 */

ShaderManager.DRAW_MODE = {
  /**
   * Draw normally. Its output will use premultiplied alpha.
   */
  default: 'default',

  /**
   * Draw with non-premultiplied alpha. Useful for reading pixels from GL into an ImageData object.
   */
  straightAlpha: 'straightAlpha',

  /**
   * Draw a silhouette using a solid color.
   */
  silhouette: 'silhouette',

  /**
   * Draw only the parts of the drawable which match a particular color.
   */
  colorMask: 'colorMask',

  /**
   * Draw a line with caps.
   */
  line: 'line',

  /**
   * Draw the background in a certain color. Must sometimes be used instead of gl.clear.
   */
  background: 'background'
};
module.exports = ShaderManager;

/***/ }),

/***/ "./node_modules/scratch-render/src/Silhouette.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-render/src/Silhouette.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview
 * A representation of a Skin's silhouette that can test if a point on the skin
 * renders a pixel where it is drawn.
 */

/**
 * <canvas> element used to update Silhouette data from skin bitmap data.
 * @type {CanvasElement}
 */
let __SilhouetteUpdateCanvas; // Optimized Math.min and Math.max for integers;
// taken from https://web.archive.org/web/20190716181049/http://guihaire.com/code/?p=549


const intMin = (i, j) => j ^ (i ^ j) & i - j >> 31;

const intMax = (i, j) => i ^ (i ^ j) & i - j >> 31;
/**
 * Internal helper function (in hopes that compiler can inline).  Get a pixel
 * from silhouette data, or 0 if outside it's bounds.
 * @private
 * @param {Silhouette} silhouette - has data width and height
 * @param {number} x - x
 * @param {number} y - y
 * @return {number} Alpha value for x/y position
 */


const getPoint = (_ref, x, y) => {
  let {
    _width: width,
    _height: height,
    _colorData: data
  } = _ref;

  // 0 if outside bounds, otherwise read from data.
  if (x >= width || y >= height || x < 0 || y < 0) {
    return 0;
  }

  return data[(y * width + x) * 4 + 3];
};
/**
 * Memory buffers for doing 4 corner sampling for linear interpolation
 */


const __cornerWork = [new Uint8ClampedArray(4), new Uint8ClampedArray(4), new Uint8ClampedArray(4), new Uint8ClampedArray(4)];
/**
 * Get the color from a given silhouette at an x/y local texture position.
 * Multiply color values by alpha for proper blending.
 * @param {Silhouette} $0 The silhouette to sample.
 * @param {number} x X position of texture [0, width).
 * @param {number} y Y position of texture [0, height).
 * @param {Uint8ClampedArray} dst A color 4b space.
 * @return {Uint8ClampedArray} The dst vector.
 */

const getColor4b = (_ref2, x, y, dst) => {
  let {
    _width: width,
    _height: height,
    _colorData: data
  } = _ref2;
  // Clamp coords to edge, matching GL_CLAMP_TO_EDGE.
  // (See github.com/LLK/scratch-render/blob/954cfff02b08069a082cbedd415c1fecd9b1e4fb/src/BitmapSkin.js#L88)
  x = intMax(0, intMin(x, width - 1));
  y = intMax(0, intMin(y, height - 1)); // 0 if outside bounds, otherwise read from data.

  if (x >= width || y >= height || x < 0 || y < 0) {
    return dst.fill(0);
  }

  const offset = (y * width + x) * 4; // premultiply alpha

  const alpha = data[offset + 3] / 255;
  dst[0] = data[offset] * alpha;
  dst[1] = data[offset + 1] * alpha;
  dst[2] = data[offset + 2] * alpha;
  dst[3] = data[offset + 3];
  return dst;
};
/**
 * Get the color from a given silhouette at an x/y local texture position.
 * Do not multiply color values by alpha, as it has already been done.
 * @param {Silhouette} $0 The silhouette to sample.
 * @param {number} x X position of texture [0, width).
 * @param {number} y Y position of texture [0, height).
 * @param {Uint8ClampedArray} dst A color 4b space.
 * @return {Uint8ClampedArray} The dst vector.
 */


const getPremultipliedColor4b = (_ref3, x, y, dst) => {
  let {
    _width: width,
    _height: height,
    _colorData: data
  } = _ref3;
  // Clamp coords to edge, matching GL_CLAMP_TO_EDGE.
  x = intMax(0, intMin(x, width - 1));
  y = intMax(0, intMin(y, height - 1));
  const offset = (y * width + x) * 4;
  dst[0] = data[offset];
  dst[1] = data[offset + 1];
  dst[2] = data[offset + 2];
  dst[3] = data[offset + 3];
  return dst;
};

class Silhouette {
  constructor() {
    /**
     * The width of the data representing the current skin data.
     * @type {number}
     */
    this._width = 0;
    /**
     * The height of the data representing the current skin date.
     * @type {number}
     */

    this._height = 0;
    this._lazyData = null;
    /**
     * The data representing a skin's silhouette shape.
     * @type {Uint8ClampedArray}
     */

    this._colorData = null; // By default, silhouettes are assumed not to contain premultiplied image data,
    // so when we get a color, we want to multiply it by its alpha channel.
    // Point `_getColor` to the version of the function that multiplies.

    this._getColor = getColor4b;

    this.colorAtNearest = this.colorAtLinear = (_, dst) => dst.fill(0);
  }
  /**
   * Update this silhouette with the bitmapData for a skin.
   * @param {ImageData|HTMLCanvasElement|HTMLImageElement} bitmapData An image, canvas or other element that the skin
   * @param {boolean} isPremultiplied True if the source bitmap data comes premultiplied (e.g. from readPixels).
   * rendering can be queried from.
   */


  update(bitmapData) {
    let isPremultiplied = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    let imageData;

    if (bitmapData instanceof ImageData) {
      // If handed ImageData directly, use it directly.
      imageData = bitmapData;
      this._width = bitmapData.width;
      this._height = bitmapData.height;
      this._lazyData = null;
      this._colorData = imageData.data;
    } else {
      // TW: No reason to read the image data now, there's a high chance it won't be needed and will
      // just waste memory and CPU time. We'll read it lazily, only when necessary.
      this._width = bitmapData.width;
      this._height = bitmapData.height;

      if (!(this._width && this._height)) {
        // TW: It might seem really weird to return here before updating anything else, but this is what
        // LLK/scratch-render does.
        return;
      }

      this._lazyData = bitmapData;
      this._colorData = null;
    }

    if (isPremultiplied) {
      this._getColor = getPremultipliedColor4b;
    } else {
      this._getColor = getColor4b;
    } // delete our custom overriden "uninitalized" color functions
    // let the prototype work for itself


    delete this.colorAtNearest;
    delete this.colorAtLinear;
  }

  unlazy() {
    if (!this._lazyData) {
      return;
    }

    const width = this._lazyData.width;
    const height = this._lazyData.height;

    if (width && height) {
      const canvas = Silhouette._updateCanvas();

      canvas.width = width;
      canvas.height = height;
      const ctx = canvas.getContext('2d');
      ctx.clearRect(0, 0, width, height);
      ctx.drawImage(this._lazyData, 0, 0, width, height);
      const textureData = ctx.getImageData(0, 0, width, height);
      this._colorData = textureData.data;
    }

    this._lazyData = null;
  }
  /**
   * Sample a color from the silhouette at a given local position using
   * "nearest neighbor"
   * @param {twgl.v3} vec [x,y] texture space (0-1)
   * @param {Uint8ClampedArray} dst The memory buffer to store the value in. (4 bytes)
   * @returns {Uint8ClampedArray} dst
   */


  colorAtNearest(vec, dst) {
    return this._getColor(this, Math.floor(vec[0] * (this._width - 1)), Math.floor(vec[1] * (this._height - 1)), dst);
  }
  /**
   * Sample a color from the silhouette at a given local position using
   * "linear interpolation"
   * @param {twgl.v3} vec [x,y] texture space (0-1)
   * @param {Uint8ClampedArray} dst The memory buffer to store the value in. (4 bytes)
   * @returns {Uint8ClampedArray} dst
   */


  colorAtLinear(vec, dst) {
    const x = vec[0] * (this._width - 1);
    const y = vec[1] * (this._height - 1);
    const x1D = x % 1;
    const y1D = y % 1;
    const x0D = 1 - x1D;
    const y0D = 1 - y1D;
    const xFloor = Math.floor(x);
    const yFloor = Math.floor(y);

    const x0y0 = this._getColor(this, xFloor, yFloor, __cornerWork[0]);

    const x1y0 = this._getColor(this, xFloor + 1, yFloor, __cornerWork[1]);

    const x0y1 = this._getColor(this, xFloor, yFloor + 1, __cornerWork[2]);

    const x1y1 = this._getColor(this, xFloor + 1, yFloor + 1, __cornerWork[3]);

    dst[0] = x0y0[0] * x0D * y0D + x0y1[0] * x0D * y1D + x1y0[0] * x1D * y0D + x1y1[0] * x1D * y1D;
    dst[1] = x0y0[1] * x0D * y0D + x0y1[1] * x0D * y1D + x1y0[1] * x1D * y0D + x1y1[1] * x1D * y1D;
    dst[2] = x0y0[2] * x0D * y0D + x0y1[2] * x0D * y1D + x1y0[2] * x1D * y0D + x1y1[2] * x1D * y1D;
    dst[3] = x0y0[3] * x0D * y0D + x0y1[3] * x0D * y1D + x1y0[3] * x1D * y0D + x1y1[3] * x1D * y1D;
    return dst;
  }
  /**
   * Test if texture coordinate touches the silhouette using nearest neighbor.
   * @param {twgl.v3} vec A texture coordinate.
   * @return {boolean} If the nearest pixel has an alpha value.
   */


  isTouchingNearest(vec) {
    if (!this._colorData) return;
    return getPoint(this, Math.floor(vec[0] * (this._width - 1)), Math.floor(vec[1] * (this._height - 1))) > 0;
  }
  /**
   * Test to see if any of the 4 pixels used in the linear interpolate touch
   * the silhouette.
   * @param {twgl.v3} vec A texture coordinate.
   * @return {boolean} Any of the pixels have some alpha.
   */


  isTouchingLinear(vec) {
    if (!this._colorData) return;
    const x = Math.floor(vec[0] * (this._width - 1));
    const y = Math.floor(vec[1] * (this._height - 1));
    return getPoint(this, x, y) > 0 || getPoint(this, x + 1, y) > 0 || getPoint(this, x, y + 1) > 0 || getPoint(this, x + 1, y + 1) > 0;
  }
  /**
   * Get the canvas element reused by Silhouettes to update their data with.
   * @private
   * @return {CanvasElement} A canvas to draw bitmap data to.
   */


  static _updateCanvas() {
    if (typeof __SilhouetteUpdateCanvas === 'undefined') {
      __SilhouetteUpdateCanvas = document.createElement('canvas');
    }

    return __SilhouetteUpdateCanvas;
  }

}

module.exports = Silhouette;

/***/ }),

/***/ "./node_modules/scratch-render/src/Skin.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-render/src/Skin.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const RenderConstants = __webpack_require__(/*! ./RenderConstants */ "./node_modules/scratch-render/src/RenderConstants.js");

const Silhouette = __webpack_require__(/*! ./Silhouette */ "./node_modules/scratch-render/src/Silhouette.js");

class Skin {
  /**
   * Create a Skin, which stores and/or generates textures for use in rendering.
   * @param {int} id - The unique ID for this Skin.
   * @param {RenderWebGL} renderer - The renderer which will use this skin.
   * @constructor
   */
  constructor(id, renderer) {
    /** @type {RenderWebGL} */
    this._renderer = renderer;
    /** @type {int} */

    this._id = id;
    /** @type {Vec3} */

    this._rotationCenter = twgl.v3.create(0, 0);
    /** @type {WebGLTexture} */

    this._texture = null;
    /**
     * The uniforms to be used by the vertex and pixel shaders.
     * Some of these are used by other parts of the renderer as well.
     * @type {Object.<string,*>}
     * @private
     */

    this._uniforms = {
      /**
       * The nominal (not necessarily current) size of the current skin.
       * @type {Array<number>}
       */
      u_skinSize: [0, 0],

      /**
       * The actual WebGL texture object for the skin.
       * @type {WebGLTexture}
       */
      u_skin: null
    };
    /**
     * A silhouette to store touching data, skins are responsible for keeping it up to date.
     * @protected
     */

    this._silhouette = new Silhouette();
    /**
     * Whether this skin might include private information about the user.
     */

    this.private = false;
  }
  /**
   * Dispose of this object. Do not use it after calling this method.
   */


  dispose() {
    this._id = RenderConstants.ID_NONE;
  }
  /**
   * @return {int} the unique ID for this Skin.
   */


  get id() {
    return this._id;
  }
  /**
   * @returns {Vec3} the origin, in object space, about which this Skin should rotate.
   */


  get rotationCenter() {
    return this._rotationCenter;
  }
  /**
   * @abstract
   * @return {Array<number>} the "native" size, in texels, of this skin.
   */


  get size() {
    return [0, 0];
  }
  /**
   * Should this skin's texture be filtered with nearest-neighbor or linear interpolation at the given scale?
   * @param {?Array<Number>} scale The screen-space X and Y scaling factors at which this skin's texture will be
   * displayed, as percentages (100 means 1 "native size" unit is 1 screen pixel; 200 means 2 screen pixels, etc).
   * @param {Drawable} drawable The drawable that this skin's texture will be applied to.
   * @return {boolean} True if this skin's texture, as returned by {@link getTexture}, should be filtered with
   * nearest-neighbor interpolation.
   */
  // eslint-disable-next-line no-unused-vars


  useNearest(scale, drawable) {
    return true;
  }
  /**
   * Get the center of the current bounding box
   * @return {Array<number>} the center of the current bounding box
   */


  calculateRotationCenter() {
    return [this.size[0] / 2, this.size[1] / 2];
  }
  /**
   * @abstract
   * @param {Array<number>} scale - The scaling factors to be used.
   * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given size.
   */
  // eslint-disable-next-line no-unused-vars


  getTexture(scale) {
    return this._emptyImageTexture;
  }
  /**
   * Get the bounds of the drawable for determining its fenced position.
   * @param {Array<number>} drawable - The Drawable instance this skin is using.
   * @param {?Rectangle} result - Optional destination for bounds calculation.
   * @return {!Rectangle} The drawable's bounds. For compatibility with Scratch 2, we always use getAABB.
   */


  getFenceBounds(drawable, result) {
    return drawable.getAABB(result);
  }
  /**
   * Update and returns the uniforms for this skin.
   * @param {Array<number>} scale - The scaling factors to be used.
   * @returns {object.<string, *>} the shader uniforms to be used when rendering with this Skin.
   */


  getUniforms(scale) {
    this._uniforms.u_skin = this.getTexture(scale);
    this._uniforms.u_skinSize = this.size;
    return this._uniforms;
  }

  emitWasAltered() {
    this._renderer.skinWasAltered(this);
  }
  /**
   * If the skin defers silhouette operations until the last possible minute,
   * this will be called before isTouching uses the silhouette.
   */


  updateSilhouette() {
    this._silhouette.unlazy();
  }
  /**
   * Set this skin's texture to the given image.
   * @param {ImageData|HTMLCanvasElement} textureData - The canvas or image data to set the texture to.
   */


  _setTexture(textureData) {
    const gl = this._renderer.gl;
    gl.bindTexture(gl.TEXTURE_2D, this._texture); // Premultiplied alpha is necessary for proper blending.
    // See http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/

    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureData);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);

    this._silhouette.update(textureData);
  }
  /**
   * Set the contents of this skin to an empty skin.
   * @fires Skin.event:WasAltered
   */


  setEmptyImageData() {
    // Free up the current reference to the _texture
    this._texture = null;

    if (!this._emptyImageData) {
      // Create a transparent pixel
      this._emptyImageData = new ImageData(1, 1); // Create a new texture and update the silhouette

      const gl = this._renderer.gl;
      const textureOptions = {
        auto: true,
        wrap: gl.CLAMP_TO_EDGE,
        src: this._emptyImageData
      }; // Note: we're using _emptyImageTexture here instead of _texture
      // so that we can cache this empty texture for later use as needed.
      // this._texture can get modified by other skins (e.g. BitmapSkin
      // and SVGSkin, so we can't use that same field for caching)

      this._emptyImageTexture = twgl.createTexture(gl, textureOptions);
    }

    this._rotationCenter[0] = 0;
    this._rotationCenter[1] = 0;

    this._silhouette.update(this._emptyImageData);

    this.emitWasAltered();
  }
  /**
   * Does this point touch an opaque or translucent point on this skin?
   * Nearest Neighbor version
   * The caller is responsible for ensuring this skin's silhouette is up-to-date.
   * @see updateSilhouette
   * @see Drawable.updateCPURenderAttributes
   * @param {twgl.v3} vec A texture coordinate.
   * @return {boolean} Did it touch?
   */


  isTouchingNearest(vec) {
    return this._silhouette.isTouchingNearest(vec);
  }
  /**
   * Does this point touch an opaque or translucent point on this skin?
   * Linear Interpolation version
   * The caller is responsible for ensuring this skin's silhouette is up-to-date.
   * @see updateSilhouette
   * @see Drawable.updateCPURenderAttributes
   * @param {twgl.v3} vec A texture coordinate.
   * @return {boolean} Did it touch?
   */


  isTouchingLinear(vec) {
    return this._silhouette.isTouchingLinear(vec);
  }

}

module.exports = Skin;

/***/ }),

/***/ "./node_modules/scratch-render/src/TextBubbleSkin.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-render/src/TextBubbleSkin.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const twgl = __webpack_require__(/*! twgl.js */ "./node_modules/twgl.js/dist/4.x/twgl-full.js");

const CanvasMeasurementProvider = __webpack_require__(/*! ./util/canvas-measurement-provider */ "./node_modules/scratch-render/src/util/canvas-measurement-provider.js");

const Skin = __webpack_require__(/*! ./Skin */ "./node_modules/scratch-render/src/Skin.js");

let _TextWrapper;

const getTextWrapper = () => {
  if (!_TextWrapper) {
    // eslint-disable-next-line global-require
    _TextWrapper = __webpack_require__(/*! ./util/text-wrapper */ "./node_modules/scratch-render/src/util/text-wrapper.js");
  }

  return _TextWrapper;
};

const BubbleStyle = {
  MAX_LINE_WIDTH: 170,
  // Maximum width, in Scratch pixels, of a single line of text
  MIN_WIDTH: 50,
  // Minimum width, in Scratch pixels, of a text bubble
  STROKE_WIDTH: 4,
  // Thickness of the stroke around the bubble. Only half's visible because it's drawn under the fill
  PADDING: 10,
  // Padding around the text area
  CORNER_RADIUS: 16,
  // Radius of the rounded corners
  TAIL_HEIGHT: 12,
  // Height of the speech bubble's "tail". Probably should be a constant.
  FONT: 'Helvetica',
  // Font to render the text with
  FONT_SIZE: 14,
  // Font size, in Scratch pixels
  FONT_HEIGHT_RATIO: 0.9,
  // Height, in Scratch pixels, of the text, as a proportion of the font's size
  LINE_HEIGHT: 16,
  // Spacing between each line of text
  COLORS: {
    BUBBLE_FILL: 'white',
    BUBBLE_STROKE: 'rgba(0, 0, 0, 0.15)',
    TEXT_FILL: '#575E75'
  }
};
const MAX_SCALE = 10;

class TextBubbleSkin extends Skin {
  /**
   * Create a new text bubble skin.
   * @param {!int} id - The ID for this Skin.
   * @param {!RenderWebGL} renderer - The renderer which will use this skin.
   * @constructor
   * @extends Skin
   */
  constructor(id, renderer) {
    super(id, renderer);
    /** @type {HTMLCanvasElement} */

    this._canvas = document.createElement('canvas');
    /** @type {Array<number>} */

    this._size = [0, 0];
    /** @type {number} */

    this._renderedScale = 0;
    /** @type {Array<string>} */

    this._lines = [];
    /** @type {object} */

    this._textAreaSize = {
      width: 0,
      height: 0
    };
    /** @type {string} */

    this._bubbleType = '';
    /** @type {boolean} */

    this._pointsLeft = false;
    /** @type {boolean} */

    this._textDirty = true;
    /** @type {boolean} */

    this._textureDirty = true;
    this.measurementProvider = new CanvasMeasurementProvider(this._canvas.getContext('2d'));
    this.textWrapper = new (getTextWrapper())(this.measurementProvider);

    this._restyleCanvas();
  }
  /**
   * Dispose of this object. Do not use it after calling this method.
   */


  dispose() {
    if (this._texture) {
      this._renderer.gl.deleteTexture(this._texture);

      this._texture = null;
    }

    this._canvas = null;
    super.dispose();
  }
  /**
   * @return {Array<number>} the dimensions, in Scratch units, of this skin.
   */


  get size() {
    if (this._textDirty) {
      this._reflowLines();
    }

    return this._size;
  }
  /**
   * Set parameters for this text bubble.
   * @param {!string} type - either "say" or "think".
   * @param {!string} text - the text for the bubble.
   * @param {!boolean} pointsLeft - which side the bubble is pointing.
   */


  setTextBubble(type, text, pointsLeft) {
    this._text = text;
    this._bubbleType = type;
    this._pointsLeft = pointsLeft;
    this._textDirty = true;
    this._textureDirty = true;
    this.emitWasAltered();
  }
  /**
   * Re-style the canvas after resizing it. This is necessary to ensure proper text measurement.
   */


  _restyleCanvas() {
    this._canvas.getContext('2d').font = "".concat(BubbleStyle.FONT_SIZE, "px ").concat(BubbleStyle.FONT, ", sans-serif");
  }
  /**
   * Update the array of wrapped lines and the text dimensions.
   */


  _reflowLines() {
    this._lines = this.textWrapper.wrapText(BubbleStyle.MAX_LINE_WIDTH, this._text); // Measure width of longest line to avoid extra-wide bubbles

    let longestLineWidth = 0;

    for (const line of this._lines) {
      longestLineWidth = Math.max(longestLineWidth, this.measurementProvider.measureText(line));
    } // Calculate the canvas-space sizes of the padded text area and full text bubble


    const paddedWidth = Math.max(longestLineWidth, BubbleStyle.MIN_WIDTH) + BubbleStyle.PADDING * 2;
    const paddedHeight = BubbleStyle.LINE_HEIGHT * this._lines.length + BubbleStyle.PADDING * 2;
    this._textAreaSize.width = paddedWidth;
    this._textAreaSize.height = paddedHeight;
    this._size[0] = paddedWidth + BubbleStyle.STROKE_WIDTH;
    this._size[1] = paddedHeight + BubbleStyle.STROKE_WIDTH + BubbleStyle.TAIL_HEIGHT;
    this._textDirty = false;
  }
  /**
   * Render this text bubble at a certain scale, using the current parameters, to the canvas.
   * @param {number} scale The scale to render the bubble at
   */


  _renderTextBubble(scale) {
    const ctx = this._canvas.getContext('2d');

    if (this._textDirty) {
      this._reflowLines();
    } // Calculate the canvas-space sizes of the padded text area and full text bubble


    const paddedWidth = this._textAreaSize.width;
    const paddedHeight = this._textAreaSize.height; // Resize the canvas to the correct screen-space size

    this._canvas.width = Math.ceil(this._size[0] * scale);
    this._canvas.height = Math.ceil(this._size[1] * scale);

    this._restyleCanvas(); // Reset the transform before clearing to ensure 100% clearage


    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
    ctx.scale(scale, scale);
    ctx.translate(BubbleStyle.STROKE_WIDTH * 0.5, BubbleStyle.STROKE_WIDTH * 0.5); // If the text bubble points leftward, flip the canvas

    ctx.save();

    if (this._pointsLeft) {
      ctx.scale(-1, 1);
      ctx.translate(-paddedWidth, 0);
    } // Draw the bubble's rounded borders


    ctx.beginPath();
    ctx.moveTo(BubbleStyle.CORNER_RADIUS, paddedHeight);
    ctx.arcTo(0, paddedHeight, 0, paddedHeight - BubbleStyle.CORNER_RADIUS, BubbleStyle.CORNER_RADIUS);
    ctx.arcTo(0, 0, paddedWidth, 0, BubbleStyle.CORNER_RADIUS);
    ctx.arcTo(paddedWidth, 0, paddedWidth, paddedHeight, BubbleStyle.CORNER_RADIUS);
    ctx.arcTo(paddedWidth, paddedHeight, paddedWidth - BubbleStyle.CORNER_RADIUS, paddedHeight, BubbleStyle.CORNER_RADIUS); // Translate the canvas so we don't have to do a bunch of width/height arithmetic

    ctx.save();
    ctx.translate(paddedWidth - BubbleStyle.CORNER_RADIUS, paddedHeight); // Draw the bubble's "tail"

    if (this._bubbleType === 'say') {
      // For a speech bubble, draw one swoopy thing
      ctx.bezierCurveTo(0, 4, 4, 8, 4, 10);
      ctx.arcTo(4, 12, 2, 12, 2);
      ctx.bezierCurveTo(-1, 12, -11, 8, -16, 0);
      ctx.closePath();
    } else {
      // For a thinking bubble, draw a partial circle attached to the bubble...
      ctx.arc(-16, 0, 4, 0, Math.PI);
      ctx.closePath(); // and two circles detached from it

      ctx.moveTo(-7, 7.25);
      ctx.arc(-9.25, 7.25, 2.25, 0, Math.PI * 2);
      ctx.moveTo(0, 9.5);
      ctx.arc(-1.5, 9.5, 1.5, 0, Math.PI * 2);
    } // Un-translate the canvas and fill + stroke the text bubble


    ctx.restore();
    ctx.fillStyle = BubbleStyle.COLORS.BUBBLE_FILL;
    ctx.strokeStyle = BubbleStyle.COLORS.BUBBLE_STROKE;
    ctx.lineWidth = BubbleStyle.STROKE_WIDTH;
    ctx.stroke();
    ctx.fill(); // Un-flip the canvas if it was flipped

    ctx.restore(); // Draw each line of text

    ctx.fillStyle = BubbleStyle.COLORS.TEXT_FILL;
    ctx.font = "".concat(BubbleStyle.FONT_SIZE, "px ").concat(BubbleStyle.FONT, ", sans-serif");
    const lines = this._lines;

    for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) {
      const line = lines[lineNumber];
      ctx.fillText(line, BubbleStyle.PADDING, BubbleStyle.PADDING + BubbleStyle.LINE_HEIGHT * lineNumber + BubbleStyle.FONT_HEIGHT_RATIO * BubbleStyle.FONT_SIZE);
    }

    this._renderedScale = scale;
  }

  updateSilhouette() {
    let scale = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [100, 100];
    // Ensure a silhouette exists.
    this.getTexture(scale);
  }
  /**
   * @param {Array<number>} scale - The scaling factors to be used, each in the [0,100] range.
   * @return {WebGLTexture} The GL texture representation of this skin when drawing at the given scale.
   */


  getTexture(scale) {
    // The texture only ever gets uniform scale. Take the larger of the two axes.
    const scaleMax = scale ? Math.max(Math.abs(scale[0]), Math.abs(scale[1])) : 100;
    const requestedScale = Math.min(MAX_SCALE, scaleMax / 100); // If we already rendered the text bubble at this scale, we can skip re-rendering it.

    if (this._textureDirty || this._renderedScale !== requestedScale) {
      this._renderTextBubble(requestedScale);

      this._textureDirty = false;

      const context = this._canvas.getContext('2d');

      const textureData = context.getImageData(0, 0, this._canvas.width, this._canvas.height);
      const gl = this._renderer.gl;

      if (this._texture === null) {
        const textureOptions = {
          auto: false,
          wrap: gl.CLAMP_TO_EDGE
        };
        this._texture = twgl.createTexture(gl, textureOptions);
      }

      this._setTexture(textureData);
    }

    return this._texture;
  }

}

module.exports = TextBubbleSkin;

/***/ }),

/***/ "./node_modules/scratch-render/src/index.js":
/*!**************************************************!*\
  !*** ./node_modules/scratch-render/src/index.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const RenderWebGL = __webpack_require__(/*! ./RenderWebGL */ "./node_modules/scratch-render/src/RenderWebGL.js");
/**
 * Export for NPM & Node.js
 * @type {RenderWebGL}
 */


module.exports = RenderWebGL;

/***/ }),

/***/ "./node_modules/scratch-render/src/util/canvas-measurement-provider.js":
/*!*****************************************************************************!*\
  !*** ./node_modules/scratch-render/src/util/canvas-measurement-provider.js ***!
  \*****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class CanvasMeasurementProvider {
  /**
   * @param {CanvasRenderingContext2D} ctx - provides a canvas rendering context
   * with 'font' set to the text style of the text to be wrapped.
   */
  constructor(ctx) {
    this._ctx = ctx;
    this._cache = {};
  } // We don't need to set up or tear down anything here. Should these be removed altogether?

  /**
   * Called by the TextWrapper before a batch of zero or more calls to measureText().
   */


  beginMeasurementSession() {}
  /**
   * Called by the TextWrapper after a batch of zero or more calls to measureText().
   */


  endMeasurementSession() {}
  /**
   * Measure a whole string as one unit.
   * @param {string} text - the text to measure.
   * @returns {number} - the length of the string.
   */


  measureText(text) {
    if (!this._cache[text]) {
      this._cache[text] = this._ctx.measureText(text).width;
    }

    return this._cache[text];
  }

}

module.exports = CanvasMeasurementProvider;

/***/ }),

/***/ "./node_modules/scratch-render/src/util/color-conversions.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-render/src/util/color-conversions.js ***!
  \*******************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Converts an RGB color value to HSV. Conversion formula
 * adapted from http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv.
 * Assumes r, g, and b are in the range [0, 255] and
 * returns h, s, and v in the range [0, 1].
 *
 * @param   {Array<number>} rgb   The RGB color value
 * @param   {number}        rgb.r The red color value
 * @param   {number}        rgb.g The green color value
 * @param   {number}        rgb.b The blue color value
 * @param   {Array<number>} dst   The array to store the HSV values in
 * @return  {Array<number>}       The `dst` array passed in
 */
const rgbToHsv = (_ref, dst) => {
  let [r, g, b] = _ref;
  let K = 0.0;
  r /= 255;
  g /= 255;
  b /= 255;
  let tmp = 0;

  if (g < b) {
    tmp = g;
    g = b;
    b = tmp;
    K = -1;
  }

  if (r < g) {
    tmp = r;
    r = g;
    g = tmp;
    K = -2 / 6 - K;
  }

  const chroma = r - Math.min(g, b);
  const h = Math.abs(K + (g - b) / (6 * chroma + Number.EPSILON));
  const s = chroma / (r + Number.EPSILON);
  const v = r;
  dst[0] = h;
  dst[1] = s;
  dst[2] = v;
  return dst;
};
/**
 * Converts an HSV color value to RGB. Conversion formula
 * adapted from https://gist.github.com/mjackson/5311256.
 * Assumes h, s, and v are contained in the set [0, 1] and
 * returns r, g, and b in the set [0, 255].
 *
 * @param   {Array<number>}                hsv The HSV color value
 * @param   {number}                       hsv.h     The hue
 * @param   {number}                       hsv.s     The saturation
 * @param   {number}                       hsv.v     The value
 * @param   {Uint8Array|Uint8ClampedArray} dst The array to store the RGB values in
 * @return  {Uint8Array|Uint8ClampedArray}     The `dst` array passed in
 */


const hsvToRgb = (_ref2, dst) => {
  let [h, s, v] = _ref2;

  if (s === 0) {
    dst[0] = dst[1] = dst[2] = v * 255 + 0.5;
    return dst;
  } // keep hue in [0,1) so the `switch(i)` below only needs 6 cases (0-5)


  h %= 1;
  const i = h * 6 | 0;
  const f = h * 6 - i;
  const p = v * (1 - s);
  const q = v * (1 - s * f);
  const t = v * (1 - s * (1 - f));
  let r = 0;
  let g = 0;
  let b = 0;

  switch (i) {
    case 0:
      r = v;
      g = t;
      b = p;
      break;

    case 1:
      r = q;
      g = v;
      b = p;
      break;

    case 2:
      r = p;
      g = v;
      b = t;
      break;

    case 3:
      r = p;
      g = q;
      b = v;
      break;

    case 4:
      r = t;
      g = p;
      b = v;
      break;

    case 5:
      r = v;
      g = p;
      b = q;
      break;
  } // Add 0.5 in order to round. Setting integer TypedArray elements implicitly floors.


  dst[0] = r * 255 + 0.5;
  dst[1] = g * 255 + 0.5;
  dst[2] = b * 255 + 0.5;
  return dst;
};

module.exports = {
  rgbToHsv,
  hsvToRgb
};

/***/ }),

/***/ "./node_modules/scratch-render/src/util/log.js":
/*!*****************************************************!*\
  !*** ./node_modules/scratch-render/src/util/log.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const minilog = __webpack_require__(/*! minilog */ "./node_modules/minilog/lib/web/index.js");

minilog.enable();
module.exports = minilog('scratch-render');

/***/ }),

/***/ "./node_modules/scratch-render/src/util/text-wrapper.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-render/src/util/text-wrapper.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const LineBreaker = __webpack_require__(/*! ify-loader!linebreak */ "./node_modules/ify-loader/index.js!./node_modules/linebreak/src/linebreaker.js");

const GraphemeBreaker = __webpack_require__(/*! ify-loader!grapheme-breaker */ "./node_modules/ify-loader/index.js!./node_modules/grapheme-breaker/src/GraphemeBreaker.js");
/**
 * Tell this text wrapper to use a specific measurement provider.
 * @typedef {object} MeasurementProvider - the new measurement provider.
 * @property {Function} beginMeasurementSession - this will be called before a batch of measurements are made.
 *      Optionally, this function may return an object to be provided to the endMeasurementSession function.
 * @property {Function} measureText - this will be called each time a piece of text must be measured.
 * @property {Function} endMeasurementSession - this will be called after a batch of measurements is finished.
 *      It will be passed whatever value beginMeasurementSession returned, if any.
 */

/**
 * Utility to wrap text across several lines, respecting Unicode grapheme clusters and, when possible, Unicode line
 * break opportunities.
 * Reference material:
 * - Unicode Standard Annex #14: http://unicode.org/reports/tr14/
 * - Unicode Standard Annex #29: http://unicode.org/reports/tr29/
 * - "JavaScript has a Unicode problem" by Mathias Bynens: https://mathiasbynens.be/notes/javascript-unicode
 */


class TextWrapper {
  /**
   * Construct a text wrapper which will measure text using the specified measurement provider.
   * @param {MeasurementProvider} measurementProvider - a helper object to provide text measurement services.
   */
  constructor(measurementProvider) {
    this._measurementProvider = measurementProvider;
    this._cache = {};
  }
  /**
   * Wrap the provided text into lines restricted to a maximum width. See Unicode Standard Annex (UAX) #14.
   * @param {number} maxWidth - the maximum allowed width of a line.
   * @param {string} text - the text to be wrapped. Will be split on whitespace.
   * @returns {Array.<string>} an array containing the wrapped lines of text.
   */


  wrapText(maxWidth, text) {
    // Normalize to canonical composition (see Unicode Standard Annex (UAX) #15)
    text = text.normalize();
    const cacheKey = "".concat(maxWidth, "-").concat(text);

    if (this._cache[cacheKey]) {
      return this._cache[cacheKey];
    }

    const measurementSession = this._measurementProvider.beginMeasurementSession();

    const breaker = new LineBreaker(text);
    let lastPosition = 0;
    let nextBreak;
    let currentLine = null;
    const lines = [];

    while (nextBreak = breaker.nextBreak()) {
      const word = text.slice(lastPosition, nextBreak.position).replace(/\n+$/, '');
      let proposedLine = (currentLine || '').concat(word);

      let proposedLineWidth = this._measurementProvider.measureText(proposedLine);

      if (proposedLineWidth > maxWidth) {
        // The next word won't fit on this line. Will it fit on a line by itself?
        const wordWidth = this._measurementProvider.measureText(word);

        if (wordWidth > maxWidth) {
          // The next word can't even fit on a line by itself. Consume it one grapheme cluster at a time.
          let lastCluster = 0;
          let nextCluster;

          while (lastCluster !== (nextCluster = GraphemeBreaker.nextBreak(word, lastCluster))) {
            const cluster = word.substring(lastCluster, nextCluster);
            proposedLine = (currentLine || '').concat(cluster);
            proposedLineWidth = this._measurementProvider.measureText(proposedLine);

            if (currentLine === null || proposedLineWidth <= maxWidth) {
              // first cluster of a new line or the cluster fits
              currentLine = proposedLine;
            } else {
              // no more can fit
              lines.push(currentLine);
              currentLine = cluster;
            }

            lastCluster = nextCluster;
          }
        } else {
          // The next word can fit on the next line. Finish the current line and move on.
          if (currentLine !== null) lines.push(currentLine);
          currentLine = word;
        }
      } else {
        // The next word fits on this line. Just keep going.
        currentLine = proposedLine;
      } // Did we find a \n or similar?


      if (nextBreak.required) {
        if (currentLine !== null) lines.push(currentLine);
        currentLine = null;
      }

      lastPosition = nextBreak.position;
    }

    currentLine = currentLine || '';

    if (currentLine.length > 0 || lines.length === 0) {
      lines.push(currentLine);
    }

    this._cache[cacheKey] = lines;

    this._measurementProvider.endMeasurementSession(measurementSession);

    return lines;
  }

}

module.exports = TextWrapper;

/***/ }),

/***/ "./node_modules/scratch-sb1-converter/index.js":
/*!*****************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/index.js ***!
  \*****************************************************/
/*! exports provided: SB1File, AssertionError, ValidationError */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _src_sb1_file__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./src/sb1-file */ "./node_modules/scratch-sb1-converter/src/sb1-file.js");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "SB1File", function() { return _src_sb1_file__WEBPACK_IMPORTED_MODULE_0__["SB1File"]; });

/* harmony import */ var _src_util_assert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./src/util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "AssertionError", function() { return _src_util_assert__WEBPACK_IMPORTED_MODULE_1__["AssertionError"]; });

/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "ValidationError", function() { return _src_util_assert__WEBPACK_IMPORTED_MODULE_1__["ValidationError"]; });





/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/adler32.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/adler32.js ***!
  \******************************************************************/
/*! exports provided: Adler32 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Adler32", function() { return Adler32; });
class Adler32 {
  constructor() {
    this.adler = 1;
  }

  update(uint8a, position, length) {
    let a = this.adler & 0xffff;
    let b = this.adler >>> 16;

    for (let i = 0; i < length; i++) {
      a = (a + uint8a[position + i]) % 65521;
      b = (b + a) % 65521;
    }

    this.adler = b << 16 | a;
    return this;
  }

  get digest() {
    return this.adler;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/byte-packets.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/byte-packets.js ***!
  \***********************************************************************/
/*! exports provided: Packet */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Packet", function() { return Packet; });
/**
 * @typedef {function} PacketConstructor
 */

/**
 * A packet of bytes represented with getter/setter properties for decoding and
 * encoding values mapped to names, located at known offsets.
 *
 * ```js
 * // Defining a subclass:
 * import {Packet} from '../coders/byte-packets';
 * import {Uint8, Uint16LE} from '../coders/byte-primitives';
 *
 * class MyIdentifiedUint16 extends Packet.extend({
 *     binaryType: Uint8,
 *     value: Uint16LE
 * }) {}
 *
 * Packet.initConstructor(MyIdentifiedUint16);
 *
 * // One way to use it:
 * const indentifiedUint16 = new MyIdentifiedUint16(uint8a, position);
 * indentifiedUint16.binaryType = IDENTIFIED_UINT_16;
 * indentifiedUint16.value = value;
 * ```
 */
class Packet {
  /**
   * @param {Uint8Array=} [uint8a=new Uint8Array(this.size)] - byte array to
   * encode to and decode from
   * @param {number=} offset - offset in addition to the member offsets to
   * encode to and decode from
   */
  constructor() {
    let uint8a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Uint8Array(this.size);
    let offset = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    /**
     * Byte array to encode to and decode from.
     * @type {Uint8Array}
     */
    this.uint8a = uint8a;
    /**
     * Offset in addition to the member offsets to encode to and decode
     * from.
     * @type {number}
     */

    this.offset = offset;
  }
  /**
   * Check that the decoded values of this Packet match the values in other.
   * @param {object} other - object to match against
   * @returns {boolean} true if all keys in other match values in this packet
   */


  equals(other) {
    for (const key in other) {
      if (this[key] !== other[key]) {
        return false;
      }
    }

    return true;
  }

  view() {
    const className = this.constructor.name;
    const obj = {
      toString() {
        return className;
      }

    };

    for (const key in this.shape) {
      obj[key] = this[key];
    }

    return obj;
  }
  /**
   * Initialize the Packet subclass constructor for easy access to static
   * members like size.
   * @param {function} PacketConstructor - constuctor function for the Packet
   * subclass
   * @returns {function} initialized constructor
   */


  static initConstructor(PacketConstructor) {
    PacketConstructor.size = PacketConstructor.prototype.size;
    return PacketConstructor;
  }
  /**
   * Extend a subclass of Packet with given BytePrimitive members.
   * @param {object} shape - shape of the packet defined with BytePrimitives
   * @returns {function} Packet subclass constructor
   */


  static extend(shape) {
    const DefinedPacket = class extends Packet {
      get shape() {
        return shape;
      }

    };
    let position = 0;
    Object.keys(shape).forEach(key => {
      Object.defineProperty(DefinedPacket.prototype, key, shape[key].asPropertyObject(position));

      if (shape[key].size === 0) {
        throw new Error('Packet cannot be defined with variable sized members.');
      }

      position += shape[key].size;
    });
    DefinedPacket.prototype.size = position;
    DefinedPacket.size = position;
    return DefinedPacket;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js ***!
  \**************************************************************************/
/*! exports provided: IS_HOST_LITTLE_ENDIAN, BytePrimitive, Uint8, Uint16BE, Int16BE, Int32BE, Uint32BE, Uint16LE, Uint32LE, DoubleBE, FixedAsciiString */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "IS_HOST_LITTLE_ENDIAN", function() { return IS_HOST_LITTLE_ENDIAN; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BytePrimitive", function() { return BytePrimitive; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Uint8", function() { return Uint8; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Uint16BE", function() { return Uint16BE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Int16BE", function() { return Int16BE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Int32BE", function() { return Int32BE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Uint32BE", function() { return Uint32BE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Uint16LE", function() { return Uint16LE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Uint32LE", function() { return Uint32LE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DoubleBE", function() { return DoubleBE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FixedAsciiString", function() { return FixedAsciiString; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");


const notImplemented = () => {
  throw new Error('Not implemented');
};
/**
 * Is the host computer little or big endian.
 * @const IS_HOST_LITTLE_ENDIAN
 * @type {boolean}
 */


const IS_HOST_LITTLE_ENDIAN = (() => {
  const ab16 = new Uint16Array(1);
  const ab8 = new Uint8Array(ab16.buffer);
  ab16[0] = 0xaabb;
  return ab8[0] === 0xbb;
})();
/**
 * @callback BytePrimitive~sizeOfCallback
 * @param {Uint8Array} uint8a
 * @param {number} position
 * @returns {number}
 */

/**
 * @callback BytePrimitive~writeSizeOfCallback
 * @param {Uint8Array} uint8a
 * @param {number} position
 * @param {*} value
 * @returns {number}
 */

/**
 * @callback BytePrimitive~writeCallback
 * @param {Uint8Array} uint8a
 * @param {number} position
 * @param {*} value
 */

/**
 * An interface for reading and writing binary values to typed arrays.
 *
 * Combined with {@link Packet Packet} this makes reading and writing packets
 * of binary values easy.
 */


class BytePrimitive {
  /**
   * @constructor
   * @param {object} options - Options to initialize BytePrimitive instance
   * with.
   * @param {number} [options.size=0] - Fixed size of the BytePrimitive.
   * Should be 0 if the primitive has a variable size.
   * @param {BytePrimitive~sizeOfCallback} [options.sizeOf=() => size] -
   * Variable size of the primitive depending on its value stored in the
   * array.
   * @param {BytePrimitive~writeSizeOfCallback} [options.writeSizeOf] -
   * Variable size of the primitive depending on the value being written.
   * @param {TypedArray} [options.toBytes=new Uint8Array(1)] - Temporary
   * space to copy bytes to/from to translate between a value and its
   * representative byte set.
   * @param {BytePrimitive#read} options.read - How is a value read
   * at the given position of the array.
   * @param {BytePrimitive~writeCallback} [options.write] - How to write a
   * value to the array at the given position.
   */
  constructor(_ref) {
    let {
      size = 0,
      sizeOf = () => size,
      writeSizeOf = notImplemented,
      toBytes = new Uint8Array(1),
      read,
      write = notImplemented
    } = _ref;
    this.size = size;
    this.sizeOf = sizeOf;
    this.writeSizeOf = writeSizeOf;
    this.toBytes = toBytes;
    this.bytes = new Uint8Array(toBytes.buffer);
    this.read = read;
    this.write = write;
  }
  /**
   * Create an object that can be used with `Object.defineProperty` to read
   * and write values offset by `position` and the object's `this.offset`
   * from `this.uint8a` by getting or setting the property.
   * @param {number} position - Additional offset with `this.offset` to read
   * from or write to.
   * @returns {object} - A object that can be passed as the third argument to
   * `Object.defineProperty`.
   */


  asPropertyObject(position) {
    const _this = this;

    return {
      get() {
        return _this.read(this.uint8a, position + this.offset);
      },

      set(value) {
        return _this.write(this.uint8a, position + this.offset, value);
      },

      enumerable: true
    };
  }
  /**
   * Read a value from `position` in `uint8a`.
   * @param {Uint8Array} uint8a - Array to read from.
   * @param {number} position - Position in `uint8a` to read from.
   * @returns {*} - Value read from `uint8a` at `position`.
   */


  read() {
    return null;
  }

}
/**
 * @const Uint8
 * @type {BytePrimitive}
 */


const Uint8 = new BytePrimitive({
  size: 1,

  read(uint8a, position) {
    return uint8a[position];
  },

  write(uint8a, position, value) {
    uint8a[position] = value;
    return value;
  }

});
const HOSTLE_BE16 = {
  size: 2,

  // toBytes: Defined by instance.
  read(uint8a, position) {
    this.bytes[1] = uint8a[position + 0];
    this.bytes[0] = uint8a[position + 1];
    return this.toBytes[0];
  },

  write(uint8a, position, value) {
    this.toBytes[0] = value;
    uint8a[position + 0] = this.bytes[1];
    uint8a[position + 1] = this.bytes[0];
    return value;
  }

};
const HOSTBE_BE16 = {
  size: 2,

  // toBytes: Defined by instance.
  read(uint8a, position) {
    this.bytes[0] = uint8a[position + 0];
    this.bytes[1] = uint8a[position + 1];
    return this.toBytes[0];
  },

  write(uint8a, position, value) {
    this.toBytes[0] = value;
    uint8a[position + 0] = this.bytes[0];
    uint8a[position + 1] = this.bytes[1];
    return value;
  }

};
let BE16;

if (IS_HOST_LITTLE_ENDIAN) {
  BE16 = HOSTLE_BE16;
} else {
  BE16 = HOSTBE_BE16;
}
/**
 * @const Uint16BE
 * @type {BytePrimitive}
 */


const Uint16BE = new BytePrimitive(Object.assign({}, BE16, {
  toBytes: new Uint16Array(1)
}));
/**
 * @const Int16BE
 * @type {BytePrimitive}
 */

const Int16BE = new BytePrimitive(Object.assign({}, BE16, {
  toBytes: new Int16Array(1)
}));
const HOSTLE_BE32 = {
  size: 4,

  // toBytes: Defined by instance.
  read(uint8a, position) {
    this.bytes[3] = uint8a[position + 0];
    this.bytes[2] = uint8a[position + 1];
    this.bytes[1] = uint8a[position + 2];
    this.bytes[0] = uint8a[position + 3];
    return this.toBytes[0];
  },

  write(uint8a, position, value) {
    this.toBytes[0] = value;
    uint8a[position + 0] = this.bytes[3];
    uint8a[position + 1] = this.bytes[2];
    uint8a[position + 2] = this.bytes[1];
    uint8a[position + 3] = this.bytes[0];
    return value;
  }

};
const HOSTBE_BE32 = {
  size: 4,

  // toBytes: Defined by instance.
  read(uint8a, position) {
    this.bytes[0] = uint8a[position + 0];
    this.bytes[1] = uint8a[position + 1];
    this.bytes[2] = uint8a[position + 2];
    this.bytes[3] = uint8a[position + 3];
    return this.toBytes[0];
  },

  write(uint8a, position, value) {
    this.toBytes[0] = value;
    uint8a[position + 0] = this.bytes[0];
    uint8a[position + 1] = this.bytes[1];
    uint8a[position + 2] = this.bytes[2];
    uint8a[position + 3] = this.bytes[3];
    return value;
  }

};
let BE32;

if (IS_HOST_LITTLE_ENDIAN) {
  BE32 = HOSTLE_BE32;
} else {
  BE32 = HOSTBE_BE32;
}
/**
 * @const Int32BE
 * @type {BytePrimitive}
 */


const Int32BE = new BytePrimitive(Object.assign({}, BE32, {
  toBytes: new Int32Array(1)
}));
/**
 * @const Uint32BE
 * @type {BytePrimitive}
 */

const Uint32BE = new BytePrimitive(Object.assign({}, BE32, {
  toBytes: new Uint32Array(1)
}));
let LE16;

if (IS_HOST_LITTLE_ENDIAN) {
  LE16 = HOSTBE_BE16;
} else {
  LE16 = HOSTLE_BE16;
}
/**
 * @const Uint16LE
 * @type {BytePrimitive}
 */


const Uint16LE = new BytePrimitive(Object.assign({}, LE16, {
  toBytes: new Uint16Array(1)
}));
let LE32;

if (IS_HOST_LITTLE_ENDIAN) {
  LE32 = HOSTBE_BE32;
} else {
  LE32 = HOSTLE_BE32;
}
/**
 * @const Uint32LE
 * @type {BytePrimitive}
 */


const Uint32LE = new BytePrimitive(Object.assign({}, LE32, {
  toBytes: new Uint32Array(1)
}));
const HOSTLE_BEDOUBLE = {
  size: 8,

  read(uint8a, position) {
    this.bytes[7] = uint8a[position + 0];
    this.bytes[6] = uint8a[position + 1];
    this.bytes[5] = uint8a[position + 2];
    this.bytes[4] = uint8a[position + 3];
    this.bytes[3] = uint8a[position + 4];
    this.bytes[2] = uint8a[position + 5];
    this.bytes[1] = uint8a[position + 6];
    this.bytes[0] = uint8a[position + 7];
    return this.toBytes[0];
  }

};
const HOSTBE_BEDOUBLE = {
  size: 8,

  read(uint8a, position) {
    this.bytes[7] = uint8a[position + 0];
    this.bytes[6] = uint8a[position + 1];
    this.bytes[5] = uint8a[position + 2];
    this.bytes[4] = uint8a[position + 3];
    this.bytes[3] = uint8a[position + 4];
    this.bytes[2] = uint8a[position + 5];
    this.bytes[1] = uint8a[position + 6];
    this.bytes[0] = uint8a[position + 7];
    return this.toBytes[0];
  }

};
let BEDOUBLE;

if (IS_HOST_LITTLE_ENDIAN) {
  BEDOUBLE = HOSTLE_BEDOUBLE;
} else {
  BEDOUBLE = HOSTBE_BEDOUBLE;
}
/**
 * @const DoubleBE
 * @type {BytePrimitive}
 */


const DoubleBE = new BytePrimitive(Object.assign({}, BEDOUBLE, {
  toBytes: new Float64Array(1)
}));
/**
 * @extends BytePrimitive
 */

class FixedAsciiString extends BytePrimitive {
  /**
   * @param {number} size - Number of bytes the FixedAsciiString uses.
   */
  constructor(size) {
    super({
      size,

      read(uint8a, position) {
        let str = '';

        for (let i = 0; i < size; i++) {
          const code = uint8a[position + i];
          Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(code <= 127, 'Non-ascii character in FixedAsciiString');
          str += String.fromCharCode(code);
        }

        return str;
      },

      write(uint8a, position, value) {
        for (let i = 0; i < size; i++) {
          const code = value.charCodeAt(i);
          Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(code <= 127, 'Non-ascii character in FixedAsciiString');
          uint8a[position + i] = code;
        }

        return value;
      }

    });
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/byte-stream.js ***!
  \**********************************************************************/
/*! exports provided: ByteStream */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ByteStream", function() { return ByteStream; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");

/**
 * Read and write a stream of {@link BytePrimitive}s, {@link Packet}s, or byte
 * arrays to an ArrayBuffer.
 */

class ByteStream {
  /**
   * @param {ArrayBuffer} buffer - The ArrayBuffer to read from or write to.
   * @param {number=} [position=0] - The position to start reading or writing
   * from in the ArrayBuffer.
   */
  constructor(buffer) {
    let position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    /**
     * The ArrayBuffer to read from or write to.
     * @type {ArrayBuffer}
     */
    this.buffer = buffer;
    /**
     * The position to start reading or writing from in the ArrayBuffer.
     * @type {number}
     */

    this.position = position;
    /**
     * The typed array view of the buffer to read and write with.
     * @type {Uint8Array}
     */

    this.uint8a = new Uint8Array(this.buffer);
  }
  /**
   * Read one instance of a given BytePrimitive and increment position based
   * on the size of that value.
   * @param {BytePrimitive} member - BytePrimitive to read and increment size
   * with.
   * @returns {*} Return the value produced by the BytePrimitive.
   */


  read(member) {
    const value = member.read(this.uint8a, this.position);

    if (member.size === 0) {
      this.position += member.sizeOf(this.uint8a, this.position);
    } else {
      this.position += member.size;
    }

    return value;
  }
  /**
   * Read one instance of a given Packet subclass and increment position
   * based on the size of that value.
   * @param {PacketConstructor} StructType - Packet subclass constructor that
   * can read from the current stream position.
   * @returns {Packet} Instance of a Packet pointed at the position of the
   * stream before calling readStruct.
   */


  readStruct(StructType) {
    const obj = new StructType(this.uint8a, this.position);
    this.position += StructType.size;
    return obj;
  }
  /**
   * Resize the internal buffer to allow for the needed amount of space.
   * @param {number} needed - How many bytes need to fit in the buffer.
   * @private
   */


  resize(needed) {
    if (this.buffer.byteLength < needed) {
      const uint8a = this.uint8a;
      const nextPowerOf2 = Math.pow(2, Math.ceil(Math.log(needed) / Math.log(2)));
      this.buffer = new ArrayBuffer(nextPowerOf2);
      this.uint8a = new Uint8Array(this.buffer);
      this.uint8a.set(uint8a);
    }
  }
  /**
   * Write a value to the stream (with a BytePrimitive defining how to do so)
   * and increment the position.
   * @param {BytePrimitive} member - BytePrimitive to define how to write the
   * value.
   * @param {*} value - Value to write.
   * @returns {*} Value passed to the method.
   */


  write(member, value) {
    if (member.size === 0) {
      this.resize(this.position + member.writeSizeOf(value));
    } else {
      this.resize(this.position + member.size);
    }

    member.write(this.uint8a, this.position, value);

    if (member.size === 0) {
      this.position += member.writeSizeOf(this.uint8a, this.position);
    } else {
      this.position += member.size;
    }

    return value;
  }
  /**
   * Write data to the stream structured by a given Packet subclass
   * constructor and increment the position.
   * @param {PacketConstructor} StructType - The Packet subclass constructor
   * defining how to write the data.
   * @param {object} data - Data to write.
   * @returns {Packet} - Constructed packet after writing data.
   */


  writeStruct(StructType, data) {
    this.resize(this.position + StructType.size);
    const st = Object.assign(new StructType(this.uint8a, this.position), data);
    this.position += StructType.size;
    return st;
  }
  /**
   * Write bytes from the given Uint8Array array to the stream and increment
   * the position.
   * @param {Uint8Array} bytes - Bytes to write to the stream.
   * @param {number=} [start=0] - Where in bytes to start writing from.
   * @param {number=} [end=bytes.length] - Where in bytes to stop writing, excluding position at bytes[end].
   * @returns {Uint8Array} Passed bytes Uint8Array.
   */


  writeBytes(bytes) {
    let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : bytes.length;
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(bytes instanceof Uint8Array, 'writeBytes must be passed an Uint8Array');
    this.resize(this.position + (end - start));

    for (let i = start; i < end; i++) {
      this.uint8a[this.position + i - start] = bytes[i];
    }

    this.position += end - start;
    return bytes;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/crc32.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/crc32.js ***!
  \****************************************************************/
/*! exports provided: CRC32 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CRC32", function() { return CRC32; });
class CRC32 {
  constructor() {
    this.bit = new Uint32Array(1);
    this.crc = 0;
    this.c = 0;
    this.table = [];
    let c;

    for (let i = 0; i < 256; i++) {
      c = i;

      for (let j = 0; j < 8; j++) {
        c = c & 1 ? 0xedb88320 ^ c >>> 1 : c >>> 1;
      }

      this.table[i] = c >>> 0;
    }
  }

  update(uint8a) {
    let position = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let length = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : uint8a.length;
    let crc = ~this.crc >>> 0;

    for (let i = 0; i < length; i++) {
      crc = crc >>> 8 ^ this.table[(crc ^ uint8a[position + i]) & 0xff];
    }

    this.crc = ~crc >>> 0;
    return this;
  }

  get digest() {
    return this.crc;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/deflate-packets.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/deflate-packets.js ***!
  \**************************************************************************/
/*! exports provided: DEFLATE_BLOCK_SIZE_MAX, DeflateHeader, DeflateChunkStart, DeflateEnd */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFLATE_BLOCK_SIZE_MAX", function() { return DEFLATE_BLOCK_SIZE_MAX; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DeflateHeader", function() { return DeflateHeader; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DeflateChunkStart", function() { return DeflateChunkStart; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DeflateEnd", function() { return DeflateEnd; });
/* harmony import */ var _byte_packets__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-packets */ "./node_modules/scratch-sb1-converter/src/coders/byte-packets.js");
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");


const DEFLATE_BLOCK_SIZE_MAX = 0xffff;


class DeflateHeader extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  cmf: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint8"],
  flag: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint8"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(DeflateHeader);


class DeflateChunkStart extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  lastPacket: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint8"],
  length: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"],
  lengthCheck: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(DeflateChunkStart);


class DeflateEnd extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  checksum: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint32LE"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(DeflateEnd);


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/deflate-stream.js":
/*!*************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/deflate-stream.js ***!
  \*************************************************************************/
/*! exports provided: DeflateStream */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DeflateStream", function() { return DeflateStream; });
/* harmony import */ var _adler32__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./adler32 */ "./node_modules/scratch-sb1-converter/src/coders/adler32.js");
/* harmony import */ var _deflate_packets__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./deflate-packets */ "./node_modules/scratch-sb1-converter/src/coders/deflate-packets.js");
/* harmony import */ var _proxy_stream__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./proxy-stream */ "./node_modules/scratch-sb1-converter/src/coders/proxy-stream.js");




class DeflateStream extends _proxy_stream__WEBPACK_IMPORTED_MODULE_2__["ProxyStream"] {
  constructor(stream) {
    super(stream);
    this.stream.writeStruct(_deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateHeader"], {
      cmf: 0b00001000,
      flag: 0b00011101
    });
    this.adler = new _adler32__WEBPACK_IMPORTED_MODULE_0__["Adler32"]();
    this.chunk = this.stream.writeStruct(_deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateChunkStart"], {
      lastPacket: 0,
      length: 0,
      lengthCheck: 0 ^ 0xffff
    });
  }

  get _deflateIndex() {
    return this.chunk.length;
  }

  set _deflateIndex(value) {
    this.chunk.length = value;
    this.chunk.lengthCheck = value ^ 0xffff;
    return this.chunk.length;
  }

  writeStruct(StructType, data) {
    this.writeBytes(Object.assign(new StructType(), data).uint8a);
  }

  writeBytes(bytes) {
    let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : bytes.length;
    let chunkStart = start;

    while (end - chunkStart > 0) {
      if (this._deflateIndex === _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DEFLATE_BLOCK_SIZE_MAX"]) {
        this.chunk = this.stream.writeStruct(_deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateChunkStart"], {
          lastPacket: 0,
          length: 0,
          lengthCheck: 0 ^ 0xffff
        });
      }

      const chunkLength = Math.min(end - chunkStart, _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DEFLATE_BLOCK_SIZE_MAX"] - this._deflateIndex);
      this.stream.writeBytes(bytes, chunkStart, chunkStart + chunkLength);
      this._deflateIndex += chunkLength;
      chunkStart += chunkLength;
    }

    this.adler.update(bytes, start, end - start);
  }

  finish() {
    this.chunk.lastPacket = 1;
    this.stream.writeStruct(_deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateEnd"], {
      checksum: this.adler.digest
    });
  }

  static estimateSize(bodySize) {
    const packets = Math.ceil(bodySize / _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DEFLATE_BLOCK_SIZE_MAX"]);
    return _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateHeader"].size + packets * _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateChunkStart"].size + _deflate_packets__WEBPACK_IMPORTED_MODULE_1__["DeflateEnd"].size + bodySize;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/png-chunk-stream.js":
/*!***************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/png-chunk-stream.js ***!
  \***************************************************************************/
/*! exports provided: PNGChunkStream */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGChunkStream", function() { return PNGChunkStream; });
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");
/* harmony import */ var _crc32__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./crc32 */ "./node_modules/scratch-sb1-converter/src/coders/crc32.js");
/* harmony import */ var _png_packets__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./png-packets */ "./node_modules/scratch-sb1-converter/src/coders/png-packets.js");
/* harmony import */ var _proxy_stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./proxy-stream */ "./node_modules/scratch-sb1-converter/src/coders/proxy-stream.js");





class PNGChunkStream extends _proxy_stream__WEBPACK_IMPORTED_MODULE_3__["ProxyStream"] {
  constructor(stream) {
    let chunkType = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'IHDR';
    super(stream);
    this.start = this.stream.writeStruct(_png_packets__WEBPACK_IMPORTED_MODULE_2__["PNGChunkStart"], {
      length: 0,
      chunkType
    });
    this.crc = new _crc32__WEBPACK_IMPORTED_MODULE_1__["CRC32"]();
  }

  finish() {
    const crcStart = this.start.offset + this.start.size;
    const length = this.position - crcStart;
    this.start.length = length;
    this.crc.update(this.stream.uint8a, crcStart - _byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint32BE"].size, length + _byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint32BE"].size);
    this.stream.writeStruct(_png_packets__WEBPACK_IMPORTED_MODULE_2__["PNGChunkEnd"], {
      checksum: this.crc.digest
    });
  }

  static size(bodySize) {
    return _png_packets__WEBPACK_IMPORTED_MODULE_2__["PNGChunkStart"].size + bodySize + _png_packets__WEBPACK_IMPORTED_MODULE_2__["PNGChunkEnd"].size;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/png-file.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/png-file.js ***!
  \*******************************************************************/
/*! exports provided: PNGFile */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGFile", function() { return PNGFile; });
/* harmony import */ var _byte_stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");
/* harmony import */ var _png_packets__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./png-packets */ "./node_modules/scratch-sb1-converter/src/coders/png-packets.js");
/* harmony import */ var _deflate_stream__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./deflate-stream */ "./node_modules/scratch-sb1-converter/src/coders/deflate-stream.js");
/* harmony import */ var _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./png-chunk-stream */ "./node_modules/scratch-sb1-converter/src/coders/png-chunk-stream.js");





class PNGFile {
  encode(width, height, pixelsUint8) {
    const rowSize = width * 4 + _png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGFilterMethodByte"].size;
    const bodySize = rowSize * height;
    const size = _png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGSignature"].size + // IHDR
    _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"].size(_png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGIHDRChunkBody"].size) + // IDAT
    _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"].size(_deflate_stream__WEBPACK_IMPORTED_MODULE_2__["DeflateStream"].estimateSize(bodySize)) + // IEND
    _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"].size(0);
    const stream = new _byte_stream__WEBPACK_IMPORTED_MODULE_0__["ByteStream"](new ArrayBuffer(size));
    stream.writeStruct(_png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGSignature"], {
      support8Bit: 0x89,
      png: 'PNG',
      dosLineEnding: '\r\n',
      dosEndOfFile: '\x1a',
      unixLineEnding: '\n'
    });
    const pngIhdr = new _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"](stream, 'IHDR');
    pngIhdr.writeStruct(_png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGIHDRChunkBody"], {
      width,
      height,
      bitDepth: 8,
      colorType: 6,
      compressionMethod: 0,
      filterMethod: 0,
      interlaceMethod: 0
    });
    pngIhdr.finish();
    const pngIdat = new _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"](stream, 'IDAT');
    const deflate = new _deflate_stream__WEBPACK_IMPORTED_MODULE_2__["DeflateStream"](pngIdat);
    let pixelsIndex = 0;

    while (pixelsIndex < pixelsUint8.length) {
      deflate.writeStruct(_png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGFilterMethodByte"], {
        method: 0
      });
      const partialLength = Math.min(pixelsUint8.length - pixelsIndex, rowSize - _png_packets__WEBPACK_IMPORTED_MODULE_1__["PNGFilterMethodByte"].size);
      deflate.writeBytes(pixelsUint8, pixelsIndex, pixelsIndex + partialLength);
      pixelsIndex += partialLength;
    }

    deflate.finish();
    pngIdat.finish();
    const pngIend = new _png_chunk_stream__WEBPACK_IMPORTED_MODULE_3__["PNGChunkStream"](stream, 'IEND');
    pngIend.finish();
    return stream.buffer;
  }

  static encode(width, height, pixels) {
    return new PNGFile().encode(width, height, pixels);
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/png-packets.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/png-packets.js ***!
  \**********************************************************************/
/*! exports provided: PNGSignature, PNGChunkStart, PNGChunkEnd, PNGIHDRChunkBody, PNGFilterMethodByte */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGSignature", function() { return PNGSignature; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGChunkStart", function() { return PNGChunkStart; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGChunkEnd", function() { return PNGChunkEnd; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGIHDRChunkBody", function() { return PNGIHDRChunkBody; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PNGFilterMethodByte", function() { return PNGFilterMethodByte; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony import */ var _byte_packets__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./byte-packets */ "./node_modules/scratch-sb1-converter/src/coders/byte-packets.js");
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");




class PNGSignature extends _byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  support8Bit: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  png: new _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](3),
  dosLineEnding: new _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](2),
  dosEndOfFile: new _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](1),
  unixLineEnding: new _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](1)
}) {
  static validate() {
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(this.equals({
      support8Bit: 0x89,
      png: 'PNG',
      dosLineEnding: '\r\n',
      dosEndOfFile: '\x1a',
      unixLineEnding: '\n'
    }), 'PNGSignature does not match the expected values');
  }

}

_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(PNGSignature);


class PNGChunkStart extends _byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  length: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"],
  chunkType: new _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](4)
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(PNGChunkStart);


class PNGChunkEnd extends _byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  checksum: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(PNGChunkEnd);


class PNGIHDRChunkBody extends _byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  width: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"],
  height: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"],
  bitDepth: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  colorType: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  compressionMethod: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  filterMethod: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  interlaceMethod: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(PNGIHDRChunkBody);


class PNGFilterMethodByte extends _byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  method: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(PNGFilterMethodByte);


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/proxy-stream.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/proxy-stream.js ***!
  \***********************************************************************/
/*! exports provided: ProxyStream */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ProxyStream", function() { return ProxyStream; });
class ProxyStream {
  constructor(stream) {
    this.stream = stream;
  }

  get uint8a() {
    return this.stream.uint8a;
  }

  set uint8a(value) {
    this.stream.uint8a = value;
    return this.stream.uint8a;
  }

  get position() {
    return this.stream.position;
  }

  set position(value) {
    this.stream.position = value;
    return this.stream.position;
  }

  writeStruct(StructType, data) {
    return this.stream.writeStruct(StructType, data);
  }

  writeBytes(bytes) {
    let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : bytes.length;
    return this.stream.writeBytes(bytes, start, end);
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/squeak-image.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/squeak-image.js ***!
  \***********************************************************************/
/*! exports provided: SqueakImage */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SqueakImage", function() { return SqueakImage; });
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");
/* harmony import */ var _byte_stream__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");


const defaultColorMap = [0x00000000, 0xFF000000, 0xFFFFFFFF, 0xFF808080, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF00FFFF, 0xFFFFFF00, 0xFFFF00FF, 0xFF202020, 0xFF404040, 0xFF606060, 0xFF9F9F9F, 0xFFBFBFBF, 0xFFDFDFDF, 0xFF080808, 0xFF101010, 0xFF181818, 0xFF282828, 0xFF303030, 0xFF383838, 0xFF484848, 0xFF505050, 0xFF585858, 0xFF686868, 0xFF707070, 0xFF787878, 0xFF878787, 0xFF8F8F8F, 0xFF979797, 0xFFA7A7A7, 0xFFAFAFAF, 0xFFB7B7B7, 0xFFC7C7C7, 0xFFCFCFCF, 0xFFD7D7D7, 0xFFE7E7E7, 0xFFEFEFEF, 0xFFF7F7F7, 0xFF000000, 0xFF003300, 0xFF006600, 0xFF009900, 0xFF00CC00, 0xFF00FF00, 0xFF000033, 0xFF003333, 0xFF006633, 0xFF009933, 0xFF00CC33, 0xFF00FF33, 0xFF000066, 0xFF003366, 0xFF006666, 0xFF009966, 0xFF00CC66, 0xFF00FF66, 0xFF000099, 0xFF003399, 0xFF006699, 0xFF009999, 0xFF00CC99, 0xFF00FF99, 0xFF0000CC, 0xFF0033CC, 0xFF0066CC, 0xFF0099CC, 0xFF00CCCC, 0xFF00FFCC, 0xFF0000FF, 0xFF0033FF, 0xFF0066FF, 0xFF0099FF, 0xFF00CCFF, 0xFF00FFFF, 0xFF330000, 0xFF333300, 0xFF336600, 0xFF339900, 0xFF33CC00, 0xFF33FF00, 0xFF330033, 0xFF333333, 0xFF336633, 0xFF339933, 0xFF33CC33, 0xFF33FF33, 0xFF330066, 0xFF333366, 0xFF336666, 0xFF339966, 0xFF33CC66, 0xFF33FF66, 0xFF330099, 0xFF333399, 0xFF336699, 0xFF339999, 0xFF33CC99, 0xFF33FF99, 0xFF3300CC, 0xFF3333CC, 0xFF3366CC, 0xFF3399CC, 0xFF33CCCC, 0xFF33FFCC, 0xFF3300FF, 0xFF3333FF, 0xFF3366FF, 0xFF3399FF, 0xFF33CCFF, 0xFF33FFFF, 0xFF660000, 0xFF663300, 0xFF666600, 0xFF669900, 0xFF66CC00, 0xFF66FF00, 0xFF660033, 0xFF663333, 0xFF666633, 0xFF669933, 0xFF66CC33, 0xFF66FF33, 0xFF660066, 0xFF663366, 0xFF666666, 0xFF669966, 0xFF66CC66, 0xFF66FF66, 0xFF660099, 0xFF663399, 0xFF666699, 0xFF669999, 0xFF66CC99, 0xFF66FF99, 0xFF6600CC, 0xFF6633CC, 0xFF6666CC, 0xFF6699CC, 0xFF66CCCC, 0xFF66FFCC, 0xFF6600FF, 0xFF6633FF, 0xFF6666FF, 0xFF6699FF, 0xFF66CCFF, 0xFF66FFFF, 0xFF990000, 0xFF993300, 0xFF996600, 0xFF999900, 0xFF99CC00, 0xFF99FF00, 0xFF990033, 0xFF993333, 0xFF996633, 0xFF999933, 0xFF99CC33, 0xFF99FF33, 0xFF990066, 0xFF993366, 0xFF996666, 0xFF999966, 0xFF99CC66, 0xFF99FF66, 0xFF990099, 0xFF993399, 0xFF996699, 0xFF999999, 0xFF99CC99, 0xFF99FF99, 0xFF9900CC, 0xFF9933CC, 0xFF9966CC, 0xFF9999CC, 0xFF99CCCC, 0xFF99FFCC, 0xFF9900FF, 0xFF9933FF, 0xFF9966FF, 0xFF9999FF, 0xFF99CCFF, 0xFF99FFFF, 0xFFCC0000, 0xFFCC3300, 0xFFCC6600, 0xFFCC9900, 0xFFCCCC00, 0xFFCCFF00, 0xFFCC0033, 0xFFCC3333, 0xFFCC6633, 0xFFCC9933, 0xFFCCCC33, 0xFFCCFF33, 0xFFCC0066, 0xFFCC3366, 0xFFCC6666, 0xFFCC9966, 0xFFCCCC66, 0xFFCCFF66, 0xFFCC0099, 0xFFCC3399, 0xFFCC6699, 0xFFCC9999, 0xFFCCCC99, 0xFFCCFF99, 0xFFCC00CC, 0xFFCC33CC, 0xFFCC66CC, 0xFFCC99CC, 0xFFCCCCCC, 0xFFCCFFCC, 0xFFCC00FF, 0xFFCC33FF, 0xFFCC66FF, 0xFFCC99FF, 0xFFCCCCFF, 0xFFCCFFFF, 0xFFFF0000, 0xFFFF3300, 0xFFFF6600, 0xFFFF9900, 0xFFFFCC00, 0xFFFFFF00, 0xFFFF0033, 0xFFFF3333, 0xFFFF6633, 0xFFFF9933, 0xFFFFCC33, 0xFFFFFF33, 0xFFFF0066, 0xFFFF3366, 0xFFFF6666, 0xFFFF9966, 0xFFFFCC66, 0xFFFFFF66, 0xFFFF0099, 0xFFFF3399, 0xFFFF6699, 0xFFFF9999, 0xFFFFCC99, 0xFFFFFF99, 0xFFFF00CC, 0xFFFF33CC, 0xFFFF66CC, 0xFFFF99CC, 0xFFFFCCCC, 0xFFFFFFCC, 0xFFFF00FF, 0xFFFF33FF, 0xFFFF66FF, 0xFFFF99FF, 0xFFFFCCFF, 0xFFFFFFFF];
const defaultOneBitColorMap = [0xFFFFFFFF, 0xFF000000];
const VariableIntBE = new _byte_primitives__WEBPACK_IMPORTED_MODULE_0__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    const count = uint8a[position];
    if (count <= 223) return 1;
    if (count <= 254) return 2;
    return 5;
  },

  read(uint8a, position) {
    const count = uint8a[position];
    if (count <= 223) return count;
    if (count <= 254) return (count - 224) * 256 + uint8a[position + 1];
    return _byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint32BE"].read(uint8a, position + 1);
  }

});

class SqueakImage {
  decode(width, height, depth, bytes, colormap) {
    const pixels = this.decodePixels(bytes, depth === 32);

    if (depth <= 8) {
      if (!colormap) {
        colormap = depth === 1 ? defaultOneBitColorMap : defaultColorMap;
      }

      return this.unpackPixels(pixels, width, height, depth, colormap);
    } else if (depth === 16) {
      return this.raster16To32(pixels, width, height);
    } else if (depth === 32) {
      return pixels;
    }

    throw new Error('Unhandled Squeak Image depth.');
  }

  decodePixels(bytes, withAlpha) {
    let result; // Already decompressed

    if (Array.isArray(bytes) || bytes instanceof Uint32Array) {
      result = new Uint32Array(bytes);

      if (withAlpha) {
        for (let i = 0; i < result.length; i++) {
          if (result[i] !== 0) {
            result[i] = 0xff000000 | result[i];
          }
        }
      }

      return result;
    }

    const stream = new _byte_stream__WEBPACK_IMPORTED_MODULE_1__["ByteStream"](bytes.buffer, bytes.byteOffset);
    const pixelsOut = stream.read(VariableIntBE);
    result = new Uint32Array(pixelsOut);
    let i = 0;

    while (i < pixelsOut) {
      const runLengthAndCode = stream.read(VariableIntBE);
      const runLength = runLengthAndCode >> 2;
      const code = runLengthAndCode & 0b11;
      let w;

      switch (code) {
        case 0:
          i += runLength;
          break;

        case 1:
          w = stream.read(_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint8"]);
          w = w << 24 | w << 16 | w << 8 | w;

          if (withAlpha && w !== 0) {
            w |= 0xff000000;
          }

          for (let j = 0; j < runLength; j++) {
            result[i++] = w;
          }

          break;

        case 2:
          w = stream.read(_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint32BE"]);

          if (withAlpha && w !== 0) {
            w |= 0xff000000;
          }

          for (let j = 0; j < runLength; j++) {
            result[i++] = w;
          }

          break;

        case 3:
          for (let j = 0; j < runLength; j++) {
            w = stream.read(_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint32BE"]);

            if (withAlpha && w !== 0) {
              w |= 0xff000000;
            }

            result[i++] = w;
          }

      }
    }

    return result;
  }

  unpackPixels(words, width, height, depth, colormap) {
    const result = new Uint32Array(width * height);
    const mask = (1 << depth) - 1;
    const pixelsPerWord = 32 / depth;
    let dst = 0;
    let src = 0;

    for (let y = 0; y < height; y++) {
      let word;
      let shift = -1;

      for (let x = 0; x < width; x++) {
        if (shift < 0) {
          shift = depth * (pixelsPerWord - 1);
          word = words[src++];
        }

        result[dst++] = colormap[word >> shift & mask];
        shift -= depth;
      }
    }

    return result;
  }

  raster16To32(words, width, height) {
    const result = new Uint32Array(2 * words.length);
    let shift;
    let word;
    let pix;
    let src = 0;
    let dst = 0;

    for (let y = 0; y < height; y++) {
      shift = -1;

      for (let x = 0; x < width; x++) {
        if (shift < 0) {
          shift = 16;
          word = words[src++];
        }

        pix = word >> shift & 0xffff;

        if (pix !== 0) {
          const red = pix >> 7 & 0b11111000;
          const green = pix >> 2 & 0b11111000;
          const blue = pix << 3 & 0b11111000;
          pix = 0xff000000 | red << 16 | green << 8 | blue;
        }

        result[dst++] = pix;
        shift -= 16;
      }
    }

    return result;
  }

  buildCustomColormap(depth, colors, table) {
    const result = new Uint32Array(1 << depth);

    for (let i = 0; i < colors.length; i++) {
      result[i] = table[colors[i].index - 1];
    }

    return result;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/squeak-sound.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/squeak-sound.js ***!
  \***********************************************************************/
/*! exports provided: SqueakSound */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SqueakSound", function() { return SqueakSound; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");
/* harmony import */ var _byte_stream__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");



const SQUEAK_SOUND_STEP_SIZE_TABLE = [7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767];
const SQUEAK_SOUND_INDEX_TABLES = {
  2: [-1, 2, -1, 2],
  3: [-1, -1, 2, 4, -1, -1, 2, 4],
  4: [-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8],
  5: [-1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16, -1, -1, -1, -1, -1, -1, -1, -1, 1, 2, 4, 6, 8, 10, 13, 16]
};

class SqueakSound {
  constructor(bitsPerSample) {
    this.bitsPerSample = bitsPerSample;
    this.indexTable = SQUEAK_SOUND_INDEX_TABLES[bitsPerSample];
    this.signMask = 1 << bitsPerSample - 1;
    this.valueMask = this.signMask - 1;
    this.valueHighBit = this.signMask >> 1;
    this.bitPosition = 0;
    this.currentByte = 0;
    this.stream = null;
    this.end = 0;
  }

  decode(data) {
    // Reset position information.
    this.bitPosition = 0;
    this.currentByte = 0;
    this.stream = new _byte_stream__WEBPACK_IMPORTED_MODULE_2__["ByteStream"](data.buffer, data.byteOffset);
    this.end = data.byteOffset + data.length;
    const size = Math.floor(data.length * 8 / this.bitsPerSample);
    const result = new Int16Array(size);
    let sample = 0;
    let index = 0;

    for (let i = 0; i < size; i++) {
      const code = this.nextCode();
      Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(code >= 0, 'Ran out of bits in Squeak Sound');
      let step = SQUEAK_SOUND_STEP_SIZE_TABLE[index];
      let delta = 0;

      for (let bit = this.valueHighBit; bit > 0; bit = bit >> 1) {
        if ((code & bit) !== 0) {
          delta += step;
        }

        step = step >> 1;
      }

      delta += step;
      sample += (code & this.signMask) === 0 ? delta : -delta;
      index += this.indexTable[code];
      if (index < 0) index = 0;
      if (index > 88) index = 88;
      if (sample > 32767) sample = 32767;
      if (sample < -32768) sample = -32768;
      result[i] = sample;
    }

    return result;
  }

  nextCode() {
    let remaining = this.bitsPerSample;
    let shift = remaining - this.bitPosition;
    let result = shift < 0 ? this.currentByte >> -shift : this.currentByte << shift;

    while (shift > 0) {
      remaining -= this.bitPosition;

      if (this.end - this.stream.position > 0) {
        this.currentByte = this.stream.read(_byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint8"]);
        this.bitPosition = 8;
      } else {
        this.currentByte = 0;
        this.bitPosition = 0;
        return -1;
      }

      shift = remaining - this.bitPosition;
      result += shift < 0 ? this.currentByte >> -shift : this.currentByte << shift;
    }

    this.bitPosition -= remaining;
    this.currentByte = this.currentByte & 0xff >> 8 - this.bitPosition;
    return result;
  }

  static samples(bitsPerSample, data) {
    return data.length * 8 / bitsPerSample;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/wav-file.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/wav-file.js ***!
  \*******************************************************************/
/*! exports provided: WAVFile */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WAVFile", function() { return WAVFile; });
/* harmony import */ var _byte_stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");
/* harmony import */ var _wav_packets__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./wav-packets */ "./node_modules/scratch-sb1-converter/src/coders/wav-packets.js");



class WAVFile {
  encode(intSamples) {
    let {
      channels = 1,
      sampleRate = 22050
    } = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const samplesUint8 = new Uint8Array(intSamples.buffer, intSamples.byteOffset, intSamples.byteLength);
    const size = _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVESignature"].size + _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"].size + _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEFMTChunkBody"].size + _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"].size + samplesUint8.length;
    const stream = new _byte_stream__WEBPACK_IMPORTED_MODULE_0__["ByteStream"](new ArrayBuffer(size));
    stream.writeStruct(_wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVESignature"], {
      riff: 'RIFF',
      length: size - 8,
      wave: 'WAVE'
    });
    stream.writeStruct(_wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"], {
      chunkType: 'fmt ',
      length: _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEFMTChunkBody"].size
    });
    stream.writeStruct(_wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEFMTChunkBody"], {
      format: 1,
      channels: channels,
      sampleRate: sampleRate,
      bytesPerSec: sampleRate * 2 * channels,
      blockAlignment: channels * 2,
      bitsPerSample: 16
    });
    stream.writeStruct(_wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"], {
      chunkType: 'data',
      length: size - stream.position - _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"].size
    });
    stream.writeBytes(samplesUint8);
    return stream.uint8a;
  }

  static encode(intSamples, options) {
    return new WAVFile().encode(intSamples, options);
  }

  static samples(bytes) {
    const headerLength = new _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"](bytes, _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVESignature"].size).length;
    const bodyLength = new _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"](bytes, _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVESignature"].size + _wav_packets__WEBPACK_IMPORTED_MODULE_1__["WAVEChunkStart"].size + headerLength).length;
    return bodyLength / 2;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/coders/wav-packets.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/coders/wav-packets.js ***!
  \**********************************************************************/
/*! exports provided: WAVESignature, WAVEChunkStart, WAVEFMTChunkBody */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WAVESignature", function() { return WAVESignature; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WAVEChunkStart", function() { return WAVEChunkStart; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WAVEFMTChunkBody", function() { return WAVEFMTChunkBody; });
/* harmony import */ var _byte_packets__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./byte-packets */ "./node_modules/scratch-sb1-converter/src/coders/byte-packets.js");
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");



class WAVESignature extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  riff: new _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["FixedAsciiString"](4),
  length: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint32LE"],
  wave: new _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["FixedAsciiString"](4)
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(WAVESignature);


class WAVEChunkStart extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  chunkType: new _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["FixedAsciiString"](4),
  length: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint32LE"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(WAVEChunkStart);


class WAVEFMTChunkBody extends _byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].extend({
  format: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"],
  channels: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"],
  sampleRate: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint32LE"],
  bytesPerSec: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint32LE"],
  blockAlignment: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"],
  bitsPerSample: _byte_primitives__WEBPACK_IMPORTED_MODULE_1__["Uint16LE"]
}) {}

_byte_packets__WEBPACK_IMPORTED_MODULE_0__["Packet"].initConstructor(WAVEFMTChunkBody);


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/sb1-file-packets.js":
/*!********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/sb1-file-packets.js ***!
  \********************************************************************/
/*! exports provided: SB1Signature, SB1Header */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SB1Signature", function() { return SB1Signature; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SB1Header", function() { return SB1Header; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony import */ var _coders_byte_packets__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./coders/byte-packets */ "./node_modules/scratch-sb1-converter/src/coders/byte-packets.js");
/* harmony import */ var _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./coders/byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");



/**
 * @augments Packet
 */

class SB1Signature extends _coders_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  /**
   * 10 byte ascii string equaling `'ScratchV01'` or `'ScratchV02'`.
   * @type {string}
   * @memberof SB1Signature#
   */
  version: new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](10),

  /**
   * Number of bytes in the info block.
   * @type {number}
   * @memberof SB1Signature#
   */
  infoByteLength: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"]
}) {
  /**
   * Is this a valid SB1Signature?
   * @method
   * @throws {AssertionError} Throws if it is not valid.
   */
  validate() {
    _util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"].validate(this.equals({
      version: 'ScratchV01'
    }) || this.equals({
      version: 'ScratchV02'
    }), 'Invalid Scratch file signature.');
  }

}

_coders_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(SB1Signature);

class SB1Header extends _coders_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].extend({
  ObjS: new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](4),
  ObjSValue: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  Stch: new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["FixedAsciiString"](4),
  StchValue: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"],
  numObjects: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"]
}) {
  validate() {
    _util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"].validate(this.equals({
      ObjS: 'ObjS',
      ObjSValue: 1,
      Stch: 'Stch',
      StchValue: 1
    }), 'Invalid Scratch file info packet header.');
  }

}

_coders_byte_packets__WEBPACK_IMPORTED_MODULE_1__["Packet"].initConstructor(SB1Header);


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/sb1-file.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/sb1-file.js ***!
  \************************************************************/
/*! exports provided: SB1File */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SB1File", function() { return SB1File; });
/* harmony import */ var _coders_byte_stream__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./coders/byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");
/* harmony import */ var _squeak_byte_take_iterator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./squeak/byte-take-iterator */ "./node_modules/scratch-sb1-converter/src/squeak/byte-take-iterator.js");
/* harmony import */ var _squeak_field_iterator__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./squeak/field-iterator */ "./node_modules/scratch-sb1-converter/src/squeak/field-iterator.js");
/* harmony import */ var _squeak_type_iterator__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./squeak/type-iterator */ "./node_modules/scratch-sb1-converter/src/squeak/type-iterator.js");
/* harmony import */ var _squeak_reference_fixer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./squeak/reference-fixer */ "./node_modules/scratch-sb1-converter/src/squeak/reference-fixer.js");
/* harmony import */ var _squeak_types__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./squeak/types */ "./node_modules/scratch-sb1-converter/src/squeak/types.js");
/* harmony import */ var _to_sb2_fake_zip__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./to-sb2/fake-zip */ "./node_modules/scratch-sb1-converter/src/to-sb2/fake-zip.js");
/* harmony import */ var _to_sb2_json_generator__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./to-sb2/json-generator */ "./node_modules/scratch-sb1-converter/src/to-sb2/json-generator.js");
/* harmony import */ var _sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./sb1-file-packets */ "./node_modules/scratch-sb1-converter/src/sb1-file-packets.js");










class SB1File {
  constructor(buffer) {
    this.buffer = buffer;
    this.stream = new _coders_byte_stream__WEBPACK_IMPORTED_MODULE_0__["ByteStream"](buffer);
    this.signature = this.stream.readStruct(_sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Signature"]);
    this.signature.validate();
    this.infoHeader = this.stream.readStruct(_sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Header"]);
    this.infoHeader.validate();
    this.stream.position += this.signature.infoByteLength - _sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Header"].size;
    this.dataHeader = this.stream.readStruct(_sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Header"]);
    this.dataHeader.validate();
  }

  get json() {
    return Object(_to_sb2_json_generator__WEBPACK_IMPORTED_MODULE_7__["toSb2Json"])({
      info: this.info(),
      stageData: this.data(),
      images: this.images(),
      sounds: this.sounds()
    });
  }

  get zip() {
    return Object(_to_sb2_fake_zip__WEBPACK_IMPORTED_MODULE_6__["toSb2FakeZipApi"])({
      // Use of this `zip` getter assumes that `json` will be used to
      // fetch the json and not have it read from the produced "fake" zip.
      images: this.images(),
      sounds: this.sounds()
    });
  }

  view() {
    return {
      signature: this.signature,
      infoHeader: this.infoHeader,
      dataHeader: this.dataHeader,

      toString() {
        return 'SB1File';
      }

    };
  }

  infoRaw() {
    return new _squeak_byte_take_iterator__WEBPACK_IMPORTED_MODULE_1__["ByteTakeIterator"](new _squeak_field_iterator__WEBPACK_IMPORTED_MODULE_2__["FieldIterator"](this.buffer, this.infoHeader.offset + _sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Header"].size), this.signature.infoByteLength + _sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Signature"].size);
  }

  infoTable() {
    return new _squeak_type_iterator__WEBPACK_IMPORTED_MODULE_3__["TypeIterator"](this.infoRaw());
  }

  info() {
    if (!this._info) {
      this._info = new _squeak_reference_fixer__WEBPACK_IMPORTED_MODULE_4__["ReferenceFixer"](this.infoTable()).table[0];
    }

    return this._info;
  }

  dataRaw() {
    return new _squeak_byte_take_iterator__WEBPACK_IMPORTED_MODULE_1__["ByteTakeIterator"](new _squeak_field_iterator__WEBPACK_IMPORTED_MODULE_2__["FieldIterator"](this.buffer, this.dataHeader.offset + _sb1_file_packets__WEBPACK_IMPORTED_MODULE_8__["SB1Header"].size), this.stream.uint8a.length);
  }

  dataTable() {
    return new _squeak_type_iterator__WEBPACK_IMPORTED_MODULE_3__["TypeIterator"](this.dataRaw());
  }

  dataFixed() {
    if (!this._data) {
      this._data = new _squeak_reference_fixer__WEBPACK_IMPORTED_MODULE_4__["ReferenceFixer"](this.dataTable()).table;
    }

    return this._data;
  }

  data() {
    return this.dataFixed()[0];
  }

  images() {
    const unique = new Set();
    return this.dataFixed().filter(obj => {
      if (obj instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_5__["ImageMediaData"]) {
        if (unique.has(obj.crc)) return false;
        unique.add(obj.crc);
        return true;
      }

      return false;
    });
  }

  sounds() {
    const unique = new Set();
    return this.dataFixed().filter(obj => {
      if (obj instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_5__["SoundMediaData"]) {
        if (unique.has(obj.crc)) return false;
        unique.add(obj.crc);
        return true;
      }

      return false;
    });
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/byte-primitives.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/byte-primitives.js ***!
  \**************************************************************************/
/*! exports provided: BUFFER_TOO_BIG, ReferenceBE, LargeInt, AsciiString, Bytes, SoundBytes, Bitmap32BE, UTF8, OpaqueColor, TranslucentColor */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BUFFER_TOO_BIG", function() { return BUFFER_TOO_BIG; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReferenceBE", function() { return ReferenceBE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "LargeInt", function() { return LargeInt; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AsciiString", function() { return AsciiString; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Bytes", function() { return Bytes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundBytes", function() { return SoundBytes; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Bitmap32BE", function() { return Bitmap32BE; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UTF8", function() { return UTF8; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OpaqueColor", function() { return OpaqueColor; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TranslucentColor", function() { return TranslucentColor; });
/* harmony import */ var text_encoding__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! text-encoding */ "./src/scaffolding/text-encoding/index.js");
/* harmony import */ var text_encoding__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(text_encoding__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony import */ var _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../coders/byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");



const BUFFER_TOO_BIG = 10 * 1024 * 1024;
/**
 * @const ReferenceBE
 * @type BytePrimitive
 */

let ReferenceBE;

if (_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["IS_HOST_LITTLE_ENDIAN"]) {
  ReferenceBE = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
    size: 3,

    read(uint8a, position) {
      return uint8a[position + 0] << 16 | uint8a[position + 1] << 8 | uint8a[position + 2];
    }

  });
} else {
  ReferenceBE = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
    size: 3,

    read(uint8a, position) {
      return uint8a[position + 2] << 16 | uint8a[position + 1] << 8 | uint8a[position + 0];
    }

  });
}
/**
 * @const LargeInt
 * @type BytePrimitive
 */


const LargeInt = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Int16BE"].read(uint8a, position);
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Int16BE"].size + count;
  },

  read(uint8a, position) {
    let num = 0;
    let multiplier = 0;
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Int16BE"].read(uint8a, position);

    for (let i = 0; i < count; i++) {
      num = num + multiplier * _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"].read(uint8a, position++);
      multiplier *= 256;
    }

    return num;
  }

});
/**
 * @const AsciiString
 * @type BytePrimitive
 */

const AsciiString = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size + count;
  },

  read(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'asciiString too big');
    position += 4;
    let str = '';

    for (let i = 0; i < count; i++) {
      str += String.fromCharCode(uint8a[position++]);
    }

    return str;
  }

});
/**
 * @const Bytes
 * @type BytePrimitive
 */

const Bytes = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size + _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
  },

  read(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'bytes too big');
    position += _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'uint8a array too big');
    return new Uint8Array(uint8a.buffer, position, count);
  }

});
/**
 * @const SoundBytes
 * @type BytePrimitive
 */

const SoundBytes = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size + _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position) * 2;
  },

  read(uint8a, position) {
    const samples = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(samples < BUFFER_TOO_BIG, 'sound too big');
    position += _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
    const count = samples * 2;
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'uint8a array too big');
    return new Uint8Array(uint8a.buffer, position, count);
  }

});
/**
 * @const Bitmap32BE
 * @type BytePrimitive
 */

const Bitmap32BE = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size + _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position) * _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
  },

  read(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'bitmap too big');
    position += _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'uint8a array too big');
    const value = new Uint32Array(count);

    for (let i = 0; i < count; i++) {
      value[i] = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
      position += _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
    }

    return value;
  }

});
let decoder;
/* global TextDecoder:true */

if (typeof TextDecoder === 'undefined') {
  decoder = new text_encoding__WEBPACK_IMPORTED_MODULE_0__["TextDecoder"]();
} else {
  decoder = new TextDecoder();
}
/**
 * @const UTF8
 * @type BytePrimitive
 */


const UTF8 = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  sizeOf(uint8a, position) {
    return _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size + _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
  },

  read(uint8a, position) {
    const count = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'utf8 too big');
    position += _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].size;
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_1__["assert"])(count < BUFFER_TOO_BIG, 'uint8a array too big');
    return decoder.decode(new Uint8Array(uint8a.buffer, position, count));
  }

});
/**
 * @const OpaqueColor
 * @type BytePrimitive
 */

const OpaqueColor = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  size: 4,

  read(uint8a, position) {
    const rgb = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    const a = 0xff;
    const r = rgb >> 22 & 0xff;
    const g = rgb >> 12 & 0xff;
    const b = rgb >> 2 & 0xff;
    return (a << 24 | r << 16 | g << 8 | b) >>> 0;
  }

});
/**
 * @const TranslucentColor
 * @type BytePrimitive
 */

const TranslucentColor = new _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["BytePrimitive"]({
  size: 5,

  read(uint8a, position) {
    const rgb = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint32BE"].read(uint8a, position);
    const a = _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Uint8"].read(uint8a, position);
    const r = rgb >> 22 & 0xff;
    const g = rgb >> 12 & 0xff;
    const b = rgb >> 2 & 0xff;
    return (a << 24 | r << 16 | g << 8 | b) >>> 0;
  }

});


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/byte-take-iterator.js":
/*!*****************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/byte-take-iterator.js ***!
  \*****************************************************************************/
/*! exports provided: ByteTakeIterator */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ByteTakeIterator", function() { return ByteTakeIterator; });
/**
 * An iterator that only takes bytes up to a certain position.
 *
 * Take iterators constrain the number of times an inner iterator can return
 * values. Normally it constrains the number of returned values.
 * ByteTakeIterator instead constrains the number of bytes the inner iterator
 * may take from its stream before ByteTakeIterator returns done objects.
 *
 * Primarily used to wrap {@link FieldIterator}.
 */
class ByteTakeIterator {
  /**
   * @param {{stream: ByteStream}} iter - Iterator with `stream` member.
   * @param {number} [maxPosition=Infinity] - Position `stream` may not go
   * beyond when yielding the next value.
   */
  constructor(iter) {
    let maxPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Infinity;
    this.iter = iter;
    this.maxPosition = maxPosition;
  }
  /**
   * @returns {ByteTakeIterator} - Returns itself.
   */


  [Symbol.iterator]() {
    return this;
  }
  /**
   * @returns {{value: *, done: boolean}} - Return the next value or indicate
   * the Iterator has reached its end.
   */


  next() {
    if (this.iter.stream.position >= this.maxPosition) {
      return {
        value: null,
        done: true
      };
    }

    return this.iter.next();
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/field-iterator.js":
/*!*************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/field-iterator.js ***!
  \*************************************************************************/
/*! exports provided: FieldIterator */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FieldIterator", function() { return FieldIterator; });
/* harmony import */ var _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../coders/byte-primitives */ "./node_modules/scratch-sb1-converter/src/coders/byte-primitives.js");
/* harmony import */ var _coders_byte_stream__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../coders/byte-stream */ "./node_modules/scratch-sb1-converter/src/coders/byte-stream.js");
/* harmony import */ var _byte_primitives__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./byte-primitives */ "./node_modules/scratch-sb1-converter/src/squeak/byte-primitives.js");
/* harmony import */ var _fields__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./fields */ "./node_modules/scratch-sb1-converter/src/squeak/fields.js");
/* harmony import */ var _ids__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./ids */ "./node_modules/scratch-sb1-converter/src/squeak/ids.js");





/**
 * Consume values for the byte stream with a iterator-like interface.
 */

class Consumer {
  /**
   * @param {object} options - Define the consumer.
   * @param {function} [options.type=Value] - The {@link Field} type to
   * create.
   * @param {BytePrimitive} options.read - How to read the third Field
   * argument. The third field argument is the value the field represented in
   * the `.sb` file. It is either the Value's value, the Reference's index,
   * or the Header's field size.
   * @param {function} [options.value] - How to produce the third Field
   * argument from a stream. Defaults to `stream =>
   * stream.read(options.read)`.
   */
  constructor(_ref) {
    let {
      type = _fields__WEBPACK_IMPORTED_MODULE_3__["Value"],
      read,
      value = read ? stream => stream.read(read) : null
    } = _ref;
    this.type = type;
    this.value = value;
  }
  /**
   * @param {ByteStream} stream - Stream to read from.
   * @param {TYPES} classId - FieldObject TYPES identifying the value to read.
   * @param {number} position - Position in the stream the classId was read
   * from.
   * @returns {{value: *, done: boolean}} - An iterator.next() return value.
   */


  next(stream, classId, position) {
    return {
      value: new this.type(classId, position, this.value(stream)),
      done: false
    };
  }

}
/**
 * @const CONSUMER_PROTOS
 * @type {Object.<number, {type, read, value}>}
 */


const CONSUMER_PROTOS = {
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].NULL]: {
    value: () => null
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].TRUE]: {
    value: () => true
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].FALSE]: {
    value: () => false
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SMALL_INT]: {
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SMALL_INT_16]: {
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int16BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].LARGE_INT_POSITIVE]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["LargeInt"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].LARGE_INT_NEGATIVE]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["LargeInt"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].FLOATING]: {
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["DoubleBE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].STRING]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["AsciiString"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SYMBOL]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["AsciiString"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].BYTES]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Bytes"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SOUND]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["SoundBytes"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].BITMAP]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["Bitmap32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].UTF8]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["UTF8"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].ARRAY]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].ORDERED_COLLECTION]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SET]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].IDENTITY_SET]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    read: _coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].DICTIONARY]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: stream => stream.read(_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]) * 2
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].IDENTITY_DICTIONARY]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: stream => stream.read(_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Int32BE"]) * 2
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].COLOR]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["OpaqueColor"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].TRANSLUCENT_COLOR]: {
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["TranslucentColor"]
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].POINT]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: () => 2
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].RECTANGLE]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: () => 4
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].FORM]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: () => 5
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].SQUEAK]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Header"],
    value: () => 6
  },
  [_ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].OBJECT_REF]: {
    type: _fields__WEBPACK_IMPORTED_MODULE_3__["Reference"],
    read: _byte_primitives__WEBPACK_IMPORTED_MODULE_2__["ReferenceBE"]
  }
};
/**
 * @const CONSUMERS
 * @type {Array.<Consumer|null>}
 */

const CONSUMERS = Array.from({
  length: 256
}, (_, i) => {
  if (CONSUMER_PROTOS[i]) return new Consumer(CONSUMER_PROTOS[i]);
  return null;
});
const builtinConsumer = new Consumer({
  type: _fields__WEBPACK_IMPORTED_MODULE_3__["BuiltinObjectHeader"],
  value: () => null
});
/**
 * Field iterator.
 */

class FieldIterator {
  /**
   * @param {ArrayBuffer} buffer - Buffer to read from.
   * @param {number} position - Position in buffer to start at.
   */
  constructor(buffer, position) {
    this.buffer = buffer;
    this.stream = new _coders_byte_stream__WEBPACK_IMPORTED_MODULE_1__["ByteStream"](buffer, position);
  }
  /**
   * @returns {FieldIterator} - Returns this.
   */


  [Symbol.iterator]() {
    return this;
  }
  /**
   * @returns {{value: *, done: boolean}} - An iterator.next() value.
   */


  next() {
    if (this.stream.position >= this.stream.uint8a.length) {
      return {
        value: null,
        done: true
      };
    }

    const position = this.stream.position;
    const classId = this.stream.read(_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint8"]);
    const consumer = CONSUMERS[classId];

    if (consumer !== null) {
      return consumer.next(this.stream, classId, position);
    } else if (classId < _ids__WEBPACK_IMPORTED_MODULE_4__["TYPES"].OBJECT_REF) {
      // TODO: Does this ever happen?
      return builtinConsumer.next(this.stream, classId, position);
    }

    const classVersion = this.stream.read(_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint8"]);
    const size = this.stream.read(_coders_byte_primitives__WEBPACK_IMPORTED_MODULE_0__["Uint8"]);
    return {
      value: new _fields__WEBPACK_IMPORTED_MODULE_3__["FieldObjectHeader"](classId, position, classVersion, size),
      done: false
    };
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/field-object.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/field-object.js ***!
  \***********************************************************************/
/*! exports provided: FieldObject */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FieldObject", function() { return FieldObject; });
/* harmony import */ var _ids__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ids */ "./node_modules/scratch-sb1-converter/src/squeak/ids.js");


const toTitleCase = str => str.toLowerCase().replace(/_(\w)/g, _ref => {
  let [, letter] = _ref;
  return letter.toUpperCase();
});
/**
 * A object representation of a {@link Header} collecting the given {@link
 * Header#size} in fields.
 */


class FieldObject {
  /**
   * @param {TYPES} classId - {@link TYPES} id that informs what the shape of
   * this object is.
   * @param {number} version - Version number of this object. Some items in
   * the same class may have different content and so will be different
   * versions.
   * @param {Array.<Field>} fields - An array of fields in this FieldObject.
   */
  constructor(_ref2) {
    let {
      classId,
      version,
      fields
    } = _ref2;

    /** @type {number} */
    this.classId = classId;
    /** @type {number} */

    this.version = version;
    /** @type {Array.<Field>} */

    this.fields = fields;
  }
  /**
   * @type {object}
   */


  get FIELDS() {
    return [];
  }
  /**
   * @type {Array.<Field>}
   */


  get RAW_FIELDS() {
    return this.fields;
  }

  string(field) {
    return String(this.fields[field]);
  }

  number(field) {
    return +this.fields[field];
  }

  boolean(field) {
    return !!this.fields[field];
  }

  toString() {
    if (this.constructor === FieldObject) {
      return "".concat(this.constructor.name, " ").concat(this.classId, " ").concat(_ids__WEBPACK_IMPORTED_MODULE_0__["TYPE_NAMES"][this.classId]);
    }

    return this.constructor.name;
  }
  /**
   * Define a FieldObject subclass by mapping field names to indices in
   * {@link FieldObject#fields}.
   * @param {object} FIELDS - Mapping of ALL_CAPS keys to index in {@link
   * FieldObject#fields}.
   * @param {function} [Super] - Parent class of the returned subclass.
   * @returns {function} - FieldObject subclass constructor.
   */


  static define(FIELDS) {
    let Super = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : FieldObject;

    class DefinedObject extends Super {
      get FIELDS() {
        return FIELDS;
      }

      static get FIELDS() {
        return FIELDS;
      }

    }

    Object.keys(FIELDS).forEach(key => {
      const index = FIELDS[key];
      Object.defineProperty(DefinedObject.prototype, toTitleCase(key), {
        get() {
          return this.fields[index];
        }

      });
    });
    return DefinedObject;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/fields.js":
/*!*****************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/fields.js ***!
  \*****************************************************************/
/*! exports provided: Field, value, Value, Header, Reference, BuiltinObjectHeader, FieldObjectHeader */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Field", function() { return Field; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "value", function() { return valueOf; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Value", function() { return Value; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Header", function() { return Header; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Reference", function() { return Reference; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BuiltinObjectHeader", function() { return BuiltinObjectHeader; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FieldObjectHeader", function() { return FieldObjectHeader; });
/* harmony import */ var _ids__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./ids */ "./node_modules/scratch-sb1-converter/src/squeak/ids.js");

/**
 * An abstract value contained in a `.sb` file.
 *
 * `.sb` files are made up of two blocks of Fields. Each field in the binary
 * file defines its "class" and is possibly followed by some binary data
 * depending on the class. Each class explicitly defines what follows. Knowing
 * all the possible classes each block can be broken up into a series of Field
 * objects.
 */

class Field {
  /**
   * @param {TYPES} classId - The class identifier of this Field.
   * @param {number} position - Byte position in the `.sb` file.
   */
  constructor(classId, position) {
    /**
     * The class identifier of this Field.
     * @type {TYPES}
     */
    this.classId = classId;
    /**
     * Byte position in the `.sb` file.
     * @type {number}
     */

    this.position = position;
  }

}

const valueOf = obj => {
  if (typeof obj === 'object' && obj) return obj.valueOf();
  return obj;
};
/**
 * A concrete value contained in a `.sb` file.
 * @extends Field
 */


class Value extends Field {
  /**
   * @param {TYPES} classId - The class identifier of this Field.
   * @param {number} position - Byte position in the `.sb` file.
   * @param {*} value - A value decoded according to `classId` from an `.sb`
   * file.
   */
  constructor(classId, position, value) {
    super(classId, position);
    /**
     * A value decoded according to `classId` from an `.sb` file.
     * @type {*}
     */

    this.value = value;
  }

  valueOf() {
    return this.value;
  }

  toJSON() {
    if (this.classId === _ids__WEBPACK_IMPORTED_MODULE_0__["TYPES"].TRANSLUCENT_COLOR || this.classId === _ids__WEBPACK_IMPORTED_MODULE_0__["TYPES"].COLOR) {
      // TODO: Can colors be 32 bit in scratch-packets?
      return this.value & 0xffffff;
    }

    return this.value;
  }

  toString() {
    return this.value;
  }

}
/**
 * A header for a FieldObject representing its class and how many fields are in
 * the object.
 *
 * The `size` of a header is the number of Fields that appear in the byte
 * stream after the header that are related to the header. That set of `size`
 * length Fields make up a FieldObject of `classId` passed to this header.
 * @extends Field
 */


class Header extends Field {
  /**
   * @param {TYPES} classId - The class identifier of this Field.
   * @param {number} position - Byte position in the `.sb` file.
   * @param {number} size - The number of fields to collect.
   */
  constructor(classId, position, size) {
    super(classId, position);
    /**
     * The number of fields to collect.
     * @type {number}
     */

    this.size = size;
  }

}
/**
 * A integer reference of an object in an array produced by TypeIterator of
 * Values and FieldObjects.
 * @extends Field
 */


class Reference extends Field {
  /**
   * @param {TYPES} classId - The class identifier of this Field.
   * @param {number} position - Byte position in the `.sb` file.
   * @param {number} index - The index this Reference refers to.
   */
  constructor(classId, position, index) {
    super(classId, position);
    /**
     * The index this Reference refers to.
     * @type {number}
     */

    this.index = index;
  }

  valueOf() {
    return "Ref(".concat(this.index, ")");
  }

}
/**
 * An object header of 0 size.
 * @extends Header
 */


class BuiltinObjectHeader extends Header {
  constructor(classId, position) {
    super(classId, position, 0);
  }

}
/**
 * An object header with an id more than 99, a version, and a size. The version
 * and size appear in the `sb` file as one byte for version followed by another
 * byte for the size.
 * @extends Header
 */


class FieldObjectHeader extends Header {
  /**
   * @param {TYPES} classId - The class identifier of this Field.
   * @param {number} position - Byte position in the `.sb` file.
   * @param {number} version - The version of this instance of a certain
   * value.
   * @param {number} size - The number of fields in this object.
   */
  constructor(classId, position, version, size) {
    super(classId, position, size);
    /**
     * The version of this instance of a certain value.
     * @type {number}
     */

    this.version = version;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/ids.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/ids.js ***!
  \**************************************************************/
/*! exports provided: TYPES, TYPE_NAMES */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TYPES", function() { return TYPES; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TYPE_NAMES", function() { return TYPE_NAMES; });
/**
 * A numeric identifier for each possible class of {@link Field} that can be in
 * a `.sb` file.
 * @enum {number}
 */
const TYPES = {
  /** A `null` {@link Value}. No data is stored after the class id. */
  NULL: 1,

  /** A `true` {@link Value}. No data is stored after the class id. */
  TRUE: 2,

  /** A `false` {@link Value}. No data is stored after the class id. */
  FALSE: 3,

  /** A small integer {@link Value}. The next 4 bytes represent an integer. */
  SMALL_INT: 4,

  /** A small integer {@link Value}. The next 2 bytes represent an integer. */
  SMALL_INT_16: 5,

  /** A large integer {@link Value}. The value is a variable number of bytes.
   * The next byte defines the number of bytes after that represent the
   * integer. The integer's bytes are stored least value first (little
   * endian). */
  LARGE_INT_POSITIVE: 6,

  /** A large integer {@link Value}. The value is a variable number of bytes.
   * The next byte defines the number of bytes after that represent the
   * integer. The integer's bytes are stored least value first (little
   * endian). */
  LARGE_INT_NEGATIVE: 7,

  /** A floating point {@link Value}. The next 8 bytes are stored as a double
   * precision floating point value. */
  FLOATING: 8,

  /** A ascii string {@link Value}. The next 4 bytes defines the number of
   * following bytes that make up the string. */
  STRING: 9,

  /** A ascii string {@link Value}. The next 4 bytes defines the number of
   * following bytes that make up the string. */
  SYMBOL: 10,

  /** A sequence of bytes ({@link Value}). The next 4 bytes defines the
   * number of bytes in the sequence. */
  BYTES: 11,

  /** A sequence of 16 bit samples ({@link Value}). The next 4 bytes defines
   * the number of samples in the sequence. */
  SOUND: 12,

  /** A sequence of 32 bit color integers ({@link Value}). The next 4 bytes
   * defines the number of colors in the bitmap. */
  BITMAP: 13,

  /** A utf8 string {@link Value}. The next 4 bytes defines the number of
   * bytes used by the string. */
  UTF8: 14,

  /** An array {@link Header}. The next 4 bytes defines the following number
   * of fields in the array. */
  ARRAY: 20,

  /** An array {@link Header}. The next 4 bytes defines the following number
   * of fields in the array. */
  ORDERED_COLLECTION: 21,

  /** An array {@link Header}. The next 4 bytes defines the following number
   * of fields in the array. */
  SET: 22,

  /** An array {@link Header}. The next 4 bytes defines the following number
   * of fields in the array. */
  IDENTITY_SET: 23,

  /** A dictionary {@link Header}. The next 4 bytes defines the following
   * number of key/value field pairs in the dictionary. */
  DICTIONARY: 24,

  /** A dictionary {@link Header}. The next 4 bytes defines the following
   * number of key/value field pairs in the dictionary. */
  IDENTITY_DICTIONARY: 25,

  /** A color {@link Value}. The next 4 bytes represents the color. */
  COLOR: 30,

  /** A color {@link Value}. The next 4 bytes represents the red, green, and
   * blue subpixels. The following byte represents the alpha. */
  TRANSLUCENT_COLOR: 31,

  /** A 2 field point {@link Header}. The next 2 fields are the x and y
   * values of this point. */
  POINT: 32,

  /** A 4 field rectangle {@link Header}. The next 4 fields are the x, y, x2,
   * y2 values of this rectangle. */
  RECTANGLE: 33,

  /** A 5 field image {@link Header}. The next 5 fields are the width,
   * height, bit depth, unused, and bytes. */
  FORM: 34,

  /** A 6 field image {@link Header}. The next 6 fields are the width,
   * height, bit depth, unsued, bytes and colormap. */
  SQUEAK: 35,

  /** An object {@link Reference} to a position in the top level array of fields in a
   * block. */
  OBJECT_REF: 99,

  /** A variable {@link FieldObjectHeader}. */
  MORPH: 100,

  /** A variable {@link FieldObjectHeader}. */
  ALIGNMENT: 104,

  /** A variable {@link FieldObjectHeader}.
   *
   * In Scratch 2 this is called String. To reduce confusion in the set of
   * types, this is called STATIC_STRING in this converter. */
  STATIC_STRING: 105,

  /** A variable {@link FieldObjectHeader}. */
  UPDATING_STRING: 106,

  /** A variable {@link FieldObjectHeader}. */
  SAMPLED_SOUND: 109,

  /** A variable {@link FieldObjectHeader}. */
  IMAGE_MORPH: 110,

  /** A variable {@link FieldObjectHeader}. */
  SPRITE: 124,

  /** A variable {@link FieldObjectHeader}. */
  STAGE: 125,

  /** A variable {@link FieldObjectHeader}. */
  WATCHER: 155,

  /** A variable {@link FieldObjectHeader}. */
  IMAGE_MEDIA: 162,

  /** A variable {@link FieldObjectHeader}. */
  SOUND_MEDIA: 164,

  /** A variable {@link FieldObjectHeader}. */
  MULTILINE_STRING: 171,

  /** A variable {@link FieldObjectHeader}. */
  WATCHER_READOUT_FRAME: 173,

  /** A variable {@link FieldObjectHeader}. */
  WATCHER_SLIDER: 174,

  /** A variable {@link FieldObjectHeader}. */
  LIST_WATCHER: 175
};
/**
 * A inverted map of TYPES. Map id numbers to their string names.
 * @type {object.<number, string>}
 */

const TYPE_NAMES = Object.entries(TYPES).reduce((carry, _ref) => {
  let [key, value] = _ref;
  carry[value] = key;
  return carry;
}, {});


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/reference-fixer.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/reference-fixer.js ***!
  \**************************************************************************/
/*! exports provided: ReferenceFixer */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ReferenceFixer", function() { return ReferenceFixer; });
/* harmony import */ var _fields__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fields */ "./node_modules/scratch-sb1-converter/src/squeak/fields.js");


class ReferenceFixer {
  constructor(table) {
    this.table = Array.from(table);
    this.fixed = this.fix(this.table);
  }

  fix() {
    const fixed = [];

    for (let i = 0; i < this.table.length; i++) {
      this.fixItem(this.table[i]);
      fixed.push(this.table[i]);
    }

    return fixed;
  }

  fixItem(item) {
    if (typeof item.fields !== 'undefined') {
      item = item.fields;
    }

    if (Array.isArray(item)) {
      for (let i = 0; i < item.length; i++) {
        item[i] = this.deref(item[i]);
      }
    }
  }

  deref(ref) {
    if (ref instanceof _fields__WEBPACK_IMPORTED_MODULE_0__["Reference"]) {
      return this.table[ref.index - 1];
    }

    return ref;
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/type-iterator.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/type-iterator.js ***!
  \************************************************************************/
/*! exports provided: TypeIterator */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TypeIterator", function() { return TypeIterator; });
/* harmony import */ var _fields__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./fields */ "./node_modules/scratch-sb1-converter/src/squeak/fields.js");
/* harmony import */ var _field_object__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./field-object */ "./node_modules/scratch-sb1-converter/src/squeak/field-object.js");
/* harmony import */ var _types__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./types */ "./node_modules/scratch-sb1-converter/src/squeak/types.js");




class TypeIterator {
  constructor(valueIterator) {
    this.valueIterator = valueIterator;
  }

  [Symbol.iterator]() {
    return this;
  }

  next() {
    const nextHeader = this.valueIterator.next();

    if (nextHeader.done) {
      return nextHeader;
    }

    const header = nextHeader.value;
    const {
      classId
    } = header;
    let value = header;

    if (header instanceof _fields__WEBPACK_IMPORTED_MODULE_0__["Header"]) {
      value = [];

      for (let i = 0; i < header.size; i++) {
        value.push(this.next().value);
      }
    }

    if (_types__WEBPACK_IMPORTED_MODULE_2__["FIELD_OBJECT_CONTRUCTORS"][classId] !== null || header instanceof _fields__WEBPACK_IMPORTED_MODULE_0__["FieldObjectHeader"]) {
      const constructor = _types__WEBPACK_IMPORTED_MODULE_2__["FIELD_OBJECT_CONTRUCTORS"][header.classId] || _field_object__WEBPACK_IMPORTED_MODULE_1__["FieldObject"];
      value = new constructor({
        classId: header.classId,
        version: header.version,
        fields: value
      });
    }

    return {
      value,
      done: false
    };
  }

}



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/squeak/types.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/squeak/types.js ***!
  \****************************************************************/
/*! exports provided: PointData, RectangleData, ImageData, StageData, SpriteData, TextDetailsData, ImageMediaData, UncompressedData, SoundMediaData, ListWatcherData, AlignmentData, MorphData, StaticStringData, UpdatingStringData, WatcherReadoutFrameData, WATCHER_MODES, WatcherData, FIELD_OBJECT_CONTRUCTORS */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PointData", function() { return PointData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RectangleData", function() { return RectangleData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImageData", function() { return ImageData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StageData", function() { return StageData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpriteData", function() { return SpriteData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextDetailsData", function() { return TextDetailsData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ImageMediaData", function() { return ImageMediaData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UncompressedData", function() { return UncompressedData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SoundMediaData", function() { return SoundMediaData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ListWatcherData", function() { return ListWatcherData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AlignmentData", function() { return AlignmentData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MorphData", function() { return MorphData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StaticStringData", function() { return StaticStringData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "UpdatingStringData", function() { return UpdatingStringData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatcherReadoutFrameData", function() { return WatcherReadoutFrameData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WATCHER_MODES", function() { return WATCHER_MODES; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "WatcherData", function() { return WatcherData; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FIELD_OBJECT_CONTRUCTORS", function() { return FIELD_OBJECT_CONTRUCTORS; });
/* harmony import */ var _coders_crc32__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../coders/crc32 */ "./node_modules/scratch-sb1-converter/src/coders/crc32.js");
/* harmony import */ var _coders_squeak_image__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../coders/squeak-image */ "./node_modules/scratch-sb1-converter/src/coders/squeak-image.js");
/* harmony import */ var _coders_squeak_sound__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../coders/squeak-sound */ "./node_modules/scratch-sb1-converter/src/coders/squeak-sound.js");
/* harmony import */ var _coders_wav_file__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../coders/wav-file */ "./node_modules/scratch-sb1-converter/src/coders/wav-file.js");
/* harmony import */ var _field_object__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./field-object */ "./node_modules/scratch-sb1-converter/src/squeak/field-object.js");
/* harmony import */ var _fields__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./fields */ "./node_modules/scratch-sb1-converter/src/squeak/fields.js");
/* harmony import */ var _ids__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./ids */ "./node_modules/scratch-sb1-converter/src/squeak/ids.js");
/* harmony import */ var js_md5__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! js-md5 */ "./node_modules/js-md5/src/md5.js");
/* harmony import */ var js_md5__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(js_md5__WEBPACK_IMPORTED_MODULE_7__);








/**
 * @extends FieldObject
 */

class PointData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  /**
   * @memberof PointData#
   * @type {Value}
   */
  X: 0,

  /**
   * @memberof PointData#
   * @type {Value}
   */
  Y: 1
}) {}



class RectangleData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  X: 0,
  Y: 1,
  X2: 2,
  Y2: 3
}) {
  get width() {
    return this.x2 - this.x;
  }

  get height() {
    return this.y2 - this.y;
  }

}



const _bgra2rgbaInPlace = uint8a => {
  for (let i = 0; i < uint8a.length; i += 4) {
    const r = uint8a[i + 2];
    const b = uint8a[i + 0];
    uint8a[i + 2] = b;
    uint8a[i + 0] = r;
  }

  return uint8a;
};
/**
 * @extends FieldObject
 */


class ImageData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  /**
   * @memberof ImageData#
   * @type {Value}
   */
  WIDTH: 0,

  /**
   * @memberof ImageData#
   * @type {Value}
   */
  HEIGHT: 1,

  /**
   * @memberof ImageData#
   * @type {Value}
   */
  DEPTH: 2,

  /**
   * @memberof ImageData#
   * @type {Value}
   */
  BYTES: 4,

  /**
   * @memberof ImageData#
   * @type {Value}
   */
  COLORMAP: 5
}) {
  /**
   * @type {Uint8Array}
   */
  get decoded() {
    if (!this._decoded) {
      this._decoded = _bgra2rgbaInPlace(new Uint8Array(new _coders_squeak_image__WEBPACK_IMPORTED_MODULE_1__["SqueakImage"]().decode(this.width.value, this.height.value, this.depth.value, this.bytes.value, this.colormap && this.colormap.map(color => color.valueOf())).buffer));
    }

    return this._decoded;
  }
  /**
   * @type {string}
   */


  get extension() {
    return 'uncompressed';
  }

}



class StageData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  STAGE_CONTENTS: 2,
  OBJ_NAME: 6,
  VARS: 7,
  BLOCKS_BIN: 8,
  IS_CLONE: 9,
  MEDIA: 10,
  CURRENT_COSTUME: 11,
  ZOOM: 12,
  H_PAN: 13,
  V_PAN: 14,
  OBSOLETE_SAVED_STATE: 15,
  SPRITE_ORDER_IN_LIBRARY: 16,
  VOLUME: 17,
  TEMPO_BPM: 18,
  SCENE_STATES: 19,
  LISTS: 20
}) {
  get spriteOrderInLibrary() {
    return this.fields[this.FIELDS.SPRITE_ORDER_IN_LIBRARY] || null;
  }

  get tempoBPM() {
    return this.fields[this.FIELDS.TEMPO_BPM] || 0;
  }

  get lists() {
    return this.fields[this.FIELDS.LISTS] || [];
  }

}



class SpriteData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  PARENT: 1,
  COLOR: 3,
  VISIBLE: 4,
  OBJ_NAME: 6,
  VARS: 7,
  BLOCKS_BIN: 8,
  IS_CLONE: 9,
  MEDIA: 10,
  CURRENT_COSTUME: 11,
  VISIBILITY: 12,
  SCALE_POINT: 13,
  ROTATION_DEGREES: 14,
  ROTATION_STYLE: 15,
  VOLUME: 16,
  TEMPO_BPM: 17,
  DRAGGABLE: 18,
  SCENE_STATES: 19,
  LISTS: 20
}) {
  get scratchX() {
    return this.box.x + this.currentCostume.rotationCenter.x - 240;
  }

  get scratchY() {
    return 180 - (this.box.y + this.currentCostume.rotationCenter.y);
  }

  get visible() {
    return (this.fields[this.FIELDS.VISIBLE] & 1) === 0;
  }

  get tempoBPM() {
    return this.fields[this.FIELDS.TEMPO_BPM] || 0;
  }

  get lists() {
    return this.fields[this.FIELDS.LISTS] || [];
  }

}



class TextDetailsData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  RECTANGLE: 0,
  FONT: 8,
  COLOR: 9,
  LINES: 11
}) {}



class ImageMediaData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  COSTUME_NAME: 0,
  BITMAP: 1,
  ROTATION_CENTER: 2,
  TEXT_DETAILS: 3,
  BASE_LAYER_DATA: 4,
  OLD_COMPOSITE: 5
}) {
  get image() {
    if (this.oldComposite instanceof ImageData) {
      return this.oldComposite;
    }

    if (this.baseLayerData.value) {
      return null;
    }

    return this.bitmap;
  }

  get width() {
    if (this.image === null) {
      return -1;
    }

    return this.image.width;
  }

  get height() {
    if (this.image === null) {
      return -1;
    }

    return this.image.height;
  }

  get rawBytes() {
    if (this.image === null) {
      return this.baseLayerData.value.slice();
    }

    return this.image.bytes.value;
  }

  get decoded() {
    if (this.image === null) {
      return this.baseLayerData.value.slice();
    }

    return this.image.decoded;
  }

  get crc() {
    if (!this._crc) {
      const crc = new _coders_crc32__WEBPACK_IMPORTED_MODULE_0__["CRC32"]().update(new Uint8Array(new Uint32Array([this.bitmap.width]).buffer)).update(new Uint8Array(new Uint32Array([this.bitmap.height]).buffer)).update(new Uint8Array(new Uint32Array([this.bitmap.depth]).buffer)).update(this.rawBytes);
      this._crc = crc.digest;
    }

    return this._crc;
  }

  get extension() {
    if (this.oldComposite instanceof ImageData) return 'uncompressed';
    if (this.baseLayerData.value) return 'jpg';
    return 'uncompressed';
  }

  toString() {
    return "ImageMediaData \"".concat(this.costumeName, "\"");
  }

}



class UncompressedData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  DATA: 3,
  RATE: 4
}) {}



const reverseBytes16 = input => {
  const uint8a = new Uint8Array(input);

  for (let i = 0; i < uint8a.length; i += 2) {
    uint8a[i] = input[i + 1];
    uint8a[i + 1] = input[i];
  }

  return uint8a;
};

class SoundMediaData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  NAME: 0,
  UNCOMPRESSED: 1,
  RATE: 4,
  BITS_PER_SAMPLE: 5,
  DATA: 6
}) {
  get rate() {
    if (this.uncompressed.data.value.length !== 0) {
      return this.uncompressed.rate;
    }

    return this.fields[this.FIELDS.RATE];
  }

  get rawBytes() {
    if (this.data && this.data.value) {
      return this.data.value;
    }

    return this.uncompressed.data.value;
  }

  get decoded() {
    if (!this._decoded) {
      if (this.data && this.data.value) {
        this._decoded = new _coders_squeak_sound__WEBPACK_IMPORTED_MODULE_2__["SqueakSound"](this.bitsPerSample.value).decode(this.data.value);
      } else {
        this._decoded = new Int16Array(reverseBytes16(this.uncompressed.data.value.slice()).buffer);
      }
    }

    return this._decoded;
  }

  get crc() {
    if (!this._crc) {
      this._crc = new _coders_crc32__WEBPACK_IMPORTED_MODULE_0__["CRC32"]().update(new Uint32Array([this.rate])).update(this.rawBytes).digest;
    }

    return this._crc;
  }

  get sampleCount() {
    if (this.data && this.data.value) {
      return _coders_squeak_sound__WEBPACK_IMPORTED_MODULE_2__["SqueakSound"].samples(this.bitsPerSample.value, this.data.value);
    }

    return this.uncompressed.data.value.length / 2;
  }

  get extension() {
    return 'pcm';
  }

  get wavEncodedData() {
    if (!this._wavEncodedData) {
      this._wavEncodedData = new Uint8Array(_coders_wav_file__WEBPACK_IMPORTED_MODULE_3__["WAVFile"].encode(this.decoded, {
        sampleRate: this.rate && this.rate.value
      }));
    }

    return this._wavEncodedData;
  }

  get md5() {
    if (!this._md5) {
      this._md5 = js_md5__WEBPACK_IMPORTED_MODULE_7___default()(this.wavEncodedData);
    }

    return this._md5;
  }

  toString() {
    return "SoundMediaData \"".concat(this.name, "\"");
  }

}



class ListWatcherData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  HIDDEN_WHEN_NULL: 1,
  LIST_NAME: 8,
  CONTENTS: 9,
  TARGET: 10
}) {
  get x() {
    if (Object(_fields__WEBPACK_IMPORTED_MODULE_5__["value"])(this.hiddenWhenNull) === null) return 5;
    return this.box.x + 1;
  }

  get y() {
    if (Object(_fields__WEBPACK_IMPORTED_MODULE_5__["value"])(this.hiddenWhenNull) === null) return 5;
    return this.box.y + 1;
  }

  get width() {
    return this.box.width - 2;
  }

  get height() {
    return this.box.height - 2;
  }

}



class AlignmentData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  PARENT: 1,
  FRAMES: 2,
  COLOR: 3,
  DIRECTION: 8,
  ALIGNMENT: 9
}) {}



class MorphData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  PARENT: 1,
  COLOR: 3
}) {}



class StaticStringData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  COLOR: 3,
  VALUE: 8
}) {}



class UpdatingStringData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  READOUT_FRAME: 1,
  COLOR: 3,
  FONT: 6,
  VALUE: 8,
  TARGET: 10,
  CMD: 11,
  PARAM: 13
}) {}



class WatcherReadoutFrameData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0
}) {}


const WATCHER_MODES = {
  NORMAL: 1,
  LARGE: 2,
  SLIDER: 3,
  TEXT: 4
};


class WatcherData extends _field_object__WEBPACK_IMPORTED_MODULE_4__["FieldObject"].define({
  BOX: 0,
  TARGET: 1,
  SHAPE: 2,
  READOUT: 14,
  READOUT_FRAME: 15,
  SLIDER: 16,
  ALIGNMENT: 17,
  SLIDER_MIN: 20,
  SLIDER_MAX: 21
}) {
  get x() {
    return this.box.x;
  }

  get y() {
    return this.box.y;
  }

  get mode() {
    if (Object(_fields__WEBPACK_IMPORTED_MODULE_5__["value"])(this.slider) === null) {
      if (this.readoutFrame.box.height <= 14) {
        return WATCHER_MODES.NORMAL;
      }

      return WATCHER_MODES.LARGE;
    }

    return WATCHER_MODES.SLIDER;
  }

  get isDiscrete() {
    return Math.floor(this.sliderMin) === this.sliderMin && Math.floor(this.sliderMax) === this.sliderMax && Math.floor(this.readout.value) === this.readout.value;
  }

}


const FIELD_OBJECT_CONTRUCTOR_PROTOS = {
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].POINT]: PointData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].RECTANGLE]: RectangleData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].FORM]: ImageData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].SQUEAK]: ImageData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].SAMPLED_SOUND]: UncompressedData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].SPRITE]: SpriteData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].STAGE]: StageData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].IMAGE_MEDIA]: ImageMediaData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].SOUND_MEDIA]: SoundMediaData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].ALIGNMENT]: AlignmentData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].MORPH]: MorphData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].WATCHER_READOUT_FRAME]: WatcherReadoutFrameData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].STATIC_STRING]: StaticStringData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].UPDATING_STRING]: UpdatingStringData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].WATCHER]: WatcherData,
  [_ids__WEBPACK_IMPORTED_MODULE_6__["TYPES"].LIST_WATCHER]: ListWatcherData
};
const FIELD_OBJECT_CONTRUCTORS = Array.from({
  length: 256
}, (_, i) => FIELD_OBJECT_CONTRUCTOR_PROTOS[i] || null);


/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/to-sb2/fake-zip.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/to-sb2/fake-zip.js ***!
  \*******************************************************************/
/*! exports provided: FakeZipFile, FakeZip, toSb2FakeZipApi */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FakeZipFile", function() { return FakeZipFile; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FakeZip", function() { return FakeZip; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toSb2FakeZipApi", function() { return toSb2FakeZipApi; });
/* harmony import */ var _util_assert__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../util/assert */ "./node_modules/scratch-sb1-converter/src/util/assert.js");
/* harmony import */ var _coders_png_file__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../coders/png-file */ "./node_modules/scratch-sb1-converter/src/coders/png-file.js");



class FakeZipFile {
  constructor(file) {
    this.file = file;
  }

  async(outputType) {
    Object(_util_assert__WEBPACK_IMPORTED_MODULE_0__["assert"])(outputType === 'uint8array', 'SB1FakeZipFile only supports uint8array');
    return Promise.resolve(this.file.bytes);
  }

}



class FakeZip {
  constructor(files) {
    this.files = files;
  }

  file(file) {
    if (file in this.files) {
      return new FakeZipFile(this.files[file]);
    }
  }

}



const toSb2ImageExtension = imageMedia => {
  if (imageMedia.extension === 'uncompressed') {
    return 'png';
  }

  return 'jpg';
};

const toSb2ImageMedia = imageMedia => {
  if (imageMedia.extension === 'uncompressed') {
    return new Uint8Array(_coders_png_file__WEBPACK_IMPORTED_MODULE_1__["PNGFile"].encode(imageMedia.width, imageMedia.height, imageMedia.decoded));
  }

  return imageMedia.decoded;
};

const toSb2SoundMedia = soundMedia => soundMedia.wavEncodedData;

const toSb2FakeZipApi = _ref => {
  let {
    images,
    sounds
  } = _ref;
  const files = {};
  let index = 0;

  for (const image of images) {
    files["".concat(index++, ".").concat(toSb2ImageExtension(image))] = {
      bytes: toSb2ImageMedia(image)
    };
  }

  index = 0;

  for (const sound of sounds) {
    files["".concat(index++, ".wav")] = {
      bytes: toSb2SoundMedia(sound)
    };
  }

  return new FakeZip(files);
};



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/to-sb2/json-generator.js":
/*!*************************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/to-sb2/json-generator.js ***!
  \*************************************************************************/
/*! exports provided: toSb2Json */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "toSb2Json", function() { return toSb2Json; });
/* harmony import */ var _squeak_types__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../squeak/types */ "./node_modules/scratch-sb1-converter/src/squeak/types.js");
/* harmony import */ var js_md5__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! js-md5 */ "./node_modules/js-md5/src/md5.js");
/* harmony import */ var js_md5__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(js_md5__WEBPACK_IMPORTED_MODULE_1__);
/* eslint no-use-before-define:1 */

 // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L292-L308

const fixMouseEdgeRef = block => {
  const oldVal = String(block[block.length - 1]);
  const last = block.length - 1;

  if (oldVal === 'mouse') {
    block[last] = '_mouse_';
  } else if (oldVal === 'edge') {
    block[last] = '_edge_';
  } else if (block[block.length - 1] instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["StageData"]) {
    block[last] = '_stage_';
  }

  return block;
};

const sb1SpecMap = {
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L197-L199
  'getParam': _ref => {
    let [a, b, c, d] = _ref;
    return [a, b, c, d || 'r'];
  },
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L200-L212
  'changeVariable': block => [block[2], block[1], block[3]],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L213-L219
  'EventHatMorph': block => {
    if (String(block[1]) === 'Scratch-StartClicked') {
      return ['whenGreenFlag'];
    }

    return ['whenIReceive', block[1]];
  },
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L220-L222
  'MouseClickEventHatMorph': () => ['whenClicked'],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L223-L226
  'KeyEventHatMorph': block => ['whenKeyPressed', block[1]],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L227-L235
  'stopScripts': block => {
    if (String(block[1]) === 'other scripts') {
      return [block[0], 'other scripts in sprite'];
    }

    return block;
  },
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L249-L253
  'abs': block => ['computeFunction:of:', 'abs', block[1]],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L254-L258
  'sqrt': block => ['computeFunction:of:', 'sqrt', block[1]],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L137
  '\\\\': block => ['%', ...block.slice(1)],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L259-L262
  'doReturn': () => ['stopScripts', 'this script'],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L263-L266
  'stopAll': () => ['stopScripts', 'all'],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L267-L270
  'showBackground:': block => ['startScene', block[1]],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L271-L273
  'nextBackground': () => ['nextScene'],
  // https://github.com/LLK/scratch-flash/blob/cb5f42f039ef633710faf9c63b69e8368b280372/src/blocks/BlockIO.as#L274-L282
  'doForeverIf': block => ['doForever', [['doIf', block[1], block[2]]]],
  'getAttribute:of:': fixMouseEdgeRef,
  'gotoSpriteOrMouse:': fixMouseEdgeRef,
  'distanceTo:': fixMouseEdgeRef,
  'pointTowards:': fixMouseEdgeRef,
  'touching:': fixMouseEdgeRef
};

const valueOf = obj => {
  if (typeof obj === 'object' && obj) return obj.valueOf();
  return obj;
};

const toSb2Json = root => {
  const {
    info,
    stageData,
    images,
    sounds
  } = root;

  const pairs = array => {
    const _pairs = [];

    for (let i = 0; i < array.length; i += 2) {
      _pairs.push([array[i], array[i + 1]]);
    }

    return _pairs;
  };

  const toSb2JsonVariable = _ref2 => {
    let [name, value] = _ref2;
    return {
      name,
      value,
      isPersistent: false
    };
  };

  const toSb2JsonList = _ref3 => {
    let [, {
      listName,
      contents,
      x,
      y,
      width,
      height,
      hiddenWhenNull
    }] = _ref3;
    return {
      listName: listName,
      contents: contents,
      isPersistent: false,
      x: x,
      y: y,
      width: width,
      height: height,
      visible: valueOf(hiddenWhenNull) !== null
    };
  }; // TODO: Implement toSb2JsonWatcher
  // const toSb2JsonWatcher = watcher => {
  //
  // };
  // TODO: Implement toSb2JsonListWatcher
  // const toSb2JsonListWatcher = listWatcher => {
  //
  // };


  const toSb2JsonSound = soundMediaData => {
    const soundID = sounds.findIndex(sound => sound.crc === soundMediaData.crc);
    return {
      soundName: soundMediaData.name,
      soundID,
      md5: "".concat(soundMediaData.md5, ".wav"),
      sampleCount: soundMediaData.sampleCount,
      rate: soundMediaData.rate,
      format: ''
    };
  };

  const toSb2ImageExtension = imageMedia => {
    if (imageMedia.extension === 'uncompressed') {
      return 'png';
    }

    return 'jpg';
  };

  const toSb2JsonCostume = imageMediaData => {
    const baseLayerID = images.findIndex(image => image.crc === imageMediaData.crc);
    return {
      costumeName: imageMediaData.costumeName,
      baseLayerID,
      baseLayerMD5: "".concat(js_md5__WEBPACK_IMPORTED_MODULE_1___default()(imageMediaData.rawBytes), ".").concat(toSb2ImageExtension(imageMediaData)),
      bitmapResolution: 1,
      rotationCenterX: imageMediaData.rotationCenter.x,
      rotationCenterY: imageMediaData.rotationCenter.y
    };
  };

  const toSb2JsonBlock = blockData => {
    let output = blockData.map(toSb2JsonBlockArg);
    const spec = sb1SpecMap[output[0]];

    if (spec) {
      output = spec(output);
    }

    return output;
  };

  const toSb2JsonStack = stackData => stackData.map(toSb2JsonBlock);

  const toSb2JsonBlockArg = argData => {
    if (argData instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["SpriteData"]) {
      return argData.objName;
    } else if (Array.isArray(argData)) {
      if (argData.length === 0 || Array.isArray(argData[0])) {
        return toSb2JsonStack(argData);
      }

      return toSb2JsonBlock(argData);
    }

    return argData;
  };

  const toSb2JsonScript = scriptData => [scriptData[0].x, scriptData[0].y, toSb2JsonStack(scriptData[1])];

  const toSb2JsonSprite = spriteData => {
    const rawCostumes = spriteData.media.filter(data => data instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["ImageMediaData"]);
    const rawSounds = spriteData.media.filter(data => data instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["SoundMediaData"]);
    return {
      objName: spriteData.objName,
      variables: pairs(spriteData.vars).map(toSb2JsonVariable),
      lists: pairs(spriteData.lists).map(toSb2JsonList),
      scripts: spriteData.blocksBin.map(toSb2JsonScript),
      costumes: rawCostumes.map(toSb2JsonCostume),
      currentCostumeIndex: rawCostumes.findIndex(image => image.crc === spriteData.currentCostume.crc),
      sounds: rawSounds.map(toSb2JsonSound),
      scratchX: spriteData.scratchX,
      scratchY: spriteData.scratchY,
      scale: spriteData.scalePoint.x,
      direction: Math.round(spriteData.rotationDegrees * 1e6) / 1e6 - 270,
      rotationStyle: spriteData.rotationStyle,
      isDraggable: spriteData.draggable,
      indexInLibrary: stageData.spriteOrderInLibrary.indexOf(spriteData),
      visible: spriteData.visible,
      spriteInfo: {}
    };
  };

  const toSb2JsonChild = child => {
    if (child instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["SpriteData"]) {
      return toSb2JsonSprite(child);
    }

    return null;
  };

  const toSb2JsonStage = _stageData => {
    const rawCostumes = _stageData.media.filter(data => data instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["ImageMediaData"]);

    const rawSounds = _stageData.media.filter(data => data instanceof _squeak_types__WEBPACK_IMPORTED_MODULE_0__["SoundMediaData"]);

    return {
      objName: _stageData.objName,
      variables: pairs(_stageData.vars).map(toSb2JsonVariable),
      lists: pairs(_stageData.lists).map(toSb2JsonList),
      scripts: _stageData.blocksBin.map(toSb2JsonScript),
      costumes: rawCostumes.map(toSb2JsonCostume),
      currentCostumeIndex: rawCostumes.findIndex(image => image.crc === _stageData.currentCostume.crc),
      sounds: rawSounds.map(toSb2JsonSound),
      // TODO: Where does this come from? Is it always the same for SB1?
      penLayerMD5: '5c81a336fab8be57adc039a8a2b33ca9.png',
      penLayerID: 0,
      tempoBPM: _stageData.tempoBPM,
      videoAlpha: 0.5,
      children: _stageData.stageContents.map(toSb2JsonChild).filter(Boolean).reverse()
    };
  };

  const toSb2JsonInfo = _info => {
    const obj = {};

    for (let i = 0; i < _info.length; i += 2) {
      if (String(_info[i]) === 'thumbnail') continue;
      obj[String(_info[i])] = String(_info[i + 1]);
    }

    return obj;
  };

  return JSON.parse(JSON.stringify(Object.assign(toSb2JsonStage(stageData), {
    info: toSb2JsonInfo(info)
  })));
};



/***/ }),

/***/ "./node_modules/scratch-sb1-converter/src/util/assert.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-sb1-converter/src/util/assert.js ***!
  \***************************************************************/
/*! exports provided: assert, AssertionError, ValidationError */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "assert", function() { return assert; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AssertionError", function() { return AssertionError; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ValidationError", function() { return ValidationError; });
/**
 * A `scratch-sb1-converter` assertion.
 */
class AssertionError extends Error {}
/**
 * A `scratch-sb1-converter` validation error.
 */


class ValidationError extends AssertionError {}

const assert = function assert(test, message) {
  if (!test) throw new AssertionError(message);
};

assert.validate = function (test, message) {
  if (!test) throw new ValidationError(message);
};



/***/ }),

/***/ "./node_modules/scratch-storage/node_modules/base64-js/index.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-storage/node_modules/base64-js/index.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

// Support decoding URL-safe base64 strings, as Node.js does.
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function getLens (b64) {
  var len = b64.length

  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // Trim off extra bytes after placeholder bytes are found
  // See: https://github.com/beatgammit/base64-js/issues/42
  var validLen = b64.indexOf('=')
  if (validLen === -1) validLen = len

  var placeHoldersLen = validLen === len
    ? 0
    : 4 - (validLen % 4)

  return [validLen, placeHoldersLen]
}

// base64 is 4/3 + up to two characters of the original data
function byteLength (b64) {
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function _byteLength (b64, validLen, placeHoldersLen) {
  return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
}

function toByteArray (b64) {
  var tmp
  var lens = getLens(b64)
  var validLen = lens[0]
  var placeHoldersLen = lens[1]

  var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))

  var curByte = 0

  // if there are placeholders, only get up to the last complete 4 chars
  var len = placeHoldersLen > 0
    ? validLen - 4
    : validLen

  for (var i = 0; i < len; i += 4) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 18) |
      (revLookup[b64.charCodeAt(i + 1)] << 12) |
      (revLookup[b64.charCodeAt(i + 2)] << 6) |
      revLookup[b64.charCodeAt(i + 3)]
    arr[curByte++] = (tmp >> 16) & 0xFF
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 2) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 2) |
      (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[curByte++] = tmp & 0xFF
  }

  if (placeHoldersLen === 1) {
    tmp =
      (revLookup[b64.charCodeAt(i)] << 10) |
      (revLookup[b64.charCodeAt(i + 1)] << 4) |
      (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[curByte++] = (tmp >> 8) & 0xFF
    arr[curByte++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] +
    lookup[num >> 12 & 0x3F] +
    lookup[num >> 6 & 0x3F] +
    lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp =
      ((uint8[i] << 16) & 0xFF0000) +
      ((uint8[i + 1] << 8) & 0xFF00) +
      (uint8[i + 2] & 0xFF)
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(
      uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
    ))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    parts.push(
      lookup[tmp >> 2] +
      lookup[(tmp << 4) & 0x3F] +
      '=='
    )
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + uint8[len - 1]
    parts.push(
      lookup[tmp >> 10] +
      lookup[(tmp >> 4) & 0x3F] +
      lookup[(tmp << 2) & 0x3F] +
      '='
    )
  }

  return parts.join('')
}


/***/ }),

/***/ "./node_modules/scratch-storage/src/Asset.js":
/*!***************************************************!*\
  !*** ./node_modules/scratch-storage/src/Asset.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Use JS implemented TextDecoder and TextEncoder if it is not provided by the
// browser.
if (typeof TextDecoder === 'undefined' || typeof TextEncoder === 'undefined') {
  // Wait to require the text encoding polyfill until we know it's needed.
  // eslint-disable-next-line global-require
  __webpack_require__(/*! fastestsmallesttextencoderdecoder */ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js");
}

const md5 = __webpack_require__(/*! js-md5 */ "./node_modules/js-md5/src/md5.js");
/**
 * The maximum length of a chunk before encoding it into base64.
 *
 * 32766 is a multiple of 3 so btoa does not need to use padding characters
 * except for the final chunk where that is fine. 32766 is also close to
 * 32768 so it is close to a size an memory allocator would prefer.
 * @const {number}
 */


const BTOA_CHUNK_MAX_LENGTH = 32766;
/**
 * An array cache of bytes to characters.
 * @const {?Array.<string>}
 */

let fromCharCode = null;
const memoizedStrings = {};

const memoizedToString = (assetId, data) => {
  if (!Object.prototype.hasOwnProperty.call(memoizedStrings, assetId)) {
    if (typeof btoa === 'undefined') {
      // Use a library that does not need btoa to run.

      /* eslint-disable-next-line global-require */
      const base64js = __webpack_require__(/*! base64-js */ "./node_modules/scratch-storage/node_modules/base64-js/index.js");

      memoizedStrings[assetId] = base64js.fromByteArray(data);
    } else {
      // Native btoa is faster than javascript translation. Use js to
      // create a "binary" string and btoa to encode it.
      if (fromCharCode === null) {
        // Cache the first 256 characters for input byte values.
        fromCharCode = new Array(256);

        for (let i = 0; i < 256; i++) {
          fromCharCode[i] = String.fromCharCode(i);
        }
      }

      const {
        length
      } = data;
      let s = ''; // Iterate over chunks of the binary data.

      for (let i = 0, e = 0; i < length; i = e) {
        // Create small chunks to cause more small allocations and
        // less large allocations.
        e = Math.min(e + BTOA_CHUNK_MAX_LENGTH, length);
        let s_ = '';

        for (let j = i; j < e; j += 1) {
          s_ += fromCharCode[data[j]];
        } // Encode the latest chunk so the we create one big output
        // string instead of creating a big input string and then
        // one big output string.

        /* global btoa */


        s += btoa(s_);
      }

      memoizedStrings[assetId] = s;
    }
  }

  return memoizedStrings[assetId];
};

const assetIdCounts = {};
/* globals FinalizationRegistry */

const finalizationRegistry = typeof FinalizationRegistry === 'function' ? new FinalizationRegistry(assetId => {
  const count = assetIdCounts[assetId];

  if (count === 1) {
    // This was the last reference
    delete assetIdCounts[assetId];
    delete memoizedStrings[assetId];
  } else {
    assetIdCounts[assetId] = count - 1;
  }
}) : null;
const addFinalizationReference = finalizationRegistry ? asset => {
  const assetId = asset.assetId;
  const count = assetIdCounts[assetId] || 0;
  assetIdCounts[assetId] = count + 1;
  finalizationRegistry.register(asset, assetId);
} : () => {};

class Asset {
  /**
   * Construct an Asset.
   * @param {AssetType} assetType - The type of this asset (sound, image, etc.)
   * @param {string} assetId - The ID of this asset.
   * @param {DataFormat} [dataFormat] - The format of the data (WAV, PNG, etc.); required iff `data` is present.
   * @param {Buffer} [data] - The in-memory data for this asset; optional.
   * @param {bool} [generateId] - Whether to create id from an md5 hash of data
   */
  constructor(assetType, assetId, dataFormat, data, generateId) {
    /** @type {AssetType} */
    this.assetType = assetType;
    /** @type {string} */

    this.assetId = assetId;
    this.setData(data, dataFormat || assetType.runtimeFormat, generateId);
    /** @type {Asset[]} */

    this.dependencies = [];
    addFinalizationReference(this);
  }

  setData(data, dataFormat, generateId) {
    if (data && !dataFormat) {
      throw new Error('Data provided without specifying its format');
    }
    /** @type {DataFormat} */


    this.dataFormat = dataFormat;
    /** @type {Buffer} */

    this.data = data;
    if (generateId) this.assetId = md5(data); // Mark as clean only if set is being called without generateId
    // If a new id is being generated, mark this asset as not clean

    this.clean = !generateId;
  }
  /**
   * @returns {string} - This asset's data, decoded as text.
   */


  decodeText() {
    const decoder = new TextDecoder();
    return decoder.decode(this.data);
  }
  /**
   * Same as `setData` but encodes text first.
   * @param {string} data - the text data to encode and store.
   * @param {DataFormat} dataFormat - the format of the data (DataFormat.SVG for example).
   * @param {bool} generateId - after setting data, set the id to an md5 of the data?
   */


  encodeTextData(data, dataFormat, generateId) {
    const encoder = new TextEncoder();
    this.setData(encoder.encode(data), dataFormat, generateId);
  }
  /**
   * @param {string} [contentType] - Optionally override the content type to be included in the data URI.
   * @returns {string} - A data URI representing the asset's data.
   */


  encodeDataURI(contentType) {
    contentType = contentType || this.assetType.contentType;
    return "data:".concat(contentType, ";base64,").concat(memoizedToString(this.assetId, this.data));
  }

}

module.exports = Asset;

/***/ }),

/***/ "./node_modules/scratch-storage/src/AssetType.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-storage/src/AssetType.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const DataFormat = __webpack_require__(/*! ./DataFormat */ "./node_modules/scratch-storage/src/DataFormat.js");
/**
 * Enumeration of the supported asset types.
 * @type {Object.<String,AssetType>}
 * @typedef {Object} AssetType - Information about a supported asset type.
 * @property {string} contentType - the MIME type associated with this kind of data. Useful for data URIs, etc.
 * @property {string} name - The human-readable name of this asset type.
 * @property {DataFormat} runtimeFormat - The default format used for runtime, in-memory storage of this asset. For
 *     example, a project stored in SB2 format on disk will be returned as JSON when loaded into memory.
 * @property {boolean} immutable - Indicates if the asset id is determined by the asset content.
 */


const AssetType = {
  ImageBitmap: {
    contentType: 'image/png',
    name: 'ImageBitmap',
    runtimeFormat: DataFormat.PNG,
    immutable: true
  },
  ImageVector: {
    contentType: 'image/svg+xml',
    name: 'ImageVector',
    runtimeFormat: DataFormat.SVG,
    immutable: true
  },
  Project: {
    contentType: 'application/json',
    name: 'Project',
    runtimeFormat: DataFormat.JSON,
    immutable: false
  },
  Sound: {
    contentType: 'audio/x-wav',
    name: 'Sound',
    runtimeFormat: DataFormat.WAV,
    immutable: true
  },
  Sprite: {
    contentType: 'application/json',
    name: 'Sprite',
    runtimeFormat: DataFormat.JSON,
    immutable: true
  }
};
module.exports = AssetType;

/***/ }),

/***/ "./node_modules/scratch-storage/src/BuiltinHelper.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-storage/src/BuiltinHelper.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer) {const md5 = __webpack_require__(/*! js-md5 */ "./node_modules/js-md5/src/md5.js");

const Asset = __webpack_require__(/*! ./Asset */ "./node_modules/scratch-storage/src/Asset.js");

const AssetType = __webpack_require__(/*! ./AssetType */ "./node_modules/scratch-storage/src/AssetType.js");

const DataFormat = __webpack_require__(/*! ./DataFormat */ "./node_modules/scratch-storage/src/DataFormat.js");

const Helper = __webpack_require__(/*! ./Helper */ "./node_modules/scratch-storage/src/Helper.js");
/**
 * @typedef {object} BuiltinAssetRecord
 * @property {AssetType} type - The type of the asset.
 * @property {DataFormat} format - The format of the asset's data.
 * @property {?string} id - The asset's unique ID.
 * @property {Buffer} data - The asset's data.
 */

/**
 * @type {BuiltinAssetRecord[]}
 */


const DefaultAssets = [{
  type: AssetType.ImageBitmap,
  format: DataFormat.PNG,
  id: null,
  data: Buffer.from(__webpack_require__(/*! arraybuffer-loader!./builtins/defaultBitmap.png */ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultBitmap.png") // eslint-disable-line global-require
  )
}, {
  type: AssetType.Sound,
  format: DataFormat.WAV,
  id: null,
  data: Buffer.from(__webpack_require__(/*! arraybuffer-loader!./builtins/defaultSound.wav */ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultSound.wav") // eslint-disable-line global-require
  )
}, {
  type: AssetType.ImageVector,
  format: DataFormat.SVG,
  id: null,
  data: Buffer.from(__webpack_require__(/*! arraybuffer-loader!./builtins/defaultVector.svg */ "./node_modules/arraybuffer-loader/index.js!./node_modules/scratch-storage/src/builtins/defaultVector.svg") // eslint-disable-line global-require
  )
}];
/**
 * @type {BuiltinAssetRecord[]}
 */

const BuiltinAssets = DefaultAssets.concat([]);

class BuiltinHelper extends Helper {
  constructor(parent) {
    super(parent);
    /**
     * In-memory storage for all built-in assets.
     * @type {Object.<AssetType, AssetIdMap>} Maps asset type to a map of asset ID to actual assets.
     * @typedef {Object.<string, BuiltinAssetRecord>} AssetIdMap - Maps asset ID to asset.
     */

    this.assets = {};
    BuiltinAssets.forEach(assetRecord => {
      assetRecord.id = this._store(assetRecord.type, assetRecord.format, assetRecord.data, assetRecord.id);
    });
  }
  /**
   * Call `setDefaultAssetId` on the parent `ScratchStorage` instance to register all built-in default assets.
   */


  registerDefaultAssets() {
    const numAssets = DefaultAssets.length;

    for (let assetIndex = 0; assetIndex < numAssets; ++assetIndex) {
      const assetRecord = DefaultAssets[assetIndex];
      this.parent.setDefaultAssetId(assetRecord.type, assetRecord.id);
    }
  }
  /**
   * Synchronously fetch a cached asset for a given asset id. Returns null if not found.
   * @param {string} assetId - The id for the asset to fetch.
   * @returns {?Asset} The asset for assetId, if it exists.
   */


  get(assetId) {
    let asset = null;

    if (Object.prototype.hasOwnProperty.call(this.assets, assetId)) {
      /** @type{BuiltinAssetRecord} */
      const assetRecord = this.assets[assetId];
      asset = new Asset(assetRecord.type, assetRecord.id, assetRecord.format, assetRecord.data);
    }

    return asset;
  }
  /**
   * Alias for store (old name of store)
   * @deprecated Use BuiltinHelper.store
   * @param {AssetType} assetType - The type of the asset to cache.
   * @param {DataFormat} dataFormat - The dataFormat of the data for the cached asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {string} id - The id for the cached asset.
   * @returns {string} The calculated id of the cached asset, or the supplied id if the asset is mutable.
   */


  cache(assetType, dataFormat, data, id) {
    return this.store(assetType, dataFormat, data, id);
  }
  /**
   * Deprecated external API for _store
   * @deprecated Not for external use. Create assets and keep track of them outside of the storage instance.
   * @param {AssetType} assetType - The type of the asset to cache.
   * @param {DataFormat} dataFormat - The dataFormat of the data for the cached asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {(string|number)} id - The id for the cached asset.
   * @returns {string} The calculated id of the cached asset, or the supplied id if the asset is mutable.
   */


  store(assetType, dataFormat, data, id) {
    return this._store(assetType, dataFormat, data, id);
  }
  /**
   * Cache an asset for future lookups by ID.
   * @param {AssetType} assetType - The type of the asset to cache.
   * @param {DataFormat} dataFormat - The dataFormat of the data for the cached asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {(string|number)} id - The id for the cached asset.
   * @returns {string} The calculated id of the cached asset, or the supplied id if the asset is mutable.
   */


  _store(assetType, dataFormat, data, id) {
    if (!dataFormat) throw new Error('Data cached without specifying its format');

    if (id !== '' && id !== null && typeof id !== 'undefined') {
      if (Object.prototype.hasOwnProperty.call(this.assets, id) && assetType.immutable) return id;
    } else if (assetType.immutable) {
      id = md5(data);
    } else {
      throw new Error('Tried to cache data without an id');
    }

    this.assets[id] = {
      type: assetType,
      format: dataFormat,
      id: id,
      data: data
    };
    return id;
  }
  /**
   * Fetch an asset but don't process dependencies.
   * @param {AssetType} assetType - The type of asset to fetch.
   * @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
   * @return {?Promise.<Asset>} A promise for the contents of the asset.
   */


  load(assetType, assetId) {
    if (!this.get(assetId)) {
      // Return null immediately so Storage can quickly move to trying the
      // next helper.
      return null;
    }

    return Promise.resolve(this.get(assetId));
  }

}

module.exports = BuiltinHelper;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

/***/ }),

/***/ "./node_modules/scratch-storage/src/DataFormat.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-storage/src/DataFormat.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Enumeration of the supported data formats.
 * @enum {string}
 */
const DataFormat = {
  JPG: 'jpg',
  JSON: 'json',
  MP3: 'mp3',
  PNG: 'png',
  SB2: 'sb2',
  SB3: 'sb3',
  SVG: 'svg',
  WAV: 'wav'
};
module.exports = DataFormat;

/***/ }),

/***/ "./node_modules/scratch-storage/src/FetchTool.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-storage/src/FetchTool.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const _excluded = ["url"],
      _excluded2 = ["url", "withCredentials"];

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

/* eslint-env browser */
const saferFetchAsArrayBuffer = __webpack_require__(/*! ./safer-fetch */ "./node_modules/scratch-storage/src/safer-fetch.js");
/**
 * Get and send assets with the fetch standard web api.
 */


class FetchTool {
  /**
   * Is get supported? false if the environment does not support fetch.
   * @returns {boolean} Is get supported?
   */
  get isGetSupported() {
    return typeof fetch !== 'undefined';
  }
  /**
   * Request data from a server with fetch.
   * @param {{url:string}} reqConfig - Request configuration for data to get.
   * @param {{method:string}} options - Additional options to configure fetch.
   * @returns {Promise.<Uint8Array>} Resolve to Buffer of data from server.
   */


  get(_ref) {
    let {
      url
    } = _ref,
        options = _objectWithoutProperties(_ref, _excluded);

    return saferFetchAsArrayBuffer(url, Object.assign({
      method: 'GET'
    }, options)).then(arrayBufferOrNull => {
      if (arrayBufferOrNull) {
        return new Uint8Array(arrayBufferOrNull);
      }

      return arrayBufferOrNull;
    });
  }
  /**
   * Is sending supported? false if the environment does not support sending
   * with fetch.
   * @returns {boolean} Is sending supported?
   */


  get isSendSupported() {
    return typeof fetch !== 'undefined';
  }
  /**
   * Send data to a server with fetch.
   * @param {Request} reqConfig - Request configuration for data to send.
   * @returns {Promise.<string>} Server returned metadata.
   */


  send(_ref2) {
    let {
      url,
      withCredentials = false
    } = _ref2,
        options = _objectWithoutProperties(_ref2, _excluded2);

    return fetch(url, Object.assign({
      credentials: withCredentials ? 'include' : 'omit'
    }, options)).then(response => {
      if (response.ok) return response.text();
      return Promise.reject(response.status);
    });
  }

}

module.exports = FetchTool;

/***/ }),

/***/ "./node_modules/scratch-storage/src/FetchWorkerTool.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-storage/src/FetchWorkerTool.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const _excluded = ["url"];

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

/**
 * Get and send assets with a worker that uses fetch.
 */
class PrivateFetchWorkerTool {
  constructor() {
    /**
     * What does the worker support of the APIs we need?
     * @type {{fetch:boolean}}
     */
    this._workerSupport = {
      fetch: typeof fetch !== 'undefined'
    };
    /**
     * A possible error occurred standing up the worker.
     * @type {!Error}
     */

    this._supportError = null;
    /**
     * The worker that runs fetch and returns data for us.
     * @type {!Worker}
     */

    this.worker = null;
    /**
     * A map of ids to fetch job objects.
     * @type {object}
     */

    this.jobs = {};

    try {
      if (this.isGetSupported) {
        // eslint-disable-next-line global-require
        const FetchWorker = __webpack_require__(/*! worker-loader?{"inline":true,"fallback":true}!./FetchWorkerTool.worker */ "./src/build/inline-worker-loader/worker-loader.js?{\"inline\":true,\"fallback\":true}!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js");

        this.worker = new FetchWorker();
        this.worker.addEventListener('message', _ref => {
          let {
            data
          } = _ref;

          if (data.support) {
            this._workerSupport = data.support;
            return;
          }

          for (const message of data) {
            if (this.jobs[message.id]) {
              if (message.error) {
                this.jobs[message.id].reject(message.error);
              } else {
                this.jobs[message.id].resolve(message.buffer);
              }

              delete this.jobs[message.id];
            }
          }
        });
      }
    } catch (error) {
      this._supportError = error;
    }
  }
  /**
   * Is get supported?
   *
   * false if the environment does not workers, fetch, or fetch from inside a
   * worker. Finding out the worker supports fetch is asynchronous and will
   * guess that it does if the window does until the worker can inform us.
   * @returns {boolean} Is get supported?
   */


  get isGetSupported() {
    return typeof Worker !== 'undefined' && this._workerSupport.fetch && !this._supportError;
  }
  /**
   * Request data from a server with a worker using fetch.
   * @param {{url:string}} reqConfig - Request configuration for data to get.
   * @param {{method:string}} options - Additional options to configure fetch.
   * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
   */


  get(_ref2) {
    let {
      url
    } = _ref2,
        options = _objectWithoutProperties(_ref2, _excluded);

    return new Promise((resolve, reject) => {
      // TODO: Use a Scratch standard ID generator ...
      const id = Math.random().toString(16).substring(2);
      this.worker.postMessage({
        id,
        url,
        options: Object.assign({
          method: 'GET'
        }, options)
      });
      this.jobs[id] = {
        id,
        resolve,
        reject
      };
    })
    /* eslint no-confusing-arrow: ["error", {"allowParens": true}] */
    .then(body => body ? new Uint8Array(body) : null);
  }
  /**
   * Is sending supported? always false for FetchWorkerTool.
   * @returns {boolean} Is sending supported?
   */


  get isSendSupported() {
    return false;
  }
  /**
   * Send data to a server.
   * @throws {Error} A not implemented error.
   */


  send() {
    throw new Error('Not implemented.');
  }
  /**
   * Return a static PrivateFetchWorkerTool instance on demand.
   * @returns {PrivateFetchWorkerTool} A static PrivateFetchWorkerTool
   *   instance
   */


  static get instance() {
    if (!this._instance) {
      this._instance = new PrivateFetchWorkerTool();
    }

    return this._instance;
  }

}
/**
 * Get and send assets with a worker that uses fetch.
 */


class PublicFetchWorkerTool {
  constructor() {
    /**
     * Shared instance of an internal worker. PublicFetchWorkerTool proxies
     * it.
     * @type {PrivateFetchWorkerTool}
     */
    this.inner = PrivateFetchWorkerTool.instance;
  }
  /**
   * Is get supported?
   * @returns {boolean} Is get supported?
   */


  get isGetSupported() {
    return this.inner.isGetSupported;
  }
  /**
   * Request data from a server with a worker that uses fetch.
   * @param {{url:string}} reqConfig - Request configuration for data to get.
   * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
   */


  get(reqConfig) {
    return this.inner.get(reqConfig);
  }
  /**
   * Is sending supported?
   * @returns {boolean} Is sending supported?
   */


  get isSendSupported() {
    return false;
  }
  /**
   * Send data to a server with a worker that uses fetch.
   * @throws {Error} A not implemented error.
   */


  send() {
    throw new Error('Not implemented.');
  }

}

module.exports = PublicFetchWorkerTool;

/***/ }),

/***/ "./node_modules/scratch-storage/src/Helper.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-storage/src/Helper.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Base class for asset load/save helpers.
 * @abstract
 */
class Helper {
  constructor(parent) {
    this.parent = parent;
  }
  /**
   * Fetch an asset but don't process dependencies.
   * @param {AssetType} assetType - The type of asset to fetch.
   * @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
   * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
   * @return {Promise.<Asset>} A promise for the contents of the asset.
   */


  load(assetType, assetId, dataFormat) {
    return Promise.reject(new Error("No asset of type ".concat(assetType, " for ID ").concat(assetId, " with format ").concat(dataFormat)));
  }

}

module.exports = Helper;

/***/ }),

/***/ "./node_modules/scratch-storage/src/ProxyTool.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-storage/src/ProxyTool.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const FetchWorkerTool = __webpack_require__(/*! ./FetchWorkerTool */ "./node_modules/scratch-storage/src/FetchWorkerTool.js");

const FetchTool = __webpack_require__(/*! ./FetchTool */ "./node_modules/scratch-storage/src/FetchTool.js");
/**
 * @typedef {object} Request
 * @property {string} url
 * @property {*} body
 * @property {string} method
 * @property {boolean} withCredentials
 */

/**
 * Get and send assets with other tools in sequence.
 */


class ProxyTool {
  constructor() {
    let filter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ProxyTool.TOOL_FILTER.ALL;
    let tools;

    if (filter === ProxyTool.TOOL_FILTER.READY) {
      tools = [new FetchTool()];
    } else {
      tools = [new FetchWorkerTool(), new FetchTool()];
    }
    /**
     * Sequence of tools to proxy.
     * @type {Array.<Tool>}
     */


    this.tools = tools;
  }
  /**
   * Is get supported? false if all proxied tool return false.
   * @returns {boolean} Is get supported?
   */


  get isGetSupported() {
    return this.tools.some(tool => tool.isGetSupported);
  }
  /**
   * Request data from with one of the proxied tools.
   * @param {Request} reqConfig - Request configuration for data to get.
   * @returns {Promise.<Buffer>} Resolve to Buffer of data from server.
   */


  get(reqConfig) {
    let toolIndex = 0;

    const nextTool = err => {
      const tool = this.tools[toolIndex++];

      if (!tool) {
        throw err;
      }

      if (!tool.isGetSupported) {
        return nextTool(err);
      }

      return tool.get(reqConfig).catch(nextTool);
    };

    return nextTool();
  }
  /**
   * Is sending supported? false if all proxied tool return false.
   * @returns {boolean} Is sending supported?
   */


  get isSendSupported() {
    return this.tools.some(tool => tool.isSendSupported);
  }
  /**
   * Send data to a server with one of the proxied tools.
   * @param {Request} reqConfig - Request configuration for data to send.
   * @returns {Promise.<Buffer|string|object>} Server returned metadata.
   */


  send(reqConfig) {
    let toolIndex = 0;

    const nextTool = err => {
      const tool = this.tools[toolIndex++];

      if (!tool) {
        throw err;
      }

      if (!tool.isSendSupported) {
        return nextTool(err);
      }

      return tool.send(reqConfig).catch(nextTool);
    };

    return nextTool();
  }

}
/**
 * Constant values that filter the set of tools in a ProxyTool instance.
 * @enum {string}
 */


ProxyTool.TOOL_FILTER = {
  /**
   * Use all tools.
   */
  ALL: 'all',

  /**
   * Use tools that are ready right now.
   */
  READY: 'ready'
};
module.exports = ProxyTool;

/***/ }),

/***/ "./node_modules/scratch-storage/src/ScratchStorage.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-storage/src/ScratchStorage.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const BuiltinHelper = __webpack_require__(/*! ./BuiltinHelper */ "./node_modules/scratch-storage/src/BuiltinHelper.js");

const WebHelper = __webpack_require__(/*! ./WebHelper */ "./node_modules/scratch-storage/src/WebHelper.js");

const _Asset = __webpack_require__(/*! ./Asset */ "./node_modules/scratch-storage/src/Asset.js");

const _AssetType = __webpack_require__(/*! ./AssetType */ "./node_modules/scratch-storage/src/AssetType.js");

const _DataFormat = __webpack_require__(/*! ./DataFormat */ "./node_modules/scratch-storage/src/DataFormat.js");

class ScratchStorage {
  constructor() {
    this.defaultAssetId = {};
    this.builtinHelper = new BuiltinHelper(this);
    this.webHelper = new WebHelper(this);
    this.builtinHelper.registerDefaultAssets(this);
    this._helpers = [{
      helper: this.builtinHelper,
      priority: 100
    }, {
      helper: this.webHelper,
      priority: -100
    }];
  }
  /**
   * @return {Asset} - the `Asset` class constructor.
   * @constructor
   */


  get Asset() {
    return _Asset;
  }
  /**
   * @return {AssetType} - the list of supported asset types.
   * @constructor
   */


  get AssetType() {
    return _AssetType;
  }
  /**
   * @return {DataFormat} - the list of supported data formats.
   * @constructor
   */


  get DataFormat() {
    return _DataFormat;
  }
  /**
   * @deprecated Please use the `Asset` member of a storage instance instead.
   * @return {Asset} - the `Asset` class constructor.
   * @constructor
   */


  static get Asset() {
    return _Asset;
  }
  /**
   * @deprecated Please use the `AssetType` member of a storage instance instead.
   * @return {AssetType} - the list of supported asset types.
   * @constructor
   */


  static get AssetType() {
    return _AssetType;
  }
  /**
   * Add a storage helper to this manager. Helpers with a higher priority number will be checked first when loading
   * or storing assets. For comparison, the helper for built-in assets has `priority=100` and the default web helper
   * has `priority=-100`. The relative order of helpers with equal priorities is undefined.
   * @param {Helper} helper - the helper to be added.
   * @param {number} [priority] - the priority for this new helper (default: 0).
   */


  addHelper(helper) {
    let priority = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    this._helpers.push({
      helper,
      priority
    });

    this._helpers.sort((a, b) => b.priority - a.priority);
  }
  /**
   * Synchronously fetch a cached asset from built-in storage. Assets are cached when they are loaded.
   * @param {string} assetId - The id of the asset to fetch.
   * @returns {?Asset} The asset, if it exists.
   */


  get(assetId) {
    return this.builtinHelper.get(assetId);
  }
  /**
   * Deprecated API for caching built-in assets. Use createAsset.
   * @param {AssetType} assetType - The type of the asset to cache.
   * @param {DataFormat} dataFormat - The dataFormat of the data for the cached asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {string} id - The id for the cached asset.
   * @returns {string} The calculated id of the cached asset, or the supplied id if the asset is mutable.
   */


  cache(assetType, dataFormat, data, id) {
    return this.builtinHelper._store(assetType, dataFormat, data, id);
  }
  /**
   * Construct an Asset, and optionally generate an md5 hash of its data to create an id
   * @param {AssetType} assetType - The type of the asset to cache.
   * @param {DataFormat} dataFormat - The dataFormat of the data for the cached asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {string} [id] - The id for the cached asset.
   * @param {bool} [generateId] - flag to set id to an md5 hash of data if `id` isn't supplied
   * @returns {Asset} generated Asset with `id` attribute set if not supplied
   */


  createAsset(assetType, dataFormat, data, id, generateId) {
    if (!dataFormat) throw new Error('Tried to create asset without a dataFormat');
    return new _Asset(assetType, id, dataFormat, data, generateId);
  }
  /**
   * Register a web-based source for assets. Sources will be checked in order of registration.
   * @param {Array.<AssetType>} types - The types of asset provided by this source.
   * @param {UrlFunction} getFunction - A function which computes a GET URL from an Asset.
   * @param {UrlFunction} createFunction - A function which computes a POST URL for asset data.
   * @param {UrlFunction} updateFunction - A function which computes a PUT URL for asset data.
   */


  addWebStore(types, getFunction, createFunction, updateFunction) {
    this.webHelper.addStore(types, getFunction, createFunction, updateFunction);
  }
  /**
   * Register a web-based source for assets. Sources will be checked in order of registration.
   * @deprecated Please use addWebStore
   * @param {Array.<AssetType>} types - The types of asset provided by this source.
   * @param {UrlFunction} urlFunction - A function which computes a GET URL from an Asset.
   */


  addWebSource(types, urlFunction) {
    this.addWebStore(types, urlFunction);
  }
  /**
   * TODO: Should this be removed in favor of requesting an asset with `null` as the ID?
   * @param {AssetType} type - Get the default ID for assets of this type.
   * @return {?string} The ID of the default asset of the given type, if any.
   */


  getDefaultAssetId(type) {
    if (Object.prototype.hasOwnProperty.call(this.defaultAssetId, type.name)) {
      return this.defaultAssetId[type.name];
    }
  }
  /**
   * Set the default ID for a particular type of asset. This default asset will be used if a requested asset cannot
   * be found and automatic fallback is enabled. Ideally this should be an asset that is available locally or even
   * one built into this module.
   * TODO: Should this be removed in favor of requesting an asset with `null` as the ID?
   * @param {AssetType} type - The type of asset for which the default will be set.
   * @param {string} id - The default ID to use for this type of asset.
   */


  setDefaultAssetId(type, id) {
    this.defaultAssetId[type.name] = id;
  }
  /**
   * Fetch an asset by type & ID.
   * @param {AssetType} assetType - The type of asset to fetch. This also determines which asset store to use.
   * @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
   * @param {DataFormat} [dataFormat] - Optional: load this format instead of the AssetType's default.
   * @return {Promise.<Asset>} A promise for the requested Asset.
   *   If the promise is resolved with non-null, the value is the requested asset.
   *   If the promise is resolved with null, the desired asset could not be found with the current asset sources.
   *   If the promise is rejected, there was an error on at least one asset source. HTTP 404 does not count as an
   *   error here, but (for example) HTTP 403 does.
   */


  load(assetType, assetId, dataFormat) {
    /** @type {Helper[]} */
    const helpers = this._helpers.map(x => x.helper);

    const errors = [];
    dataFormat = dataFormat || assetType.runtimeFormat;
    let helperIndex = 0;
    let helper;

    const tryNextHelper = err => {
      if (err) {
        // Track the error, but continue looking
        errors.push(err);
      }

      helper = helpers[helperIndex++];

      if (helper) {
        const loading = helper.load(assetType, assetId, dataFormat);

        if (loading === null) {
          return tryNextHelper();
        } // Note that other attempts may have logged errors; if this succeeds they will be suppressed.


        return loading // TODO: maybe some types of error should prevent trying the next helper?
        .catch(tryNextHelper);
      } else if (errors.length > 0) {
        // We looked through all the helpers and couldn't find the asset, AND
        // at least one thing went wrong while we were looking.
        return Promise.reject(errors);
      } // Nothing went wrong but we couldn't find the asset.


      return Promise.resolve(null);
    };

    return tryNextHelper();
  }
  /**
   * Store an asset by type & ID.
   * @param {AssetType} assetType - The type of asset to fetch. This also determines which asset store to use.
   * @param {?DataFormat} [dataFormat] - Optional: load this format instead of the AssetType's default.
   * @param {Buffer} data - Data to store for the asset
   * @param {?string} [assetId] - The ID of the asset to fetch: a project ID, MD5, etc.
   * @return {Promise.<object>} A promise for asset metadata
   */


  store(assetType, dataFormat, data, assetId) {
    dataFormat = dataFormat || assetType.runtimeFormat;
    return new Promise((resolve, reject) => this.webHelper.store(assetType, dataFormat, data, assetId).then(body => {
      this.builtinHelper._store(assetType, dataFormat, data, body.id);

      return resolve(body);
    }).catch(error => reject(error)));
  }

}

module.exports = ScratchStorage;

/***/ }),

/***/ "./node_modules/scratch-storage/src/WebHelper.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-storage/src/WebHelper.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Asset = __webpack_require__(/*! ./Asset */ "./node_modules/scratch-storage/src/Asset.js");

const Helper = __webpack_require__(/*! ./Helper */ "./node_modules/scratch-storage/src/Helper.js");

const ProxyTool = __webpack_require__(/*! ./ProxyTool */ "./node_modules/scratch-storage/src/ProxyTool.js");

const ensureRequestConfig = reqConfig => {
  if (typeof reqConfig === 'string') {
    return {
      url: reqConfig
    };
  }

  return reqConfig;
};
/**
 * @typedef {function} UrlFunction - A function which computes a URL from asset information.
 * @param {Asset} - The asset for which the URL should be computed.
 * @returns {(string|object)} - A string representing the URL for the asset request OR an object with configuration for
 *                              the underlying fetch call (necessary for configuring e.g. authentication)
 */


class WebHelper extends Helper {
  constructor(parent) {
    super(parent);
    /**
     * @type {Array.<StoreRecord>}
     * @typedef {object} StoreRecord
     * @property {Array.<string>} types - The types of asset provided by this store, from AssetType's name field.
     * @property {UrlFunction} getFunction - A function which computes a URL from an Asset.
     * @property {UrlFunction} createFunction - A function which computes a URL from an Asset.
     * @property {UrlFunction} updateFunction - A function which computes a URL from an Asset.
     */

    this.stores = [];
    /**
     * Set of tools to best load many assets in parallel. If one tool
     * cannot be used, it will use the next.
     * @type {ProxyTool}
     */

    this.assetTool = new ProxyTool();
    /**
     * Set of tools to best load project data in parallel with assets. This
     * tool set prefers tools that are immediately ready. Some tools have
     * to initialize before they can load files.
     * @type {ProxyTool}
     */

    this.projectTool = new ProxyTool(ProxyTool.TOOL_FILTER.READY);
  }
  /**
   * Register a web-based source for assets. Sources will be checked in order of registration.
   * @deprecated Please use addStore
   * @param {Array.<AssetType>} types - The types of asset provided by this source.
   * @param {UrlFunction} urlFunction - A function which computes a URL from an Asset.
   */


  addSource(types, urlFunction) {
    this.addStore(types, urlFunction);
  }
  /**
   * Register a web-based store for assets. Sources will be checked in order of registration.
   * @param {Array.<AssetType>} types - The types of asset provided by this store.
   * @param {UrlFunction} getFunction - A function which computes a GET URL for an Asset
   * @param {UrlFunction} createFunction - A function which computes a POST URL for an Asset
   * @param {UrlFunction} updateFunction - A function which computes a PUT URL for an Asset
   */


  addStore(types, getFunction, createFunction, updateFunction) {
    this.stores.push({
      types: types.map(assetType => assetType.name),
      get: getFunction,
      create: createFunction,
      update: updateFunction
    });
  }
  /**
   * Fetch an asset but don't process dependencies.
   * @param {AssetType} assetType - The type of asset to fetch.
   * @param {string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
   * @param {DataFormat} dataFormat - The file format / file extension of the asset to fetch: PNG, JPG, etc.
   * @return {Promise.<Asset>} A promise for the contents of the asset.
   */


  load(assetType, assetId, dataFormat) {
    /** @type {Array.<{url:string, result:*}>} List of URLs attempted & errors encountered. */
    const errors = [];
    const stores = this.stores.slice().filter(store => store.types.indexOf(assetType.name) >= 0); // New empty asset but it doesn't have data yet

    const asset = new Asset(assetType, assetId, dataFormat);
    let tool = this.assetTool;

    if (assetType.name === 'Project') {
      tool = this.projectTool;
    }

    let storeIndex = 0;

    const tryNextSource = err => {
      if (err) {
        errors.push(err);
      }

      const store = stores[storeIndex++];
      /** @type {UrlFunction} */

      const reqConfigFunction = store && store.get;

      if (reqConfigFunction) {
        const reqConfig = ensureRequestConfig(reqConfigFunction(asset));

        if (reqConfig === false) {
          return tryNextSource();
        }

        return tool.get(reqConfig).then(body => {
          if (body) {
            asset.setData(body, dataFormat);
            return asset;
          }

          return tryNextSource();
        }).catch(tryNextSource);
      } else if (errors.length > 0) {
        return Promise.reject(errors);
      } // no stores matching asset


      return Promise.resolve(null);
    };

    return tryNextSource();
  }
  /**
   * Create or update an asset with provided data. The create function is called if no asset id is provided
   * @param {AssetType} assetType - The type of asset to create or update.
   * @param {?DataFormat} dataFormat - DataFormat of the data for the stored asset.
   * @param {Buffer} data - The data for the cached asset.
   * @param {?string} assetId - The ID of the asset to fetch: a project ID, MD5, etc.
   * @return {Promise.<object>} A promise for the response from the create or update request
   */


  store(assetType, dataFormat, data, assetId) {
    const asset = new Asset(assetType, assetId, dataFormat); // If we have an asset id, we should update, otherwise create to get an id

    const create = assetId === '' || assetId === null || typeof assetId === 'undefined'; // Use the first store with the appropriate asset type and url function

    const store = this.stores.filter(s => // Only use stores for the incoming asset type
    s.types.indexOf(assetType.name) !== -1 && ( // Only use stores that have a create function if this is a create request
    // or an update function if this is an update request
    create && s.create || s.update))[0];
    const method = create ? 'post' : 'put';
    if (!store) return Promise.reject(new Error('No appropriate stores'));
    let tool = this.assetTool;

    if (assetType.name === 'Project') {
      tool = this.projectTool;
    }

    const reqConfig = ensureRequestConfig(create ? store.create(asset) : store.update(asset));
    const reqBodyConfig = Object.assign({
      body: data,
      method
    }, reqConfig);
    return tool.send(reqBodyConfig).then(body => {
      // xhr makes it difficult to both send FormData and
      // automatically parse a JSON response. So try to parse
      // everything as JSON.
      if (typeof body === 'string') {
        try {
          body = JSON.parse(body);
        } catch (parseError) {
          // If it's not parseable, then we can't add the id even
          // if we want to, so stop here
          return body;
        }
      }

      return Object.assign({
        id: body['content-name'] || assetId
      }, body);
    });
  }

}

module.exports = WebHelper;

/***/ }),

/***/ "./node_modules/scratch-storage/src/index.js":
/*!***************************************************!*\
  !*** ./node_modules/scratch-storage/src/index.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ScratchStorage = __webpack_require__(/*! ./ScratchStorage */ "./node_modules/scratch-storage/src/ScratchStorage.js");
/**
 * Export for use with NPM & Node.js.
 * @type {ScratchStorage}
 */


module.exports = ScratchStorage;

/***/ }),

/***/ "./node_modules/scratch-storage/src/safer-fetch.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-storage/src/safer-fetch.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* eslint-env browser */

/* eslint-disable no-use-before-define */
// This throttles and retries fetch() to mitigate the effect of random network errors and
// random browser errors (especially in Chrome)
let currentFetches = 0;
const queue = [];

const startNextFetch = _ref => {
  let [resolve, url, options] = _ref;
  let firstError;
  let failedAttempts = 0;

  const attemptToFetch = () => fetch(url, options).then(result => {
    // In a macOS WKWebView, requests from file: URLs to other file: URLs always have status: 0 and ok: false
    // even though the requests were successful. If the requested file doesn't exist, fetch() rejects instead.
    // We aren't aware of any other cases where fetch() can resolve with status 0, so this should be safe.
    if (result.ok || result.status === 0) return result.arrayBuffer();
    if (result.status === 404) return null;
    return Promise.reject(result.status);
  }).then(buffer => {
    currentFetches--;
    checkStartNextFetch();
    return buffer;
  }).catch(error => {
    if (error === 403) {
      // Retrying this request will not help, so return an error now.
      throw error;
    }

    console.warn("Attempt to fetch ".concat(url, " failed"), error);

    if (!firstError) {
      firstError = error;
    }

    if (failedAttempts < 2) {
      failedAttempts++;
      return new Promise(cb => setTimeout(cb, (failedAttempts + Math.random() - 1) * 5000)).then(attemptToFetch);
    }

    currentFetches--;
    checkStartNextFetch();
    throw firstError;
  });

  return resolve(attemptToFetch());
};

const checkStartNextFetch = () => {
  if (currentFetches < 100 && queue.length > 0) {
    currentFetches++;
    startNextFetch(queue.shift());
  }
};

const saferFetchAsArrayBuffer = (url, options) => new Promise(resolve => {
  queue.push([resolve, url, options]);
  checkStartNextFetch();
});

module.exports = saferFetchAsArrayBuffer;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/node_modules/base64-js/index.js":
/*!***************************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/node_modules/base64-js/index.js ***!
  \***************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.byteLength = byteLength
exports.toByteArray = toByteArray
exports.fromByteArray = fromByteArray

var lookup = []
var revLookup = []
var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array

var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for (var i = 0, len = code.length; i < len; ++i) {
  lookup[i] = code[i]
  revLookup[code.charCodeAt(i)] = i
}

revLookup['-'.charCodeAt(0)] = 62
revLookup['_'.charCodeAt(0)] = 63

function placeHoldersCount (b64) {
  var len = b64.length
  if (len % 4 > 0) {
    throw new Error('Invalid string. Length must be a multiple of 4')
  }

  // the number of equal signs (place holders)
  // if there are two placeholders, than the two characters before it
  // represent one byte
  // if there is only one, then the three characters before it represent 2 bytes
  // this is just a cheap hack to not do indexOf twice
  return b64[len - 2] === '=' ? 2 : b64[len - 1] === '=' ? 1 : 0
}

function byteLength (b64) {
  // base64 is 4/3 + up to two characters of the original data
  return (b64.length * 3 / 4) - placeHoldersCount(b64)
}

function toByteArray (b64) {
  var i, l, tmp, placeHolders, arr
  var len = b64.length
  placeHolders = placeHoldersCount(b64)

  arr = new Arr((len * 3 / 4) - placeHolders)

  // if there are placeholders, only get up to the last complete 4 chars
  l = placeHolders > 0 ? len - 4 : len

  var L = 0

  for (i = 0; i < l; i += 4) {
    tmp = (revLookup[b64.charCodeAt(i)] << 18) | (revLookup[b64.charCodeAt(i + 1)] << 12) | (revLookup[b64.charCodeAt(i + 2)] << 6) | revLookup[b64.charCodeAt(i + 3)]
    arr[L++] = (tmp >> 16) & 0xFF
    arr[L++] = (tmp >> 8) & 0xFF
    arr[L++] = tmp & 0xFF
  }

  if (placeHolders === 2) {
    tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
    arr[L++] = tmp & 0xFF
  } else if (placeHolders === 1) {
    tmp = (revLookup[b64.charCodeAt(i)] << 10) | (revLookup[b64.charCodeAt(i + 1)] << 4) | (revLookup[b64.charCodeAt(i + 2)] >> 2)
    arr[L++] = (tmp >> 8) & 0xFF
    arr[L++] = tmp & 0xFF
  }

  return arr
}

function tripletToBase64 (num) {
  return lookup[num >> 18 & 0x3F] + lookup[num >> 12 & 0x3F] + lookup[num >> 6 & 0x3F] + lookup[num & 0x3F]
}

function encodeChunk (uint8, start, end) {
  var tmp
  var output = []
  for (var i = start; i < end; i += 3) {
    tmp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
    output.push(tripletToBase64(tmp))
  }
  return output.join('')
}

function fromByteArray (uint8) {
  var tmp
  var len = uint8.length
  var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
  var output = ''
  var parts = []
  var maxChunkLength = 16383 // must be multiple of 3

  // go through the array every three bytes, we'll deal with trailing stuff later
  for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
    parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)))
  }

  // pad the end with zeros, but make sure to not forget the extra bytes
  if (extraBytes === 1) {
    tmp = uint8[len - 1]
    output += lookup[tmp >> 2]
    output += lookup[(tmp << 4) & 0x3F]
    output += '=='
  } else if (extraBytes === 2) {
    tmp = (uint8[len - 2] << 8) + (uint8[len - 1])
    output += lookup[tmp >> 10]
    output += lookup[(tmp >> 4) & 0x3F]
    output += lookup[(tmp << 2) & 0x3F]
    output += '='
  }

  parts.push(output)

  return parts.join('')
}


/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/bitmap-adapter.js":
/*!*****************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/bitmap-adapter.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const base64js = __webpack_require__(/*! base64-js */ "./node_modules/scratch-svg-renderer/node_modules/base64-js/index.js");
/**
 * Adapts Scratch 2.0 bitmaps for use in scratch 3.0
 */


class BitmapAdapter {
  /**
   * @param {?function} makeImage HTML image constructor. Tests can provide this.
   * @param {?function} makeCanvas HTML canvas constructor. Tests can provide this.
   */
  constructor(makeImage, makeCanvas) {
    this._makeImage = makeImage ? makeImage : () => new Image();
    this._makeCanvas = makeCanvas ? makeCanvas : () => document.createElement('canvas');
    this.stageWidth = 480;
    this.stageHeight = 360;
  }

  setStageSize(width, height) {
    this.stageWidth = width;
    this.stageHeight = height;
  }
  /**
   * Return a canvas with the resized version of the given image, done using nearest-neighbor interpolation
   * @param {CanvasImageSource} image The image to resize
   * @param {int} newWidth The desired post-resize width of the image
   * @param {int} newHeight The desired post-resize height of the image
   * @returns {HTMLCanvasElement} A canvas with the resized image drawn on it.
   */


  resize(image, newWidth, newHeight) {
    // We want to always resize using nearest-neighbor interpolation. However, canvas implementations are free to
    // use linear interpolation (or other "smooth" interpolation methods) when downscaling:
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1360415
    // It seems we can get around this by resizing in two steps: first width, then height. This will always result
    // in nearest-neighbor interpolation, even when downscaling.
    const stretchWidthCanvas = this._makeCanvas();

    stretchWidthCanvas.width = newWidth;
    stretchWidthCanvas.height = image.height;
    let context = stretchWidthCanvas.getContext('2d');
    context.imageSmoothingEnabled = false;
    context.drawImage(image, 0, 0, stretchWidthCanvas.width, stretchWidthCanvas.height);

    const stretchHeightCanvas = this._makeCanvas();

    stretchHeightCanvas.width = newWidth;
    stretchHeightCanvas.height = newHeight;
    context = stretchHeightCanvas.getContext('2d');
    context.imageSmoothingEnabled = false;
    context.drawImage(stretchWidthCanvas, 0, 0, stretchHeightCanvas.width, stretchHeightCanvas.height);
    return stretchHeightCanvas;
  }
  /**
   * Scratch 2.0 had resolution 1 and 2 bitmaps. All bitmaps in Scratch 3.0 are equivalent
   * to resolution 2 bitmaps. Therefore, converting a resolution 1 bitmap means doubling
   * it in width and height.
   * @param {!string} dataURI Base 64 encoded image data of the bitmap
   * @param {!function} callback Node-style callback that returns updated dataURI if conversion succeeded
   */


  convertResolution1Bitmap(dataURI, callback) {
    const image = this._makeImage();

    image.src = dataURI;

    image.onload = () => {
      callback(null, this.resize(image, image.width * 2, image.height * 2).toDataURL());
    };

    image.onerror = () => {
      callback('Image load failed');
    };
  }
  /**
   * Given width/height of an uploaded item, return width/height the image will be resized
   * to in Scratch 3.0
   * @param {!number} oldWidth original width
   * @param {!number} oldHeight original height
   * @return {object} Array of new width, new height
   */


  getResizedWidthHeight(oldWidth, oldHeight) {
    const STAGE_WIDTH = this.stageWidth;
    const STAGE_HEIGHT = this.stageHeight;
    const STAGE_RATIO = STAGE_WIDTH / STAGE_HEIGHT; // If both dimensions are smaller than or equal to corresponding stage dimension,
    // double both dimensions

    if (oldWidth <= STAGE_WIDTH && oldHeight <= STAGE_HEIGHT) {
      return {
        width: oldWidth * 2,
        height: oldHeight * 2
      };
    } // If neither dimension is larger than 2x corresponding stage dimension,
    // this is an in-between image, return it as is


    if (oldWidth <= STAGE_WIDTH * 2 && oldHeight <= STAGE_HEIGHT * 2) {
      return {
        width: oldWidth,
        height: oldHeight
      };
    }

    const imageRatio = oldWidth / oldHeight; // Otherwise, figure out how to resize

    if (imageRatio >= STAGE_RATIO) {
      // Wide Image
      return {
        width: STAGE_WIDTH * 2,
        height: STAGE_WIDTH * 2 / imageRatio
      };
    } // In this case we have either:
    // - A wide image, but not with as big a ratio between width and height,
    // making it so that fitting the width to double stage size would leave
    // the height too big to fit in double the stage height
    // - A square image that's still larger than the double at least
    // one of the stage dimensions, so pick the smaller of the two dimensions (to fit)
    // - A tall image
    // In any of these cases, resize the image to fit the height to double the stage height


    return {
      width: STAGE_HEIGHT * 2 * imageRatio,
      height: STAGE_HEIGHT * 2
    };
  }
  /**
   * Given bitmap data, resize as necessary.
   * @param {ArrayBuffer | string} fileData Base 64 encoded image data of the bitmap
   * @param {string} fileType The MIME type of this file
   * @returns {Promise} Resolves to resized image data Uint8Array
   */


  importBitmap(fileData, fileType) {
    let dataURI = fileData;

    if (fileData instanceof ArrayBuffer) {
      dataURI = this.convertBinaryToDataURI(fileData, fileType);
    }

    return new Promise((resolve, reject) => {
      const image = this._makeImage();

      image.src = dataURI;

      image.onload = () => {
        const newSize = this.getResizedWidthHeight(image.width, image.height);

        if (newSize.width === image.width && newSize.height === image.height) {
          // No change
          resolve(this.convertDataURIToBinary(dataURI));
        } else {
          const resizedDataURI = this.resize(image, newSize.width, newSize.height).toDataURL();
          resolve(this.convertDataURIToBinary(resizedDataURI));
        }
      };

      image.onerror = () => {
        reject('Image load failed');
      };
    });
  } // TODO consolidate with scratch-vm/src/util/base64-util.js
  // From https://gist.github.com/borismus/1032746


  convertDataURIToBinary(dataURI) {
    const BASE64_MARKER = ';base64,';
    const base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
    const base64 = dataURI.substring(base64Index);
    const raw = window.atob(base64);
    const rawLength = raw.length;
    const array = new Uint8Array(new ArrayBuffer(rawLength));

    for (let i = 0; i < rawLength; i++) {
      array[i] = raw.charCodeAt(i);
    }

    return array;
  }

  convertBinaryToDataURI(arrayBuffer, contentType) {
    return "data:".concat(contentType, ";base64,").concat(base64js.fromByteArray(new Uint8Array(arrayBuffer)));
  }

}

module.exports = BitmapAdapter;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/fixup-svg-string.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/fixup-svg-string.js ***!
  \*******************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Fixup svg string prior to parsing.
 * @param {!string} svgString String of the svg to fix.
 * @returns {!string} fixed svg that should be parseable.
 */
module.exports = function (svgString) {
  // Add root svg namespace if it does not exist.
  const svgAttrs = svgString.match(/<svg [^>]*>/);

  if (svgAttrs && svgAttrs[0].indexOf('xmlns=') === -1) {
    svgString = svgString.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
  } // There are some SVGs from Illustrator that use undeclared entities.
  // Just replace those entities with fake namespace references to prevent
  // DOMParser from crashing


  if (svgAttrs && svgAttrs[0].indexOf('&ns_') !== -1 && svgString.indexOf('<!DOCTYPE') === -1) {
    svgString = svgString.replace(svgAttrs[0], svgAttrs[0].replace(/&ns_[^;]+;/g, 'http://ns.adobe.com/Extensibility/1.0/'));
  } // Some SVGs exported from Photoshop have been found to have an invalid mime type
  // Chrome and Safari won't render these SVGs, so we correct it here


  if (svgString.includes('data:img/png')) {
    svgString = svgString.replace( // capture entire image tag with xlink:href=and the quote - dont capture data: bit
    /(<image[^>]+?xlink:href=["'])data:img\/png/g, // use the captured <image ..... xlink:href=" then append the right data uri mime type
    ($0, $1) => "".concat($1, "data:image/png"));
  } // Some SVGs from Inkscape attempt to bind a prefix to a reserved namespace name.
  // This will cause SVG parsing to fail, so replace these with a dummy namespace name.
  // This namespace name is only valid for "xml", and if we bind "xmlns:xml" to the dummy namespace,
  // parsing will fail yet again, so exclude "xmlns:xml" declarations.


  const xmlnsRegex = /(<[^>]+?xmlns:(?!xml=)[^ ]+=)"http:\/\/www.w3.org\/XML\/1998\/namespace"/g;

  if (svgString.match(xmlnsRegex) !== null) {
    svgString = svgString.replace( // capture the entire attribute
    xmlnsRegex, // use the captured attribute name; replace only the URL
    ($0, $1) => "".concat($1, "\"http://dummy.namespace\""));
  } // Strip `svg:` prefix (sometimes added by Inkscape) from all tags. They interfere with DOMPurify (prefixed tag
  // names are not recognized) and the paint editor.
  // This matches opening and closing tags--the capture group captures the slash if it exists, and it is reinserted
  // in the replacement text.


  svgString = svgString.replace(/<(\/?)\s*svg:/g, '<$1'); // The <metadata> element is not needed for rendering and sometimes contains
  // unparseable garbage from Illustrator :( Empty out the contents.
  // Note: [\s\S] matches everything including newlines, which .* does not

  svgString = svgString.replace(/<metadata>[\s\S]*<\/metadata>/, '<metadata></metadata>'); // Empty script tags and javascript executing

  svgString = svgString.replace(/<script[\s\S]*>[\s\S]*<\/script>/, '<script></scri'+'pt>');
  return svgString;
};

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/font-converter.js":
/*!*****************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/font-converter.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileOverview Convert 2.0 fonts to 3.0 fonts.
 */

/**
 * Given an SVG, replace Scratch 2.0 fonts with new 3.0 fonts. Add defaults where there are none.
 * @param {SVGElement} svgTag The SVG dom object
 * @return {void}
 */
const convertFonts = function convertFonts(svgTag) {
  // Collect all text elements into a list.
  const textElements = [];

  const collectText = domElement => {
    if (domElement.localName === 'text') {
      textElements.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectText(domElement.childNodes[i]);
    }
  };

  collectText(svgTag); // If there's an old font-family, switch to the new one.

  for (const textElement of textElements) {
    // If there's no font-family provided, provide one.
    if (!textElement.getAttribute('font-family') || textElement.getAttribute('font-family') === 'Helvetica') {
      textElement.setAttribute('font-family', 'Sans Serif');
    } else if (textElement.getAttribute('font-family') === 'Mystery') {
      textElement.setAttribute('font-family', 'Curly');
    } else if (textElement.getAttribute('font-family') === 'Gloria') {
      textElement.setAttribute('font-family', 'Handwriting');
    } else if (textElement.getAttribute('font-family') === 'Donegal') {
      textElement.setAttribute('font-family', 'Serif');
    }
  }
};

module.exports = convertFonts;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/font-inliner.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/font-inliner.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileOverview Import bitmap data into Scratch 3.0, resizing image as necessary.
 */
const getFonts = __webpack_require__(/*! scratch-render-fonts */ "./node_modules/scratch-render-fonts/src/index.js");
/**
 * Given SVG data, inline the fonts. This allows them to be rendered correctly when set
 * as the source of an HTMLImageElement. Here is a note from tmickel:
 *   // Inject fonts that are needed.
 *   // It would be nice if there were another way to get the SVG-in-canvas
 *   // to render the correct font family, but I couldn't find any other way.
 *   // Other things I tried:
 *   // Just injecting the font-family into the document: no effect.
 *   // External stylesheet linked to by SVG: no effect.
 *   // Using a <link> or <style>@import</style> to link to font-family
 *   // injected into the document: no effect.
 * @param {string} svgString The string representation of the svg to modify
 * @return {string} The svg with any needed fonts inlined
 */


const inlineSvgFonts = function inlineSvgFonts(svgString) {
  const FONTS = getFonts(); // Make it clear that this function only operates on strings.
  // If we don't explicitly throw this here, the function silently fails.

  if (typeof svgString !== 'string') {
    throw new Error('SVG to be inlined is not a string');
  } // Collect fonts that need injection.


  const fontsNeeded = new Set();
  const fontRegex = /font-family="([^"]*)"/g;
  let matches = fontRegex.exec(svgString);

  while (matches) {
    fontsNeeded.add(matches[1]);
    matches = fontRegex.exec(svgString);
  }

  if (fontsNeeded.size > 0) {
    let str = '<defs><style>';

    for (const font of fontsNeeded) {
      if (Object.prototype.hasOwnProperty.call(FONTS, font)) {
        str += "".concat(FONTS[font]);
      }
    }

    str += '</style></defs>';
    svgString = svgString.replace(/<svg[^>]*>/, "$&".concat(str));
    return svgString;
  }

  return svgString;
};

module.exports = inlineSvgFonts;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/index.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/index.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const SVGRenderer = __webpack_require__(/*! ./svg-renderer */ "./node_modules/scratch-svg-renderer/src/svg-renderer.js");

const BitmapAdapter = __webpack_require__(/*! ./bitmap-adapter */ "./node_modules/scratch-svg-renderer/src/bitmap-adapter.js");

const inlineSvgFonts = __webpack_require__(/*! ./font-inliner */ "./node_modules/scratch-svg-renderer/src/font-inliner.js");

const loadSvgString = __webpack_require__(/*! ./load-svg-string */ "./node_modules/scratch-svg-renderer/src/load-svg-string.js");

const sanitizeSvg = __webpack_require__(/*! ./sanitize-svg */ "./node_modules/scratch-svg-renderer/src/sanitize-svg.js");

const serializeSvgToString = __webpack_require__(/*! ./serialize-svg-to-string */ "./node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-svg-renderer/src/svg-element.js");

const convertFonts = __webpack_require__(/*! ./font-converter */ "./node_modules/scratch-svg-renderer/src/font-converter.js"); // /**
//  * Export for NPM & Node.js
//  * @type {RenderWebGL}
//  */


module.exports = {
  BitmapAdapter: BitmapAdapter,
  convertFonts: convertFonts,
  inlineSvgFonts: inlineSvgFonts,
  loadSvgString: loadSvgString,
  sanitizeSvg: sanitizeSvg,
  serializeSvgToString: serializeSvgToString,
  SvgElement: SvgElement,
  SVGRenderer: SVGRenderer
};

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/load-svg-string.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/load-svg-string.js ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const DOMPurify = __webpack_require__(/*! dompurify */ "./node_modules/dompurify/dist/purify.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-svg-renderer/src/svg-element.js");

const convertFonts = __webpack_require__(/*! ./font-converter */ "./node_modules/scratch-svg-renderer/src/font-converter.js");

const fixupSvgString = __webpack_require__(/*! ./fixup-svg-string */ "./node_modules/scratch-svg-renderer/src/fixup-svg-string.js");

const transformStrokeWidths = __webpack_require__(/*! ./transform-applier */ "./node_modules/scratch-svg-renderer/src/transform-applier.js");
/**
 * @param {SVGElement} svgTag the tag to search within
 * @param {string} [tagName] svg tag to search for (or collect all elements if not given)
 * @return {Array} a list of elements with the given tagname
 */


const collectElements = (svgTag, tagName) => {
  const elts = [];

  const collectElementsInner = domElement => {
    if ((domElement.localName === tagName || typeof tagName === 'undefined') && domElement.getAttribute) {
      elts.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectElementsInner(domElement.childNodes[i]);
    }
  };

  collectElementsInner(svgTag);
  return elts;
};
/**
 * Fix SVGs to comply with SVG spec. Scratch 2 defaults to x2 = 0 when x2 is missing, but
 * SVG defaults to x2 = 1 when missing.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformGradients = svgTag => {
  const linearGradientElements = collectElements(svgTag, 'linearGradient'); // For each gradient element, supply x2 if necessary.

  for (const gradientElement of linearGradientElements) {
    if (!gradientElement.getAttribute('x2')) {
      gradientElement.setAttribute('x2', '0');
    }
  }
};
/**
 * Fix SVGs to match appearance in Scratch 2, which used nearest neighbor scaling for bitmaps
 * within SVGs.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformImages = svgTag => {
  const imageElements = collectElements(svgTag, 'image'); // For each image element, set image rendering to pixelated

  const pixelatedImages = 'image-rendering: optimizespeed; image-rendering: pixelated;';

  for (const elt of imageElements) {
    if (elt.getAttribute('style')) {
      elt.setAttribute('style', "".concat(pixelatedImages, " ").concat(elt.getAttribute('style')));
    } else {
      elt.setAttribute('style', pixelatedImages);
    }
  }
};
/**
 * Transforms an SVG's text elements for Scratch 2.0 quirks.
 * These quirks include:
 * 1. `x` and `y` properties are removed/ignored.
 * 2. Alignment is set to `text-before-edge`.
 * 3. Line-breaks are converted to explicit <tspan> elements.
 * 4. Any required fonts are injected.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformText = svgTag => {
  // Collect all text elements into a list.
  const textElements = [];

  const collectText = domElement => {
    if (domElement.localName === 'text') {
      textElements.push(domElement);
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectText(domElement.childNodes[i]);
    }
  };

  collectText(svgTag);
  convertFonts(svgTag); // For each text element, apply quirks.

  for (const textElement of textElements) {
    // Remove x and y attributes - they are not used in Scratch.
    textElement.removeAttribute('x');
    textElement.removeAttribute('y'); // Set text-before-edge alignment:
    // Scratch renders all text like this.

    textElement.setAttribute('alignment-baseline', 'text-before-edge');
    textElement.setAttribute('xml:space', 'preserve'); // If there's no font size provided, provide one.

    if (!textElement.getAttribute('font-size')) {
      textElement.setAttribute('font-size', '18');
    }

    let text = textElement.textContent; // Fix line breaks in text, which are not natively supported by SVG.
    // Only fix if text does not have child tspans.
    // @todo this will not work for font sizes with units such as em, percent
    // However, text made in scratch 2 should only ever export size 22 font.

    const fontSize = parseFloat(textElement.getAttribute('font-size'));
    const tx = 2;
    let ty = 0;
    let spacing = 1.2; // Try to match the position and spacing of Scratch 2.0's fonts.
    // Different fonts seem to use different line spacing.
    // Scratch 2 always uses alignment-baseline=text-before-edge
    // However, most SVG readers don't support this attribute
    // or don't support it alongside use of tspan, so the translations
    // here are to make up for that.

    if (textElement.getAttribute('font-family') === 'Handwriting') {
      spacing = 2;
      ty = -11 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Scratch') {
      spacing = 0.89;
      ty = -3 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Curly') {
      spacing = 1.38;
      ty = -6 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Marker') {
      spacing = 1.45;
      ty = -6 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Sans Serif') {
      spacing = 1.13;
      ty = -3 * fontSize / 22;
    } else if (textElement.getAttribute('font-family') === 'Serif') {
      spacing = 1.25;
      ty = -4 * fontSize / 22;
    }

    if (textElement.transform.baseVal.numberOfItems === 0) {
      const transform = svgTag.createSVGTransform();
      textElement.transform.baseVal.appendItem(transform);
    } // Right multiply matrix by a translation of (tx, ty)


    const mtx = textElement.transform.baseVal.getItem(0).matrix;
    mtx.e += mtx.a * tx + mtx.c * ty;
    mtx.f += mtx.b * tx + mtx.d * ty;

    if (text && textElement.childElementCount === 0) {
      textElement.textContent = '';
      const lines = text.split('\n');
      text = '';

      for (const line of lines) {
        const tspanNode = SvgElement.create('tspan');
        tspanNode.setAttribute('x', '0');
        tspanNode.setAttribute('style', 'white-space: pre');
        tspanNode.setAttribute('dy', "".concat(spacing, "em"));
        tspanNode.textContent = line ? line : ' ';
        textElement.appendChild(tspanNode);
      }
    }
  }
};
/**
 * Find the largest stroke width in the svg. If a shape has no
 * `stroke` property, it has a stroke-width of 0. If it has a `stroke`,
 * it is by default a stroke-width of 1.
 * This is used to enlarge the computed bounding box, which doesn't take
 * stroke width into account.
 * @param {SVGSVGElement} rootNode The root SVG node to traverse.
 * @return {number} The largest stroke width in the SVG.
 */


const findLargestStrokeWidth = rootNode => {
  let largestStrokeWidth = 0;

  const collectStrokeWidths = domElement => {
    if (domElement.getAttribute) {
      if (domElement.getAttribute('stroke')) {
        largestStrokeWidth = Math.max(largestStrokeWidth, 1);
      }

      if (domElement.getAttribute('stroke-width')) {
        largestStrokeWidth = Math.max(largestStrokeWidth, Number(domElement.getAttribute('stroke-width')) || 0);
      }
    }

    for (let i = 0; i < domElement.childNodes.length; i++) {
      collectStrokeWidths(domElement.childNodes[i]);
    }
  };

  collectStrokeWidths(rootNode);
  return largestStrokeWidth;
};
/**
 * Transform the measurements of the SVG.
 * In Scratch 2.0, SVGs are drawn without respect to the width,
 * height, and viewBox attribute on the tag. The exporter
 * does output these properties - but they appear to be incorrect often.
 * To address the incorrect measurements, we append the DOM to the
 * document, and then use SVG's native `getBBox` to find the real
 * drawn dimensions. This ensures things drawn in negative dimensions,
 * outside the given viewBox, etc., are all eventually drawn to the canvas.
 * I tried to do this several other ways: stripping the width/height/viewBox
 * attributes and then drawing (Firefox won't draw anything),
 * or inflating them and then measuring a canvas. But this seems to be
 * a natural and performant way.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const transformMeasurements = svgTag => {
  // Append the SVG dom to the document.
  // This allows us to use `getBBox` on the page,
  // which returns the full bounding-box of all drawn SVG
  // elements, similar to how Scratch 2.0 did measurement.
  const svgSpot = document.createElement('span'); // Since we're adding user-provided SVG to document.body,
  // sanitizing is required. This should not affect bounding box calculation.
  // outerHTML is attribute of Element (and not HTMLElement), so use it instead of
  // calling serializer or toString()
  // NOTE: svgTag remains untouched!

  const rawValue = svgTag.outerHTML;
  const sanitizedValue = DOMPurify.sanitize(rawValue, {
    // Use SVG profile (no HTML elements)
    USE_PROFILES: {
      svg: true
    },
    // Remove some tags that Scratch does not use.
    FORBID_TAGS: ['a', 'audio', 'canvas', 'video'],
    // Allow data URI in image tags (e.g. SVGs converted from bitmap)
    ADD_DATA_URI_TAGS: ['image']
  });
  let bbox;

  try {
    // Insert sanitized value.
    svgSpot.innerHTML = sanitizedValue;
    document.body.appendChild(svgSpot); // Take the bounding box. We have to get elements via svgSpot
    // because we added it via innerHTML.

    bbox = svgSpot.children[0].getBBox();
  } finally {
    // Always destroy the element, even if, for example, getBBox throws.
    document.body.removeChild(svgSpot);
  } // Enlarge the bbox from the largest found stroke width
  // This may have false-positives, but at least the bbox will always
  // contain the full graphic including strokes.
  // If the width or height is zero however, don't enlarge since
  // they won't have a stroke width that needs to be enlarged.


  let halfStrokeWidth;

  if (bbox.width === 0 || bbox.height === 0) {
    halfStrokeWidth = 0;
  } else {
    halfStrokeWidth = findLargestStrokeWidth(svgTag) / 2;
  }

  const width = bbox.width + halfStrokeWidth * 2;
  const height = bbox.height + halfStrokeWidth * 2;
  const x = bbox.x - halfStrokeWidth;
  const y = bbox.y - halfStrokeWidth; // Set the correct measurements on the SVG tag

  svgTag.setAttribute('width', width);
  svgTag.setAttribute('height', height);
  svgTag.setAttribute('viewBox', "".concat(x, " ").concat(y, " ").concat(width, " ").concat(height));
};
/**
 * Find all instances of a URL-referenced `stroke` in the svg. In 2.0, all gradient strokes
 * have a round `stroke-linejoin` and `stroke-linecap`... for some reason.
 * @param {SVGSVGElement} svgTag the SVG tag to apply the transformation to
 */


const setGradientStrokeRoundedness = svgTag => {
  const elements = collectElements(svgTag);

  for (const elt of elements) {
    if (!elt.style) continue;
    const stroke = elt.style.stroke || elt.getAttribute('stroke');

    if (stroke && stroke.match(/^url\(#.*\)$/)) {
      elt.style['stroke-linejoin'] = 'round';
      elt.style['stroke-linecap'] = 'round';
    }
  }
};
/**
 * In-place, convert passed SVG to something consistent that will be rendered the way we want them to be.
 * @param {SVGSvgElement} svgTag root SVG node to operate upon
 * @param {boolean} [fromVersion2] True if we should perform conversion from version 2 to version 3 svg.
 */


const normalizeSvg = (svgTag, fromVersion2) => {
  if (fromVersion2) {
    // Fix gradients. Scratch 2 exports no x2 when x2 = 0, but
    // SVG default is that x2 is 1. This must be done before
    // transformStrokeWidths since transformStrokeWidths affects
    // gradients.
    transformGradients(svgTag);
  }

  transformStrokeWidths(svgTag, window);
  transformImages(svgTag);

  if (fromVersion2) {
    // Transform all text elements.
    transformText(svgTag); // Transform measurements.

    transformMeasurements(svgTag); // Fix stroke roundedness.

    setGradientStrokeRoundedness(svgTag);
  } else if (!svgTag.getAttribute('viewBox')) {
    // Renderer expects a view box.
    transformMeasurements(svgTag);
  } else if (!svgTag.getAttribute('width') || !svgTag.getAttribute('height')) {
    svgTag.setAttribute('width', svgTag.viewBox.baseVal.width);
    svgTag.setAttribute('height', svgTag.viewBox.baseVal.height);
  }
};
/**
 * Load an SVG string and normalize it. All the steps before drawing/measuring.
 * Currently, this will normalize stroke widths (see transform-applier.js) and render all embedded images pixelated.
 * The returned SVG will be guaranteed to always have a `width`, `height` and `viewBox`.
 * In addition, if the `fromVersion2` parameter is `true`, several "quirks-mode" transformations will be applied which
 * mimic Scratch 2.0's SVG rendering.
 * @param {!string} svgString String of SVG data to draw in quirks-mode.
 * @param {boolean} [fromVersion2] True if we should perform conversion from version 2 to version 3 svg.
 * @return {SVGSVGElement} The normalized SVG element.
 */


const loadSvgString = (svgString, fromVersion2) => {
  // Parse string into SVG XML.
  const parser = new DOMParser();
  svgString = fixupSvgString(svgString);
  const svgDom = parser.parseFromString(svgString, 'text/xml');

  if (svgDom.childNodes.length < 1 || svgDom.documentElement.localName !== 'svg') {
    throw new Error('Document does not appear to be SVG.');
  }

  const svgTag = svgDom.documentElement;
  normalizeSvg(svgTag, fromVersion2);
  return svgTag;
};

module.exports = loadSvgString;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/sanitize-svg.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/sanitize-svg.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileOverview Sanitize the content of an SVG aggressively, to make it as safe
 * as possible
 */
const fixupSvgString = __webpack_require__(/*! ./fixup-svg-string */ "./node_modules/scratch-svg-renderer/src/fixup-svg-string.js");

const DOMPurify = __webpack_require__(/*! dompurify */ "./node_modules/dompurify/dist/purify.js");

const sanitizeSvg = {};
DOMPurify.addHook('beforeSanitizeAttributes', currentNode => {
  if (currentNode && currentNode.href && currentNode.href.baseVal) {
    const href = currentNode.href.baseVal.replace(/\s/g, ''); // "data:" and "#" are valid hrefs

    if (href.slice(0, 5) !== 'data:' && href.slice(0, 1) !== '#') {
      if (currentNode.attributes.getNamedItem('xlink:href')) {
        currentNode.attributes.removeNamedItem('xlink:href');
        delete currentNode['xlink:href'];
      }

      if (currentNode.attributes.getNamedItem('href')) {
        currentNode.attributes.removeNamedItem('href');
        delete currentNode.href;
      }
    }
  }

  return currentNode;
}); // Use JS implemented TextDecoder and TextEncoder if it is not provided by the
// browser.

let _TextDecoder;

let _TextEncoder;

if (typeof TextDecoder === 'undefined' || typeof TextEncoder === 'undefined') {
  // Wait to require the text encoding polyfill until we know it's needed.
  // eslint-disable-next-line global-require
  const encoding = __webpack_require__(/*! fastestsmallesttextencoderdecoder */ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js");

  _TextDecoder = encoding.TextDecoder;
  _TextEncoder = encoding.TextEncoder;
} else {
  _TextDecoder = TextDecoder;
  _TextEncoder = TextEncoder;
}
/**
 * Load an SVG Uint8Array of bytes and "sanitize" it
 * @param {!Uint8Array} rawData unsanitized SVG daata
 * @return {Uint8Array} sanitized SVG data
 */


sanitizeSvg.sanitizeByteStream = function (rawData) {
  const decoder = new _TextDecoder();
  const encoder = new _TextEncoder();
  const sanitizedText = sanitizeSvg.sanitizeSvgText(decoder.decode(rawData));
  return encoder.encode(sanitizedText);
}; // TW: Don't remove extra metadata tag: <!--rotationCenter:10,10-->
// Using literal HTML comments tokens will cause this script to be very hard to inline in
// a <script> element, so we'll instead do this terrible hack which the minifier probably
// won't be able to optimize away.


const HTML_COMMENT_START = "<!".concat('-'.repeat(2));
const HTML_COMMENT_END = "".concat('-'.repeat(2), ">");
const extraMetadataRegex = new RegExp("".concat(HTML_COMMENT_START, "rotationCenter:(-?[\\d\\.]+):(-?[\\d\\.]+)").concat(HTML_COMMENT_END, "$"));
/**
 * Load an SVG string and "sanitize" it. This is more aggressive than the handling in
 * fixup-svg-string.js, and thus more risky; there are known examples of SVGs that
 * it will clobber. We use DOMPurify's svg profile, which restricts many types of tag.
 * @param {!string} rawSvgText unsanitized SVG string
 * @return {string} sanitized SVG text
 */

sanitizeSvg.sanitizeSvgText = function (rawSvgText) {
  let sanitizedText = DOMPurify.sanitize(rawSvgText, {
    USE_PROFILES: {
      svg: true
    }
  }); // Remove partial XML comment that is sometimes left in the HTML

  const badTag = sanitizedText.indexOf(']&gt;');

  if (badTag >= 0) {
    sanitizedText = sanitizedText.substring(5, sanitizedText.length);
  } // also use our custom fixup rules


  sanitizedText = fixupSvgString(sanitizedText); // TW: don't remove extra metadata comment

  const extraMetadataMatch = rawSvgText.match(extraMetadataRegex);

  if (extraMetadataMatch) {
    sanitizedText += extraMetadataMatch[0];
  }

  return sanitizedText;
};

module.exports = sanitizeSvg;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js ***!
  \**************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const inlineSvgFonts = __webpack_require__(/*! ./font-inliner */ "./node_modules/scratch-svg-renderer/src/font-inliner.js");
/**
 * Serialize a given SVG DOM to a string.
 * @param {SVGSVGElement} svgTag The SVG element to serialize.
 * @param {?boolean} shouldInjectFonts True if fonts should be included in the SVG as
 *     base64 data.
 * @returns {string} String representing current SVG data.
 */


const serializeSvgToString = (svgTag, shouldInjectFonts) => {
  const serializer = new XMLSerializer();
  let string = serializer.serializeToString(svgTag);

  if (shouldInjectFonts) {
    string = inlineSvgFonts(string);
  }

  return string;
};

module.exports = serializeSvgToString;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/svg-element.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/svg-element.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* Adapted from
 * Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
 * http://paperjs.org/
 *
 * Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
 * http://scratchdisk.com/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 */

/**
 * @name SvgElement
 * @namespace
 * @private
 */
class SvgElement {
  // SVG related namespaces
  static get svg() {
    return 'http://www.w3.org/2000/svg';
  }

  static get xmlns() {
    return 'http://www.w3.org/2000/xmlns';
  }

  static get xlink() {
    return 'http://www.w3.org/1999/xlink';
  } // Mapping of attribute names to required namespaces:


  static attributeNamespace() {
    return {
      'href': SvgElement.xlink,
      'xlink': SvgElement.xmlns,
      // Only the xmlns attribute needs the trailing slash. See #984
      'xmlns': "".concat(SvgElement.xmlns, "/"),
      // IE needs the xmlns namespace when setting 'xmlns:xlink'. See #984
      'xmlns:xlink': "".concat(SvgElement.xmlns, "/")
    };
  }

  static create(tag, attributes, formatter) {
    return SvgElement.set(document.createElementNS(SvgElement.svg, tag), attributes, formatter);
  }

  static get(node, name) {
    const namespace = SvgElement.attributeNamespace[name];
    const value = namespace ? node.getAttributeNS(namespace, name) : node.getAttribute(name);
    return value === 'null' ? null : value;
  }

  static set(node, attributes, formatter) {
    for (const name in attributes) {
      let value = attributes[name];
      const namespace = SvgElement.attributeNamespace[name];

      if (typeof value === 'number' && formatter) {
        value = formatter.number(value);
      }

      if (namespace) {
        node.setAttributeNS(namespace, name, value);
      } else {
        node.setAttribute(name, value);
      }
    }

    return node;
  }

}

module.exports = SvgElement;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/svg-renderer.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/svg-renderer.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const loadSvgString = __webpack_require__(/*! ./load-svg-string */ "./node_modules/scratch-svg-renderer/src/load-svg-string.js");

const serializeSvgToString = __webpack_require__(/*! ./serialize-svg-to-string */ "./node_modules/scratch-svg-renderer/src/serialize-svg-to-string.js");
/**
 * Main quirks-mode SVG rendering code.
 * @deprecated Call into individual methods exported from this library instead.
 */


class SvgRenderer {
  /**
   * Create a quirks-mode SVG renderer for a particular canvas.
   * @param {HTMLCanvasElement} [canvas] An optional canvas element to draw to. If this is not provided, the renderer
   * will create a new canvas.
   * @constructor
   */
  constructor(canvas) {
    /**
     * The canvas that this SVG renderer will render to.
     * @type {HTMLCanvasElement}
     * @private
     */
    this._canvas = canvas || document.createElement('canvas');
    this._context = this._canvas.getContext('2d');
    /**
     * A measured SVG "viewbox"
     * @typedef {object} SvgRenderer#SvgMeasurements
     * @property {number} x - The left edge of the SVG viewbox.
     * @property {number} y - The top edge of the SVG viewbox.
     * @property {number} width - The width of the SVG viewbox.
     * @property {number} height - The height of the SVG viewbox.
     */

    /**
     * The measurement box of the currently loaded SVG.
     * @type {SvgRenderer#SvgMeasurements}
     * @private
     */

    this._measurements = {
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
    /**
     * The `<img>` element with the contents of the currently loaded SVG.
     * @type {?HTMLImageElement}
     * @private
     */

    this._cachedImage = null;
    /**
     * True if this renderer's current SVG is loaded and can be rendered to the canvas.
     * @type {boolean}
     */

    this.loaded = false;
  }
  /**
   * @returns {!HTMLCanvasElement} this renderer's target canvas.
   */


  get canvas() {
    return this._canvas;
  }
  /**
   * @return {Array<number>} the natural size, in Scratch units, of this SVG.
   */


  get size() {
    return [this._measurements.width, this._measurements.height];
  }
  /**
   * @return {Array<number>} the offset (upper left corner) of the SVG's view box.
   */


  get viewOffset() {
    return [this._measurements.x, this._measurements.y];
  }
  /**
   * Load an SVG string and normalize it. All the steps before drawing/measuring.
   * @param {!string} svgString String of SVG data to draw in quirks-mode.
   * @param {?boolean} fromVersion2 True if we should perform conversion from
   *     version 2 to version 3 svg.
   */


  loadString(svgString, fromVersion2) {
    // New svg string invalidates the cached image
    this._cachedImage = null;
    const svgTag = loadSvgString(svgString, fromVersion2);
    this._svgTag = svgTag;
    this._measurements = {
      width: svgTag.viewBox.baseVal.width,
      height: svgTag.viewBox.baseVal.height,
      x: svgTag.viewBox.baseVal.x,
      y: svgTag.viewBox.baseVal.y
    };
  }
  /**
   * Load an SVG string, normalize it, and prepare it for (synchronous) rendering.
   * @param {!string} svgString String of SVG data to draw in quirks-mode.
   * @param {?boolean} fromVersion2 True if we should perform conversion from version 2 to version 3 svg.
   * @param {Function} [onFinish] - An optional callback to call when the SVG is loaded and can be rendered.
   */


  loadSVG(svgString, fromVersion2, onFinish) {
    this.loadString(svgString, fromVersion2);

    this._createSVGImage(onFinish);
  }
  /**
   * Creates an <img> element for the currently loaded SVG string, then calls the callback once it's loaded.
   * @param {Function} [onFinish] - An optional callback to call when the <img> has loaded.
   */


  _createSVGImage(onFinish) {
    if (this._cachedImage === null) this._cachedImage = new Image();
    const img = this._cachedImage;

    img.onload = () => {
      this.loaded = true;
      if (onFinish) onFinish();
    };

    const svgText = this.toString(true
    /* shouldInjectFonts */
    );
    img.src = "data:image/svg+xml;utf8,".concat(encodeURIComponent(svgText));
    this.loaded = false;
  }
  /**
   * Serialize the active SVG DOM to a string.
   * @param {?boolean} shouldInjectFonts True if fonts should be included in the SVG as
   *     base64 data.
   * @returns {string} String representing current SVG data.
   * @deprecated Use the standalone `serializeSvgToString` export instead.
   */


  toString(shouldInjectFonts) {
    return serializeSvgToString(this._svgTag, shouldInjectFonts);
  }
  /**
   * Synchronously draw the loaded SVG to this renderer's `canvas`.
   * @param {number} [scale] - Optionally, also scale the image by this factor.
   */


  draw(scale) {
    if (!this.loaded) throw new Error('SVG image has not finished loading');

    this._drawFromImage(scale);
  }
  /**
   * Draw to the canvas from a loaded image element.
   * @param {number} [scale] - Optionally, also scale the image by this factor.
   **/


  _drawFromImage(scale) {
    if (this._cachedImage === null) return;
    const ratio = Number.isFinite(scale) ? scale : 1;
    const bbox = this._measurements;
    this._canvas.width = bbox.width * ratio;
    this._canvas.height = bbox.height * ratio; // Even if the canvas at the current scale has a nonzero size, the image's dimensions are floored pre-scaling.
    // e.g. if an image has a width of 0.4 and is being rendered at 3x scale, the canvas will have a width of 1, but
    // the image's width will be rounded down to 0 on some browsers (Firefox) prior to being drawn at that scale.

    if (this._canvas.width <= 0 || this._canvas.height <= 0 || this._cachedImage.naturalWidth <= 0 || this._cachedImage.naturalHeight <= 0) return;

    this._context.clearRect(0, 0, this._canvas.width, this._canvas.height);

    this._context.setTransform(ratio, 0, 0, ratio, 0, 0);

    this._context.drawImage(this._cachedImage, 0, 0);
  }

}

module.exports = SvgRenderer;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/transform-applier.js":
/*!********************************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/transform-applier.js ***!
  \********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Matrix = __webpack_require__(/*! transformation-matrix */ "./node_modules/transformation-matrix/build-umd/transformation-matrix.min.js");

const SvgElement = __webpack_require__(/*! ./svg-element */ "./node_modules/scratch-svg-renderer/src/svg-element.js");

const log = __webpack_require__(/*! ./util/log */ "./node_modules/scratch-svg-renderer/src/util/log.js");
/**
 * @fileOverview Apply transforms to match stroke width appearance in 2.0 and 3.0
 */
// Adapted from paper.js's Path.applyTransform


const _parseTransform = function _parseTransform(domElement) {
  let matrix = Matrix.identity();
  const string = domElement.attributes && domElement.attributes.transform && domElement.attributes.transform.value;
  if (!string) return matrix; // https://www.w3.org/TR/SVG/types.html#DataTypeTransformList
  // Parse SVG transform string. First we split at /)\s*/, to separate
  // commands

  const transforms = string.split(/\)\s*/g);

  for (const transform of transforms) {
    if (!transform) break; // Command come before the '(', values after

    const parts = transform.split(/\(\s*/);
    const command = parts[0].trim();
    const v = parts[1].split(/[\s,]+/g); // Convert values to floats

    for (let j = 0; j < v.length; j++) {
      v[j] = parseFloat(v[j]);
    }

    switch (command) {
      case 'matrix':
        matrix = Matrix.compose(matrix, {
          a: v[0],
          b: v[1],
          c: v[2],
          d: v[3],
          e: v[4],
          f: v[5]
        });
        break;

      case 'rotate':
        matrix = Matrix.compose(matrix, Matrix.rotateDEG(v[0], v[1] || 0, v[2] || 0));
        break;

      case 'translate':
        matrix = Matrix.compose(matrix, Matrix.translate(v[0], v[1] || 0));
        break;

      case 'scale':
        matrix = Matrix.compose(matrix, Matrix.scale(v[0], v[1] || v[0]));
        break;

      case 'skewX':
        matrix = Matrix.compose(matrix, Matrix.skewDEG(v[0], 0));
        break;

      case 'skewY':
        matrix = Matrix.compose(matrix, Matrix.skewDEG(0, v[0]));
        break;

      default:
        log.error("Couldn't parse: ".concat(command));
    }
  }

  return matrix;
}; // Adapted from paper.js's Matrix.decompose
// Given a matrix, return the x and y scale factors of the matrix


const _getScaleFactor = function _getScaleFactor(matrix) {
  const a = matrix.a;
  const b = matrix.b;
  const c = matrix.c;
  const d = matrix.d;
  const det = a * d - b * c;

  if (a !== 0 || b !== 0) {
    const r = Math.sqrt(a * a + b * b);
    return {
      x: r,
      y: det / r
    };
  }

  if (c !== 0 || d !== 0) {
    const s = Math.sqrt(c * c + d * d);
    return {
      x: det / s,
      y: s
    };
  } // a = b = c = d = 0


  return {
    x: 0,
    y: 0
  };
}; // Returns null if matrix is not invertible. Otherwise returns given ellipse
// transformed by transform, an object {radiusX, radiusY, rotation}.


const _calculateTransformedEllipse = function _calculateTransformedEllipse(radiusX, radiusY, theta, transform) {
  theta = -theta * Math.PI / 180;
  const a = transform.a;
  const b = -transform.c;
  const c = -transform.b;
  const d = transform.d; // Since other parameters determine the translation of the ellipse in SVG, we do not need to worry
  // about what e and f are.

  const det = a * d - b * c; // Non-invertible matrix

  if (det === 0) return null; // rotA, rotB, and rotC represent Ax^2 + Bxy + Cy^2 = 1 coefficients for a rotated ellipse formula

  const sinT = Math.sin(theta);
  const cosT = Math.cos(theta);
  const sin2T = Math.sin(2 * theta);
  const rotA = cosT * cosT / radiusX / radiusX + sinT * sinT / radiusY / radiusY;
  const rotB = sin2T / radiusX / radiusX - sin2T / radiusY / radiusY;
  const rotC = sinT * sinT / radiusX / radiusX + cosT * cosT / radiusY / radiusY; // Calculate the ellipse formula of the transformed ellipse
  // A, B, and C represent Ax^2 + Bxy + Cy^2 = 1 / det / det coefficients in a transformed ellipse formula
  // scaled by inverse det squared (to preserve accuracy)

  const A = rotA * d * d - rotB * d * c + rotC * c * c;
  const B = -2 * rotA * b * d + rotB * a * d + rotB * b * c - 2 * rotC * a * c;
  const C = rotA * b * b - rotB * a * b + rotC * a * a; // Derive new radii and theta from the transformed ellipse formula

  const newRadiusXOverDet = Math.sqrt(2) * Math.sqrt((A + C - Math.sqrt(A * A + B * B - 2 * A * C + C * C)) / (-B * B + 4 * A * C));
  const newRadiusYOverDet = 1 / Math.sqrt(A + C - 1 / newRadiusXOverDet / newRadiusXOverDet);
  let temp = (A - 1 / newRadiusXOverDet / newRadiusXOverDet) / (1 / newRadiusYOverDet / newRadiusYOverDet - 1 / newRadiusXOverDet / newRadiusXOverDet);
  if (temp < 0 && Math.abs(temp) < 1e-8) temp = 0; // Fix floating point issue

  temp = Math.sqrt(temp);
  if (Math.abs(1 - temp) < 1e-8) temp = 1; // Fix floating point issue
  // Solve for which of the two possible thetas is correct

  let newTheta = Math.asin(temp);
  temp = B / (1 / newRadiusXOverDet / newRadiusXOverDet - 1 / newRadiusYOverDet / newRadiusYOverDet);
  const newTheta2 = -newTheta;

  if (Math.abs(Math.sin(2 * newTheta2) - temp) < Math.abs(Math.sin(2 * newTheta) - temp)) {
    newTheta = newTheta2;
  }

  return {
    radiusX: newRadiusXOverDet * det,
    radiusY: newRadiusYOverDet * det,
    rotation: -newTheta * 180 / Math.PI
  };
}; // Adapted from paper.js's PathItem.setPathData


const _transformPath = function _transformPath(pathString, transform) {
  if (!transform || Matrix.toString(transform) === Matrix.toString(Matrix.identity())) return pathString; // First split the path data into parts of command-coordinates pairs
  // Commands are any of these characters: mzlhvcsqta

  const parts = pathString && pathString.match(/[mlhvcsqtaz][^mlhvcsqtaz]*/ig);
  let coords;
  let relative = false;
  let previous;
  let control;
  let current = {
    x: 0,
    y: 0
  };
  let start = {
    x: 0,
    y: 0
  };
  let result = '';

  const getCoord = function getCoord(index, coord) {
    let val = +coords[index];

    if (relative) {
      val += current[coord];
    }

    return val;
  };

  const getPoint = function getPoint(index) {
    return {
      x: getCoord(index, 'x'),
      y: getCoord(index + 1, 'y')
    };
  };

  const roundTo4Places = function roundTo4Places(num) {
    return Math.round(num * 1e4) / 1e4;
  }; // Returns the transformed point as a string


  const getString = function getString(point) {
    const transformed = Matrix.applyToPoint(transform, point);
    return "".concat(roundTo4Places(transformed.x), " ").concat(roundTo4Places(transformed.y), " ");
  };

  for (let i = 0, l = parts && parts.length; i < l; i++) {
    const part = parts[i];
    const command = part[0];
    const lower = command.toLowerCase(); // Match all coordinate values

    coords = part.match(/[+-]?(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g);
    const length = coords && coords.length;
    relative = command === lower; // Fix issues with z in the middle of SVG path data, not followed by
    // a m command, see paper.js#413:

    if (previous === 'z' && !/[mz]/.test(lower)) {
      result += "M ".concat(current.x, " ").concat(current.y, " ");
    }

    switch (lower) {
      case 'm': // Move to

      case 'l':
        // Line to
        {
          let move = lower === 'm';

          for (let j = 0; j < length; j += 2) {
            result += move ? 'M ' : 'L ';
            current = getPoint(j);
            result += getString(current);

            if (move) {
              start = current;
              move = false;
            }
          }

          control = current;
          break;
        }

      case 'h': // Horizontal line

      case 'v':
        // Vertical line
        {
          const coord = lower === 'h' ? 'x' : 'y';
          current = {
            x: current.x,
            y: current.y
          }; // Clone as we're going to modify it.

          for (let j = 0; j < length; j++) {
            current[coord] = getCoord(j, coord);
            result += "L ".concat(getString(current));
          }

          control = current;
          break;
        }

      case 'c':
        // Cubic Bezier curve
        for (let j = 0; j < length; j += 6) {
          const handle1 = getPoint(j);
          control = getPoint(j + 2);
          current = getPoint(j + 4);
          result += "C ".concat(getString(handle1)).concat(getString(control)).concat(getString(current));
        }

        break;

      case 's':
        // Smooth cubic Bezier curve
        for (let j = 0; j < length; j += 4) {
          const handle1 = /[cs]/.test(previous) ? {
            x: current.x * 2 - control.x,
            y: current.y * 2 - control.y
          } : current;
          control = getPoint(j);
          current = getPoint(j + 2);
          result += "C ".concat(getString(handle1)).concat(getString(control)).concat(getString(current));
          previous = lower;
        }

        break;

      case 'q':
        // Quadratic Bezier curve
        for (let j = 0; j < length; j += 4) {
          control = getPoint(j);
          current = getPoint(j + 2);
          result += "Q ".concat(getString(control)).concat(getString(current));
        }

        break;

      case 't':
        // Smooth quadratic Bezier curve
        for (let j = 0; j < length; j += 2) {
          control = /[qt]/.test(previous) ? {
            x: current.x * 2 - control.x,
            y: current.y * 2 - control.y
          } : current;
          current = getPoint(j);
          result += "Q ".concat(getString(control)).concat(getString(current));
          previous = lower;
        }

        break;

      case 'a':
        // Elliptical arc curve
        for (let j = 0; j < length; j += 7) {
          current = getPoint(j + 5);
          const rx = +coords[j];
          const ry = +coords[j + 1];
          const rotation = +coords[j + 2];
          const largeArcFlag = +coords[j + 3];
          let clockwiseFlag = +coords[j + 4];

          const newEllipse = _calculateTransformedEllipse(rx, ry, rotation, transform);

          const matrixScale = _getScaleFactor(transform);

          if (newEllipse) {
            if (matrixScale.x > 0 && matrixScale.y < 0 || matrixScale.x < 0 && matrixScale.y > 0) {
              clockwiseFlag = clockwiseFlag ^ 1;
            }

            result += "A ".concat(roundTo4Places(Math.abs(newEllipse.radiusX)), " ") + "".concat(roundTo4Places(Math.abs(newEllipse.radiusY)), " ") + "".concat(roundTo4Places(newEllipse.rotation), " ").concat(largeArcFlag, " ") + "".concat(clockwiseFlag, " ").concat(getString(current));
          } else {
            result += "L ".concat(getString(current));
          }
        }

        break;

      case 'z':
        // Close path
        result += "Z "; // Correctly handle relative m commands, see paper.js#1101:

        current = start;
        break;
    }

    previous = lower;
  }

  return result;
};

const GRAPHICS_ELEMENTS = ['circle', 'ellipse', 'image', 'line', 'path', 'polygon', 'polyline', 'rect', 'text', 'use'];
const CONTAINER_ELEMENTS = ['a', 'defs', 'g', 'marker', 'glyph', 'missing-glyph', 'pattern', 'svg', 'switch', 'symbol'];

const _isContainerElement = function _isContainerElement(element) {
  return element.tagName && CONTAINER_ELEMENTS.includes(element.tagName.toLowerCase());
};

const _isGraphicsElement = function _isGraphicsElement(element) {
  return element.tagName && GRAPHICS_ELEMENTS.includes(element.tagName.toLowerCase());
};

const _isPathWithTransformAndStroke = function _isPathWithTransformAndStroke(element, strokeWidth) {
  if (!element.attributes) return false;
  strokeWidth = element.attributes['stroke-width'] ? Number(element.attributes['stroke-width'].value) : Number(strokeWidth);
  return strokeWidth && element.tagName && element.tagName.toLowerCase() === 'path' && element.attributes.d && element.attributes.d.value;
};

const _quadraticMean = function _quadraticMean(a, b) {
  return Math.sqrt((a * a + b * b) / 2);
};

const _createGradient = function _createGradient(gradientId, svgTag, bbox, matrix) {
  // Adapted from Paper.js's SvgImport.getValue
  const getValue = function getValue(node, name, isString, allowNull, allowPercent, defaultValue) {
    // Interpret value as number. Never return NaN, but 0 instead.
    // If the value is a sequence of numbers, parseFloat will
    // return the first occurring number, which is enough for now.
    let value = SvgElement.get(node, name);
    let res;

    if (value === null) {
      if (defaultValue) {
        res = defaultValue;

        if (/%\s*$/.test(res)) {
          value = defaultValue;
          res = parseFloat(value);
        }
      } else if (allowNull) {
        res = null;
      } else if (isString) {
        res = '';
      } else {
        res = 0;
      }
    } else if (isString) {
      res = value;
    } else {
      res = parseFloat(value);
    } // Support for dimensions in percentage of the root size. If root-size
    // is not set (e.g. during <defs>), just scale the percentage value to
    // 0..1, as required by gradients with gradientUnits="objectBoundingBox"


    if (/%\s*$/.test(value)) {
      const size = allowPercent ? 1 : bbox[/x|^width/.test(name) ? 'width' : 'height'];
      return res / 100 * size;
    }

    return res;
  };

  const getPoint = function getPoint(node, x, y, allowNull, allowPercent, defaultX, defaultY) {
    x = getValue(node, x || 'x', false, allowNull, allowPercent, defaultX);
    y = getValue(node, y || 'y', false, allowNull, allowPercent, defaultY);
    return allowNull && (x === null || y === null) ? null : {
      x,
      y
    };
  };

  let defs = svgTag.getElementsByTagName('defs');

  if (defs.length === 0) {
    defs = SvgElement.create('defs');
    svgTag.appendChild(defs);
  } else {
    defs = defs[0];
  } // Clone the old gradient. We'll make a new one, since the gradient might be reused elsewhere
  // with different transform matrix


  const oldGradient = svgTag.getElementById(gradientId);
  if (!oldGradient) return;
  const radial = oldGradient.tagName.toLowerCase() === 'radialgradient';
  const newGradient = svgTag.getElementById(gradientId).cloneNode(true
  /* deep */
  ); // Give the new gradient a new ID

  let matrixString = Matrix.toString(matrix);
  matrixString = matrixString.substring(8, matrixString.length - 1);
  const newGradientId = "".concat(gradientId, "-").concat(matrixString);
  newGradient.setAttribute('id', newGradientId); // This gradient already exists and was transformed before. Just reuse the already-transformed one.

  if (svgTag.getElementById(newGradientId)) {
    // This is the same code as in the end of the function, but I don't feel like wrapping the next 80 lines
    // in an `if (!svgTag.getElementById(newGradientId))` block
    return "url(#".concat(newGradientId, ")");
  }

  const scaleToBounds = getValue(newGradient, 'gradientUnits', true) !== 'userSpaceOnUse';
  let origin;
  let destination;
  let radius;
  let focal;

  if (radial) {
    origin = getPoint(newGradient, 'cx', 'cy', false, scaleToBounds, '50%', '50%');
    radius = getValue(newGradient, 'r', false, false, scaleToBounds, '50%');
    focal = getPoint(newGradient, 'fx', 'fy', true, scaleToBounds);
  } else {
    origin = getPoint(newGradient, 'x1', 'y1', false, scaleToBounds);
    destination = getPoint(newGradient, 'x2', 'y2', false, scaleToBounds, '1');

    if (origin.x === destination.x && origin.y === destination.y) {
      // If it's degenerate, use the color of the last stop, as described by
      // https://www.w3.org/TR/SVG/pservers.html#LinearGradientNotes
      const stops = newGradient.getElementsByTagName('stop');

      if (!stops.length || !stops[stops.length - 1].attributes || !stops[stops.length - 1].attributes['stop-color']) {
        return null;
      }

      return stops[stops.length - 1].attributes['stop-color'].value;
    }
  } // Transform points
  // Emulate SVG's gradientUnits="objectBoundingBox"


  if (scaleToBounds) {
    const boundsMatrix = Matrix.compose(Matrix.translate(bbox.x, bbox.y), Matrix.scale(bbox.width, bbox.height));
    origin = Matrix.applyToPoint(boundsMatrix, origin);
    if (destination) destination = Matrix.applyToPoint(boundsMatrix, destination);

    if (radius) {
      radius = _quadraticMean(bbox.width, bbox.height) * radius;
    }

    if (focal) focal = Matrix.applyToPoint(boundsMatrix, focal);
  }

  if (radial) {
    origin = Matrix.applyToPoint(matrix, origin);

    const matrixScale = _getScaleFactor(matrix);

    radius = _quadraticMean(matrixScale.x, matrixScale.y) * radius;
    if (focal) focal = Matrix.applyToPoint(matrix, focal);
  } else {
    const dot = (a, b) => a.x * b.x + a.y * b.y;

    const multiply = (coefficient, v) => ({
      x: coefficient * v.x,
      y: coefficient * v.y
    });

    const add = (a, b) => ({
      x: a.x + b.x,
      y: a.y + b.y
    });

    const subtract = (a, b) => ({
      x: a.x - b.x,
      y: a.y - b.y
    }); // The line through origin and gradientPerpendicular is the line at which the gradient starts


    let gradientPerpendicular = Math.abs(origin.x - destination.x) < 1e-8 ? add(origin, {
      x: 1,
      y: (origin.x - destination.x) / (destination.y - origin.y)
    }) : add(origin, {
      x: (destination.y - origin.y) / (origin.x - destination.x),
      y: 1
    }); // Transform points

    gradientPerpendicular = Matrix.applyToPoint(matrix, gradientPerpendicular);
    origin = Matrix.applyToPoint(matrix, origin);
    destination = Matrix.applyToPoint(matrix, destination); // Calculate the direction that the gradient has changed to

    const originToPerpendicular = subtract(gradientPerpendicular, origin);
    const originToDestination = subtract(destination, origin);
    const gradientDirection = Math.abs(originToPerpendicular.x) < 1e-8 ? {
      x: 1,
      y: -originToPerpendicular.x / originToPerpendicular.y
    } : {
      x: -originToPerpendicular.y / originToPerpendicular.x,
      y: 1
    }; // Set the destination so that the gradient moves in the correct direction, by projecting the destination vector
    // onto the gradient direction vector

    const projectionCoeff = dot(originToDestination, gradientDirection) / dot(gradientDirection, gradientDirection);
    const projection = multiply(projectionCoeff, gradientDirection);
    destination = {
      x: origin.x + projection.x,
      y: origin.y + projection.y
    };
  } // Put values back into svg


  if (radial) {
    newGradient.setAttribute('cx', Number(origin.x.toFixed(4)));
    newGradient.setAttribute('cy', Number(origin.y.toFixed(4)));
    newGradient.setAttribute('r', Number(radius.toFixed(4)));

    if (focal) {
      newGradient.setAttribute('fx', Number(focal.x.toFixed(4)));
      newGradient.setAttribute('fy', Number(focal.y.toFixed(4)));
    }
  } else {
    newGradient.setAttribute('x1', Number(origin.x.toFixed(4)));
    newGradient.setAttribute('y1', Number(origin.y.toFixed(4)));
    newGradient.setAttribute('x2', Number(destination.x.toFixed(4)));
    newGradient.setAttribute('y2', Number(destination.y.toFixed(4)));
  }

  newGradient.setAttribute('gradientUnits', 'userSpaceOnUse');
  defs.appendChild(newGradient);
  return "url(#".concat(newGradientId, ")");
}; // Adapted from paper.js's SvgImport.getDefinition


const _parseUrl = (value, windowRef) => {
  // When url() comes from a style property, '#'' seems to be missing on
  // WebKit. We also get variations of quotes or no quotes, single or
  // double, so handle it all with one regular expression:
  const match = value && value.match(/\((?:["'#]*)([^"')]+)/);
  const name = match && match[1];
  const res = name && windowRef ? // This is required by Firefox, which can produce absolute
  // urls for local gradients, see paperjs#1001:
  name.replace("".concat(windowRef.location.href.split('#')[0], "#"), '') : name;
  return res;
};
/**
 * Scratch 2.0 displays stroke widths in a "normalized" way, that is,
 * if a shape with a stroke width has a transform applied, it will be
 * rendered with a stroke that is the same width all the way around,
 * instead of stretched looking.
 *
 * The vector paint editor also prefers to normalize the stroke width,
 * rather than keep track of transforms at the group level, as this
 * simplifies editing (e.g. stroke width 3 always means the same thickness)
 *
 * This function performs that normalization process, pushing transforms
 * on groups down to the leaf level and averaging out the stroke width
 * around the shapes. Note that this doens't just change stroke widths, it
 * changes path data and attributes throughout the SVG.
 *
 * @param {SVGElement} svgTag The SVG dom object
 * @param {Window} windowRef The window to use. Need to pass in for
 *     tests to work, as they get angry at even the mention of window.
 * @param {object} bboxForTesting The bounds to use. Need to pass in for
 *     tests only, because getBBox doesn't work in Node. This should
 *     be the bounds of the svgTag without including stroke width or transforms.
 * @return {void}
 */


const transformStrokeWidths = function transformStrokeWidths(svgTag, windowRef, bboxForTesting) {
  const inherited = Matrix.identity();

  const applyTransforms = (element, matrix, strokeWidth, fill, stroke) => {
    if (_isContainerElement(element)) {
      // Push fills and stroke width down to leaves
      if (element.attributes['stroke-width']) {
        strokeWidth = element.attributes['stroke-width'].value;
      }

      if (element.attributes) {
        if (element.attributes.fill) fill = element.attributes.fill.value;
        if (element.attributes.stroke) stroke = element.attributes.stroke.value;
      } // If any child nodes don't take attributes, leave the attributes
      // at the parent level.


      for (let i = 0; i < element.childNodes.length; i++) {
        applyTransforms(element.childNodes[i], Matrix.compose(matrix, _parseTransform(element)), strokeWidth, fill, stroke);
      }

      element.removeAttribute('transform');
      element.removeAttribute('stroke-width');
      element.removeAttribute('fill');
      element.removeAttribute('stroke');
    } else if (_isPathWithTransformAndStroke(element, strokeWidth)) {
      if (element.attributes['stroke-width']) {
        strokeWidth = element.attributes['stroke-width'].value;
      }

      if (element.attributes.fill) fill = element.attributes.fill.value;
      if (element.attributes.stroke) stroke = element.attributes.stroke.value;
      matrix = Matrix.compose(matrix, _parseTransform(element));

      if (Matrix.toString(matrix) === Matrix.toString(Matrix.identity())) {
        element.removeAttribute('transform');
        element.setAttribute('stroke-width', strokeWidth);
        if (fill) element.setAttribute('fill', fill);
        if (stroke) element.setAttribute('stroke', stroke);
        return;
      } // Transform gradient


      const fillGradientId = _parseUrl(fill, windowRef);

      const strokeGradientId = _parseUrl(stroke, windowRef);

      if (fillGradientId || strokeGradientId) {
        const doc = windowRef.document; // Need path bounds to transform gradient

        const svgSpot = doc.createElement('span');
        let bbox;

        if (bboxForTesting) {
          bbox = bboxForTesting;
        } else {
          try {
            doc.body.appendChild(svgSpot);
            const svg = SvgElement.set(doc.createElementNS(SvgElement.svg, 'svg'));
            const path = SvgElement.set(doc.createElementNS(SvgElement.svg, 'path'));
            path.setAttribute('d', element.attributes.d.value);
            svg.appendChild(path);
            svgSpot.appendChild(svg); // Take the bounding box.

            bbox = svg.getBBox();
          } finally {
            // Always destroy the element, even if, for example, getBBox throws.
            doc.body.removeChild(svgSpot);
          }
        }

        if (fillGradientId) {
          const newFillRef = _createGradient(fillGradientId, svgTag, bbox, matrix);

          if (newFillRef) fill = newFillRef;
        }

        if (strokeGradientId) {
          const newStrokeRef = _createGradient(strokeGradientId, svgTag, bbox, matrix);

          if (newStrokeRef) stroke = newStrokeRef;
        }
      } // Transform path data


      element.setAttribute('d', _transformPath(element.attributes.d.value, matrix));
      element.removeAttribute('transform'); // Transform stroke width

      const matrixScale = _getScaleFactor(matrix);

      element.setAttribute('stroke-width', _quadraticMean(matrixScale.x, matrixScale.y) * strokeWidth);
      if (fill) element.setAttribute('fill', fill);
      if (stroke) element.setAttribute('stroke', stroke);
    } else if (_isGraphicsElement(element)) {
      // Push stroke width, fill, and stroke down to leaves
      if (strokeWidth && !element.attributes['stroke-width']) {
        element.setAttribute('stroke-width', strokeWidth);
      }

      if (fill && !element.attributes.fill) {
        element.setAttribute('fill', fill);
      }

      if (stroke && !element.attributes.stroke) {
        element.setAttribute('stroke', stroke);
      } // Push transform down to leaves


      matrix = Matrix.compose(matrix, _parseTransform(element));

      if (Matrix.toString(matrix) === Matrix.toString(Matrix.identity())) {
        element.removeAttribute('transform');
      } else {
        element.setAttribute('transform', Matrix.toString(matrix));
      }
    }
  };

  applyTransforms(svgTag, inherited, 1
  /* default SVG stroke width */
  );
};

module.exports = transformStrokeWidths;

/***/ }),

/***/ "./node_modules/scratch-svg-renderer/src/util/log.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-svg-renderer/src/util/log.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const minilog = __webpack_require__(/*! minilog */ "./node_modules/minilog/lib/web/index.js");

minilog.enable();
module.exports = minilog('scratch-svg-render');

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_control.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_control.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

class Scratch3ControlBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * The "counter" block value. For compatibility with 2.0.
     * @type {number}
     */

    this._counter = 0;
    this.runtime.on('RUNTIME_DISPOSED', this.clearCounter.bind(this));
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      control_repeat: this.repeat,
      control_repeat_until: this.repeatUntil,
      control_while: this.repeatWhile,
      control_for_each: this.forEach,
      control_forever: this.forever,
      control_wait: this.wait,
      control_wait_until: this.waitUntil,
      control_if: this.if,
      control_if_else: this.ifElse,
      control_stop: this.stop,
      control_create_clone_of: this.createClone,
      control_delete_this_clone: this.deleteClone,
      control_get_counter: this.getCounter,
      control_incr_counter: this.incrCounter,
      control_clear_counter: this.clearCounter,
      control_all_at_once: this.allAtOnce
    };
  }

  getHats() {
    return {
      control_start_as_clone: {
        restartExistingThreads: false
      }
    };
  }

  repeat(args, util) {
    const times = Math.round(Cast.toNumber(args.TIMES)); // Initialize loop

    if (typeof util.stackFrame.loopCounter === 'undefined') {
      util.stackFrame.loopCounter = times;
    } // Only execute once per frame.
    // When the branch finishes, `repeat` will be executed again and
    // the second branch will be taken, yielding for the rest of the frame.
    // Decrease counter


    util.stackFrame.loopCounter--; // If we still have some left, start the branch.

    if (util.stackFrame.loopCounter >= 0) {
      util.startBranch(1, true);
    }
  }

  repeatUntil(args, util) {
    const condition = Cast.toBoolean(args.CONDITION); // If the condition is false (repeat UNTIL), start the branch.

    if (!condition) {
      util.startBranch(1, true);
    }
  }

  repeatWhile(args, util) {
    const condition = Cast.toBoolean(args.CONDITION); // If the condition is true (repeat WHILE), start the branch.

    if (condition) {
      util.startBranch(1, true);
    }
  }

  forEach(args, util) {
    const variable = util.target.lookupOrCreateVariable(args.VARIABLE.id, args.VARIABLE.name);

    if (typeof util.stackFrame.index === 'undefined') {
      util.stackFrame.index = 0;
    }

    if (util.stackFrame.index < Number(args.VALUE)) {
      util.stackFrame.index++;
      variable.value = util.stackFrame.index;
      util.startBranch(1, true);
    }
  }

  waitUntil(args, util) {
    const condition = Cast.toBoolean(args.CONDITION);

    if (!condition) {
      util.yield();
    }
  }

  forever(args, util) {
    util.startBranch(1, true);
  }

  wait(args, util) {
    if (util.stackTimerNeedsInit()) {
      const duration = Math.max(0, 1000 * Cast.toNumber(args.DURATION));
      util.startStackTimer(duration);
      this.runtime.requestRedraw();
      util.yield();
    } else if (!util.stackTimerFinished()) {
      util.yield();
    }
  }

  if(args, util) {
    const condition = Cast.toBoolean(args.CONDITION);

    if (condition) {
      util.startBranch(1, false);
    }
  }

  ifElse(args, util) {
    const condition = Cast.toBoolean(args.CONDITION);

    if (condition) {
      util.startBranch(1, false);
    } else {
      util.startBranch(2, false);
    }
  }

  stop(args, util) {
    const option = args.STOP_OPTION;

    if (option === 'all') {
      util.stopAll();
    } else if (option === 'other scripts in sprite' || option === 'other scripts in stage') {
      util.stopOtherTargetThreads();
    } else if (option === 'this script') {
      util.stopThisScript();
    }
  }

  createClone(args, util) {
    this._createClone(Cast.toString(args.CLONE_OPTION), util.target);
  }

  _createClone(cloneOption, target) {
    // used by compiler
    // Set clone target
    let cloneTarget;

    if (cloneOption === '_myself_') {
      cloneTarget = target;
    } else {
      cloneTarget = this.runtime.getSpriteTargetByName(cloneOption);
    } // If clone target is not found, return


    if (!cloneTarget) return; // Create clone

    const newClone = cloneTarget.makeClone();

    if (newClone) {
      this.runtime.addTarget(newClone); // Place behind the original target.

      newClone.goBehindOther(cloneTarget);
    }
  }

  deleteClone(args, util) {
    if (util.target.isOriginal) return;
    this.runtime.disposeTarget(util.target);
    this.runtime.stopForTarget(util.target);
  }

  getCounter() {
    return this._counter;
  }

  clearCounter() {
    this._counter = 0;
  }

  incrCounter() {
    this._counter++;
  }

  allAtOnce(args, util) {
    // Since the "all at once" block is implemented for compatiblity with
    // Scratch 2.0 projects, it behaves the same way it did in 2.0, which
    // is to simply run the contained script (like "if 1 = 1").
    // (In early versions of Scratch 2.0, it would work the same way as
    // "run without screen refresh" custom blocks do now, but this was
    // removed before the release of 2.0.)
    util.startBranch(1, false);
  }

}

module.exports = Scratch3ControlBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_core_example.js":
/*!*********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_core_example.js ***!
  \*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const BlockType = __webpack_require__(/*! ../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const ArgumentType = __webpack_require__(/*! ../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");
/* eslint-disable-next-line max-len */


const blockIconURI = 'data:image/svg+xml,%3Csvg id="rotate-counter-clockwise" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill:%233d79cc;%7D.cls-2%7Bfill:%23fff;%7D%3C/style%3E%3C/defs%3E%3Ctitle%3Erotate-counter-clockwise%3C/title%3E%3Cpath class="cls-1" d="M22.68,12.2a1.6,1.6,0,0,1-1.27.63H13.72a1.59,1.59,0,0,1-1.16-2.58l1.12-1.41a4.82,4.82,0,0,0-3.14-.77,4.31,4.31,0,0,0-2,.8,4.25,4.25,0,0,0-1.34,1.73,5.06,5.06,0,0,0,.54,4.62A5.58,5.58,0,0,0,12,17.74h0a2.26,2.26,0,0,1-.16,4.52A10.25,10.25,0,0,1,3.74,18,10.14,10.14,0,0,1,2.25,8.78,9.7,9.7,0,0,1,5.08,4.64,9.92,9.92,0,0,1,9.66,2.5a10.66,10.66,0,0,1,7.72,1.68l1.08-1.35a1.57,1.57,0,0,1,1.24-.6,1.6,1.6,0,0,1,1.54,1.21l1.7,7.37A1.57,1.57,0,0,1,22.68,12.2Z"/%3E%3Cpath class="cls-2" d="M21.38,11.83H13.77a.59.59,0,0,1-.43-1l1.75-2.19a5.9,5.9,0,0,0-4.7-1.58,5.07,5.07,0,0,0-4.11,3.17A6,6,0,0,0,7,15.77a6.51,6.51,0,0,0,5,2.92,1.31,1.31,0,0,1-.08,2.62,9.3,9.3,0,0,1-7.35-3.82A9.16,9.16,0,0,1,3.17,9.12,8.51,8.51,0,0,1,5.71,5.4,8.76,8.76,0,0,1,9.82,3.48a9.71,9.71,0,0,1,7.75,2.07l1.67-2.1a.59.59,0,0,1,1,.21L22,11.08A.59.59,0,0,1,21.38,11.83Z"/%3E%3C/svg%3E';
/**
 * An example core block implemented using the extension spec.
 * This is not loaded as part of the core blocks in the VM but it is provided
 * and used as part of tests.
 */

class Scratch3CoreExample {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: 'coreExample',
      name: 'CoreEx',
      // This string does not need to be translated as this extension is only used as an example.
      blocks: [{
        func: 'MAKE_A_VARIABLE',
        blockType: BlockType.BUTTON,
        text: 'make a variable (CoreEx)'
      }, {
        opcode: 'exampleOpcode',
        blockType: BlockType.REPORTER,
        text: 'example block'
      }, {
        opcode: 'exampleWithInlineImage',
        blockType: BlockType.COMMAND,
        text: 'block with image [CLOCKWISE] inline',
        arguments: {
          CLOCKWISE: {
            type: ArgumentType.IMAGE,
            dataURI: blockIconURI
          }
        }
      }]
    };
  }
  /**
   * Example opcode just returns the name of the stage target.
   * @returns {string} The name of the first target in the project.
   */


  exampleOpcode() {
    const stage = this.runtime.getTargetForStage();
    return stage ? stage.getName() : 'no stage yet';
  }

  exampleWithInlineImage() {
    return;
  }

}

module.exports = Scratch3CoreExample;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_data.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_data.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

class Scratch3DataBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      data_variable: this.getVariable,
      data_setvariableto: this.setVariableTo,
      data_changevariableby: this.changeVariableBy,
      data_hidevariable: this.hideVariable,
      data_showvariable: this.showVariable,
      data_listcontents: this.getListContents,
      data_addtolist: this.addToList,
      data_deleteoflist: this.deleteOfList,
      data_deletealloflist: this.deleteAllOfList,
      data_insertatlist: this.insertAtList,
      data_replaceitemoflist: this.replaceItemOfList,
      data_itemoflist: this.getItemOfList,
      data_itemnumoflist: this.getItemNumOfList,
      data_lengthoflist: this.lengthOfList,
      data_listcontainsitem: this.listContainsItem,
      data_hidelist: this.hideList,
      data_showlist: this.showList
    };
  }

  getVariable(args, util) {
    const variable = util.target.lookupOrCreateVariable(args.VARIABLE.id, args.VARIABLE.name);
    return variable.value;
  }

  setVariableTo(args, util) {
    const variable = util.target.lookupOrCreateVariable(args.VARIABLE.id, args.VARIABLE.name);
    variable.value = args.VALUE;

    if (variable.isCloud) {
      util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, args.VALUE]);
    }
  }

  changeVariableBy(args, util) {
    const variable = util.target.lookupOrCreateVariable(args.VARIABLE.id, args.VARIABLE.name);
    const castedValue = Cast.toNumber(variable.value);
    const dValue = Cast.toNumber(args.VALUE);
    const newValue = castedValue + dValue;
    variable.value = newValue;

    if (variable.isCloud) {
      util.ioQuery('cloud', 'requestUpdateVariable', [variable.name, newValue]);
    }
  }

  changeMonitorVisibility(id, visible) {
    // Send the monitor blocks an event like the flyout checkbox event.
    // This both updates the monitor state and changes the isMonitored block flag.
    this.runtime.monitorBlocks.changeBlock({
      id: id,
      // Monitor blocks for variables are the variable ID.
      element: 'checkbox',
      // Mimic checkbox event from flyout.
      value: visible
    }, this.runtime);
  }

  showVariable(args) {
    this.changeMonitorVisibility(args.VARIABLE.id, true);
  }

  hideVariable(args) {
    this.changeMonitorVisibility(args.VARIABLE.id, false);
  }

  showList(args) {
    this.changeMonitorVisibility(args.LIST.id, true);
  }

  hideList(args) {
    this.changeMonitorVisibility(args.LIST.id, false);
  }

  getListContents(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name); // If block is running for monitors, return copy of list as an array if changed.

    if (util.thread.updateMonitor) {
      // Return original list value if up-to-date, which doesn't trigger monitor update.
      if (list._monitorUpToDate) return list.value; // If value changed, reset the flag and return a copy to trigger monitor update.
      // Because monitors use Immutable data structures, only new objects trigger updates.

      list._monitorUpToDate = true;
      return list.value.slice();
    } // Determine if the list is all single letters.
    // If it is, report contents joined together with no separator.
    // If it's not, report contents joined together with a space.


    let allSingleLetters = true;

    for (let i = 0; i < list.value.length; i++) {
      const listItem = list.value[i];

      if (!(typeof listItem === 'string' && listItem.length === 1)) {
        allSingleLetters = false;
        break;
      }
    }

    if (allSingleLetters) {
      return list.value.join('');
    }

    return list.value.join(' ');
  }

  addToList(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    list.value.push(args.ITEM);
    list._monitorUpToDate = false;
  }

  deleteOfList(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    const index = Cast.toListIndex(args.INDEX, list.value.length, true);

    if (index === Cast.LIST_INVALID) {
      return;
    } else if (index === Cast.LIST_ALL) {
      list.value = [];
      return;
    }

    list.value.splice(index - 1, 1);
    list._monitorUpToDate = false;
  }

  deleteAllOfList(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    list.value = [];
    return;
  }

  insertAtList(args, util) {
    const item = args.ITEM;
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    const index = Cast.toListIndex(args.INDEX, list.value.length + 1, false);

    if (index === Cast.LIST_INVALID) {
      return;
    }

    list.value.splice(index - 1, 0, item);
    list._monitorUpToDate = false;
  }

  replaceItemOfList(args, util) {
    const item = args.ITEM;
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    const index = Cast.toListIndex(args.INDEX, list.value.length, false);

    if (index === Cast.LIST_INVALID) {
      return;
    }

    list.value[index - 1] = item;
    list._monitorUpToDate = false;
  }

  getItemOfList(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    const index = Cast.toListIndex(args.INDEX, list.value.length, false);

    if (index === Cast.LIST_INVALID) {
      return '';
    }

    return list.value[index - 1];
  }

  getItemNumOfList(args, util) {
    const item = args.ITEM;
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name); // Go through the list items one-by-one using Cast.compare. This is for
    // cases like checking if 123 is contained in a list [4, 7, '123'] --
    // Scratch considers 123 and '123' to be equal.

    for (let i = 0; i < list.value.length; i++) {
      if (Cast.compare(list.value[i], item) === 0) {
        return i + 1;
      }
    } // We don't bother using .indexOf() at all, because it would end up with
    // edge cases such as the index of '123' in [4, 7, 123, '123', 9].
    // If we use indexOf(), this block would return 4 instead of 3, because
    // indexOf() sees the first occurence of the string 123 as the fourth
    // item in the list. With Scratch, this would be confusing -- after all,
    // '123' and 123 look the same, so one would expect the block to say
    // that the first occurrence of '123' (or 123) to be the third item.
    // Default to 0 if there's no match. Since Scratch lists are 1-indexed,
    // we don't have to worry about this conflicting with the "this item is
    // the first value" number (in JS that is 0, but in Scratch it's 1).


    return 0;
  }

  lengthOfList(args, util) {
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);
    return list.value.length;
  }

  listContainsItem(args, util) {
    const item = args.ITEM;
    const list = util.target.lookupOrCreateList(args.LIST.id, args.LIST.name);

    if (list.value.indexOf(item) >= 0) {
      return true;
    } // Try using Scratch comparison operator on each item.
    // (Scratch considers the string '123' equal to the number 123).


    for (let i = 0; i < list.value.length; i++) {
      if (Cast.compare(list.value[i], item) === 0) {
        return true;
      }
    }

    return false;
  }

}

module.exports = Scratch3DataBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_event.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_event.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

class Scratch3EventBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    this.runtime.on('KEY_PRESSED', key => {
      this.runtime.startHats('event_whenkeypressed', {
        KEY_OPTION: key
      });
      this.runtime.startHats('event_whenkeypressed', {
        KEY_OPTION: 'any'
      });
    });
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      event_whentouchingobject: this.touchingObject,
      event_broadcast: this.broadcast,
      event_broadcastandwait: this.broadcastAndWait,
      event_whengreaterthan: this.hatGreaterThanPredicate
    };
  }

  getHats() {
    return {
      event_whenflagclicked: {
        restartExistingThreads: true
      },
      event_whenkeypressed: {
        restartExistingThreads: false
      },
      event_whenthisspriteclicked: {
        restartExistingThreads: true
      },
      event_whentouchingobject: {
        restartExistingThreads: false,
        edgeActivated: true
      },
      event_whenstageclicked: {
        restartExistingThreads: true
      },
      event_whenbackdropswitchesto: {
        restartExistingThreads: true
      },
      event_whengreaterthan: {
        restartExistingThreads: false,
        edgeActivated: true
      },
      event_whenbroadcastreceived: {
        restartExistingThreads: true
      }
    };
  }

  touchingObject(args, util) {
    return util.target.isTouchingObject(args.TOUCHINGOBJECTMENU);
  }

  hatGreaterThanPredicate(args, util) {
    const option = Cast.toString(args.WHENGREATERTHANMENU).toLowerCase();
    const value = Cast.toNumber(args.VALUE);

    switch (option) {
      case 'timer':
        return util.ioQuery('clock', 'projectTimer') > value;

      case 'loudness':
        return this.runtime.audioEngine && this.runtime.audioEngine.getLoudness() > value;
    }

    return false;
  }

  broadcast(args, util) {
    const broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg(args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);

    if (broadcastVar) {
      const broadcastOption = broadcastVar.name;
      util.startHats('event_whenbroadcastreceived', {
        BROADCAST_OPTION: broadcastOption
      });
    }
  }

  broadcastAndWait(args, util) {
    if (!util.stackFrame.broadcastVar) {
      util.stackFrame.broadcastVar = util.runtime.getTargetForStage().lookupBroadcastMsg(args.BROADCAST_OPTION.id, args.BROADCAST_OPTION.name);
    }

    if (util.stackFrame.broadcastVar) {
      const broadcastOption = util.stackFrame.broadcastVar.name; // Have we run before, starting threads?

      if (!util.stackFrame.startedThreads) {
        // No - start hats for this broadcast.
        util.stackFrame.startedThreads = util.startHats('event_whenbroadcastreceived', {
          BROADCAST_OPTION: broadcastOption
        });

        if (util.stackFrame.startedThreads.length === 0) {
          // Nothing was started.
          return;
        }
      } // We've run before; check if the wait is still going on.


      const instance = this; // Scratch 2 considers threads to be waiting if they are still in
      // runtime.threads. Threads that have run all their blocks, or are
      // marked done but still in runtime.threads are still considered to
      // be waiting.

      const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.threads.indexOf(thread) !== -1);

      if (waiting) {
        // If all threads are waiting for the next tick or later yield
        // for a tick as well. Otherwise yield until the next loop of
        // the threads.
        if (util.stackFrame.startedThreads.every(thread => instance.runtime.isWaitingThread(thread))) {
          util.yieldTick();
        } else {
          util.yield();
        }
      }
    }
  }

}

module.exports = Scratch3EventBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_looks.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_looks.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Clone = __webpack_require__(/*! ../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const RenderedTarget = __webpack_require__(/*! ../sprites/rendered-target */ "./node_modules/scratch-vm/src/sprites/rendered-target.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

const getMonitorIdForBlockWithArgs = __webpack_require__(/*! ../util/get-monitor-id */ "./node_modules/scratch-vm/src/util/get-monitor-id.js");

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");
/**
 * @typedef {object} BubbleState - the bubble state associated with a particular target.
 * @property {Boolean} onSpriteRight - tracks whether the bubble is right or left of the sprite.
 * @property {?int} drawableId - the ID of the associated bubble Drawable, null if none.
 * @property {string} text - the text of the bubble.
 * @property {string} type - the type of the bubble, "say" or "think"
 * @property {?string} usageId - ID indicating the most recent usage of the say/think bubble.
 *      Used for comparison when determining whether to clear a say/think bubble.
 */


class Scratch3LooksBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    this._onTargetChanged = this._onTargetChanged.bind(this);
    this._onResetBubbles = this._onResetBubbles.bind(this);
    this._onTargetWillExit = this._onTargetWillExit.bind(this);
    this._updateBubble = this._updateBubble.bind(this); // Reset all bubbles on start/stop

    this.runtime.on('PROJECT_STOP_ALL', this._onResetBubbles);
    this.runtime.on('targetWasRemoved', this._onTargetWillExit); // Enable other blocks to use bubbles like ask/answer

    this.runtime.on(Scratch3LooksBlocks.SAY_OR_THINK, this._updateBubble);
  }
  /**
   * The default bubble state, to be used when a target has no existing bubble state.
   * @type {BubbleState}
   */


  static get DEFAULT_BUBBLE_STATE() {
    return {
      drawableId: null,
      onSpriteRight: true,
      skinId: null,
      text: '',
      type: 'say',
      usageId: null
    };
  }
  /**
   * The key to load & store a target's bubble-related state.
   * @type {string}
   */


  static get STATE_KEY() {
    return 'Scratch.looks';
  }
  /**
   * Event name for a text bubble being created or updated.
   * @const {string}
   */


  static get SAY_OR_THINK() {
    // There are currently many places in the codebase which explicitly refer to this event by the string 'SAY',
    // so keep this as the string 'SAY' for now rather than changing it to 'SAY_OR_THINK' and breaking things.
    return 'SAY';
  }
  /**
   * Limit for say bubble string.
   * @const {string}
   */


  static get SAY_BUBBLE_LIMIT() {
    return 330;
  }
  /**
   * Limit for ghost effect
   * @const {object}
   */


  static get EFFECT_GHOST_LIMIT() {
    return {
      min: 0,
      max: 100
    };
  }
  /**
   * Limit for brightness effect
   * @const {object}
   */


  static get EFFECT_BRIGHTNESS_LIMIT() {
    return {
      min: -100,
      max: 100
    };
  }
  /**
   * @param {Target} target - collect bubble state for this target. Probably, but not necessarily, a RenderedTarget.
   * @returns {BubbleState} the mutable bubble state associated with that target. This will be created if necessary.
   * @private
   */


  _getBubbleState(target) {
    let bubbleState = target.getCustomState(Scratch3LooksBlocks.STATE_KEY);

    if (!bubbleState) {
      bubbleState = Clone.simple(Scratch3LooksBlocks.DEFAULT_BUBBLE_STATE);
      target.setCustomState(Scratch3LooksBlocks.STATE_KEY, bubbleState);
    }

    return bubbleState;
  }
  /**
   * Handle a target which has moved.
   * @param {RenderedTarget} target - the target which has moved.
   * @private
   */


  _onTargetChanged(target) {
    const bubbleState = this._getBubbleState(target);

    if (bubbleState.drawableId) {
      this._positionBubble(target);
    }
  }
  /**
   * Handle a target which is exiting.
   * @param {RenderedTarget} target - the target.
   * @private
   */


  _onTargetWillExit(target) {
    const bubbleState = this._getBubbleState(target);

    if (bubbleState.drawableId && bubbleState.skinId) {
      this.runtime.renderer.destroyDrawable(bubbleState.drawableId, StageLayering.SPRITE_LAYER);
      this.runtime.renderer.destroySkin(bubbleState.skinId);
      bubbleState.drawableId = null;
      bubbleState.skinId = null;
      this.runtime.requestRedraw();
    }

    target.onTargetVisualChange = null;
  }
  /**
   * Handle project start/stop by clearing all visible bubbles.
   * @private
   */


  _onResetBubbles() {
    for (let n = 0; n < this.runtime.targets.length; n++) {
      const bubbleState = this._getBubbleState(this.runtime.targets[n]);

      bubbleState.text = '';

      this._onTargetWillExit(this.runtime.targets[n]);
    }

    clearTimeout(this._bubbleTimeout);
  }
  /**
   * Position the bubble of a target. If it doesn't fit on the specified side, flip and rerender.
   * @param {!Target} target Target whose bubble needs positioning.
   * @private
   */


  _positionBubble(target) {
    if (!target.visible) return;

    const bubbleState = this._getBubbleState(target);

    const [bubbleWidth, bubbleHeight] = this.runtime.renderer.getCurrentSkinSize(bubbleState.drawableId);
    let targetBounds;

    try {
      targetBounds = target.getBoundsForBubble();
    } catch (error_) {
      // Bounds calculation could fail (e.g. on empty costumes), in that case
      // use the x/y position of the target.
      targetBounds = {
        left: target.x,
        right: target.x,
        top: target.y,
        bottom: target.y
      };
    }

    const stageSize = this.runtime.renderer.getNativeSize();
    const stageBounds = {
      left: -stageSize[0] / 2,
      right: stageSize[0] / 2,
      top: stageSize[1] / 2,
      bottom: -stageSize[1] / 2
    };

    if (bubbleState.onSpriteRight && bubbleWidth + targetBounds.right > stageBounds.right && targetBounds.left - bubbleWidth > stageBounds.left) {
      // Only flip if it would fit
      bubbleState.onSpriteRight = false;

      this._renderBubble(target);
    } else if (!bubbleState.onSpriteRight && targetBounds.left - bubbleWidth < stageBounds.left && bubbleWidth + targetBounds.right < stageBounds.right) {
      // Only flip if it would fit
      bubbleState.onSpriteRight = true;

      this._renderBubble(target);
    } else {
      this.runtime.renderer.updateDrawablePosition(bubbleState.drawableId, [bubbleState.onSpriteRight ? Math.max(stageBounds.left, // Bubble should not extend past left edge of stage
      Math.min(stageBounds.right - bubbleWidth, targetBounds.right)) : Math.min(stageBounds.right - bubbleWidth, // Bubble should not extend past right edge of stage
      Math.max(stageBounds.left, targetBounds.left - bubbleWidth)), // Bubble should not extend past the top of the stage
      Math.min(stageBounds.top, targetBounds.bottom + bubbleHeight)]);
      this.runtime.requestRedraw();
    }
  }
  /**
   * Create a visible bubble for a target. If a bubble exists for the target,
   * just set it to visible and update the type/text. Otherwise create a new
   * bubble and update the relevant custom state.
   * @param {!Target} target Target who needs a bubble.
   * @return {undefined} Early return if text is empty string.
   * @private
   */


  _renderBubble(target) {
    // used by compiler
    if (!this.runtime.renderer) return;

    const bubbleState = this._getBubbleState(target);

    const {
      type,
      text,
      onSpriteRight
    } = bubbleState; // Remove the bubble if target is not visible, or text is being set to blank.

    if (!target.visible || text === '') {
      this._onTargetWillExit(target);

      return;
    }

    if (bubbleState.skinId) {
      this.runtime.renderer.updateTextSkin(bubbleState.skinId, type, text, onSpriteRight, [0, 0]);
    } else {
      target.onTargetVisualChange = this._onTargetChanged;
      bubbleState.drawableId = this.runtime.renderer.createDrawable(StageLayering.SPRITE_LAYER);
      bubbleState.skinId = this.runtime.renderer.createTextSkin(type, text, bubbleState.onSpriteRight, [0, 0]);
      this.runtime.renderer.updateDrawableSkinId(bubbleState.drawableId, bubbleState.skinId);
    }

    this._positionBubble(target);
  }
  /**
   * Properly format text for a text bubble.
   * @param {string} text The text to be formatted
   * @return {string} The formatted text
   * @private
   */


  _formatBubbleText(text) {
    if (text === '') return text; // Non-integers should be rounded to 2 decimal places (no more, no less), unless they're small enough that
    // rounding would display them as 0.00. This matches 2.0's behavior:
    // https://github.com/LLK/scratch-flash/blob/2e4a402ceb205a042887f54b26eebe1c2e6da6c0/src/scratch/ScratchSprite.as#L579-L585

    if (typeof text === 'number' && Math.abs(text) >= 0.01 && text % 1 !== 0) {
      text = text.toFixed(2);
    } // Limit the length of the string.


    text = String(text).substr(0, Scratch3LooksBlocks.SAY_BUBBLE_LIMIT);
    return text;
  }
  /**
   * The entry point for say/think blocks. Clears existing bubble if the text is empty.
   * Set the bubble custom state and then call _renderBubble.
   * @param {!Target} target Target that say/think blocks are being called on.
   * @param {!string} type Either "say" or "think"
   * @param {!string} text The text for the bubble, empty string clears the bubble.
   * @private
   */


  _updateBubble(target, type, text) {
    const bubbleState = this._getBubbleState(target);

    bubbleState.type = type;
    bubbleState.text = this._formatBubbleText(text);
    bubbleState.usageId = uid();

    this._renderBubble(target);
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      looks_say: this.say,
      looks_sayforsecs: this.sayforsecs,
      looks_think: this.think,
      looks_thinkforsecs: this.thinkforsecs,
      looks_show: this.show,
      looks_hide: this.hide,
      looks_hideallsprites: () => {},
      // legacy no-op block
      looks_switchcostumeto: this.switchCostume,
      looks_switchbackdropto: this.switchBackdrop,
      looks_switchbackdroptoandwait: this.switchBackdropAndWait,
      looks_nextcostume: this.nextCostume,
      looks_nextbackdrop: this.nextBackdrop,
      looks_changeeffectby: this.changeEffect,
      looks_seteffectto: this.setEffect,
      looks_cleargraphiceffects: this.clearEffects,
      looks_changesizeby: this.changeSize,
      looks_setsizeto: this.setSize,
      looks_changestretchby: () => {},
      // legacy no-op blocks
      looks_setstretchto: () => {},
      looks_gotofrontback: this.goToFrontBack,
      looks_goforwardbackwardlayers: this.goForwardBackwardLayers,
      looks_size: this.getSize,
      looks_costumenumbername: this.getCostumeNumberName,
      looks_backdropnumbername: this.getBackdropNumberName
    };
  }

  getMonitored() {
    return {
      looks_size: {
        isSpriteSpecific: true,
        getId: targetId => "".concat(targetId, "_size")
      },
      looks_costumenumbername: {
        isSpriteSpecific: true,
        getId: (targetId, fields) => getMonitorIdForBlockWithArgs("".concat(targetId, "_costumenumbername"), fields)
      },
      looks_backdropnumbername: {
        getId: (_, fields) => getMonitorIdForBlockWithArgs('backdropnumbername', fields)
      }
    };
  }

  say(args, util) {
    // @TODO in 2.0 calling say/think resets the right/left bias of the bubble
    const message = args.MESSAGE;

    this._say(message, util.target);
  }

  _say(message, target) {
    // used by compiler
    this.runtime.emit(Scratch3LooksBlocks.SAY_OR_THINK, target, 'say', message);
  }

  sayforsecs(args, util) {
    this.say(args, util);
    const target = util.target;

    const usageId = this._getBubbleState(target).usageId;

    return new Promise(resolve => {
      this._bubbleTimeout = setTimeout(() => {
        this._bubbleTimeout = null; // Clear say bubble if it hasn't been changed and proceed.

        if (this._getBubbleState(target).usageId === usageId) {
          this._updateBubble(target, 'say', '');
        }

        resolve();
      }, 1000 * args.SECS);
    });
  }

  think(args, util) {
    this.runtime.emit(Scratch3LooksBlocks.SAY_OR_THINK, util.target, 'think', args.MESSAGE);
  }

  thinkforsecs(args, util) {
    this.think(args, util);
    const target = util.target;

    const usageId = this._getBubbleState(target).usageId;

    return new Promise(resolve => {
      this._bubbleTimeout = setTimeout(() => {
        this._bubbleTimeout = null; // Clear think bubble if it hasn't been changed and proceed.

        if (this._getBubbleState(target).usageId === usageId) {
          this._updateBubble(target, 'think', '');
        }

        resolve();
      }, 1000 * args.SECS);
    });
  }

  show(args, util) {
    util.target.setVisible(true);

    this._renderBubble(util.target);
  }

  hide(args, util) {
    util.target.setVisible(false);

    this._renderBubble(util.target);
  }
  /**
   * Utility function to set the costume of a target.
   * Matches the behavior of Scratch 2.0 for different types of arguments.
   * @param {!Target} target Target to set costume to.
   * @param {Any} requestedCostume Costume requested, e.g., 0, 'name', etc.
   * @param {boolean=} optZeroIndex Set to zero-index the requestedCostume.
   * @return {Array.<!Thread>} Any threads started by this switch.
   */


  _setCostume(target, requestedCostume, optZeroIndex) {
    // used by compiler
    if (typeof requestedCostume === 'number') {
      // Numbers should be treated as costume indices, always
      target.setCostume(optZeroIndex ? requestedCostume : requestedCostume - 1);
    } else {
      // Strings should be treated as costume names, where possible
      const costumeIndex = target.getCostumeIndexByName(requestedCostume.toString());

      if (costumeIndex !== -1) {
        target.setCostume(costumeIndex);
      } else if (requestedCostume === 'next costume') {
        target.setCostume(target.currentCostume + 1);
      } else if (requestedCostume === 'previous costume') {
        target.setCostume(target.currentCostume - 1); // Try to cast the string to a number (and treat it as a costume index)
        // Pure whitespace should not be treated as a number
        // Note: isNaN will cast the string to a number before checking if it's NaN
      } else if (!(isNaN(requestedCostume) || Cast.isWhiteSpace(requestedCostume))) {
        target.setCostume(optZeroIndex ? Number(requestedCostume) : Number(requestedCostume) - 1);
      }
    } // Per 2.0, 'switch costume' can't start threads even in the Stage.


    return [];
  }
  /**
   * Utility function to set the backdrop of a target.
   * Matches the behavior of Scratch 2.0 for different types of arguments.
   * @param {!Target} stage Target to set backdrop to.
   * @param {Any} requestedBackdrop Backdrop requested, e.g., 0, 'name', etc.
   * @param {boolean=} optZeroIndex Set to zero-index the requestedBackdrop.
   * @return {Array.<!Thread>} Any threads started by this switch.
   */


  _setBackdrop(stage, requestedBackdrop, optZeroIndex) {
    // used by compiler
    if (typeof requestedBackdrop === 'number') {
      // Numbers should be treated as backdrop indices, always
      stage.setCostume(optZeroIndex ? requestedBackdrop : requestedBackdrop - 1);
    } else {
      // Strings should be treated as backdrop names where possible
      const costumeIndex = stage.getCostumeIndexByName(requestedBackdrop.toString());

      if (costumeIndex !== -1) {
        stage.setCostume(costumeIndex);
      } else if (requestedBackdrop === 'next backdrop') {
        stage.setCostume(stage.currentCostume + 1);
      } else if (requestedBackdrop === 'previous backdrop') {
        stage.setCostume(stage.currentCostume - 1);
      } else if (requestedBackdrop === 'random backdrop') {
        const numCostumes = stage.getCostumes().length;

        if (numCostumes > 1) {
          // Don't pick the current backdrop, so that the block
          // will always have an observable effect.
          const lowerBound = 0;
          const upperBound = numCostumes - 1;
          const costumeToExclude = stage.currentCostume;
          const nextCostume = MathUtil.inclusiveRandIntWithout(lowerBound, upperBound, costumeToExclude);
          stage.setCostume(nextCostume);
        } // Try to cast the string to a number (and treat it as a costume index)
        // Pure whitespace should not be treated as a number
        // Note: isNaN will cast the string to a number before checking if it's NaN

      } else if (!(isNaN(requestedBackdrop) || Cast.isWhiteSpace(requestedBackdrop))) {
        stage.setCostume(optZeroIndex ? Number(requestedBackdrop) : Number(requestedBackdrop) - 1);
      }
    }

    const newName = stage.getCostumes()[stage.currentCostume].name;
    return this.runtime.startHats('event_whenbackdropswitchesto', {
      BACKDROP: newName
    });
  }

  switchCostume(args, util) {
    this._setCostume(util.target, args.COSTUME); // used by compiler

  }

  nextCostume(args, util) {
    this._setCostume(util.target, util.target.currentCostume + 1, true);
  }

  switchBackdrop(args) {
    this._setBackdrop(this.runtime.getTargetForStage(), args.BACKDROP);
  }

  switchBackdropAndWait(args, util) {
    // Have we run before, starting threads?
    if (!util.stackFrame.startedThreads) {
      // No - switch the backdrop.
      util.stackFrame.startedThreads = this._setBackdrop(this.runtime.getTargetForStage(), args.BACKDROP);

      if (util.stackFrame.startedThreads.length === 0) {
        // Nothing was started.
        return;
      }
    } // We've run before; check if the wait is still going on.


    const instance = this; // Scratch 2 considers threads to be waiting if they are still in
    // runtime.threads. Threads that have run all their blocks, or are
    // marked done but still in runtime.threads are still considered to
    // be waiting.

    const waiting = util.stackFrame.startedThreads.some(thread => instance.runtime.threads.indexOf(thread) !== -1);

    if (waiting) {
      // If all threads are waiting for the next tick or later yield
      // for a tick as well. Otherwise yield until the next loop of
      // the threads.
      if (util.stackFrame.startedThreads.every(thread => instance.runtime.isWaitingThread(thread))) {
        util.yieldTick();
      } else {
        util.yield();
      }
    }
  }

  nextBackdrop() {
    const stage = this.runtime.getTargetForStage();

    this._setBackdrop(stage, stage.currentCostume + 1, true);
  }

  clampEffect(effect, value) {
    // used by compiler
    let clampedValue = value;

    switch (effect) {
      case 'ghost':
        clampedValue = MathUtil.clamp(value, Scratch3LooksBlocks.EFFECT_GHOST_LIMIT.min, Scratch3LooksBlocks.EFFECT_GHOST_LIMIT.max);
        break;

      case 'brightness':
        clampedValue = MathUtil.clamp(value, Scratch3LooksBlocks.EFFECT_BRIGHTNESS_LIMIT.min, Scratch3LooksBlocks.EFFECT_BRIGHTNESS_LIMIT.max);
        break;
    }

    return clampedValue;
  }

  changeEffect(args, util) {
    const effect = Cast.toString(args.EFFECT).toLowerCase();
    const change = Cast.toNumber(args.CHANGE);
    if (!util.target.effects.hasOwnProperty(effect)) return;
    let newValue = change + util.target.effects[effect];
    newValue = this.clampEffect(effect, newValue);
    util.target.setEffect(effect, newValue);
  }

  setEffect(args, util) {
    const effect = Cast.toString(args.EFFECT).toLowerCase();
    let value = Cast.toNumber(args.VALUE);
    value = this.clampEffect(effect, value);
    util.target.setEffect(effect, value);
  }

  clearEffects(args, util) {
    util.target.clearEffects();
  }

  changeSize(args, util) {
    const change = Cast.toNumber(args.CHANGE);
    util.target.setSize(util.target.size + change);
  }

  setSize(args, util) {
    const size = Cast.toNumber(args.SIZE);
    util.target.setSize(size);
  }

  goToFrontBack(args, util) {
    if (!util.target.isStage) {
      if (args.FRONT_BACK === 'front') {
        util.target.goToFront();
      } else {
        util.target.goToBack();
      }
    }
  }

  goForwardBackwardLayers(args, util) {
    if (!util.target.isStage) {
      if (args.FORWARD_BACKWARD === 'forward') {
        util.target.goForwardLayers(Cast.toNumber(args.NUM));
      } else {
        util.target.goBackwardLayers(Cast.toNumber(args.NUM));
      }
    }
  }

  getSize(args, util) {
    return Math.round(util.target.size);
  }

  getBackdropNumberName(args) {
    const stage = this.runtime.getTargetForStage();

    if (args.NUMBER_NAME === 'number') {
      return stage.currentCostume + 1;
    } // Else return name


    return stage.getCostumes()[stage.currentCostume].name;
  }

  getCostumeNumberName(args, util) {
    if (args.NUMBER_NAME === 'number') {
      return util.target.currentCostume + 1;
    } // Else return name


    return util.target.getCostumes()[util.target.currentCostume].name;
  }

}

module.exports = Scratch3LooksBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_motion.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_motion.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");

class Scratch3MotionBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      motion_movesteps: this.moveSteps,
      motion_gotoxy: this.goToXY,
      motion_goto: this.goTo,
      motion_turnright: this.turnRight,
      motion_turnleft: this.turnLeft,
      motion_pointindirection: this.pointInDirection,
      motion_pointtowards: this.pointTowards,
      motion_glidesecstoxy: this.glide,
      motion_glideto: this.glideTo,
      motion_ifonedgebounce: this.ifOnEdgeBounce,
      motion_setrotationstyle: this.setRotationStyle,
      motion_changexby: this.changeX,
      motion_setx: this.setX,
      motion_changeyby: this.changeY,
      motion_sety: this.setY,
      motion_xposition: this.getX,
      motion_yposition: this.getY,
      motion_direction: this.getDirection,
      // Legacy no-op blocks:
      motion_scroll_right: () => {},
      motion_scroll_up: () => {},
      motion_align_scene: () => {},
      motion_xscroll: () => {},
      motion_yscroll: () => {}
    };
  }

  getMonitored() {
    return {
      motion_xposition: {
        isSpriteSpecific: true,
        getId: targetId => "".concat(targetId, "_xposition")
      },
      motion_yposition: {
        isSpriteSpecific: true,
        getId: targetId => "".concat(targetId, "_yposition")
      },
      motion_direction: {
        isSpriteSpecific: true,
        getId: targetId => "".concat(targetId, "_direction")
      }
    };
  }

  moveSteps(args, util) {
    const steps = Cast.toNumber(args.STEPS);

    this._moveSteps(steps, util.target);
  }

  _moveSteps(steps, target) {
    // used by compiler
    const radians = MathUtil.degToRad(90 - target.direction);
    const dx = steps * Math.cos(radians);
    const dy = steps * Math.sin(radians);
    target.setXY(target.x + dx, target.y + dy);
  }

  goToXY(args, util) {
    const x = Cast.toNumber(args.X);
    const y = Cast.toNumber(args.Y);
    util.target.setXY(x, y);
  }

  getTargetXY(targetName, util) {
    let targetX = 0;
    let targetY = 0;

    if (targetName === '_mouse_') {
      targetX = util.ioQuery('mouse', 'getScratchX');
      targetY = util.ioQuery('mouse', 'getScratchY');
    } else if (targetName === '_random_') {
      const stageWidth = this.runtime.stageWidth;
      const stageHeight = this.runtime.stageHeight;
      targetX = Math.round(stageWidth * (Math.random() - 0.5));
      targetY = Math.round(stageHeight * (Math.random() - 0.5));
    } else {
      targetName = Cast.toString(targetName);
      const goToTarget = this.runtime.getSpriteTargetByName(targetName);
      if (!goToTarget) return;
      targetX = goToTarget.x;
      targetY = goToTarget.y;
    }

    return [targetX, targetY];
  }

  goTo(args, util) {
    const targetXY = this.getTargetXY(args.TO, util);

    if (targetXY) {
      util.target.setXY(targetXY[0], targetXY[1]);
    }
  }

  turnRight(args, util) {
    const degrees = Cast.toNumber(args.DEGREES);
    util.target.setDirection(util.target.direction + degrees);
  }

  turnLeft(args, util) {
    const degrees = Cast.toNumber(args.DEGREES);
    util.target.setDirection(util.target.direction - degrees);
  }

  pointInDirection(args, util) {
    const direction = Cast.toNumber(args.DIRECTION);
    util.target.setDirection(direction);
  }

  pointTowards(args, util) {
    let targetX = 0;
    let targetY = 0;

    if (args.TOWARDS === '_mouse_') {
      targetX = util.ioQuery('mouse', 'getScratchX');
      targetY = util.ioQuery('mouse', 'getScratchY');
    } else if (args.TOWARDS === '_random_') {
      util.target.setDirection(Math.round(Math.random() * 360) - 180);
      return;
    } else {
      args.TOWARDS = Cast.toString(args.TOWARDS);
      const pointTarget = this.runtime.getSpriteTargetByName(args.TOWARDS);
      if (!pointTarget) return;
      targetX = pointTarget.x;
      targetY = pointTarget.y;
    }

    const dx = targetX - util.target.x;
    const dy = targetY - util.target.y;
    const direction = 90 - MathUtil.radToDeg(Math.atan2(dy, dx));
    util.target.setDirection(direction);
  }

  glide(args, util) {
    if (util.stackFrame.timer) {
      const timeElapsed = util.stackFrame.timer.timeElapsed();

      if (timeElapsed < util.stackFrame.duration * 1000) {
        // In progress: move to intermediate position.
        const frac = timeElapsed / (util.stackFrame.duration * 1000);
        const dx = frac * (util.stackFrame.endX - util.stackFrame.startX);
        const dy = frac * (util.stackFrame.endY - util.stackFrame.startY);
        util.target.setXY(util.stackFrame.startX + dx, util.stackFrame.startY + dy);
        util.yield();
      } else {
        // Finished: move to final position.
        util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);
      }
    } else {
      // First time: save data for future use.
      util.stackFrame.timer = new Timer();
      util.stackFrame.timer.start();
      util.stackFrame.duration = Cast.toNumber(args.SECS);
      util.stackFrame.startX = util.target.x;
      util.stackFrame.startY = util.target.y;
      util.stackFrame.endX = Cast.toNumber(args.X);
      util.stackFrame.endY = Cast.toNumber(args.Y);

      if (util.stackFrame.duration <= 0) {
        // Duration too short to glide.
        util.target.setXY(util.stackFrame.endX, util.stackFrame.endY);
        return;
      }

      util.yield();
    }
  }

  glideTo(args, util) {
    const targetXY = this.getTargetXY(args.TO, util);

    if (targetXY) {
      this.glide({
        SECS: args.SECS,
        X: targetXY[0],
        Y: targetXY[1]
      }, util);
    }
  }

  ifOnEdgeBounce(args, util) {
    this._ifOnEdgeBounce(util.target);
  }

  _ifOnEdgeBounce(target) {
    // used by compiler
    const bounds = target.getBounds();

    if (!bounds) {
      return;
    } // Measure distance to edges.
    // Values are positive when the sprite is far away,
    // and clamped to zero when the sprite is beyond.


    const stageWidth = this.runtime.stageWidth;
    const stageHeight = this.runtime.stageHeight;
    const distLeft = Math.max(0, stageWidth / 2 + bounds.left);
    const distTop = Math.max(0, stageHeight / 2 - bounds.top);
    const distRight = Math.max(0, stageWidth / 2 - bounds.right);
    const distBottom = Math.max(0, stageHeight / 2 + bounds.bottom); // Find the nearest edge.

    let nearestEdge = '';
    let minDist = Infinity;

    if (distLeft < minDist) {
      minDist = distLeft;
      nearestEdge = 'left';
    }

    if (distTop < minDist) {
      minDist = distTop;
      nearestEdge = 'top';
    }

    if (distRight < minDist) {
      minDist = distRight;
      nearestEdge = 'right';
    }

    if (distBottom < minDist) {
      minDist = distBottom;
      nearestEdge = 'bottom';
    }

    if (minDist > 0) {
      return; // Not touching any edge.
    } // Point away from the nearest edge.


    const radians = MathUtil.degToRad(90 - target.direction);
    let dx = Math.cos(radians);
    let dy = -Math.sin(radians);

    if (nearestEdge === 'left') {
      dx = Math.max(0.2, Math.abs(dx));
    } else if (nearestEdge === 'top') {
      dy = Math.max(0.2, Math.abs(dy));
    } else if (nearestEdge === 'right') {
      dx = 0 - Math.max(0.2, Math.abs(dx));
    } else if (nearestEdge === 'bottom') {
      dy = 0 - Math.max(0.2, Math.abs(dy));
    }

    const newDirection = MathUtil.radToDeg(Math.atan2(dy, dx)) + 90;
    target.setDirection(newDirection); // Keep within the stage.

    const fencedPosition = target.keepInFence(target.x, target.y);
    target.setXY(fencedPosition[0], fencedPosition[1]);
  }

  setRotationStyle(args, util) {
    util.target.setRotationStyle(args.STYLE);
  }

  changeX(args, util) {
    const dx = Cast.toNumber(args.DX);
    util.target.setXY(util.target.x + dx, util.target.y);
  }

  setX(args, util) {
    const x = Cast.toNumber(args.X);
    util.target.setXY(x, util.target.y);
  }

  changeY(args, util) {
    const dy = Cast.toNumber(args.DY);
    util.target.setXY(util.target.x, util.target.y + dy);
  }

  setY(args, util) {
    const y = Cast.toNumber(args.Y);
    util.target.setXY(util.target.x, y);
  }

  getX(args, util) {
    return this.limitPrecision(util.target.x);
  }

  getY(args, util) {
    return this.limitPrecision(util.target.y);
  }

  getDirection(args, util) {
    return util.target.direction;
  } // This corresponds to snapToInteger in Scratch 2


  limitPrecision(coordinate) {
    const rounded = Math.round(coordinate);
    const delta = coordinate - rounded;
    const limitedCoord = Math.abs(delta) < 1e-9 ? rounded : coordinate;
    return limitedCoord;
  }

}

module.exports = Scratch3MotionBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_operators.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_operators.js ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast.js */ "./node_modules/scratch-vm/src/util/cast.js");

const MathUtil = __webpack_require__(/*! ../util/math-util.js */ "./node_modules/scratch-vm/src/util/math-util.js");

class Scratch3OperatorsBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      operator_add: this.add,
      operator_subtract: this.subtract,
      operator_multiply: this.multiply,
      operator_divide: this.divide,
      operator_lt: this.lt,
      operator_equals: this.equals,
      operator_gt: this.gt,
      operator_and: this.and,
      operator_or: this.or,
      operator_not: this.not,
      operator_random: this.random,
      operator_join: this.join,
      operator_letter_of: this.letterOf,
      operator_length: this.length,
      operator_contains: this.contains,
      operator_mod: this.mod,
      operator_round: this.round,
      operator_mathop: this.mathop
    };
  }

  add(args) {
    return Cast.toNumber(args.NUM1) + Cast.toNumber(args.NUM2);
  }

  subtract(args) {
    return Cast.toNumber(args.NUM1) - Cast.toNumber(args.NUM2);
  }

  multiply(args) {
    return Cast.toNumber(args.NUM1) * Cast.toNumber(args.NUM2);
  }

  divide(args) {
    return Cast.toNumber(args.NUM1) / Cast.toNumber(args.NUM2);
  }

  lt(args) {
    return Cast.compare(args.OPERAND1, args.OPERAND2) < 0;
  }

  equals(args) {
    return Cast.compare(args.OPERAND1, args.OPERAND2) === 0;
  }

  gt(args) {
    return Cast.compare(args.OPERAND1, args.OPERAND2) > 0;
  }

  and(args) {
    return Cast.toBoolean(args.OPERAND1) && Cast.toBoolean(args.OPERAND2);
  }

  or(args) {
    return Cast.toBoolean(args.OPERAND1) || Cast.toBoolean(args.OPERAND2);
  }

  not(args) {
    return !Cast.toBoolean(args.OPERAND);
  }

  random(args) {
    return this._random(args.FROM, args.TO);
  }

  _random(from, to) {
    // used by compiler
    const nFrom = Cast.toNumber(from);
    const nTo = Cast.toNumber(to);
    const low = nFrom <= nTo ? nFrom : nTo;
    const high = nFrom <= nTo ? nTo : nFrom;
    if (low === high) return low; // If both arguments are ints, truncate the result to an int.

    if (Cast.isInt(from) && Cast.isInt(to)) {
      return low + Math.floor(Math.random() * (high + 1 - low));
    }

    return Math.random() * (high - low) + low;
  }

  join(args) {
    return Cast.toString(args.STRING1) + Cast.toString(args.STRING2);
  }

  letterOf(args) {
    const index = Cast.toNumber(args.LETTER) - 1;
    const str = Cast.toString(args.STRING); // Out of bounds?

    if (index < 0 || index >= str.length) {
      return '';
    }

    return str.charAt(index);
  }

  length(args) {
    return Cast.toString(args.STRING).length;
  }

  contains(args) {
    const format = function format(string) {
      return Cast.toString(string).toLowerCase();
    };

    return format(args.STRING1).includes(format(args.STRING2));
  }

  mod(args) {
    const n = Cast.toNumber(args.NUM1);
    const modulus = Cast.toNumber(args.NUM2);
    let result = n % modulus; // Scratch mod uses floored division instead of truncated division.

    if (result / modulus < 0) result += modulus;
    return result;
  }

  round(args) {
    return Math.round(Cast.toNumber(args.NUM));
  }

  mathop(args) {
    const operator = Cast.toString(args.OPERATOR).toLowerCase();
    const n = Cast.toNumber(args.NUM);

    switch (operator) {
      case 'abs':
        return Math.abs(n);

      case 'floor':
        return Math.floor(n);

      case 'ceiling':
        return Math.ceil(n);

      case 'sqrt':
        return Math.sqrt(n);

      case 'sin':
        return Math.round(Math.sin(Math.PI * n / 180) * 1e10) / 1e10;

      case 'cos':
        return Math.round(Math.cos(Math.PI * n / 180) * 1e10) / 1e10;

      case 'tan':
        return MathUtil.tan(n);

      case 'asin':
        return Math.asin(n) * 180 / Math.PI;

      case 'acos':
        return Math.acos(n) * 180 / Math.PI;

      case 'atan':
        return Math.atan(n) * 180 / Math.PI;

      case 'ln':
        return Math.log(n);

      case 'log':
        return Math.log(n) / Math.LN10;

      case 'e ^':
        return Math.exp(n);

      case '10 ^':
        return Math.pow(10, n);
    }

    return 0;
  }

}

module.exports = Scratch3OperatorsBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_procedures.js":
/*!*******************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_procedures.js ***!
  \*******************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class Scratch3ProcedureBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      procedures_definition: this.definition,
      procedures_call: this.call,
      argument_reporter_string_number: this.argumentReporterStringNumber,
      argument_reporter_boolean: this.argumentReporterBoolean
    };
  }

  definition() {// No-op: execute the blocks.
  }

  call(args, util) {
    if (!util.stackFrame.executed) {
      const procedureCode = args.mutation.proccode;
      const paramNamesIdsAndDefaults = util.getProcedureParamNamesIdsAndDefaults(procedureCode); // If null, procedure could not be found, which can happen if custom
      // block is dragged between sprites without the definition.
      // Match Scratch 2.0 behavior and noop.

      if (paramNamesIdsAndDefaults === null) {
        return;
      }

      const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults; // Initialize params for the current stackFrame to {}, even if the procedure does
      // not take any arguments. This is so that `getParam` down the line does not look
      // at earlier stack frames for the values of a given parameter (#1729)

      util.initParams();

      for (let i = 0; i < paramIds.length; i++) {
        if (args.hasOwnProperty(paramIds[i])) {
          util.pushParam(paramNames[i], args[paramIds[i]]);
        } else {
          util.pushParam(paramNames[i], paramDefaults[i]);
        }
      }

      const addonBlock = util.runtime.getAddonBlock(procedureCode);

      if (addonBlock) {
        const result = addonBlock.callback(util.thread.getAllparams(), util);

        if (util.thread.status === 1
        /* STATUS_PROMISE_WAIT */
        ) {
          // If the addon block is using STATUS_PROMISE_WAIT to force us to sleep,
          // make sure to not re-run this block when we resume.
          util.stackFrame.executed = true;
        }

        return result;
      }

      util.stackFrame.executed = true;
      util.startProcedure(procedureCode);
    }
  }

  argumentReporterStringNumber(args, util) {
    const value = util.getParam(args.VALUE);

    if (value === null) {
      // tw: support legacy block
      if (String(args.VALUE).toLowerCase() === 'last key pressed') {
        return util.ioQuery('keyboard', 'getLastKeyPressed');
      } // When the parameter is not found in the most recent procedure
      // call, the default is always 0.


      return 0;
    }

    return value;
  }

  argumentReporterBoolean(args, util) {
    const value = util.getParam(args.VALUE);

    if (value === null) {
      // tw: implement is compiled? and is turbowarp?
      const lowercaseValue = String(args.VALUE).toLowerCase();

      if (util.target.runtime.compilerOptions.enabled && lowercaseValue === 'is compiled?') {
        return true;
      }

      if (lowercaseValue === 'is turbowarp?') {
        return true;
      } // When the parameter is not found in the most recent procedure
      // call, the default is always 0.


      return 0;
    }

    return value;
  }

}

module.exports = Scratch3ProcedureBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_sensing.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_sensing.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");

const getMonitorIdForBlockWithArgs = __webpack_require__(/*! ../util/get-monitor-id */ "./node_modules/scratch-vm/src/util/get-monitor-id.js");

class Scratch3SensingBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * The "answer" block value.
     * @type {string}
     */

    this._answer = ''; // used by compiler

    /**
     * The timer utility.
     * @type {Timer}
     */

    this._timer = new Timer();
    /**
     * The stored microphone loudness measurement.
     * @type {number}
     */

    this._cachedLoudness = -1;
    /**
     * The time of the most recent microphone loudness measurement.
     * @type {number}
     */

    this._cachedLoudnessTimestamp = 0;
    /**
     * The list of queued questions and respective `resolve` callbacks.
     * @type {!Array}
     */

    this._questionList = [];
    this.runtime.on('ANSWER', this._onAnswer.bind(this));
    this.runtime.on('PROJECT_START', this._resetAnswer.bind(this));
    this.runtime.on('PROJECT_STOP_ALL', this._clearAllQuestions.bind(this));
    this.runtime.on('STOP_FOR_TARGET', this._clearTargetQuestions.bind(this));
    this.runtime.on('RUNTIME_DISPOSED', this._resetAnswer.bind(this));
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      sensing_touchingobject: this.touchingObject,
      sensing_touchingcolor: this.touchingColor,
      sensing_coloristouchingcolor: this.colorTouchingColor,
      sensing_distanceto: this.distanceTo,
      sensing_timer: this.getTimer,
      sensing_resettimer: this.resetTimer,
      sensing_of: this.getAttributeOf,
      sensing_mousex: this.getMouseX,
      sensing_mousey: this.getMouseY,
      sensing_setdragmode: this.setDragMode,
      sensing_mousedown: this.getMouseDown,
      sensing_keypressed: this.getKeyPressed,
      sensing_current: this.current,
      sensing_dayssince2000: this.daysSince2000,
      sensing_loudness: this.getLoudness,
      sensing_loud: this.isLoud,
      sensing_askandwait: this.askAndWait,
      sensing_answer: this.getAnswer,
      sensing_username: this.getUsername,
      sensing_userid: () => {} // legacy no-op block

    };
  }

  getMonitored() {
    return {
      sensing_answer: {
        getId: () => 'answer'
      },
      sensing_mousedown: {
        getId: () => 'mousedown'
      },
      sensing_mousex: {
        getId: () => 'mousex'
      },
      sensing_mousey: {
        getId: () => 'mousey'
      },
      sensing_loudness: {
        getId: () => 'loudness'
      },
      sensing_timer: {
        getId: () => 'timer'
      },
      sensing_dayssince2000: {
        getId: () => 'dayssince2000'
      },
      sensing_current: {
        // This is different from the default toolbox xml id in order to support
        // importing multiple monitors from the same opcode from sb2 files,
        // something that is not currently supported in scratch 3.
        getId: (_, fields) => getMonitorIdForBlockWithArgs('current', fields) // _${param}`

      }
    };
  }

  _onAnswer(answer) {
    this._answer = answer;

    const questionObj = this._questionList.shift();

    if (questionObj) {
      const [_question, resolve, target, wasVisible, wasStage] = questionObj; // If the target was visible when asked, hide the say bubble unless the target was the stage.

      if (wasVisible && !wasStage) {
        this.runtime.emit('SAY', target, 'say', '');
      }

      resolve();

      this._askNextQuestion();
    }
  }

  _resetAnswer() {
    this._answer = '';
  }

  _enqueueAsk(question, resolve, target, wasVisible, wasStage) {
    this._questionList.push([question, resolve, target, wasVisible, wasStage]);
  }

  _askNextQuestion() {
    if (this._questionList.length > 0) {
      const [question, _resolve, target, wasVisible, wasStage] = this._questionList[0]; // If the target is visible, emit a blank question and use the
      // say event to trigger a bubble unless the target was the stage.

      if (wasVisible && !wasStage) {
        this.runtime.emit('SAY', target, 'say', question);
        this.runtime.emit('QUESTION', '');
      } else {
        this.runtime.emit('QUESTION', question);
      }
    }
  }

  _clearAllQuestions() {
    this._questionList = [];
    this.runtime.emit('QUESTION', null);
  }

  _clearTargetQuestions(stopTarget) {
    const currentlyAsking = this._questionList.length > 0 && this._questionList[0][2] === stopTarget;
    this._questionList = this._questionList.filter(question => question[2] !== stopTarget);

    if (currentlyAsking) {
      this.runtime.emit('SAY', stopTarget, 'say', '');

      if (this._questionList.length > 0) {
        this._askNextQuestion();
      } else {
        this.runtime.emit('QUESTION', null);
      }
    }
  }

  askAndWait(args, util) {
    const _target = util.target;
    return new Promise(resolve => {
      const isQuestionAsked = this._questionList.length > 0;

      this._enqueueAsk(String(args.QUESTION), resolve, _target, _target.visible, _target.isStage);

      if (!isQuestionAsked) {
        this._askNextQuestion();
      }
    });
  }

  getAnswer() {
    return this._answer;
  }

  touchingObject(args, util) {
    return util.target.isTouchingObject(args.TOUCHINGOBJECTMENU);
  }

  touchingColor(args, util) {
    const color = Cast.toRgbColorList(args.COLOR);
    return util.target.isTouchingColor(color);
  }

  colorTouchingColor(args, util) {
    const maskColor = Cast.toRgbColorList(args.COLOR);
    const targetColor = Cast.toRgbColorList(args.COLOR2);
    return util.target.colorIsTouchingColor(targetColor, maskColor);
  }

  distanceTo(args, util) {
    if (util.target.isStage) return 10000;
    let targetX = 0;
    let targetY = 0;

    if (args.DISTANCETOMENU === '_mouse_') {
      targetX = util.ioQuery('mouse', 'getScratchX');
      targetY = util.ioQuery('mouse', 'getScratchY');
    } else {
      args.DISTANCETOMENU = Cast.toString(args.DISTANCETOMENU);
      const distTarget = this.runtime.getSpriteTargetByName(args.DISTANCETOMENU);
      if (!distTarget) return 10000;
      targetX = distTarget.x;
      targetY = distTarget.y;
    }

    const dx = util.target.x - targetX;
    const dy = util.target.y - targetY;
    return Math.sqrt(dx * dx + dy * dy);
  }

  setDragMode(args, util) {
    util.target.setDraggable(args.DRAG_MODE === 'draggable');
  }

  getTimer(args, util) {
    return util.ioQuery('clock', 'projectTimer');
  }

  resetTimer(args, util) {
    util.ioQuery('clock', 'resetProjectTimer');
  }

  getMouseX(args, util) {
    return util.ioQuery('mouse', 'getScratchX');
  }

  getMouseY(args, util) {
    return util.ioQuery('mouse', 'getScratchY');
  }

  getMouseDown(args, util) {
    return util.ioQuery('mouse', 'getIsDown');
  }

  current(args) {
    const menuOption = Cast.toString(args.CURRENTMENU).toLowerCase();
    const date = new Date();

    switch (menuOption) {
      case 'year':
        return date.getFullYear();

      case 'month':
        return date.getMonth() + 1;
      // getMonth is zero-based

      case 'date':
        return date.getDate();

      case 'dayofweek':
        return date.getDay() + 1;
      // getDay is zero-based, Sun=0

      case 'hour':
        return date.getHours();

      case 'minute':
        return date.getMinutes();

      case 'second':
        return date.getSeconds();
    }

    return 0;
  }

  getKeyPressed(args, util) {
    return util.ioQuery('keyboard', 'getKeyIsDown', [args.KEY_OPTION]);
  }

  daysSince2000() {
    const msPerDay = 24 * 60 * 60 * 1000;
    const start = new Date(2000, 0, 1); // Months are 0-indexed.

    const today = new Date();
    const dstAdjust = today.getTimezoneOffset() - start.getTimezoneOffset();
    let mSecsSinceStart = today.valueOf() - start.valueOf();
    mSecsSinceStart += (today.getTimezoneOffset() - dstAdjust) * 60 * 1000;
    return mSecsSinceStart / msPerDay;
  }

  getLoudness() {
    if (typeof this.runtime.audioEngine === 'undefined') return -1;
    if (this.runtime.currentStepTime === null) return -1; // Only measure loudness once per step

    const timeSinceLoudness = this._timer.time() - this._cachedLoudnessTimestamp;

    if (timeSinceLoudness < this.runtime.currentStepTime) {
      return this._cachedLoudness;
    }

    this._cachedLoudnessTimestamp = this._timer.time();
    this._cachedLoudness = this.runtime.audioEngine.getLoudness();
    return this._cachedLoudness;
  }

  isLoud() {
    return this.getLoudness() > 10;
  }

  getAttributeOf(args) {
    let attrTarget;

    if (args.OBJECT === '_stage_') {
      attrTarget = this.runtime.getTargetForStage();
    } else {
      args.OBJECT = Cast.toString(args.OBJECT);
      attrTarget = this.runtime.getSpriteTargetByName(args.OBJECT);
    } // attrTarget can be undefined if the target does not exist
    // (e.g. single sprite uploaded from larger project referencing
    // another sprite that wasn't uploaded)


    if (!attrTarget) return 0; // Generic attributes

    if (attrTarget.isStage) {
      switch (args.PROPERTY) {
        // Scratch 1.4 support
        case 'background #':
          return attrTarget.currentCostume + 1;

        case 'backdrop #':
          return attrTarget.currentCostume + 1;

        case 'backdrop name':
          return attrTarget.getCostumes()[attrTarget.currentCostume].name;

        case 'volume':
          return attrTarget.volume;
      }
    } else {
      switch (args.PROPERTY) {
        case 'x position':
          return attrTarget.x;

        case 'y position':
          return attrTarget.y;

        case 'direction':
          return attrTarget.direction;

        case 'costume #':
          return attrTarget.currentCostume + 1;

        case 'costume name':
          return attrTarget.getCostumes()[attrTarget.currentCostume].name;

        case 'size':
          return attrTarget.size;

        case 'volume':
          return attrTarget.volume;
      }
    } // Target variables.


    const varName = args.PROPERTY;
    const variable = attrTarget.lookupVariableByNameAndType(varName, '', true);

    if (variable) {
      return variable.value;
    } // Otherwise, 0


    return 0;
  }

  getUsername(args, util) {
    return util.ioQuery('userData', 'getUsername');
  }

}

module.exports = Scratch3SensingBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/blocks/scratch3_sound.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/blocks/scratch3_sound.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Clone = __webpack_require__(/*! ../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");
/**
 * Occluded boolean value to make its use more understandable.
 * @const {boolean}
 */


const STORE_WAITING = true;

class Scratch3SoundBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    this.waitingSounds = {}; // Clear sound effects on green flag and stop button events.

    this.stopAllSounds = this.stopAllSounds.bind(this);
    this._stopWaitingSoundsForTarget = this._stopWaitingSoundsForTarget.bind(this);
    this._clearEffectsForAllTargets = this._clearEffectsForAllTargets.bind(this);

    if (this.runtime) {
      this.runtime.on('PROJECT_STOP_ALL', this.stopAllSounds);
      this.runtime.on('PROJECT_STOP_ALL', this._clearEffectsForAllTargets);
      this.runtime.on('STOP_FOR_TARGET', this._stopWaitingSoundsForTarget);
      this.runtime.on('PROJECT_START', this._clearEffectsForAllTargets);
    }

    this._onTargetCreated = this._onTargetCreated.bind(this);

    if (this.runtime) {
      runtime.on('targetWasCreated', this._onTargetCreated);
    }
  }
  /**
   * The key to load & store a target's sound-related state.
   * @type {string}
   */


  static get STATE_KEY() {
    return 'Scratch.sound';
  }
  /**
   * The default sound-related state, to be used when a target has no existing sound state.
   * @type {SoundState}
   */


  static get DEFAULT_SOUND_STATE() {
    return {
      effects: {
        pitch: 0,
        pan: 0
      }
    };
  }
  /**
   * The minimum and maximum MIDI note numbers, for clamping the input to play note.
   * @type {{min: number, max: number}}
   */


  static get MIDI_NOTE_RANGE() {
    return {
      min: 36,
      max: 96
    }; // C2 to C7
  }
  /**
   * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
   * 100 beats at the default tempo of 60bpm is 100 seconds.
   * @type {{min: number, max: number}}
   */


  static get BEAT_RANGE() {
    return {
      min: 0,
      max: 100
    };
  }
  /** The minimum and maximum tempo values, in bpm.
   * @type {{min: number, max: number}}
   */


  static get TEMPO_RANGE() {
    return {
      min: 20,
      max: 500
    };
  }
  /** The minimum and maximum values for each sound effect.
   * @type {{effect:{min: number, max: number}}}
   */


  static get EFFECT_RANGE() {
    return {
      pitch: {
        min: -360,
        max: 360
      },
      // -3 to 3 octaves
      pan: {
        min: -100,
        max: 100
      } // 100% left to 100% right

    };
  }
  /** The minimum and maximum values for sound effects when miscellaneous limits are removed. */


  static get LARGER_EFFECT_RANGE() {
    return {
      // scratch-audio throws if pitch is too big because some math results in Infinity
      pitch: {
        min: -1000,
        max: 1000
      },
      // No reason for these to go beyond 100
      pan: {
        min: -100,
        max: 100
      }
    };
  }
  /**
   * @param {Target} target - collect sound state for this target.
   * @returns {SoundState} the mutable sound state associated with that target. This will be created if necessary.
   * @private
   */


  _getSoundState(target) {
    let soundState = target.getCustomState(Scratch3SoundBlocks.STATE_KEY);

    if (!soundState) {
      soundState = Clone.simple(Scratch3SoundBlocks.DEFAULT_SOUND_STATE);
      target.setCustomState(Scratch3SoundBlocks.STATE_KEY, soundState);
      target.soundEffects = soundState.effects;
    }

    return soundState;
  }
  /**
   * When a Target is cloned, clone the sound state.
   * @param {Target} newTarget - the newly created target.
   * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
   * @listens Runtime#event:targetWasCreated
   * @private
   */


  _onTargetCreated(newTarget, sourceTarget) {
    if (sourceTarget) {
      const soundState = sourceTarget.getCustomState(Scratch3SoundBlocks.STATE_KEY);

      if (soundState && newTarget) {
        newTarget.setCustomState(Scratch3SoundBlocks.STATE_KEY, Clone.simple(soundState));

        this._syncEffectsForTarget(newTarget);
      }
    }
  }
  /**
   * Retrieve the block primitives implemented by this package.
   * @return {object.<string, Function>} Mapping of opcode to Function.
   */


  getPrimitives() {
    return {
      sound_play: this.playSound,
      sound_playuntildone: this.playSoundAndWait,
      sound_stopallsounds: this.stopAllSounds,
      sound_seteffectto: this.setEffect,
      sound_changeeffectby: this.changeEffect,
      sound_cleareffects: this.clearEffects,
      sound_sounds_menu: this.soundsMenu,
      sound_beats_menu: this.beatsMenu,
      sound_effects_menu: this.effectsMenu,
      sound_setvolumeto: this.setVolume,
      sound_changevolumeby: this.changeVolume,
      sound_volume: this.getVolume
    };
  }

  getMonitored() {
    return {
      sound_volume: {
        isSpriteSpecific: true,
        getId: targetId => "".concat(targetId, "_volume")
      }
    };
  }

  playSound(args, util) {
    // Don't return the promise, it's the only difference for AndWait
    this._playSound(args, util);
  }

  playSoundAndWait(args, util) {
    return this._playSound(args, util, STORE_WAITING);
  }

  _playSound(args, util, storeWaiting) {
    const index = this._getSoundIndex(args.SOUND_MENU, util);

    if (index >= 0) {
      const {
        target
      } = util;
      const {
        sprite
      } = target;
      const {
        soundId
      } = sprite.sounds[index];

      if (sprite.soundBank) {
        if (storeWaiting === STORE_WAITING) {
          this._addWaitingSound(target.id, soundId);
        } else {
          this._removeWaitingSound(target.id, soundId);
        }

        return sprite.soundBank.playSound(target, soundId);
      }
    }
  }

  _addWaitingSound(targetId, soundId) {
    if (!this.waitingSounds[targetId]) {
      this.waitingSounds[targetId] = new Set();
    }

    this.waitingSounds[targetId].add(soundId);
  }

  _removeWaitingSound(targetId, soundId) {
    if (!this.waitingSounds[targetId]) {
      return;
    }

    this.waitingSounds[targetId].delete(soundId);
  }

  _getSoundIndex(soundName, util) {
    // if the sprite has no sounds, return -1
    const len = util.target.sprite.sounds.length;

    if (len === 0) {
      return -1;
    } // look up by name first


    const index = this.getSoundIndexByName(soundName, util);

    if (index !== -1) {
      return index;
    } // then try using the sound name as a 1-indexed index


    const oneIndexedIndex = parseInt(soundName, 10);

    if (!isNaN(oneIndexedIndex)) {
      return MathUtil.wrapClamp(oneIndexedIndex - 1, 0, len - 1);
    } // could not be found as a name or converted to index, return -1


    return -1;
  }

  getSoundIndexByName(soundName, util) {
    const sounds = util.target.sprite.sounds;

    for (let i = 0; i < sounds.length; i++) {
      if (sounds[i].name === soundName) {
        return i;
      }
    } // if there is no sound by that name, return -1


    return -1;
  }

  stopAllSounds() {
    if (this.runtime.targets === null) return;
    const allTargets = this.runtime.targets;

    for (let i = 0; i < allTargets.length; i++) {
      this._stopAllSoundsForTarget(allTargets[i]);
    }
  }

  _stopAllSoundsForTarget(target) {
    if (target.sprite.soundBank) {
      target.sprite.soundBank.stopAllSounds(target);

      if (this.waitingSounds[target.id]) {
        this.waitingSounds[target.id].clear();
      }
    }
  }

  _stopWaitingSoundsForTarget(target) {
    if (target.sprite.soundBank) {
      if (this.waitingSounds[target.id]) {
        for (const soundId of this.waitingSounds[target.id].values()) {
          target.sprite.soundBank.stop(target, soundId);
        }

        this.waitingSounds[target.id].clear();
      }
    }
  }

  setEffect(args, util) {
    return this._updateEffect(args, util, false);
  }

  changeEffect(args, util) {
    return this._updateEffect(args, util, true);
  }

  _updateEffect(args, util, change) {
    const effect = Cast.toString(args.EFFECT).toLowerCase();
    const value = Cast.toNumber(args.VALUE);

    const soundState = this._getSoundState(util.target);

    if (!soundState.effects.hasOwnProperty(effect)) return;

    if (change) {
      soundState.effects[effect] += value;
    } else {
      soundState.effects[effect] = value;
    }

    const miscLimits = this.runtime.runtimeOptions.miscLimits;
    const {
      min,
      max
    } = miscLimits ? Scratch3SoundBlocks.EFFECT_RANGE[effect] : Scratch3SoundBlocks.LARGER_EFFECT_RANGE[effect];
    soundState.effects[effect] = MathUtil.clamp(soundState.effects[effect], min, max);

    this._syncEffectsForTarget(util.target);

    if (miscLimits) {
      // Yield until the next tick.
      return Promise.resolve();
    } // Requesting a redraw makes sure that "forever: change pitch by 1" still work but without
    // yielding unnecessarily in other cases


    this.runtime.requestRedraw();
  }

  _syncEffectsForTarget(target) {
    if (!target || !target.sprite.soundBank) return;
    target.soundEffects = this._getSoundState(target).effects;
    target.sprite.soundBank.setEffects(target);
  }

  clearEffects(args, util) {
    this._clearEffectsForTarget(util.target);
  }

  _clearEffectsForTarget(target) {
    const soundState = this._getSoundState(target);

    for (const effect in soundState.effects) {
      if (!soundState.effects.hasOwnProperty(effect)) continue;
      soundState.effects[effect] = 0;
    }

    this._syncEffectsForTarget(target);
  }

  _clearEffectsForAllTargets() {
    if (this.runtime.targets === null) return;
    const allTargets = this.runtime.targets;

    for (let i = 0; i < allTargets.length; i++) {
      this._clearEffectsForTarget(allTargets[i]);
    }
  }

  setVolume(args, util) {
    const volume = Cast.toNumber(args.VOLUME);
    return this._updateVolume(volume, util);
  }

  changeVolume(args, util) {
    const volume = Cast.toNumber(args.VOLUME) + util.target.volume;
    return this._updateVolume(volume, util);
  }

  _updateVolume(volume, util) {
    volume = MathUtil.clamp(volume, 0, 100);
    util.target.volume = volume;

    this._syncEffectsForTarget(util.target);

    if (this.runtime.runtimeOptions.miscLimits) {
      // Yield until the next tick.
      return Promise.resolve();
    }

    this.runtime.requestRedraw();
  }

  getVolume(args, util) {
    return util.target.volume;
  }

  soundsMenu(args) {
    return args.SOUND_MENU;
  }

  beatsMenu(args) {
    return args.BEATS;
  }

  effectsMenu(args) {
    return args.EFFECT;
  }

}

module.exports = Scratch3SoundBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/compat-block-utility.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/compat-block-utility.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const BlockUtility = __webpack_require__(/*! ../engine/block-utility */ "./node_modules/scratch-vm/src/engine/block-utility.js");

class CompatibilityLayerBlockUtility extends BlockUtility {
  constructor() {
    super();
    /**
     * @type {string|null}
     */

    this._fakeBlockId = null;
  } // Branching operations are not supported.


  startBranch() {
    throw new Error('startBranch is not supported by this BlockUtility');
  }

  startProcedure() {
    throw new Error('startProcedure is not supported by this BlockUtility');
  } // Parameters are not used by compiled scripts.


  initParams() {
    throw new Error('initParams is not supported by this BlockUtility');
  }

  pushParam() {
    throw new Error('pushParam is not supported by this BlockUtility');
  }

  getParam() {
    throw new Error('getParam is not supported by this BlockUtility');
  }

  init(thread, fakeBlockId) {
    this.thread = thread;
    this.sequencer = thread.target.runtime.sequencer;
    thread.stack[0] = fakeBlockId;
  }

} // Export a single instance to be reused.


module.exports = new CompatibilityLayerBlockUtility();

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/compat-blocks.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/compat-blocks.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview List of blocks to be supported in the compiler compatibility layer.
 * This is only for native blocks. Extensions should not be listed here.
 */
// Please keep these lists alphabetical.
const stacked = ['control_clear_counter', 'control_incr_counter', 'looks_changestretchby', 'looks_hideallsprites', 'looks_say', 'looks_sayforsecs', 'looks_setstretchto', 'looks_switchbackdroptoandwait', 'looks_think', 'looks_thinkforsecs', 'motion_align_scene', 'motion_glidesecstoxy', 'motion_glideto', 'motion_goto', 'motion_pointtowards', 'motion_scroll_right', 'motion_scroll_up', 'sensing_askandwait', 'sensing_setdragmode', 'sound_changeeffectby', 'sound_changevolumeby', 'sound_cleareffects', 'sound_play', 'sound_playuntildone', 'sound_seteffectto', 'sound_setvolumeto', 'sound_stopallsounds'];
const inputs = ['control_get_counter', 'motion_xscroll', 'motion_yscroll', 'sensing_loud', 'sensing_loudness', 'sensing_userid', 'sound_volume'];
module.exports = {
  stacked,
  inputs
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/compile.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/compile.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const IRGenerator = __webpack_require__(/*! ./irgen */ "./node_modules/scratch-vm/src/compiler/irgen.js");

const JSGenerator = __webpack_require__(/*! ./jsgen */ "./node_modules/scratch-vm/src/compiler/jsgen.js");

const compile = thread => {
  const irGenerator = new IRGenerator(thread);
  const ir = irGenerator.generate();
  const procedures = {};
  const target = thread.target;

  const compileScript = script => {
    if (script.cachedCompileResult) {
      return script.cachedCompileResult;
    }

    const compiler = new JSGenerator(script, ir, target);
    const result = compiler.compile();
    script.cachedCompileResult = result;
    return result;
  };

  const entry = compileScript(ir.entry);

  for (const procedureVariant of Object.keys(ir.procedures)) {
    const procedureData = ir.procedures[procedureVariant];
    const procedureTree = compileScript(procedureData);
    procedures[procedureVariant] = procedureTree;
  }

  return {
    startingFunction: entry,
    procedures
  };
};

module.exports = compile;

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/environment.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/environment.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* eslint-disable no-eval */

/**
 * @returns {boolean} true if the nullish coalescing operator (x ?? y) is supported.
 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator
 */
const supportsNullishCoalescing = () => {
  try {
    // eslint-disable-next-line no-unused-vars
    const fn = new Function('undefined ?? 3'); // if function construction succeeds, the browser understood the syntax.

    return true;
  } catch (e) {
    return false;
  }
};

module.exports = {
  supportsNullishCoalescing: supportsNullishCoalescing()
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/intermediate.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/intermediate.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview Common intermediates shared amongst parts of the compiler.
 */

/**
 * An IntermediateScript describes a single script.
 * Scripts do not necessarily have hats.
 */
class IntermediateScript {
  constructor() {
    /**
     * The ID of the top block of this script.
     * @type {string}
     */
    this.topBlockId = null;
    /**
     * List of nodes that make up this script.
     * @type {Array|null}
     */

    this.stack = null;
    /**
     * Whether this script is a procedure.
     * @type {boolean}
     */

    this.isProcedure = false;
    /**
     * This procedure's code, if any.
     * @type {string}
     */

    this.procedureCode = '';
    /**
     * List of names of arguments accepted by this function, if it is a procedure.
     * @type {string[]}
     */

    this.arguments = [];
    /**
     * Whether this script should be run in warp mode.
     * @type {boolean}
     */

    this.isWarp = false;
    /**
     * Whether this script can `yield`
     * If false, this script will be compiled as a regular JavaScript function (function)
     * If true, this script will be compiled as a generator function (function*)
     * @type {boolean}
     */

    this.yields = true;
    /**
     * Whether this script should use the "warp timer"
     * @type {boolean}
     */

    this.warpTimer = false;
    /**
     * List of procedure IDs that this script needs.
     * @readonly
     */

    this.dependedProcedures = [];
    /**
     * Cached result of compiling this script.
     * @type {Function|null}
     */

    this.cachedCompileResult = null;
  }

}
/**
 * An IntermediateRepresentation contains scripts.
 */


class IntermediateRepresentation {
  constructor() {
    /**
     * The entry point of this IR.
     * @type {IntermediateScript}
     */
    this.entry = null;
    /**
     * Maps procedure variants to their intermediate script.
     * @type {Object.<string, IntermediateScript>}
     */

    this.procedures = {};
  }

}

module.exports = {
  IntermediateScript,
  IntermediateRepresentation
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/irgen.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/irgen.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const BlockType = __webpack_require__(/*! ../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const {
  IntermediateScript,
  IntermediateRepresentation
} = __webpack_require__(/*! ./intermediate */ "./node_modules/scratch-vm/src/compiler/intermediate.js");

const compatBlocks = __webpack_require__(/*! ./compat-blocks */ "./node_modules/scratch-vm/src/compiler/compat-blocks.js");
/**
 * @fileoverview Generate intermediate representations from Scratch blocks.
 */


const SCALAR_TYPE = '';
const LIST_TYPE = 'list';
/**
 * @typedef {Object.<string, *>} Node
 * @property {string} kind
 */

/**
 * Create a variable codegen object.
 * @param {'target'|'stage'} scope The scope of this variable -- which object owns it.
 * @param {import('../engine/variable.js')} varObj The Scratch Variable
 * @returns {*} A variable codegen object.
 */

const createVariableData = (scope, varObj) => ({
  scope,
  id: varObj.id,
  name: varObj.name,
  isCloud: varObj.isCloud
});
/**
 * @param {string} code
 * @param {boolean} warp
 * @returns {string}
 */


const generateProcedureVariant = (code, warp) => {
  if (warp) {
    return "W".concat(code);
  }

  return "Z".concat(code);
};
/**
 * @param {string} variant Variant generated by generateProcedureVariant()
 * @returns {string} original procedure code
 */


const parseProcedureCode = variant => variant.substring(1);
/**
 * @param {string} variant Variant generated by generateProcedureVariant()
 * @returns {boolean} true if warp enabled
 */


const parseIsWarp = variant => variant.charAt(0) === 'W';

class ScriptTreeGenerator {
  constructor(thread) {
    /** @private */
    this.thread = thread;
    /** @private */

    this.target = thread.target;
    /** @private */

    this.blocks = thread.blockContainer;
    /** @private */

    this.runtime = this.target.runtime;
    /** @private */

    this.stage = this.runtime.getTargetForStage();
    /**
     * This script's intermediate representation.
     */

    this.script = new IntermediateScript();
    this.script.warpTimer = this.target.runtime.compilerOptions.warpTimer;
    /**
     * Cache of variable ID to variable data object.
     * @type {Object.<string, object>}
     * @private
     */

    this.variableCache = {};
    this.usesTimer = false;
  }

  setProcedureVariant(procedureVariant) {
    const procedureCode = parseProcedureCode(procedureVariant);
    this.script.procedureCode = procedureCode;
    this.script.isProcedure = true;
    this.script.yields = false;
    const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);

    if (paramNamesIdsAndDefaults === null) {
      throw new Error("IR: cannot find procedure: ".concat(procedureVariant));
    }

    const [paramNames, _paramIds, _paramDefaults] = paramNamesIdsAndDefaults;
    this.script.arguments = paramNames;
  }

  enableWarp() {
    this.script.isWarp = true;
  }

  getBlockById(blockId) {
    // Flyout blocks are stored in a special container.
    return this.blocks.getBlock(blockId) || this.blocks.runtime.flyoutBlocks.getBlock(blockId);
  }

  getBlockInfo(fullOpcode) {
    const [category, opcode] = StringUtil.splitFirst(fullOpcode, '_');

    if (!category || !opcode) {
      return null;
    }

    const categoryInfo = this.runtime._blockInfo.find(ci => ci.id === category);

    if (!categoryInfo) {
      return null;
    }

    const blockInfo = categoryInfo.blocks.find(b => b.info.opcode === opcode);

    if (!blockInfo) {
      return null;
    }

    return blockInfo;
  }
  /**
   * Descend into a child input of a block. (eg. the input STRING of "length of ( )")
   * @param {*} parentBlock The parent Scratch block that contains the input.
   * @param {string} inputName The name of the input to descend into.
   * @private
   * @returns {Node} Compiled input node for this input.
   */


  descendInputOfBlock(parentBlock, inputName) {
    const input = parentBlock.inputs[inputName];

    if (!input) {
      log.warn("IR: ".concat(parentBlock.opcode, ": missing input ").concat(inputName), parentBlock);
      return {
        kind: 'constant',
        value: 0
      };
    }

    const inputId = input.block;
    const block = this.getBlockById(inputId);

    if (!block) {
      log.warn("IR: ".concat(parentBlock.opcode, ": could not find input ").concat(inputName, " with ID ").concat(inputId));
      return {
        kind: 'constant',
        value: 0
      };
    }

    return this.descendInput(block);
  }
  /**
   * Descend into an input. (eg. "length of ( )")
   * @param {*} block The parent Scratch block input.
   * @private
   * @returns {Node} Compiled input node for this input.
   */


  descendInput(block) {
    switch (block.opcode) {
      case 'colour_picker':
        return {
          kind: 'constant',
          value: block.fields.COLOUR.value
        };

      case 'math_angle':
      case 'math_integer':
      case 'math_number':
      case 'math_positive_number':
      case 'math_whole_number':
        return {
          kind: 'constant',
          value: block.fields.NUM.value
        };

      case 'text':
        return {
          kind: 'constant',
          value: block.fields.TEXT.value
        };

      case 'argument_reporter_string_number':
        {
          const name = block.fields.VALUE.value; // lastIndexOf because multiple parameters with the same name will use the value of the last definition

          const index = this.script.arguments.lastIndexOf(name);

          if (index === -1) {
            // Legacy support
            if (name.toLowerCase() === 'last key pressed') {
              return {
                kind: 'tw.lastKeyPressed'
              };
            }
          }

          if (index === -1) {
            return {
              kind: 'constant',
              value: 0
            };
          }

          return {
            kind: 'args.stringNumber',
            index: index
          };
        }

      case 'argument_reporter_boolean':
        {
          // see argument_reporter_string_number above
          const name = block.fields.VALUE.value;
          const index = this.script.arguments.lastIndexOf(name);

          if (index === -1) {
            if (name.toLowerCase() === 'is compiled?' || name.toLowerCase() === 'is turbowarp?') {
              return {
                kind: 'constant',
                value: true
              };
            }

            return {
              kind: 'constant',
              value: 0
            };
          }

          return {
            kind: 'args.boolean',
            index: index
          };
        }

      case 'data_variable':
        return {
          kind: 'var.get',
          variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE)
        };

      case 'data_itemoflist':
        return {
          kind: 'list.get',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          index: this.descendInputOfBlock(block, 'INDEX')
        };

      case 'data_lengthoflist':
        return {
          kind: 'list.length',
          list: this.descendVariable(block, 'LIST', LIST_TYPE)
        };

      case 'data_listcontainsitem':
        return {
          kind: 'list.contains',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          item: this.descendInputOfBlock(block, 'ITEM')
        };

      case 'data_itemnumoflist':
        return {
          kind: 'list.indexOf',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          item: this.descendInputOfBlock(block, 'ITEM')
        };

      case 'data_listcontents':
        return {
          kind: 'list.contents',
          list: this.descendVariable(block, 'LIST', LIST_TYPE)
        };

      case 'event_broadcast_menu':
        {
          const broadcastOption = block.fields.BROADCAST_OPTION;
          const broadcastVariable = this.target.lookupBroadcastMsg(broadcastOption.id, broadcastOption.value); // TODO: empty string probably isn't the correct fallback

          const broadcastName = broadcastVariable ? broadcastVariable.name : '';
          return {
            kind: 'constant',
            value: broadcastName
          };
        }

      case 'looks_backdropnumbername':
        if (block.fields.NUMBER_NAME.value === 'number') {
          return {
            kind: 'looks.backdropNumber'
          };
        }

        return {
          kind: 'looks.backdropName'
        };

      case 'looks_costumenumbername':
        if (block.fields.NUMBER_NAME.value === 'number') {
          return {
            kind: 'looks.costumeNumber'
          };
        }

        return {
          kind: 'looks.costumeName'
        };

      case 'looks_size':
        return {
          kind: 'looks.size'
        };

      case 'motion_direction':
        return {
          kind: 'motion.direction'
        };

      case 'motion_xposition':
        return {
          kind: 'motion.x'
        };

      case 'motion_yposition':
        return {
          kind: 'motion.y'
        };

      case 'operator_add':
        return {
          kind: 'op.add',
          left: this.descendInputOfBlock(block, 'NUM1'),
          right: this.descendInputOfBlock(block, 'NUM2')
        };

      case 'operator_and':
        return {
          kind: 'op.and',
          left: this.descendInputOfBlock(block, 'OPERAND1'),
          right: this.descendInputOfBlock(block, 'OPERAND2')
        };

      case 'operator_contains':
        return {
          kind: 'op.contains',
          string: this.descendInputOfBlock(block, 'STRING1'),
          contains: this.descendInputOfBlock(block, 'STRING2')
        };

      case 'operator_divide':
        return {
          kind: 'op.divide',
          left: this.descendInputOfBlock(block, 'NUM1'),
          right: this.descendInputOfBlock(block, 'NUM2')
        };

      case 'operator_equals':
        return {
          kind: 'op.equals',
          left: this.descendInputOfBlock(block, 'OPERAND1'),
          right: this.descendInputOfBlock(block, 'OPERAND2')
        };

      case 'operator_gt':
        return {
          kind: 'op.greater',
          left: this.descendInputOfBlock(block, 'OPERAND1'),
          right: this.descendInputOfBlock(block, 'OPERAND2')
        };

      case 'operator_join':
        return {
          kind: 'op.join',
          left: this.descendInputOfBlock(block, 'STRING1'),
          right: this.descendInputOfBlock(block, 'STRING2')
        };

      case 'operator_length':
        return {
          kind: 'op.length',
          string: this.descendInputOfBlock(block, 'STRING')
        };

      case 'operator_letter_of':
        return {
          kind: 'op.letterOf',
          letter: this.descendInputOfBlock(block, 'LETTER'),
          string: this.descendInputOfBlock(block, 'STRING')
        };

      case 'operator_lt':
        return {
          kind: 'op.less',
          left: this.descendInputOfBlock(block, 'OPERAND1'),
          right: this.descendInputOfBlock(block, 'OPERAND2')
        };

      case 'operator_mathop':
        {
          const value = this.descendInputOfBlock(block, 'NUM');
          const operator = block.fields.OPERATOR.value.toLowerCase();

          switch (operator) {
            case 'abs':
              return {
                kind: 'op.abs',
                value
              };

            case 'floor':
              return {
                kind: 'op.floor',
                value
              };

            case 'ceiling':
              return {
                kind: 'op.ceiling',
                value
              };

            case 'sqrt':
              return {
                kind: 'op.sqrt',
                value
              };

            case 'sin':
              return {
                kind: 'op.sin',
                value
              };

            case 'cos':
              return {
                kind: 'op.cos',
                value
              };

            case 'tan':
              return {
                kind: 'op.tan',
                value
              };

            case 'asin':
              return {
                kind: 'op.asin',
                value
              };

            case 'acos':
              return {
                kind: 'op.acos',
                value
              };

            case 'atan':
              return {
                kind: 'op.atan',
                value
              };

            case 'ln':
              return {
                kind: 'op.ln',
                value
              };

            case 'log':
              return {
                kind: 'op.log',
                value
              };

            case 'e ^':
              return {
                kind: 'op.e^',
                value
              };

            case '10 ^':
              return {
                kind: 'op.10^',
                value
              };

            default:
              return {
                kind: 'constant',
                value: 0
              };
          }
        }

      case 'operator_mod':
        return {
          kind: 'op.mod',
          left: this.descendInputOfBlock(block, 'NUM1'),
          right: this.descendInputOfBlock(block, 'NUM2')
        };

      case 'operator_multiply':
        return {
          kind: 'op.multiply',
          left: this.descendInputOfBlock(block, 'NUM1'),
          right: this.descendInputOfBlock(block, 'NUM2')
        };

      case 'operator_not':
        return {
          kind: 'op.not',
          operand: this.descendInputOfBlock(block, 'OPERAND')
        };

      case 'operator_or':
        return {
          kind: 'op.or',
          left: this.descendInputOfBlock(block, 'OPERAND1'),
          right: this.descendInputOfBlock(block, 'OPERAND2')
        };

      case 'operator_random':
        {
          const from = this.descendInputOfBlock(block, 'FROM');
          const to = this.descendInputOfBlock(block, 'TO'); // If both values are known at compile time, we can do some optimizations.
          // TODO: move optimizations to jsgen?

          if (from.kind === 'constant' && to.kind === 'constant') {
            const sFrom = from.value;
            const sTo = to.value;
            const nFrom = Cast.toNumber(sFrom);
            const nTo = Cast.toNumber(sTo); // If both numbers are the same, random is unnecessary.
            // todo: this probably never happens so consider removing

            if (nFrom === nTo) {
              return {
                kind: 'constant',
                value: nFrom
              };
            } // If both are ints, hint this to the compiler


            if (Cast.isInt(sFrom) && Cast.isInt(sTo)) {
              return {
                kind: 'op.random',
                low: nFrom <= nTo ? from : to,
                high: nFrom <= nTo ? to : from,
                useInts: true,
                useFloats: false
              };
            } // Otherwise hint that these are floats


            return {
              kind: 'op.random',
              low: nFrom <= nTo ? from : to,
              high: nFrom <= nTo ? to : from,
              useInts: false,
              useFloats: true
            };
          } else if (from.kind === 'constant') {
            // If only one value is known at compile-time, we can still attempt some optimizations.
            if (!Cast.isInt(Cast.toNumber(from.value))) {
              return {
                kind: 'op.random',
                low: from,
                high: to,
                useInts: false,
                useFloats: true
              };
            }
          } else if (to.kind === 'constant') {
            if (!Cast.isInt(Cast.toNumber(to.value))) {
              return {
                kind: 'op.random',
                low: from,
                high: to,
                useInts: false,
                useFloats: true
              };
            }
          } // No optimizations possible


          return {
            kind: 'op.random',
            low: from,
            high: to,
            useInts: false,
            useFloats: false
          };
        }

      case 'operator_round':
        return {
          kind: 'op.round',
          value: this.descendInputOfBlock(block, 'NUM')
        };

      case 'operator_subtract':
        return {
          kind: 'op.subtract',
          left: this.descendInputOfBlock(block, 'NUM1'),
          right: this.descendInputOfBlock(block, 'NUM2')
        };

      case 'sensing_answer':
        return {
          kind: 'sensing.answer'
        };

      case 'sensing_coloristouchingcolor':
        return {
          kind: 'sensing.colorTouchingColor',
          target: this.descendInputOfBlock(block, 'COLOR2'),
          mask: this.descendInputOfBlock(block, 'COLOR')
        };

      case 'sensing_current':
        switch (block.fields.CURRENTMENU.value.toLowerCase()) {
          case 'year':
            return {
              kind: 'sensing.year'
            };

          case 'month':
            return {
              kind: 'sensing.month'
            };

          case 'date':
            return {
              kind: 'sensing.date'
            };

          case 'dayofweek':
            return {
              kind: 'sensing.dayofweek'
            };

          case 'hour':
            return {
              kind: 'sensing.hour'
            };

          case 'minute':
            return {
              kind: 'sensing.minute'
            };

          case 'second':
            return {
              kind: 'sensing.second'
            };
        }

        return {
          kind: 'constant',
          value: 0
        };

      case 'sensing_dayssince2000':
        return {
          kind: 'sensing.daysSince2000'
        };

      case 'sensing_distanceto':
        return {
          kind: 'sensing.distance',
          target: this.descendInputOfBlock(block, 'DISTANCETOMENU')
        };

      case 'sensing_keypressed':
        return {
          kind: 'keyboard.pressed',
          key: this.descendInputOfBlock(block, 'KEY_OPTION')
        };

      case 'sensing_mousedown':
        return {
          kind: 'mouse.down'
        };

      case 'sensing_mousex':
        return {
          kind: 'mouse.x'
        };

      case 'sensing_mousey':
        return {
          kind: 'mouse.y'
        };

      case 'sensing_of':
        return {
          kind: 'sensing.of',
          property: block.fields.PROPERTY.value,
          object: this.descendInputOfBlock(block, 'OBJECT')
        };

      case 'sensing_timer':
        this.usesTimer = true;
        return {
          kind: 'timer.get'
        };

      case 'sensing_touchingcolor':
        return {
          kind: 'sensing.touchingColor',
          color: this.descendInputOfBlock(block, 'COLOR')
        };

      case 'sensing_touchingobject':
        return {
          kind: 'sensing.touching',
          object: this.descendInputOfBlock(block, 'TOUCHINGOBJECTMENU')
        };

      case 'sensing_username':
        return {
          kind: 'sensing.username'
        };

      case 'sound_sounds_menu':
        // This menu is special compared to other menus -- it actually has an opcode function.
        return {
          kind: 'constant',
          value: block.fields.SOUND_MENU.value
        };

      case 'tw_getLastKeyPressed':
        return {
          kind: 'tw.lastKeyPressed'
        };

      default:
        {
          const opcodeFunction = this.runtime.getOpcodeFunction(block.opcode);

          if (opcodeFunction) {
            // It might be a non-compiled primitive from a standard category
            if (compatBlocks.inputs.includes(block.opcode)) {
              return this.descendCompatLayer(block);
            } // It might be an extension block.


            const blockInfo = this.getBlockInfo(block.opcode);

            if (blockInfo) {
              const type = blockInfo.info.blockType;

              if (type === BlockType.REPORTER || type === BlockType.BOOLEAN) {
                return this.descendCompatLayer(block);
              }
            }
          } // It might be a menu.


          const inputs = Object.keys(block.inputs);
          const fields = Object.keys(block.fields);

          if (inputs.length === 0 && fields.length === 1) {
            return {
              kind: 'constant',
              value: block.fields[fields[0]].value
            };
          }

          log.warn("IR: Unknown input: ".concat(block.opcode), block);
          throw new Error("IR: Unknown input: ".concat(block.opcode));
        }
    }
  }
  /**
   * Descend into a stacked block. (eg. "move ( ) steps")
   * @param {*} block The Scratch block to parse.
   * @private
   * @returns {Node} Compiled node for this block.
   */


  descendStackedBlock(block) {
    switch (block.opcode) {
      case 'control_all_at_once':
        // In Scratch 3, this block behaves like "if 1 = 1"
        return {
          kind: 'control.if',
          condition: {
            kind: 'constant',
            value: true
          },
          whenTrue: this.descendSubstack(block, 'SUBSTACK'),
          whenFalse: []
        };

      case 'control_create_clone_of':
        return {
          kind: 'control.createClone',
          target: this.descendInputOfBlock(block, 'CLONE_OPTION')
        };

      case 'control_delete_this_clone':
        this.script.yields = true;
        return {
          kind: 'control.deleteClone'
        };

      case 'control_forever':
        this.analyzeLoop();
        return {
          kind: 'control.while',
          condition: {
            kind: 'constant',
            value: true
          },
          do: this.descendSubstack(block, 'SUBSTACK')
        };

      case 'control_for_each':
        this.analyzeLoop();
        return {
          kind: 'control.for',
          variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE),
          count: this.descendInputOfBlock(block, 'VALUE'),
          do: this.descendSubstack(block, 'SUBSTACK')
        };

      case 'control_if':
        return {
          kind: 'control.if',
          condition: this.descendInputOfBlock(block, 'CONDITION'),
          whenTrue: this.descendSubstack(block, 'SUBSTACK'),
          whenFalse: []
        };

      case 'control_if_else':
        return {
          kind: 'control.if',
          condition: this.descendInputOfBlock(block, 'CONDITION'),
          whenTrue: this.descendSubstack(block, 'SUBSTACK'),
          whenFalse: this.descendSubstack(block, 'SUBSTACK2')
        };

      case 'control_repeat':
        this.analyzeLoop();
        return {
          kind: 'control.repeat',
          times: this.descendInputOfBlock(block, 'TIMES'),
          do: this.descendSubstack(block, 'SUBSTACK')
        };

      case 'control_repeat_until':
        {
          this.analyzeLoop(); // Dirty hack: automatically enable warp timer for this block if it uses timer
          // This fixes project that do things like "repeat until timer > 0.5"

          this.usesTimer = false;
          const condition = this.descendInputOfBlock(block, 'CONDITION');
          const needsWarpTimer = this.usesTimer;

          if (needsWarpTimer) {
            this.script.yields = true;
          }

          return {
            kind: 'control.while',
            condition: {
              kind: 'op.not',
              operand: condition
            },
            do: this.descendSubstack(block, 'SUBSTACK'),
            warpTimer: needsWarpTimer
          };
        }

      case 'control_stop':
        {
          const level = block.fields.STOP_OPTION.value;

          if (level === 'all') {
            this.script.yields = true;
            return {
              kind: 'control.stopAll'
            };
          } else if (level === 'other scripts in sprite' || level === 'other scripts in stage') {
            return {
              kind: 'control.stopOthers'
            };
          } else if (level === 'this script') {
            return {
              kind: 'control.stopScript'
            };
          }

          return {
            kind: 'noop'
          };
        }

      case 'control_wait':
        this.script.yields = true;
        return {
          kind: 'control.wait',
          seconds: this.descendInputOfBlock(block, 'DURATION')
        };

      case 'control_wait_until':
        this.script.yields = true;
        return {
          kind: 'control.waitUntil',
          condition: this.descendInputOfBlock(block, 'CONDITION')
        };

      case 'control_while':
        this.analyzeLoop();
        return {
          kind: 'control.while',
          condition: this.descendInputOfBlock(block, 'CONDITION'),
          do: this.descendSubstack(block, 'SUBSTACK'),
          // We should consider analyzing this like we do for control_repeat_until
          warpTimer: false
        };

      case 'data_addtolist':
        return {
          kind: 'list.add',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          item: this.descendInputOfBlock(block, 'ITEM')
        };

      case 'data_changevariableby':
        {
          const variable = this.descendVariable(block, 'VARIABLE', SCALAR_TYPE);
          return {
            kind: 'var.set',
            variable,
            value: {
              kind: 'op.add',
              left: {
                kind: 'var.get',
                variable
              },
              right: this.descendInputOfBlock(block, 'VALUE')
            }
          };
        }

      case 'data_deletealloflist':
        return {
          kind: 'list.deleteAll',
          list: this.descendVariable(block, 'LIST', LIST_TYPE)
        };

      case 'data_deleteoflist':
        {
          const index = this.descendInputOfBlock(block, 'INDEX');

          if (index.kind === 'constant' && index.value === 'all') {
            return {
              kind: 'list.deleteAll',
              list: this.descendVariable(block, 'LIST', LIST_TYPE)
            };
          }

          return {
            kind: 'list.delete',
            list: this.descendVariable(block, 'LIST', LIST_TYPE),
            index: index
          };
        }

      case 'data_hidelist':
        return {
          kind: 'list.hide',
          list: this.descendVariable(block, 'LIST', LIST_TYPE)
        };

      case 'data_hidevariable':
        return {
          kind: 'var.hide',
          variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE)
        };

      case 'data_insertatlist':
        return {
          kind: 'list.insert',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          index: this.descendInputOfBlock(block, 'INDEX'),
          item: this.descendInputOfBlock(block, 'ITEM')
        };

      case 'data_replaceitemoflist':
        return {
          kind: 'list.replace',
          list: this.descendVariable(block, 'LIST', LIST_TYPE),
          index: this.descendInputOfBlock(block, 'INDEX'),
          item: this.descendInputOfBlock(block, 'ITEM')
        };

      case 'data_setvariableto':
        return {
          kind: 'var.set',
          variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE),
          value: this.descendInputOfBlock(block, 'VALUE')
        };

      case 'data_showlist':
        return {
          kind: 'list.show',
          list: this.descendVariable(block, 'LIST', LIST_TYPE)
        };

      case 'data_showvariable':
        return {
          kind: 'var.show',
          variable: this.descendVariable(block, 'VARIABLE', SCALAR_TYPE)
        };

      case 'event_broadcast':
        return {
          kind: 'event.broadcast',
          broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT')
        };

      case 'event_broadcastandwait':
        this.script.yields = true;
        return {
          kind: 'event.broadcastAndWait',
          broadcast: this.descendInputOfBlock(block, 'BROADCAST_INPUT')
        };

      case 'looks_changeeffectby':
        return {
          kind: 'looks.changeEffect',
          effect: block.fields.EFFECT.value.toLowerCase(),
          value: this.descendInputOfBlock(block, 'CHANGE')
        };

      case 'looks_changesizeby':
        return {
          kind: 'looks.changeSize',
          size: this.descendInputOfBlock(block, 'CHANGE')
        };

      case 'looks_cleargraphiceffects':
        return {
          kind: 'looks.clearEffects'
        };

      case 'looks_goforwardbackwardlayers':
        if (block.fields.FORWARD_BACKWARD.value === 'forward') {
          return {
            kind: 'looks.forwardLayers',
            layers: this.descendInputOfBlock(block, 'NUM')
          };
        }

        return {
          kind: 'looks.backwardLayers',
          layers: this.descendInputOfBlock(block, 'NUM')
        };

      case 'looks_gotofrontback':
        if (block.fields.FRONT_BACK.value === 'front') {
          return {
            kind: 'looks.goToFront'
          };
        }

        return {
          kind: 'looks.goToBack'
        };

      case 'looks_hide':
        return {
          kind: 'looks.hide'
        };

      case 'looks_nextbackdrop':
        return {
          kind: 'looks.nextBackdrop'
        };

      case 'looks_nextcostume':
        return {
          kind: 'looks.nextCostume'
        };

      case 'looks_seteffectto':
        return {
          kind: 'looks.setEffect',
          effect: block.fields.EFFECT.value.toLowerCase(),
          value: this.descendInputOfBlock(block, 'VALUE')
        };

      case 'looks_setsizeto':
        return {
          kind: 'looks.setSize',
          size: this.descendInputOfBlock(block, 'SIZE')
        };

      case 'looks_show':
        return {
          kind: 'looks.show'
        };

      case 'looks_switchbackdropto':
        return {
          kind: 'looks.switchBackdrop',
          backdrop: this.descendInputOfBlock(block, 'BACKDROP')
        };

      case 'looks_switchcostumeto':
        return {
          kind: 'looks.switchCostume',
          costume: this.descendInputOfBlock(block, 'COSTUME')
        };

      case 'motion_changexby':
        return {
          kind: 'motion.changeX',
          dx: this.descendInputOfBlock(block, 'DX')
        };

      case 'motion_changeyby':
        return {
          kind: 'motion.changeY',
          dy: this.descendInputOfBlock(block, 'DY')
        };

      case 'motion_gotoxy':
        return {
          kind: 'motion.setXY',
          x: this.descendInputOfBlock(block, 'X'),
          y: this.descendInputOfBlock(block, 'Y')
        };

      case 'motion_ifonedgebounce':
        return {
          kind: 'motion.ifOnEdgeBounce'
        };

      case 'motion_movesteps':
        return {
          kind: 'motion.step',
          steps: this.descendInputOfBlock(block, 'STEPS')
        };

      case 'motion_pointindirection':
        return {
          kind: 'motion.setDirection',
          direction: this.descendInputOfBlock(block, 'DIRECTION')
        };

      case 'motion_setrotationstyle':
        return {
          kind: 'motion.setRotationStyle',
          style: block.fields.STYLE.value
        };

      case 'motion_setx':
        return {
          kind: 'motion.setX',
          x: this.descendInputOfBlock(block, 'X')
        };

      case 'motion_sety':
        return {
          kind: 'motion.setY',
          y: this.descendInputOfBlock(block, 'Y')
        };

      case 'motion_turnleft':
        return {
          kind: 'motion.setDirection',
          direction: {
            kind: 'op.subtract',
            left: {
              kind: 'motion.direction'
            },
            right: this.descendInputOfBlock(block, 'DEGREES')
          }
        };

      case 'motion_turnright':
        return {
          kind: 'motion.setDirection',
          direction: {
            kind: 'op.add',
            left: {
              kind: 'motion.direction'
            },
            right: this.descendInputOfBlock(block, 'DEGREES')
          }
        };

      case 'pen_clear':
        return {
          kind: 'pen.clear'
        };

      case 'pen_changePenColorParamBy':
        return {
          kind: 'pen.changeParam',
          param: this.descendInputOfBlock(block, 'COLOR_PARAM'),
          value: this.descendInputOfBlock(block, 'VALUE')
        };

      case 'pen_changePenHueBy':
        return {
          kind: 'pen.legacyChangeHue',
          hue: this.descendInputOfBlock(block, 'HUE')
        };

      case 'pen_changePenShadeBy':
        return {
          kind: 'pen.legacyChangeShade',
          shade: this.descendInputOfBlock(block, 'SHADE')
        };

      case 'pen_penDown':
        return {
          kind: 'pen.down'
        };

      case 'pen_penUp':
        return {
          kind: 'pen.up'
        };

      case 'pen_setPenColorParamTo':
        return {
          kind: 'pen.setParam',
          param: this.descendInputOfBlock(block, 'COLOR_PARAM'),
          value: this.descendInputOfBlock(block, 'VALUE')
        };

      case 'pen_setPenColorToColor':
        return {
          kind: 'pen.setColor',
          color: this.descendInputOfBlock(block, 'COLOR')
        };

      case 'pen_setPenHueToNumber':
        return {
          kind: 'pen.legacySetHue',
          hue: this.descendInputOfBlock(block, 'HUE')
        };

      case 'pen_setPenShadeToNumber':
        return {
          kind: 'pen.legacySetShade',
          shade: this.descendInputOfBlock(block, 'SHADE')
        };

      case 'pen_setPenSizeTo':
        return {
          kind: 'pen.setSize',
          size: this.descendInputOfBlock(block, 'SIZE')
        };

      case 'pen_changePenSizeBy':
        return {
          kind: 'pen.changeSize',
          size: this.descendInputOfBlock(block, 'SIZE')
        };

      case 'pen_stamp':
        return {
          kind: 'pen.stamp'
        };

      case 'procedures_call':
        {
          // setting of yields will be handled later in the analysis phase
          const procedureCode = block.mutation.proccode;

          if (procedureCode === 'tw:debugger;') {
            return {
              kind: 'tw.debugger'
            };
          }

          const paramNamesIdsAndDefaults = this.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);

          if (paramNamesIdsAndDefaults === null) {
            return {
              kind: 'noop'
            };
          }

          const [paramNames, paramIds, paramDefaults] = paramNamesIdsAndDefaults;
          const addonBlock = this.runtime.getAddonBlock(procedureCode);

          if (addonBlock) {
            this.script.yields = true;
            const args = {};

            for (let i = 0; i < paramIds.length; i++) {
              let value;

              if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
                value = this.descendInputOfBlock(block, paramIds[i]);
              } else {
                value = {
                  kind: 'constant',
                  value: paramDefaults[i]
                };
              }

              args[paramNames[i]] = value;
            }

            return {
              kind: 'addons.call',
              code: procedureCode,
              arguments: args,
              blockId: block.id
            };
          }

          const definitionId = this.blocks.getProcedureDefinition(procedureCode);
          const definitionBlock = this.blocks.getBlock(definitionId);

          if (!definitionBlock) {
            return {
              kind: 'noop'
            };
          }

          const innerDefinition = this.blocks.getBlock(definitionBlock.inputs.custom_block.block);
          let isWarp = this.script.isWarp;

          if (!isWarp) {
            if (innerDefinition && innerDefinition.mutation) {
              const warp = innerDefinition.mutation.warp;

              if (typeof warp === 'boolean') {
                isWarp = warp;
              } else if (typeof warp === 'string') {
                isWarp = JSON.parse(warp);
              }
            }
          }

          const variant = generateProcedureVariant(procedureCode, isWarp);

          if (!this.script.dependedProcedures.includes(variant)) {
            this.script.dependedProcedures.push(variant);
          } // Non-warp direct recursion yields.


          if (!this.script.isWarp) {
            if (procedureCode === this.script.procedureCode) {
              this.script.yields = true;
            }
          }

          const args = [];

          for (let i = 0; i < paramIds.length; i++) {
            let value;

            if (block.inputs[paramIds[i]] && block.inputs[paramIds[i]].block) {
              value = this.descendInputOfBlock(block, paramIds[i]);
            } else {
              value = {
                kind: 'constant',
                value: paramDefaults[i]
              };
            }

            args.push(value);
          }

          return {
            kind: 'procedures.call',
            code: procedureCode,
            variant,
            arguments: args
          };
        }

      case 'sensing_resettimer':
        return {
          kind: 'timer.reset'
        };

      default:
        {
          const opcodeFunction = this.runtime.getOpcodeFunction(block.opcode);

          if (opcodeFunction) {
            // It might be a non-compiled primitive from a standard category
            if (compatBlocks.stacked.includes(block.opcode)) {
              return this.descendCompatLayer(block);
            } // It might be an extension block.


            const blockInfo = this.getBlockInfo(block.opcode);

            if (blockInfo) {
              const type = blockInfo.info.blockType;

              if (type === BlockType.COMMAND) {
                return this.descendCompatLayer(block);
              }
            }
          } // When this thread was triggered by a stack click, attempt to compile as an input.
          // TODO: perhaps this should be moved to generate()?


          if (this.thread.stackClick) {
            try {
              const inputNode = this.descendInput(block);
              return {
                kind: 'visualReport',
                input: inputNode
              };
            } catch (e) {// Ignore
            }
          }

          log.warn("IR: Unknown stacked block: ".concat(block.opcode), block);
          throw new Error("IR: Unknown stacked block: ".concat(block.opcode));
        }
    }
  }
  /**
   * Descend into a stack of blocks (eg. the blocks contained within an "if" block)
   * @param {*} parentBlock The parent Scratch block that contains the stack to parse.
   * @param {*} substackName The name of the stack to descend into.
   * @private
   * @returns {Node[]} List of stacked block nodes.
   */


  descendSubstack(parentBlock, substackName) {
    const input = parentBlock.inputs[substackName];

    if (!input) {
      return [];
    }

    const stackId = input.block;
    return this.walkStack(stackId);
  }
  /**
   * Descend into and walk the siblings of a stack.
   * @param {string} startingBlockId The ID of the first block of a stack.
   * @private
   * @returns {Node[]} List of stacked block nodes.
   */


  walkStack(startingBlockId) {
    const result = [];
    let blockId = startingBlockId;

    while (blockId !== null) {
      const block = this.getBlockById(blockId);

      if (!block) {
        break;
      }

      const node = this.descendStackedBlock(block);
      result.push(node);
      blockId = block.next;
    }

    return result;
  }
  /**
   * Descend into a variable.
   * @param {*} block The block that has the variable.
   * @param {string} fieldName The name of the field that the variable is stored in.
   * @param {''|'list'} type Variable type, '' for scalar and 'list' for list.
   * @private
   * @returns {*} A parsed variable object.
   */


  descendVariable(block, fieldName, type) {
    const variable = block.fields[fieldName];
    const id = variable.id;

    if (this.variableCache.hasOwnProperty(id)) {
      return this.variableCache[id];
    }

    const data = this._descendVariable(id, variable.value, type);

    this.variableCache[id] = data;
    return data;
  }
  /**
   * @param {string} id The ID of the variable.
   * @param {string} name The name of the variable.
   * @param {''|'list'} type The variable type.
   * @private
   * @returns {*} A parsed variable object.
   */


  _descendVariable(id, name, type) {
    const target = this.target;
    const stage = this.stage; // Look for by ID in target...

    if (target.variables.hasOwnProperty(id)) {
      return createVariableData('target', target.variables[id]);
    } // Look for by ID in stage...


    if (!target.isStage) {
      if (stage && stage.variables.hasOwnProperty(id)) {
        return createVariableData('stage', stage.variables[id]);
      }
    } // Look for by name and type in target...


    for (const varId in target.variables) {
      if (target.variables.hasOwnProperty(varId)) {
        const currVar = target.variables[varId];

        if (currVar.name === name && currVar.type === type) {
          return createVariableData('target', currVar);
        }
      }
    } // Look for by name and type in stage...


    if (!target.isStage && stage) {
      for (const varId in stage.variables) {
        if (stage.variables.hasOwnProperty(varId)) {
          const currVar = stage.variables[varId];

          if (currVar.name === name && currVar.type === type) {
            return createVariableData('stage', currVar);
          }
        }
      }
    } // Create it locally...


    const newVariable = new Variable(id, name, type, false);
    target.variables[id] = newVariable;

    if (target.sprite) {
      // Create the variable in all instances of this sprite.
      // This is necessary because the script cache is shared between clones.
      // sprite.clones has all instances of this sprite including the original and all clones
      for (const clone of target.sprite.clones) {
        if (!clone.variables.hasOwnProperty(id)) {
          clone.variables[id] = new Variable(id, name, type, false);
        }
      }
    }

    return createVariableData('target', newVariable);
  }
  /**
   * Descend into a block that uses the compatibility layer.
   * @param {*} block The block to use the compatibility layer for.
   * @private
   * @returns {Node} The parsed node.
   */


  descendCompatLayer(block) {
    this.script.yields = true;
    const inputs = {};
    const fields = {};

    for (const name of Object.keys(block.inputs)) {
      inputs[name] = this.descendInputOfBlock(block, name);
    }

    for (const name of Object.keys(block.fields)) {
      fields[name] = block.fields[name].value;
    }

    return {
      kind: 'compat',
      opcode: block.opcode,
      inputs,
      fields
    };
  }

  analyzeLoop() {
    if (!this.script.isWarp || this.script.warpTimer) {
      this.script.yields = true;
    }
  }

  readTopBlockComment(commentId) {
    const comment = this.target.comments[commentId];

    if (!comment) {
      // can't find the comment
      // this is safe to ignore
      return;
    }

    const text = comment.text;

    for (const line of text.split('\n')) {
      if (!/^tw\b/.test(line)) {
        continue;
      }

      const flags = line.split(' ');

      for (const flag of flags) {
        switch (flag) {
          case 'nocompile':
            throw new Error('Script explicitly disables compilation');

          case 'stuck':
            this.script.warpTimer = true;
            break;
        }
      } // Only the first 'tw' line is parsed.


      break;
    }
  }
  /**
   * @param {string} topBlockId The ID of the top block of the script.
   * @returns {IntermediateScript}
   */


  generate(topBlockId) {
    this.blocks.populateProcedureCache();
    this.script.topBlockId = topBlockId;
    const topBlock = this.getBlockById(topBlockId);

    if (!topBlock) {
      if (this.script.isProcedure) {
        // Empty procedure
        return this.script;
      }

      throw new Error('Cannot find top block');
    }

    if (topBlock.comment) {
      this.readTopBlockComment(topBlock.comment);
    } // If the top block is a hat, advance to its child.


    let entryBlock;

    if (this.runtime.getIsHat(topBlock.opcode) || topBlock.opcode === 'procedures_definition') {
      if (this.runtime.getIsEdgeActivatedHat(topBlock.opcode)) {
        throw new Error("Not compiling an edge-activated hat: ".concat(topBlock.opcode));
      }

      entryBlock = topBlock.next;
    } else {
      entryBlock = topBlockId;
    }

    if (!entryBlock) {
      // This is an empty script.
      return this.script;
    }

    this.script.stack = this.walkStack(entryBlock);
    return this.script;
  }

}

class IRGenerator {
  constructor(thread) {
    this.thread = thread;
    this.blocks = thread.blockContainer;
    this.proceduresToCompile = new Map();
    this.compilingProcedures = new Map();
    /** @type {Object.<string, IntermediateScript>} */

    this.procedures = {};
    this.analyzedProcedures = [];
  }

  addProcedureDependencies(dependencies) {
    for (const procedureVariant of dependencies) {
      if (this.procedures.hasOwnProperty(procedureVariant)) {
        continue;
      }

      if (this.compilingProcedures.has(procedureVariant)) {
        continue;
      }

      if (this.proceduresToCompile.has(procedureVariant)) {
        continue;
      }

      const procedureCode = parseProcedureCode(procedureVariant);
      const definition = this.blocks.getProcedureDefinition(procedureCode);
      this.proceduresToCompile.set(procedureVariant, definition);
    }
  }
  /**
   * @param {ScriptTreeGenerator} generator The generator to run.
   * @param {string} topBlockId The ID of the top block in the stack.
   * @returns {IntermediateScript} Intermediate script.
   */


  generateScriptTree(generator, topBlockId) {
    const result = generator.generate(topBlockId);
    this.addProcedureDependencies(result.dependedProcedures);
    return result;
  }
  /**
   * Recursively analyze a script and its dependencies.
   * @param {IntermediateScript} script Intermediate script.
   */


  analyzeScript(script) {
    let madeChanges = false;

    for (const procedureCode of script.dependedProcedures) {
      const procedureData = this.procedures[procedureCode]; // Analyze newly found procedures.

      if (!this.analyzedProcedures.includes(procedureCode)) {
        this.analyzedProcedures.push(procedureCode);

        if (this.analyzeScript(procedureData)) {
          madeChanges = true;
        }

        this.analyzedProcedures.pop();
      } // If a procedure used by a script may yield, the script itself may yield.


      if (procedureData.yields && !script.yields) {
        script.yields = true;
        madeChanges = true;
      }
    }

    return madeChanges;
  }
  /**
   * @returns {IntermediateRepresentation} Intermediate representation.
   */


  generate() {
    const entry = this.generateScriptTree(new ScriptTreeGenerator(this.thread), this.thread.topBlock); // Compile any required procedures.
    // As procedures can depend on other procedures, this process may take several iterations.

    const procedureTreeCache = this.blocks._cache.compiledProcedures;

    while (this.proceduresToCompile.size > 0) {
      this.compilingProcedures = this.proceduresToCompile;
      this.proceduresToCompile = new Map();

      for (const [procedureVariant, definitionId] of this.compilingProcedures.entries()) {
        if (procedureTreeCache[procedureVariant]) {
          const result = procedureTreeCache[procedureVariant];
          this.procedures[procedureVariant] = result;
          this.addProcedureDependencies(result.dependedProcedures);
        } else {
          const isWarp = parseIsWarp(procedureVariant);
          const generator = new ScriptTreeGenerator(this.thread);
          generator.setProcedureVariant(procedureVariant);
          if (isWarp) generator.enableWarp();
          const compiledProcedure = this.generateScriptTree(generator, definitionId);
          this.procedures[procedureVariant] = compiledProcedure;
          procedureTreeCache[procedureVariant] = compiledProcedure;
        }
      }
    } // Analyze scripts until no changes are made.


    while (this.analyzeScript(entry));

    const ir = new IntermediateRepresentation();
    ir.entry = entry;
    ir.procedures = this.procedures;
    return ir;
  }

}

module.exports = IRGenerator;

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/jsexecute.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/jsexecute.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview Runtime for scripts generated by jsgen
 */

/* eslint-disable no-unused-vars */

/* eslint-disable prefer-template */

/* eslint-disable valid-jsdoc */

/* eslint-disable max-len */
const globalState = {
  Timer: __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js"),
  Cast: __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js"),
  log: __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js"),
  blockUtility: __webpack_require__(/*! ./compat-block-utility */ "./node_modules/scratch-vm/src/compiler/compat-block-utility.js"),
  thread: null
};
let baseRuntime = '';
const runtimeFunctions = {};
/**
 * Determine whether the current tick is likely stuck.
 * This implements similar functionality to the warp timer found in Scratch.
 * @returns {boolean} true if the current tick is likely stuck.
 */

baseRuntime += "let stuckCounter = 0;\nconst isStuck = () => {\n    // The real time is not checked on every call for performance.\n    stuckCounter++;\n    if (stuckCounter === 100) {\n        stuckCounter = 0;\n        return globalState.thread.target.runtime.sequencer.timer.timeElapsed() > 500;\n    }\n    return false;\n};";
/**
 * Start hats by opcode.
 * @param {string} requestedHat The opcode of the hat to start.
 * @param {*} optMatchFields Fields to match.
 * @returns {Array} A list of threads that were started.
 */

runtimeFunctions.startHats = "const startHats = (requestedHat, optMatchFields) => {\n    const thread = globalState.thread;\n    const threads = thread.target.runtime.startHats(requestedHat, optMatchFields);\n    return threads;\n}";
/**
 * Implements "thread waiting", where scripts are halted until all the scripts have finished executing.
 * @param {Array} threads The list of threads.
 */

runtimeFunctions.waitThreads = "const waitThreads = function*(threads) {\n    const thread = globalState.thread;\n    const runtime = thread.target.runtime;\n\n    while (true) {\n        // determine whether any threads are running\n        let anyRunning = false;\n        for (let i = 0; i < threads.length; i++) {\n            if (runtime.threads.indexOf(threads[i]) !== -1) {\n                anyRunning = true;\n                break;\n            }\n        }\n        if (!anyRunning) {\n            // all threads are finished, can resume\n            return;\n        }\n\n        let allWaiting = true;\n        for (let i = 0; i < threads.length; i++) {\n            if (!runtime.isWaitingThread(threads[i])) {\n                allWaiting = false;\n                break;\n            }\n        }\n        if (allWaiting) {\n            thread.status = 3; // STATUS_YIELD_TICK\n        }\n\n        yield;\n    }\n}";
/**
 * waitPromise: Wait until a Promise resolves or rejects before continuing.
 * @param {Promise} promise The promise to wait for.
 * @returns {*} the value that the promise resolves to, otherwise undefined if the promise rejects
 */

/**
 * isPromise: Determine if a value is Promise-like
 * @param {unknown} promise The value to check
 * @returns {promise is PromiseLike} True if the value is Promise-like (has a .then())
 */

/**
 * executeInCompatibilityLayer: Execute a scratch-vm primitive.
 * @param {*} inputs The inputs to pass to the block.
 * @param {function} blockFunction The primitive's function.
 * @param {boolean} useFlags Whether to set flags (hasResumedFromPromise)
 * @param {string} blockId Block ID to set on the emulated block utility.
 * @returns {*} the value returned by the block, if any.
 */

runtimeFunctions.executeInCompatibilityLayer = "let hasResumedFromPromise = false;\nconst waitPromise = function*(promise) {\n    const thread = globalState.thread;\n    let returnValue;\n\n    promise\n        .then(value => {\n            returnValue = value;\n            thread.status = 0; // STATUS_RUNNING\n        })\n        .catch(error => {\n            thread.status = 0; // STATUS_RUNNING\n            globalState.log.warn('Promise rejected in compiled script:', error);\n        });\n\n    // enter STATUS_PROMISE_WAIT and yield\n    // this will stop script execution until the promise handlers reset the thread status\n    thread.status = 1; // STATUS_PROMISE_WAIT\n    yield;\n\n    return returnValue;\n};\nconst isPromise = value => (\n    // see engine/execute.js\n    value !== null &&\n    typeof value === 'object' &&\n    typeof value.then === 'function'\n);\nconst executeInCompatibilityLayer = function*(inputs, blockFunction, isWarp, useFlags, blockId) {\n    const thread = globalState.thread;\n\n    // reset the stackframe\n    // we only ever use one stackframe at a time, so this shouldn't cause issues\n    thread.stackFrames[thread.stackFrames.length - 1].reuse(isWarp);\n\n    const executeBlock = () => {\n        const blockUtility = globalState.blockUtility;\n        blockUtility.init(thread, blockId);\n        return blockFunction(inputs, blockUtility);\n    };\n\n    let returnValue = executeBlock();\n\n    if (isPromise(returnValue)) {\n        returnValue = yield* waitPromise(returnValue);\n        if (useFlags) {\n            hasResumedFromPromise = true;\n        }\n        return returnValue;\n    }\n\n    if (thread.status === 1 /* STATUS_PROMISE_WAIT */) {\n        // Something external is forcing us to stop\n        yield;\n        // Make up a return value because whatever is forcing us to stop can't specify one\n        return '';\n    }\n\n    while (thread.status === 2 /* STATUS_YIELD */ || thread.status === 3 /* STATUS_YIELD_TICK */) {\n        // Yielded threads will run next iteration.\n        if (thread.status === 2 /* STATUS_YIELD */) {\n            thread.status = 0; // STATUS_RUNNING\n            // Yield back to the event loop when stuck or not in warp mode.\n            if (!isWarp || isStuck()) {\n                yield;\n            }\n        } else {\n            // status is STATUS_YIELD_TICK, always yield to the event loop\n            yield;\n        }\n\n        returnValue = executeBlock();\n\n        if (isPromise(returnValue)) {\n            returnValue = yield* waitPromise(returnValue);\n            if (useFlags) {\n                hasResumedFromPromise = true;\n            }\n            return returnValue;\n        }\n\n        if (thread.status === 1 /* STATUS_PROMISE_WAIT */) {\n            yield;\n            return '';\n        }\n    }\n\n    // todo: do we have to do anything extra if status is STATUS_DONE?\n\n    return returnValue;\n}";
/**
 * End the current script.
 */

runtimeFunctions.retire = "const retire = () => {\n    const thread = globalState.thread;\n    thread.target.runtime.sequencer.retireThread(thread);\n}";
/**
 * Scratch cast to boolean.
 * Similar to Cast.toBoolean()
 * @param {*} value The value to cast
 * @returns {boolean} The value cast to a boolean
 */

runtimeFunctions.toBoolean = "const toBoolean = value => {\n    if (typeof value === 'boolean') {\n        return value;\n    }\n    if (typeof value === 'string') {\n        if (value === '' || value === '0' || value.toLowerCase() === 'false') {\n            return false;\n        }\n        return true;\n    }\n    return !!value;\n}";
/**
 * If a number is very close to a whole number, round to that whole number.
 * @param {number} value Value to round
 * @returns {number} Rounded number or original number
 */

runtimeFunctions.limitPrecision = "const limitPrecision = value => {\n    const rounded = Math.round(value);\n    const delta = value - rounded;\n    return (Math.abs(delta) < 1e-9) ? rounded : value;\n}";
/**
 * Used internally by the compare family of function.
 * See similar method in cast.js.
 * @param {*} val A value that evaluates to 0 in JS string-to-number conversation such as empty string, 0, or tab.
 * @returns {boolean} True if the value should not be treated as the number zero.
 */

baseRuntime += "const isNotActuallyZero = val => {\n    if (typeof val !== 'string') return false;\n    for (let i = 0; i < val.length; i++) {\n        const code = val.charCodeAt(i);\n        if (code === 48 || code === 9) {\n            return false;\n        }\n    }\n    return true;\n};";
/**
 * Determine if two values are equal.
 * @param {*} v1 First value
 * @param {*} v2 Second value
 * @returns {boolean} true if v1 is equal to v2
 */

baseRuntime += "const compareEqualSlow = (v1, v2) => {\n    const n1 = +v1;\n    if (isNaN(n1) || (n1 === 0 && isNotActuallyZero(v1))) return ('' + v1).toLowerCase() === ('' + v2).toLowerCase();\n    const n2 = +v2;\n    if (isNaN(n2) || (n2 === 0 && isNotActuallyZero(v2))) return ('' + v1).toLowerCase() === ('' + v2).toLowerCase();\n    return n1 === n2;\n};\nconst compareEqual = (v1, v2) => (typeof v1 === 'number' && typeof v2 === 'number' && !isNaN(v1) && !isNaN(v2) || v1 === v2) ? v1 === v2 : compareEqualSlow(v1, v2);";
/**
 * Determine if one value is greater than another.
 * @param {*} v1 First value
 * @param {*} v2 Second value
 * @returns {boolean} true if v1 is greater than v2
 */

runtimeFunctions.compareGreaterThan = "const compareGreaterThanSlow = (v1, v2) => {\n    let n1 = +v1;\n    let n2 = +v2;\n    if (n1 === 0 && isNotActuallyZero(v1)) {\n        n1 = NaN;\n    } else if (n2 === 0 && isNotActuallyZero(v2)) {\n        n2 = NaN;\n    }\n    if (isNaN(n1) || isNaN(n2)) {\n        const s1 = ('' + v1).toLowerCase();\n        const s2 = ('' + v2).toLowerCase();\n        return s1 > s2;\n    }\n    return n1 > n2;\n};\nconst compareGreaterThan = (v1, v2) => typeof v1 === 'number' && typeof v2 === 'number' && !isNaN(v1) ? v1 > v2 : compareGreaterThanSlow(v1, v2)";
/**
 * Determine if one value is less than another.
 * @param {*} v1 First value
 * @param {*} v2 Second value
 * @returns {boolean} true if v1 is less than v2
 */

runtimeFunctions.compareLessThan = "const compareLessThanSlow = (v1, v2) => {\n    let n1 = +v1;\n    let n2 = +v2;\n    if (n1 === 0 && isNotActuallyZero(v1)) {\n        n1 = NaN;\n    } else if (n2 === 0 && isNotActuallyZero(v2)) {\n        n2 = NaN;\n    }\n    if (isNaN(n1) || isNaN(n2)) {\n        const s1 = ('' + v1).toLowerCase();\n        const s2 = ('' + v2).toLowerCase();\n        return s1 < s2;\n    }\n    return n1 < n2;\n};\nconst compareLessThan = (v1, v2) => typeof v1 === 'number' && typeof v2 === 'number' && !isNaN(v2) ? v1 < v2 : compareLessThanSlow(v1, v2)";
/**
 * Generate a random integer.
 * @param {number} low Lower bound
 * @param {number} high Upper bound
 * @returns {number} A random integer between low and high, inclusive.
 */

runtimeFunctions.randomInt = "const randomInt = (low, high) => low + Math.floor(Math.random() * ((high + 1) - low))";
/**
 * Generate a random float.
 * @param {number} low Lower bound
 * @param {number} high Upper bound
 * @returns {number} A random floating point number between low and high.
 */

runtimeFunctions.randomFloat = "const randomFloat = (low, high) => (Math.random() * (high - low)) + low";
/**
 * Create and start a timer.
 * @returns {Timer} A started timer
 */

runtimeFunctions.timer = "const timer = () => {\n    const t = new globalState.Timer({\n        now: () => globalState.thread.target.runtime.currentMSecs\n    });\n    t.start();\n    return t;\n}";
/**
 * Returns the amount of days since January 1st, 2000.
 * @returns {number} Days since 2000.
 */
// Date.UTC(2000, 0, 1) === 946684800000
// Hardcoding it is marginally faster

runtimeFunctions.daysSince2000 = "const daysSince2000 = () => (Date.now() - 946684800000) / (24 * 60 * 60 * 1000)";
/**
 * Determine distance to a sprite or point.
 * @param {string} menu The name of the sprite or location to find.
 * @returns {number} Distance to the point, or 10000 if it cannot be calculated.
 */

runtimeFunctions.distance = "const distance = menu => {\n    const thread = globalState.thread;\n    if (thread.target.isStage) return 10000;\n\n    let targetX = 0;\n    let targetY = 0;\n    if (menu === '_mouse_') {\n        targetX = thread.target.runtime.ioDevices.mouse.getScratchX();\n        targetY = thread.target.runtime.ioDevices.mouse.getScratchY();\n    } else {\n        const distTarget = thread.target.runtime.getSpriteTargetByName(menu);\n        if (!distTarget) return 10000;\n        targetX = distTarget.x;\n        targetY = distTarget.y;\n    }\n\n    const dx = thread.target.x - targetX;\n    const dy = thread.target.y - targetY;\n    return Math.sqrt((dx * dx) + (dy * dy));\n}";
/**
 * Convert a Scratch list index to a JavaScript list index.
 * "all" is not considered as a list index.
 * Similar to Cast.toListIndex()
 * @param {number} index Scratch list index.
 * @param {number} length Length of the list.
 * @returns {number} 0 based list index, or -1 if invalid.
 */

baseRuntime += "const listIndexSlow = (index, length) => {\n    if (index === 'last') {\n        return length - 1;\n    } else if (index === 'random' || index === 'any') {\n        if (length > 0) {\n            return (Math.random() * length) | 0;\n        }\n        return -1;\n    }\n    index = (+index || 0) | 0;\n    if (index < 1 || index > length) {\n        return -1;\n    }\n    return index - 1;\n};\nconst listIndex = (index, length) => {\n    if (typeof index !== 'number') {\n      return listIndexSlow(index, length);\n    }\n    index = index | 0;\n    return index < 1 || index > length ? -1 : index - 1;\n};";
/**
 * Get a value from a list.
 * @param {Array} list The list
 * @param {*} idx The 1-indexed index in the list.
 * @returns {*} The list item, otherwise empty string if it does not exist.
 */

runtimeFunctions.listGet = "const listGet = (list, idx) => {\n    const index = listIndex(idx, list.length);\n    if (index === -1) {\n        return '';\n    }\n    return list[index];\n}";
/**
 * Replace a value in a list.
 * @param {import('../engine/variable')} list The list
 * @param {*} idx List index, Scratch style.
 * @param {*} value The new value.
 */

runtimeFunctions.listReplace = "const listReplace = (list, idx, value) => {\n    const index = listIndex(idx, list.value.length);\n    if (index === -1) {\n        return;\n    }\n    list.value[index] = value;\n    list._monitorUpToDate = false;\n}";
/**
 * Insert a value in a list.
 * @param {import('../engine/variable')} list The list.
 * @param {*} idx The Scratch index in the list.
 * @param {*} value The value to insert.
 */

runtimeFunctions.listInsert = "const listInsert = (list, idx, value) => {\n    const index = listIndex(idx, list.value.length + 1);\n    if (index === -1) {\n        return;\n    }\n    list.value.splice(index, 0, value);\n    list._monitorUpToDate = false;\n}";
/**
 * Delete a value from a list.
 * @param {import('../engine/variable')} list The list.
 * @param {*} idx The Scratch index in the list.
 */

runtimeFunctions.listDelete = "const listDelete = (list, idx) => {\n    if (idx === 'all') {\n        list.value = [];\n        return;\n    }\n    const index = listIndex(idx, list.value.length);\n    if (index === -1) {\n        return;\n    }\n    list.value.splice(index, 1);\n    list._monitorUpToDate = false;\n}";
/**
 * Return whether a list contains a value.
 * @param {import('../engine/variable')} list The list.
 * @param {*} item The value to search for.
 * @returns {boolean} True if the list contains the item
 */

runtimeFunctions.listContains = "const listContains = (list, item) => {\n    // TODO: evaluate whether indexOf is worthwhile here\n    if (list.value.indexOf(item) !== -1) {\n        return true;\n    }\n    for (let i = 0; i < list.value.length; i++) {\n        if (compareEqual(list.value[i], item)) {\n            return true;\n        }\n    }\n    return false;\n}";
/**
 * Find the 1-indexed index of an item in a list.
 * @param {import('../engine/variable')} list The list.
 * @param {*} item The item to search for
 * @returns {number} The 1-indexed index of the item in the list, otherwise 0
 */

runtimeFunctions.listIndexOf = "const listIndexOf = (list, item) => {\n    for (let i = 0; i < list.value.length; i++) {\n        if (compareEqual(list.value[i], item)) {\n            return i + 1;\n        }\n    }\n    return 0;\n}";
/**
 * Get the stringified form of a list.
 * @param {import('../engine/variable')} list The list.
 * @returns {string} Stringified form of the list.
 */

runtimeFunctions.listContents = "const listContents = list => {\n    for (let i = 0; i < list.value.length; i++) {\n        const listItem = list.value[i];\n        // this is an intentional break from what scratch 3 does to address our automatic string -> number conversions\n        // it fixes more than it breaks\n        if ((listItem + '').length !== 1) {\n            return list.value.join(' ');\n        }\n    }\n    return list.value.join('');\n}";
/**
 * Convert a color to an RGB list
 * @param {*} color The color value to convert
 * @return {Array.<number>} [r,g,b], values between 0-255.
 */

runtimeFunctions.colorToList = "const colorToList = color => globalState.Cast.toRgbColorList(color)";
/**
 * Implements Scratch modulo (floored division instead of truncated division)
 * @param {number} n Number
 * @param {number} modulus Base
 * @returns {number} n % modulus (floored division)
 */

runtimeFunctions.mod = "const mod = (n, modulus) => {\n    let result = n % modulus;\n    if (result / modulus < 0) result += modulus;\n    return result;\n}";
/**
 * Implements Scratch tangent.
 * @param {number} angle Angle in degrees.
 * @returns {number} value of tangent or Infinity or -Infinity
 */

runtimeFunctions.tan = "const tan = (angle) => {\n    switch (angle % 360) {\n    case -270: case 90: return Infinity;\n    case -90: case 270: return -Infinity;\n    }\n    return Math.round(Math.tan((Math.PI * angle) / 180) * 1e10) / 1e10;\n}";
/**
 * Step a compiled thread.
 * @param {Thread} thread The thread to step.
 */

const execute = thread => {
  globalState.thread = thread;
  thread.generator.next();
};

const insertRuntime = source => {
  let result = baseRuntime;

  for (const functionName of Object.keys(runtimeFunctions)) {
    if (source.includes(functionName)) {
      result += "".concat(runtimeFunctions[functionName], ";");
    }
  }

  result += "return ".concat(source);
  return result;
};
/**
 * Evaluate arbitrary JS in the context of the runtime.
 * @param {string} source The string to evaluate.
 * @returns {*} The result of evaluating the string.
 */


const scopedEval = source => {
  const withRuntime = insertRuntime(source);

  try {
    return new Function('globalState', withRuntime)(globalState);
  } catch (e) {
    globalState.log.error('was unable to compile script', withRuntime);
    throw e;
  }
};

execute.scopedEval = scopedEval;
execute.runtimeFunctions = runtimeFunctions;
module.exports = execute;

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/jsgen.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/jsgen.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const VariablePool = __webpack_require__(/*! ./variable-pool */ "./node_modules/scratch-vm/src/compiler/variable-pool.js");

const jsexecute = __webpack_require__(/*! ./jsexecute */ "./node_modules/scratch-vm/src/compiler/jsexecute.js");

const environment = __webpack_require__(/*! ./environment */ "./node_modules/scratch-vm/src/compiler/environment.js"); // Imported for JSDoc types, not to actually use
// eslint-disable-next-line no-unused-vars


const {
  IntermediateScript,
  IntermediateRepresentation
} = __webpack_require__(/*! ./intermediate */ "./node_modules/scratch-vm/src/compiler/intermediate.js");
/**
 * @fileoverview Convert intermediate representations to JavaScript functions.
 */

/* eslint-disable max-len */

/* eslint-disable prefer-template */


const sanitize = string => {
  if (typeof string !== 'string') {
    log.warn("sanitize got unexpected type: ".concat(typeof string));
    string = '' + string;
  }

  return JSON.stringify(string).slice(1, -1);
};

const TYPE_NUMBER = 1;
const TYPE_STRING = 2;
const TYPE_BOOLEAN = 3;
const TYPE_UNKNOWN = 4;
const TYPE_NUMBER_NAN = 5; // Pen-related constants

const PEN_EXT = 'runtime.ext_pen';
const PEN_STATE = "".concat(PEN_EXT, "._getPenState(target)");
/**
 * Variable pool used for factory function names.
 */

const factoryNameVariablePool = new VariablePool('factory');
/**
 * Variable pool used for generated functions (non-generator)
 */

const functionNameVariablePool = new VariablePool('fun');
/**
 * Variable pool used for generated generator functions.
 */

const generatorNameVariablePool = new VariablePool('gen');
/**
 * @typedef Input
 * @property {() => string} asNumber
 * @property {() => string} asNumberOrNaN
 * @property {() => string} asString
 * @property {() => string} asBoolean
 * @property {() => string} asColor
 * @property {() => string} asUnknown
 * @property {() => string} asSafe
 * @property {() => boolean} isAlwaysNumber
 * @property {() => boolean} isAlwaysNumberOrNaN
 * @property {() => boolean} isNeverNumber
 */

/**
 * @implements {Input}
 */

class TypedInput {
  constructor(source, type) {
    // for debugging
    if (typeof type !== 'number') throw new Error('type is invalid');
    this.source = source;
    this.type = type;
  }

  asNumber() {
    if (this.type === TYPE_NUMBER) return this.source;
    if (this.type === TYPE_NUMBER_NAN) return "(".concat(this.source, " || 0)");
    return "(+".concat(this.source, " || 0)");
  }

  asNumberOrNaN() {
    if (this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN) return this.source;
    return "(+".concat(this.source, ")");
  }

  asString() {
    if (this.type === TYPE_STRING) return this.source;
    return "(\"\" + ".concat(this.source, ")");
  }

  asBoolean() {
    if (this.type === TYPE_BOOLEAN) return this.source;
    return "toBoolean(".concat(this.source, ")");
  }

  asColor() {
    return this.asUnknown();
  }

  asUnknown() {
    return this.source;
  }

  asSafe() {
    return this.asUnknown();
  }

  isAlwaysNumber() {
    return this.type === TYPE_NUMBER;
  }

  isAlwaysNumberOrNaN() {
    return this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN;
  }

  isNeverNumber() {
    return false;
  }

}
/**
 * @implements {Input}
 */


class ConstantInput {
  constructor(constantValue, safe) {
    this.constantValue = constantValue;
    this.safe = safe;
  }

  asNumber() {
    // Compute at compilation time
    const numberValue = +this.constantValue;

    if (numberValue) {
      // It's important that we use the number's stringified value and not the constant value
      // Using the constant value allows numbers such as "010" to be interpreted as 8 (or SyntaxError in strict mode) instead of 10.
      return numberValue.toString();
    } // numberValue is one of 0, -0, or NaN


    if (Object.is(numberValue, -0)) {
      return '-0';
    }

    return '0';
  }

  asNumberOrNaN() {
    return this.asNumber();
  }

  asString() {
    return "\"".concat(sanitize('' + this.constantValue), "\"");
  }

  asBoolean() {
    // Compute at compilation time
    return Cast.toBoolean(this.constantValue).toString();
  }

  asColor() {
    // Attempt to parse hex code at compilation time
    if (/^#[0-9a-f]{6,8}$/i.test(this.constantValue)) {
      const hex = this.constantValue.substr(1);
      return Number.parseInt(hex, 16).toString();
    }

    return this.asUnknown();
  }

  asUnknown() {
    // Attempt to convert strings to numbers if it is unlikely to break things
    if (typeof this.constantValue === 'number') {
      // todo: handle NaN?
      return this.constantValue;
    }

    const numberValue = +this.constantValue;

    if (numberValue.toString() === this.constantValue) {
      return this.constantValue;
    }

    return this.asString();
  }

  asSafe() {
    if (this.safe) {
      return this.asUnknown();
    }

    return this.asString();
  }

  isAlwaysNumber() {
    const value = +this.constantValue;

    if (Number.isNaN(value)) {
      return false;
    } // Empty strings evaluate to 0 but should not be considered a number.


    if (value === 0) {
      return this.constantValue.toString().trim() !== '';
    }

    return true;
  }

  isAlwaysNumberOrNaN() {
    return this.isAlwaysNumber();
  }

  isNeverNumber() {
    return Number.isNaN(+this.constantValue);
  }

}
/**
 * @implements {Input}
 */


class VariableInput {
  constructor(source) {
    this.source = source;
    this.type = TYPE_UNKNOWN;
    /**
     * The value this variable was most recently set to, if any.
     * @type {Input}
     * @private
     */

    this._value = null;
  }
  /**
   * @param {Input} input The input this variable was most recently set to.
   */


  setInput(input) {
    if (input instanceof VariableInput) {
      // When being set to another variable, extract the value it was set to.
      // Otherwise, you may end up with infinite recursion in analysis methods when a variable is set to itself.
      if (input._value) {
        input = input._value;
      } else {
        this.type = TYPE_UNKNOWN;
        this._value = null;
        return;
      }
    }

    this._value = input;

    if (input instanceof TypedInput) {
      this.type = input.type;
    } else {
      this.type = TYPE_UNKNOWN;
    }
  }

  asNumber() {
    if (this.type === TYPE_NUMBER) return this.source;
    if (this.type === TYPE_NUMBER_NAN) return "(".concat(this.source, " || 0)");
    return "(+".concat(this.source, " || 0)");
  }

  asNumberOrNaN() {
    if (this.type === TYPE_NUMBER || this.type === TYPE_NUMBER_NAN) return this.source;
    return "(+".concat(this.source, ")");
  }

  asString() {
    if (this.type === TYPE_STRING) return this.source;
    return "(\"\" + ".concat(this.source, ")");
  }

  asBoolean() {
    if (this.type === TYPE_BOOLEAN) return this.source;
    return "toBoolean(".concat(this.source, ")");
  }

  asColor() {
    return this.asUnknown();
  }

  asUnknown() {
    return this.source;
  }

  asSafe() {
    return this.asUnknown();
  }

  isAlwaysNumber() {
    if (this._value) {
      return this._value.isAlwaysNumber();
    }

    return false;
  }

  isAlwaysNumberOrNaN() {
    if (this._value) {
      return this._value.isAlwaysNumberOrNaN();
    }

    return false;
  }

  isNeverNumber() {
    if (this._value) {
      return this._value.isNeverNumber();
    }

    return false;
  }

}

const getNamesOfCostumesAndSounds = runtime => {
  const result = new Set();

  for (const target of runtime.targets) {
    if (target.isOriginal) {
      const sprite = target.sprite;

      for (const costume of sprite.costumes) {
        result.add(costume.name);
      }

      for (const sound of sprite.sounds) {
        result.add(sound.name);
      }
    }
  }

  return result;
};

const isSafeConstantForEqualsOptimization = input => {
  const numberValue = +input.constantValue; // Do not optimize 0

  if (!numberValue) {
    return false;
  } // Do not optimize numbers when the original form does not match


  return numberValue.toString() === input.constantValue.toString();
};
/**
 * A frame contains some information about the current substack being compiled.
 */


class Frame {
  constructor(isLoop) {
    /**
     * Whether the current stack runs in a loop (while, for)
     * @type {boolean}
     * @readonly
     */
    this.isLoop = isLoop;
    /**
     * Whether the current block is the last block in the stack.
     * @type {boolean}
     */

    this.isLastBlock = false;
  }

}

class JSGenerator {
  /**
   * @param {IntermediateScript} script
   * @param {IntermediateRepresentation} ir
   * @param {Target} target
   */
  constructor(script, ir, target) {
    this.script = script;
    this.ir = ir;
    this.target = target;
    this.source = '';
    /**
     * @type {Object.<string, VariableInput>}
     */

    this.variableInputs = {};
    this.isWarp = script.isWarp;
    this.isProcedure = script.isProcedure;
    this.warpTimer = script.warpTimer;
    /**
     * Stack of frames, most recent is last item.
     * @type {Frame[]}
     */

    this.frames = [];
    /**
     * The current Frame.
     * @type {Frame}
     */

    this.currentFrame = null;
    this.namesOfCostumesAndSounds = getNamesOfCostumesAndSounds(target.runtime);
    this.localVariables = new VariablePool('a');
    this._setupVariablesPool = new VariablePool('b');
    this._setupVariables = {};
    this.descendedIntoModulo = false;
    this.debug = this.target.runtime.debug;
  }
  /**
   * Enter a new frame
   * @param {Frame} frame New frame.
   */


  pushFrame(frame) {
    this.frames.push(frame);
    this.currentFrame = frame;
  }
  /**
   * Exit the current frame
   */


  popFrame() {
    this.frames.pop();
    this.currentFrame = this.frames[this.frames.length - 1];
  }
  /**
   * @returns {boolean} true if the current block is the last command of a loop
   */


  isLastBlockInLoop() {
    for (let i = this.frames.length - 1; i >= 0; i--) {
      const frame = this.frames[i];

      if (!frame.isLastBlock) {
        return false;
      }

      if (frame.isLoop) {
        return true;
      }
    }

    return false;
  }
  /**
   * @param {object} node Input node to compile.
   * @returns {Input} Compiled input.
   */


  descendInput(node) {
    switch (node.kind) {
      case 'args.boolean':
        return new TypedInput("toBoolean(p".concat(node.index, ")"), TYPE_BOOLEAN);

      case 'args.stringNumber':
        return new TypedInput("p".concat(node.index), TYPE_UNKNOWN);

      case 'compat':
        // Compatibility layer inputs never use flags.
        return new TypedInput("(".concat(this.generateCompatibilityLayerCall(node, false), ")"), TYPE_UNKNOWN);

      case 'constant':
        return this.safeConstantInput(node.value);

      case 'keyboard.pressed':
        return new TypedInput("runtime.ioDevices.keyboard.getKeyIsDown(".concat(this.descendInput(node.key).asSafe(), ")"), TYPE_BOOLEAN);

      case 'list.contains':
        return new TypedInput("listContains(".concat(this.referenceVariable(node.list), ", ").concat(this.descendInput(node.item).asUnknown(), ")"), TYPE_BOOLEAN);

      case 'list.contents':
        return new TypedInput("listContents(".concat(this.referenceVariable(node.list), ")"), TYPE_STRING);

      case 'list.get':
        {
          const index = this.descendInput(node.index);

          if (environment.supportsNullishCoalescing) {
            if (index.isAlwaysNumberOrNaN()) {
              return new TypedInput("(".concat(this.referenceVariable(node.list), ".value[(").concat(index.asNumber(), " | 0) - 1] ?? \"\")"), TYPE_UNKNOWN);
            }

            if (index instanceof ConstantInput && index.constantValue === 'last') {
              return new TypedInput("(".concat(this.referenceVariable(node.list), ".value[").concat(this.referenceVariable(node.list), ".value.length - 1] ?? \"\")"), TYPE_UNKNOWN);
            }
          }

          return new TypedInput("listGet(".concat(this.referenceVariable(node.list), ".value, ").concat(index.asUnknown(), ")"), TYPE_UNKNOWN);
        }

      case 'list.indexOf':
        return new TypedInput("listIndexOf(".concat(this.referenceVariable(node.list), ", ").concat(this.descendInput(node.item).asUnknown(), ")"), TYPE_NUMBER);

      case 'list.length':
        return new TypedInput("".concat(this.referenceVariable(node.list), ".value.length"), TYPE_NUMBER);

      case 'looks.size':
        return new TypedInput('Math.round(target.size)', TYPE_NUMBER);

      case 'looks.backdropName':
        return new TypedInput('stage.getCostumes()[stage.currentCostume].name', TYPE_STRING);

      case 'looks.backdropNumber':
        return new TypedInput('(stage.currentCostume + 1)', TYPE_NUMBER);

      case 'looks.costumeName':
        return new TypedInput('target.getCostumes()[target.currentCostume].name', TYPE_STRING);

      case 'looks.costumeNumber':
        return new TypedInput('(target.currentCostume + 1)', TYPE_NUMBER);

      case 'motion.direction':
        return new TypedInput('target.direction', TYPE_NUMBER);

      case 'motion.x':
        return new TypedInput('limitPrecision(target.x)', TYPE_NUMBER);

      case 'motion.y':
        return new TypedInput('limitPrecision(target.y)', TYPE_NUMBER);

      case 'mouse.down':
        return new TypedInput('runtime.ioDevices.mouse.getIsDown()', TYPE_BOOLEAN);

      case 'mouse.x':
        return new TypedInput('runtime.ioDevices.mouse.getScratchX()', TYPE_NUMBER);

      case 'mouse.y':
        return new TypedInput('runtime.ioDevices.mouse.getScratchY()', TYPE_NUMBER);

      case 'op.abs':
        return new TypedInput("Math.abs(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'op.acos':
        // Needs to be marked as NaN because Math.acos(1.0001) === NaN
        return new TypedInput("((Math.acos(".concat(this.descendInput(node.value).asNumber(), ") * 180) / Math.PI)"), TYPE_NUMBER_NAN);

      case 'op.add':
        // Needs to be marked as NaN because Infinity + -Infinity === NaN
        return new TypedInput("(".concat(this.descendInput(node.left).asNumber(), " + ").concat(this.descendInput(node.right).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.and':
        return new TypedInput("(".concat(this.descendInput(node.left).asBoolean(), " && ").concat(this.descendInput(node.right).asBoolean(), ")"), TYPE_BOOLEAN);

      case 'op.asin':
        // Needs to be marked as NaN because Math.asin(1.0001) === NaN
        return new TypedInput("((Math.asin(".concat(this.descendInput(node.value).asNumber(), ") * 180) / Math.PI)"), TYPE_NUMBER_NAN);

      case 'op.atan':
        return new TypedInput("((Math.atan(".concat(this.descendInput(node.value).asNumber(), ") * 180) / Math.PI)"), TYPE_NUMBER);

      case 'op.ceiling':
        return new TypedInput("Math.ceil(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'op.contains':
        return new TypedInput("(".concat(this.descendInput(node.string).asString(), ".toLowerCase().indexOf(").concat(this.descendInput(node.contains).asString(), ".toLowerCase()) !== -1)"), TYPE_BOOLEAN);

      case 'op.cos':
        return new TypedInput("(Math.round(Math.cos((Math.PI * ".concat(this.descendInput(node.value).asNumber(), ") / 180) * 1e10) / 1e10)"), TYPE_NUMBER_NAN);

      case 'op.divide':
        // Needs to be marked as NaN because 0 / 0 === NaN
        return new TypedInput("(".concat(this.descendInput(node.left).asNumber(), " / ").concat(this.descendInput(node.right).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.equals':
        {
          const left = this.descendInput(node.left);
          const right = this.descendInput(node.right); // When both operands are known to never be numbers, only use string comparison to avoid all number parsing.

          if (left.isNeverNumber() || right.isNeverNumber()) {
            return new TypedInput("(".concat(left.asString(), ".toLowerCase() === ").concat(right.asString(), ".toLowerCase())"), TYPE_BOOLEAN);
          }

          const leftAlwaysNumber = left.isAlwaysNumber();
          const rightAlwaysNumber = right.isAlwaysNumber(); // When both operands are known to be numbers, we can use ===

          if (leftAlwaysNumber && rightAlwaysNumber) {
            return new TypedInput("(".concat(left.asNumber(), " === ").concat(right.asNumber(), ")"), TYPE_BOOLEAN);
          } // In certain conditions, we can use === when one of the operands is known to be a safe number.


          if (leftAlwaysNumber && left instanceof ConstantInput && isSafeConstantForEqualsOptimization(left)) {
            return new TypedInput("(".concat(left.asNumber(), " === ").concat(right.asNumber(), ")"), TYPE_BOOLEAN);
          }

          if (rightAlwaysNumber && right instanceof ConstantInput && isSafeConstantForEqualsOptimization(right)) {
            return new TypedInput("(".concat(left.asNumber(), " === ").concat(right.asNumber(), ")"), TYPE_BOOLEAN);
          } // No compile-time optimizations possible - use fallback method.


          return new TypedInput("compareEqual(".concat(left.asUnknown(), ", ").concat(right.asUnknown(), ")"), TYPE_BOOLEAN);
        }

      case 'op.e^':
        return new TypedInput("Math.exp(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'op.floor':
        return new TypedInput("Math.floor(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'op.greater':
        {
          const left = this.descendInput(node.left);
          const right = this.descendInput(node.right); // When the left operand is a number and the right operand is a number or NaN, we can use >

          if (left.isAlwaysNumber() && right.isAlwaysNumberOrNaN()) {
            return new TypedInput("(".concat(left.asNumber(), " > ").concat(right.asNumberOrNaN(), ")"), TYPE_BOOLEAN);
          } // When the left operand is a number or NaN and the right operand is a number, we can negate <=


          if (left.isAlwaysNumberOrNaN() && right.isAlwaysNumber()) {
            return new TypedInput("!(".concat(left.asNumberOrNaN(), " <= ").concat(right.asNumber(), ")"), TYPE_BOOLEAN);
          } // When either operand is known to never be a number, avoid all number parsing.


          if (left.isNeverNumber() || right.isNeverNumber()) {
            return new TypedInput("(".concat(left.asString(), ".toLowerCase() > ").concat(right.asString(), ".toLowerCase())"), TYPE_BOOLEAN);
          } // No compile-time optimizations possible - use fallback method.


          return new TypedInput("compareGreaterThan(".concat(left.asUnknown(), ", ").concat(right.asUnknown(), ")"), TYPE_BOOLEAN);
        }

      case 'op.join':
        return new TypedInput("(".concat(this.descendInput(node.left).asString(), " + ").concat(this.descendInput(node.right).asString(), ")"), TYPE_STRING);

      case 'op.length':
        return new TypedInput("".concat(this.descendInput(node.string).asString(), ".length"), TYPE_NUMBER);

      case 'op.less':
        {
          const left = this.descendInput(node.left);
          const right = this.descendInput(node.right); // When the left operand is a number or NaN and the right operand is a number, we can use <

          if (left.isAlwaysNumberOrNaN() && right.isAlwaysNumber()) {
            return new TypedInput("(".concat(left.asNumberOrNaN(), " < ").concat(right.asNumber(), ")"), TYPE_BOOLEAN);
          } // When the left operand is a number and the right operand is a number or NaN, we can negate >=


          if (left.isAlwaysNumber() && right.isAlwaysNumberOrNaN()) {
            return new TypedInput("!(".concat(left.asNumber(), " >= ").concat(right.asNumberOrNaN(), ")"), TYPE_BOOLEAN);
          } // When either operand is known to never be a number, avoid all number parsing.


          if (left.isNeverNumber() || right.isNeverNumber()) {
            return new TypedInput("(".concat(left.asString(), ".toLowerCase() < ").concat(right.asString(), ".toLowerCase())"), TYPE_BOOLEAN);
          } // No compile-time optimizations possible - use fallback method.


          return new TypedInput("compareLessThan(".concat(left.asUnknown(), ", ").concat(right.asUnknown(), ")"), TYPE_BOOLEAN);
        }

      case 'op.letterOf':
        return new TypedInput("((".concat(this.descendInput(node.string).asString(), ")[(").concat(this.descendInput(node.letter).asNumber(), " | 0) - 1] || \"\")"), TYPE_STRING);

      case 'op.ln':
        // Needs to be marked as NaN because Math.log(-1) == NaN
        return new TypedInput("Math.log(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.log':
        // Needs to be marked as NaN because Math.log(-1) == NaN
        return new TypedInput("(Math.log(".concat(this.descendInput(node.value).asNumber(), ") / Math.LN10)"), TYPE_NUMBER_NAN);

      case 'op.mod':
        this.descendedIntoModulo = true; // Needs to be marked as NaN because mod(0, 0) (and others) == NaN

        return new TypedInput("mod(".concat(this.descendInput(node.left).asNumber(), ", ").concat(this.descendInput(node.right).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.multiply':
        // Needs to be marked as NaN because Infinity * 0 === NaN
        return new TypedInput("(".concat(this.descendInput(node.left).asNumber(), " * ").concat(this.descendInput(node.right).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.not':
        return new TypedInput("!".concat(this.descendInput(node.operand).asBoolean()), TYPE_BOOLEAN);

      case 'op.or':
        return new TypedInput("(".concat(this.descendInput(node.left).asBoolean(), " || ").concat(this.descendInput(node.right).asBoolean(), ")"), TYPE_BOOLEAN);

      case 'op.random':
        if (node.useInts) {
          // Both inputs are ints, so we know neither are NaN
          return new TypedInput("randomInt(".concat(this.descendInput(node.low).asNumber(), ", ").concat(this.descendInput(node.high).asNumber(), ")"), TYPE_NUMBER);
        }

        if (node.useFloats) {
          return new TypedInput("randomFloat(".concat(this.descendInput(node.low).asNumber(), ", ").concat(this.descendInput(node.high).asNumber(), ")"), TYPE_NUMBER_NAN);
        }

        return new TypedInput("runtime.ext_scratch3_operators._random(".concat(this.descendInput(node.low).asUnknown(), ", ").concat(this.descendInput(node.high).asUnknown(), ")"), TYPE_NUMBER_NAN);

      case 'op.round':
        return new TypedInput("Math.round(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'op.sin':
        return new TypedInput("(Math.round(Math.sin((Math.PI * ".concat(this.descendInput(node.value).asNumber(), ") / 180) * 1e10) / 1e10)"), TYPE_NUMBER_NAN);

      case 'op.sqrt':
        // Needs to be marked as NaN because Math.sqrt(-1) === NaN
        return new TypedInput("Math.sqrt(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.subtract':
        // Needs to be marked as NaN because Infinity - Infinity === NaN
        return new TypedInput("(".concat(this.descendInput(node.left).asNumber(), " - ").concat(this.descendInput(node.right).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.tan':
        return new TypedInput("tan(".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER_NAN);

      case 'op.10^':
        return new TypedInput("(10 ** ".concat(this.descendInput(node.value).asNumber(), ")"), TYPE_NUMBER);

      case 'sensing.answer':
        return new TypedInput("runtime.ext_scratch3_sensing._answer", TYPE_STRING);

      case 'sensing.colorTouchingColor':
        return new TypedInput("target.colorIsTouchingColor(colorToList(".concat(this.descendInput(node.target).asColor(), "), colorToList(").concat(this.descendInput(node.mask).asColor(), "))"), TYPE_BOOLEAN);

      case 'sensing.date':
        return new TypedInput("(new Date().getDate())", TYPE_NUMBER);

      case 'sensing.dayofweek':
        return new TypedInput("(new Date().getDay() + 1)", TYPE_NUMBER);

      case 'sensing.daysSince2000':
        return new TypedInput('daysSince2000()', TYPE_NUMBER);

      case 'sensing.distance':
        // TODO: on stages, this can be computed at compile time
        return new TypedInput("distance(".concat(this.descendInput(node.target).asString(), ")"), TYPE_NUMBER);

      case 'sensing.hour':
        return new TypedInput("(new Date().getHours())", TYPE_NUMBER);

      case 'sensing.minute':
        return new TypedInput("(new Date().getMinutes())", TYPE_NUMBER);

      case 'sensing.month':
        return new TypedInput("(new Date().getMonth() + 1)", TYPE_NUMBER);

      case 'sensing.of':
        {
          const object = this.descendInput(node.object).asString();
          const property = node.property;

          if (node.object.kind === 'constant') {
            const isStage = node.object.value === '_stage_'; // Note that if target isn't a stage, we can't assume it exists

            const objectReference = isStage ? 'stage' : this.evaluateOnce("runtime.getSpriteTargetByName(".concat(object, ")"));

            if (property === 'volume') {
              return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".volume : 0)"), TYPE_NUMBER);
            }

            if (isStage) {
              switch (property) {
                case 'background #': // fallthrough for scratch 1.0 compatibility

                case 'backdrop #':
                  return new TypedInput("(".concat(objectReference, ".currentCostume + 1)"), TYPE_NUMBER);

                case 'backdrop name':
                  return new TypedInput("".concat(objectReference, ".getCostumes()[").concat(objectReference, ".currentCostume].name"), TYPE_STRING);
              }
            } else {
              switch (property) {
                case 'x position':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".x : 0)"), TYPE_NUMBER);

                case 'y position':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".y : 0)"), TYPE_NUMBER);

                case 'direction':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".direction : 0)"), TYPE_NUMBER);

                case 'costume #':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".currentCostume + 1 : 0)"), TYPE_NUMBER);

                case 'costume name':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".getCostumes()[").concat(objectReference, ".currentCostume].name : 0)"), TYPE_UNKNOWN);

                case 'size':
                  return new TypedInput("(".concat(objectReference, " ? ").concat(objectReference, ".size : 0)"), TYPE_NUMBER);
              }
            }

            const variableReference = this.evaluateOnce("".concat(objectReference, " && ").concat(objectReference, ".lookupVariableByNameAndType(\"").concat(sanitize(property), "\", \"\", true)"));
            return new TypedInput("(".concat(variableReference, " ? ").concat(variableReference, ".value : 0)"), TYPE_UNKNOWN);
          }

          return new TypedInput("runtime.ext_scratch3_sensing.getAttributeOf({OBJECT: ".concat(object, ", PROPERTY: \"").concat(sanitize(property), "\" })"), TYPE_UNKNOWN);
        }

      case 'sensing.second':
        return new TypedInput("(new Date().getSeconds())", TYPE_NUMBER);

      case 'sensing.touching':
        return new TypedInput("target.isTouchingObject(".concat(this.descendInput(node.object).asUnknown(), ")"), TYPE_BOOLEAN);

      case 'sensing.touchingColor':
        return new TypedInput("target.isTouchingColor(colorToList(".concat(this.descendInput(node.color).asColor(), "))"), TYPE_BOOLEAN);

      case 'sensing.username':
        return new TypedInput('runtime.ioDevices.userData.getUsername()', TYPE_STRING);

      case 'sensing.year':
        return new TypedInput("(new Date().getFullYear())", TYPE_NUMBER);

      case 'timer.get':
        return new TypedInput('runtime.ioDevices.clock.projectTimer()', TYPE_NUMBER);

      case 'tw.lastKeyPressed':
        return new TypedInput('runtime.ioDevices.keyboard.getLastKeyPressed()', TYPE_STRING);

      case 'var.get':
        return this.descendVariable(node.variable);

      default:
        log.warn("JS: Unknown input: ".concat(node.kind), node);
        throw new Error("JS: Unknown input: ".concat(node.kind));
    }
  }
  /**
   * @param {*} node Stacked node to compile.
   */


  descendStackedBlock(node) {
    switch (node.kind) {
      case 'addons.call':
        {
          const inputs = this.descendInputRecord(node.arguments);
          const blockFunction = "runtime.getAddonBlock(\"".concat(sanitize(node.code), "\").callback");
          const blockId = "\"".concat(sanitize(node.blockId), "\"");
          this.source += "yield* executeInCompatibilityLayer(".concat(inputs, ", ").concat(blockFunction, ", ").concat(this.isWarp, ", false, ").concat(blockId, ");\n");
          break;
        }

      case 'compat':
        {
          // If the last command in a loop returns a promise, immediately continue to the next iteration.
          // If you don't do this, the loop effectively yields twice per iteration and will run at half-speed.
          const isLastInLoop = this.isLastBlockInLoop();
          this.source += "".concat(this.generateCompatibilityLayerCall(node, isLastInLoop), ";\n");

          if (isLastInLoop) {
            this.source += 'if (hasResumedFromPromise) {hasResumedFromPromise = false;continue;}\n';
          }

          break;
        }

      case 'control.createClone':
        this.source += "runtime.ext_scratch3_control._createClone(".concat(this.descendInput(node.target).asString(), ", target);\n");
        break;

      case 'control.deleteClone':
        this.source += 'if (!target.isOriginal) {\n';
        this.source += '  runtime.disposeTarget(target);\n';
        this.source += '  runtime.stopForTarget(target);\n';
        this.retire();
        this.source += '}\n';
        break;

      case 'control.for':
        {
          this.resetVariableInputs();
          const index = this.localVariables.next();
          this.source += "var ".concat(index, " = 0; ");
          this.source += "while (".concat(index, " < ").concat(this.descendInput(node.count).asNumber(), ") { ");
          this.source += "".concat(index, "++; ");
          this.source += "".concat(this.referenceVariable(node.variable), ".value = ").concat(index, ";\n");
          this.descendStack(node.do, new Frame(true));
          this.yieldLoop();
          this.source += '}\n';
          break;
        }

      case 'control.if':
        this.source += "if (".concat(this.descendInput(node.condition).asBoolean(), ") {\n");
        this.descendStack(node.whenTrue, new Frame(false)); // only add the else branch if it won't be empty
        // this makes scripts have a bit less useless noise in them

        if (node.whenFalse.length) {
          this.source += "} else {\n";
          this.descendStack(node.whenFalse, new Frame(false));
        }

        this.source += "}\n";
        break;

      case 'control.repeat':
        {
          const i = this.localVariables.next();
          this.source += "for (var ".concat(i, " = ").concat(this.descendInput(node.times).asNumber(), "; ").concat(i, " >= 0.5; ").concat(i, "--) {\n");
          this.descendStack(node.do, new Frame(true));
          this.yieldLoop();
          this.source += "}\n";
          break;
        }

      case 'control.stopAll':
        this.source += 'runtime.stopAll();\n';
        this.retire();
        break;

      case 'control.stopOthers':
        this.source += 'runtime.stopForTarget(target, thread);\n';
        break;

      case 'control.stopScript':
        if (this.isProcedure) {
          this.source += 'return;\n';
        } else {
          this.retire();
        }

        break;

      case 'control.wait':
        {
          const duration = this.localVariables.next();
          this.source += "thread.timer = timer();\n";
          this.source += "var ".concat(duration, " = Math.max(0, 1000 * ").concat(this.descendInput(node.seconds).asNumber(), ");\n");
          this.requestRedraw(); // always yield at least once, even on 0 second durations

          this.yieldNotWarp();
          this.source += "while (thread.timer.timeElapsed() < ".concat(duration, ") {\n");
          this.yieldStuckOrNotWarp();
          this.source += '}\n';
          this.source += 'thread.timer = null;\n';
          break;
        }

      case 'control.waitUntil':
        {
          this.resetVariableInputs();
          this.source += "while (!".concat(this.descendInput(node.condition).asBoolean(), ") {\n");
          this.yieldStuckOrNotWarp();
          this.source += "}\n";
          break;
        }

      case 'control.while':
        this.resetVariableInputs();
        this.source += "while (".concat(this.descendInput(node.condition).asBoolean(), ") {\n");
        this.descendStack(node.do, new Frame(true));

        if (node.warpTimer) {
          this.yieldStuckOrNotWarp();
        } else {
          this.yieldLoop();
        }

        this.source += "}\n";
        break;

      case 'event.broadcast':
        this.source += "startHats(\"event_whenbroadcastreceived\", { BROADCAST_OPTION: ".concat(this.descendInput(node.broadcast).asString(), " });\n");
        this.resetVariableInputs();
        break;

      case 'event.broadcastAndWait':
        this.source += "yield* waitThreads(startHats(\"event_whenbroadcastreceived\", { BROADCAST_OPTION: ".concat(this.descendInput(node.broadcast).asString(), " }));\n");
        this.yielded();
        break;

      case 'list.add':
        {
          const list = this.referenceVariable(node.list);
          this.source += "".concat(list, ".value.push(").concat(this.descendInput(node.item).asSafe(), ");\n");
          this.source += "".concat(list, "._monitorUpToDate = false;\n");
          break;
        }

      case 'list.delete':
        {
          const list = this.referenceVariable(node.list);
          const index = this.descendInput(node.index);

          if (index instanceof ConstantInput) {
            if (index.constantValue === 'last') {
              this.source += "".concat(list, ".value.pop();\n");
              this.source += "".concat(list, "._monitorUpToDate = false;\n");
              break;
            }

            if (+index.constantValue === 1) {
              this.source += "".concat(list, ".value.shift();\n");
              this.source += "".concat(list, "._monitorUpToDate = false;\n");
              break;
            } // do not need a special case for all as that is handled in IR generation (list.deleteAll)

          }

          this.source += "listDelete(".concat(list, ", ").concat(index.asUnknown(), ");\n");
          break;
        }

      case 'list.deleteAll':
        this.source += "".concat(this.referenceVariable(node.list), ".value = [];\n");
        break;

      case 'list.hide':
        this.source += "runtime.monitorBlocks.changeBlock({ id: \"".concat(sanitize(node.list.id), "\", element: \"checkbox\", value: false }, runtime);\n");
        break;

      case 'list.insert':
        {
          const list = this.referenceVariable(node.list);
          const index = this.descendInput(node.index);
          const item = this.descendInput(node.item);

          if (index instanceof ConstantInput && +index.constantValue === 1) {
            this.source += "".concat(list, ".value.unshift(").concat(item.asSafe(), ");\n");
            this.source += "".concat(list, "._monitorUpToDate = false;\n");
            break;
          }

          this.source += "listInsert(".concat(list, ", ").concat(index.asUnknown(), ", ").concat(item.asSafe(), ");\n");
          break;
        }

      case 'list.replace':
        this.source += "listReplace(".concat(this.referenceVariable(node.list), ", ").concat(this.descendInput(node.index).asUnknown(), ", ").concat(this.descendInput(node.item).asSafe(), ");\n");
        break;

      case 'list.show':
        this.source += "runtime.monitorBlocks.changeBlock({ id: \"".concat(sanitize(node.list.id), "\", element: \"checkbox\", value: true }, runtime);\n");
        break;

      case 'looks.backwardLayers':
        if (!this.target.isStage) {
          this.source += "target.goBackwardLayers(".concat(this.descendInput(node.layers).asNumber(), ");\n");
        }

        break;

      case 'looks.clearEffects':
        this.source += 'target.clearEffects();\n';
        break;

      case 'looks.changeEffect':
        if (this.target.effects.hasOwnProperty(node.effect)) {
          this.source += "target.setEffect(\"".concat(sanitize(node.effect), "\", runtime.ext_scratch3_looks.clampEffect(\"").concat(sanitize(node.effect), "\", ").concat(this.descendInput(node.value).asNumber(), " + target.effects[\"").concat(sanitize(node.effect), "\"]));\n");
        }

        break;

      case 'looks.changeSize':
        this.source += "target.setSize(target.size + ".concat(this.descendInput(node.size).asNumber(), ");\n");
        break;

      case 'looks.forwardLayers':
        if (!this.target.isStage) {
          this.source += "target.goForwardLayers(".concat(this.descendInput(node.layers).asNumber(), ");\n");
        }

        break;

      case 'looks.goToBack':
        if (!this.target.isStage) {
          this.source += 'target.goToBack();\n';
        }

        break;

      case 'looks.goToFront':
        if (!this.target.isStage) {
          this.source += 'target.goToFront();\n';
        }

        break;

      case 'looks.hide':
        this.source += 'target.setVisible(false);\n';
        this.source += 'runtime.ext_scratch3_looks._renderBubble(target);\n';
        break;

      case 'looks.nextBackdrop':
        this.source += 'runtime.ext_scratch3_looks._setBackdrop(stage, stage.currentCostume + 1, true);\n';
        break;

      case 'looks.nextCostume':
        this.source += 'target.setCostume(target.currentCostume + 1);\n';
        break;

      case 'looks.setEffect':
        if (this.target.effects.hasOwnProperty(node.effect)) {
          this.source += "target.setEffect(\"".concat(sanitize(node.effect), "\", runtime.ext_scratch3_looks.clampEffect(\"").concat(sanitize(node.effect), "\", ").concat(this.descendInput(node.value).asNumber(), "));\n");
        }

        break;

      case 'looks.setSize':
        this.source += "target.setSize(".concat(this.descendInput(node.size).asNumber(), ");\n");
        break;

      case 'looks.show':
        this.source += 'target.setVisible(true);\n';
        this.source += 'runtime.ext_scratch3_looks._renderBubble(target);\n';
        break;

      case 'looks.switchBackdrop':
        this.source += "runtime.ext_scratch3_looks._setBackdrop(stage, ".concat(this.descendInput(node.backdrop).asSafe(), ");\n");
        break;

      case 'looks.switchCostume':
        this.source += "runtime.ext_scratch3_looks._setCostume(target, ".concat(this.descendInput(node.costume).asSafe(), ");\n");
        break;

      case 'motion.changeX':
        this.source += "target.setXY(target.x + ".concat(this.descendInput(node.dx).asNumber(), ", target.y);\n");
        break;

      case 'motion.changeY':
        this.source += "target.setXY(target.x, target.y + ".concat(this.descendInput(node.dy).asNumber(), ");\n");
        break;

      case 'motion.ifOnEdgeBounce':
        this.source += "runtime.ext_scratch3_motion._ifOnEdgeBounce(target);\n";
        break;

      case 'motion.setDirection':
        this.source += "target.setDirection(".concat(this.descendInput(node.direction).asNumber(), ");\n");
        break;

      case 'motion.setRotationStyle':
        this.source += "target.setRotationStyle(\"".concat(sanitize(node.style), "\");\n");
        break;

      case 'motion.setX': // fallthrough

      case 'motion.setY': // fallthrough

      case 'motion.setXY':
        {
          this.descendedIntoModulo = false;
          const x = 'x' in node ? this.descendInput(node.x).asNumber() : 'target.x';
          const y = 'y' in node ? this.descendInput(node.y).asNumber() : 'target.y';
          this.source += "target.setXY(".concat(x, ", ").concat(y, ");\n");

          if (this.descendedIntoModulo) {
            this.source += "if (target.interpolationData) target.interpolationData = null;\n";
          }

          break;
        }

      case 'motion.step':
        this.source += "runtime.ext_scratch3_motion._moveSteps(".concat(this.descendInput(node.steps).asNumber(), ", target);\n");
        break;

      case 'noop':
        // todo: remove noop entirely
        break;

      case 'pen.clear':
        this.source += "".concat(PEN_EXT, ".clear();\n");
        break;

      case 'pen.down':
        this.source += "".concat(PEN_EXT, "._penDown(target);\n");
        break;

      case 'pen.changeParam':
        this.source += "".concat(PEN_EXT, "._setOrChangeColorParam(").concat(this.descendInput(node.param).asString(), ", ").concat(this.descendInput(node.value).asNumber(), ", ").concat(PEN_STATE, ", true);\n");
        break;

      case 'pen.changeSize':
        this.source += "".concat(PEN_EXT, "._changePenSizeBy(").concat(this.descendInput(node.size).asNumber(), ", target);\n");
        break;

      case 'pen.legacyChangeHue':
        this.source += "".concat(PEN_EXT, "._changePenHueBy(").concat(this.descendInput(node.hue).asNumber(), ", target);\n");
        break;

      case 'pen.legacyChangeShade':
        this.source += "".concat(PEN_EXT, "._changePenShadeBy(").concat(this.descendInput(node.shade).asNumber(), ", target);\n");
        break;

      case 'pen.legacySetHue':
        this.source += "".concat(PEN_EXT, "._setPenHueToNumber(").concat(this.descendInput(node.hue).asNumber(), ", target);\n");
        break;

      case 'pen.legacySetShade':
        this.source += "".concat(PEN_EXT, "._setPenShadeToNumber(").concat(this.descendInput(node.shade).asNumber(), ", target);\n");
        break;

      case 'pen.setColor':
        this.source += "".concat(PEN_EXT, "._setPenColorToColor(").concat(this.descendInput(node.color).asColor(), ", target);\n");
        break;

      case 'pen.setParam':
        this.source += "".concat(PEN_EXT, "._setOrChangeColorParam(").concat(this.descendInput(node.param).asString(), ", ").concat(this.descendInput(node.value).asNumber(), ", ").concat(PEN_STATE, ", false);\n");
        break;

      case 'pen.setSize':
        this.source += "".concat(PEN_EXT, "._setPenSizeTo(").concat(this.descendInput(node.size).asNumber(), ", target);\n");
        break;

      case 'pen.stamp':
        this.source += "".concat(PEN_EXT, "._stamp(target);\n");
        break;

      case 'pen.up':
        this.source += "".concat(PEN_EXT, "._penUp(target);\n");
        break;

      case 'procedures.call':
        {
          const procedureCode = node.code;
          const procedureVariant = node.variant; // Do not generate any code for empty procedures.

          const procedureData = this.ir.procedures[procedureVariant];

          if (procedureData.stack === null) {
            break;
          }

          if (!this.isWarp && procedureCode === this.script.procedureCode) {
            // Direct recursion yields.
            this.yieldNotWarp();
          }

          if (procedureData.yields) {
            this.source += 'yield* ';

            if (!this.script.yields) {
              throw new Error('Script uses yielding procedure but is not marked as yielding.');
            }
          }

          this.source += "thread.procedures[\"".concat(sanitize(procedureVariant), "\"]("); // Only include arguments if the procedure accepts any.

          if (procedureData.arguments.length) {
            const args = [];

            for (const input of node.arguments) {
              args.push(this.descendInput(input).asSafe());
            }

            this.source += args.join(',');
          }

          this.source += ");\n"; // Variable input types may have changes after a procedure call.

          this.resetVariableInputs();
          break;
        }

      case 'timer.reset':
        this.source += 'runtime.ioDevices.clock.resetProjectTimer();\n';
        break;

      case 'tw.debugger':
        this.source += 'debugger;\n';
        break;

      case 'var.hide':
        this.source += "runtime.monitorBlocks.changeBlock({ id: \"".concat(sanitize(node.variable.id), "\", element: \"checkbox\", value: false }, runtime);\n");
        break;

      case 'var.set':
        {
          const variable = this.descendVariable(node.variable);
          const value = this.descendInput(node.value);
          variable.setInput(value);
          this.source += "".concat(variable.source, " = ").concat(value.asSafe(), ";\n");

          if (node.variable.isCloud) {
            this.source += "runtime.ioDevices.cloud.requestUpdateVariable(\"".concat(sanitize(node.variable.name), "\", ").concat(variable.source, ");\n");
          }

          break;
        }

      case 'var.show':
        this.source += "runtime.monitorBlocks.changeBlock({ id: \"".concat(sanitize(node.variable.id), "\", element: \"checkbox\", value: true }, runtime);\n");
        break;

      case 'visualReport':
        {
          const value = this.localVariables.next();
          this.source += "const ".concat(value, " = ").concat(this.descendInput(node.input).asUnknown(), ";"); // blocks like legacy no-ops can return a literal `undefined`

          this.source += "if (".concat(value, " !== undefined) runtime.visualReport(\"").concat(sanitize(this.script.topBlockId), "\", ").concat(value, ");\n");
          break;
        }

      default:
        log.warn("JS: Unknown stacked block: ".concat(node.kind), node);
        throw new Error("JS: Unknown stacked block: ".concat(node.kind));
    }
  }
  /**
   * Compile a Record of input objects into a safe JS string.
   * @param {Record<string, unknown>} inputs
   * @returns {string}
   */


  descendInputRecord(inputs) {
    let result = '{';

    for (const name of Object.keys(inputs)) {
      const node = inputs[name];
      result += "\"".concat(sanitize(name), "\":").concat(this.descendInput(node).asSafe(), ",");
    }

    result += '}';
    return result;
  }

  resetVariableInputs() {
    this.variableInputs = {};
  }

  descendStack(nodes, frame) {
    // Entering a stack -- all bets are off.
    // TODO: allow if/else to inherit values
    this.resetVariableInputs();
    this.pushFrame(frame);

    for (let i = 0; i < nodes.length; i++) {
      frame.isLastBlock = i === nodes.length - 1;
      this.descendStackedBlock(nodes[i]);
    } // Leaving a stack -- any assumptions made in the current stack do not apply outside of it
    // TODO: in if/else this might create an extra unused object


    this.resetVariableInputs();
    this.popFrame();
  }

  descendVariable(variable) {
    if (this.variableInputs.hasOwnProperty(variable.id)) {
      return this.variableInputs[variable.id];
    }

    const input = new VariableInput("".concat(this.referenceVariable(variable), ".value"));
    this.variableInputs[variable.id] = input;
    return input;
  }

  referenceVariable(variable) {
    if (variable.scope === 'target') {
      return this.evaluateOnce("target.variables[\"".concat(sanitize(variable.id), "\"]"));
    }

    return this.evaluateOnce("stage.variables[\"".concat(sanitize(variable.id), "\"]"));
  }

  evaluateOnce(source) {
    if (this._setupVariables.hasOwnProperty(source)) {
      return this._setupVariables[source];
    }

    const variable = this._setupVariablesPool.next();

    this._setupVariables[source] = variable;
    return variable;
  }

  retire() {
    // After running retire() (sets thread status and cleans up some unused data), we need to return to the event loop.
    // When in a procedure, return will only send us back to the previous procedure, so instead we yield back to the sequencer.
    // Outside of a procedure, return will correctly bring us back to the sequencer.
    if (this.isProcedure) {
      this.source += 'retire(); yield;\n';
    } else {
      this.source += 'retire(); return;\n';
    }
  }

  yieldLoop() {
    if (this.warpTimer) {
      this.yieldStuckOrNotWarp();
    } else {
      this.yieldNotWarp();
    }
  }
  /**
   * Write JS to yield the current thread if warp mode is disabled.
   */


  yieldNotWarp() {
    if (!this.isWarp) {
      this.source += 'yield;\n';
      this.yielded();
    }
  }
  /**
   * Write JS to yield the current thread if warp mode is disabled or if the script seems to be stuck.
   */


  yieldStuckOrNotWarp() {
    if (this.isWarp) {
      this.source += 'if (isStuck()) yield;\n';
    } else {
      this.source += 'yield;\n';
    }

    this.yielded();
  }

  yielded() {
    if (!this.script.yields) {
      throw new Error('Script yielded but is not marked as yielding.');
    } // Control may have been yielded to another script -- all bets are off.


    this.resetVariableInputs();
  }
  /**
   * Write JS to request a redraw.
   */


  requestRedraw() {
    this.source += 'runtime.requestRedraw();\n';
  }

  safeConstantInput(value) {
    const unsafe = typeof value === 'string' && this.namesOfCostumesAndSounds.has(value);
    return new ConstantInput(value, !unsafe);
  }
  /**
   * Generate a call into the compatibility layer.
   * @param {*} node The "compat" kind node to generate from.
   * @param {boolean} setFlags Whether flags should be set describing how this function was processed.
   * @returns {string} The JS of the call.
   */


  generateCompatibilityLayerCall(node, setFlags) {
    const opcode = node.opcode;
    let result = 'yield* executeInCompatibilityLayer({';

    for (const inputName of Object.keys(node.inputs)) {
      const input = node.inputs[inputName];
      const compiledInput = this.descendInput(input).asSafe();
      result += "\"".concat(sanitize(inputName), "\":").concat(compiledInput, ",");
    }

    for (const fieldName of Object.keys(node.fields)) {
      const field = node.fields[fieldName];
      result += "\"".concat(sanitize(fieldName), "\":\"").concat(sanitize(field), "\",");
    }

    const opcodeFunction = this.evaluateOnce("runtime.getOpcodeFunction(\"".concat(sanitize(opcode), "\")"));
    result += "}, ".concat(opcodeFunction, ", ").concat(this.isWarp, ", ").concat(setFlags, ", null)");
    return result;
  }

  getScriptFactoryName() {
    return factoryNameVariablePool.next();
  }

  getScriptName(yields) {
    let name = yields ? generatorNameVariablePool.next() : functionNameVariablePool.next();

    if (this.isProcedure) {
      const simplifiedProcedureCode = this.script.procedureCode.replace(/%[\w]/g, '') // remove arguments
      .replace(/[^a-zA-Z0-9]/g, '_') // remove unsafe
      .substring(0, 20); // keep length reasonable

      name += "_".concat(simplifiedProcedureCode);
    }

    return name;
  }
  /**
   * Generate the JS to pass into eval() based on the current state of the compiler.
   * @returns {string} JS to pass into eval()
   */


  createScriptFactory() {
    let script = ''; // Setup the factory

    script += "(function ".concat(this.getScriptFactoryName(), "(thread) { ");
    script += 'const target = thread.target; ';
    script += 'const runtime = target.runtime; ';
    script += 'const stage = runtime.getTargetForStage();\n';

    for (const varValue of Object.keys(this._setupVariables)) {
      const varName = this._setupVariables[varValue];
      script += "const ".concat(varName, " = ").concat(varValue, ";\n");
    } // Generated script


    script += 'return ';

    if (this.script.yields) {
      script += "function* ";
    } else {
      script += "function ";
    }

    script += this.getScriptName(this.script.yields);
    script += ' (';

    if (this.script.arguments.length) {
      const args = [];

      for (let i = 0; i < this.script.arguments.length; i++) {
        args.push("p".concat(i));
      }

      script += args.join(',');
    }

    script += ') {\n';
    script += this.source;

    if (!this.isProcedure) {
      script += 'retire();\n';
    }

    script += '}; })';
    return script;
  }
  /**
   * Compile this script.
   * @returns {Function} The factory function for the script.
   */


  compile() {
    if (this.script.stack) {
      this.descendStack(this.script.stack, new Frame(false));
    }

    const factory = this.createScriptFactory();
    const fn = jsexecute.scopedEval(factory);

    if (this.debug) {
      log.info("JS: ".concat(this.target.getName(), ": compiled ").concat(this.script.procedureCode || 'script'), factory);
    }

    if (JSGenerator.testingApparatus) {
      JSGenerator.testingApparatus.report(this, factory);
    }

    return fn;
  }

} // Test hook used by automated snapshot testing.


JSGenerator.testingApparatus = null;
module.exports = JSGenerator;

/***/ }),

/***/ "./node_modules/scratch-vm/src/compiler/variable-pool.js":
/*!***************************************************************!*\
  !*** ./node_modules/scratch-vm/src/compiler/variable-pool.js ***!
  \***************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class VariablePool {
  /**
   * @param {string} prefix The prefix at the start of the variable name.
   */
  constructor(prefix) {
    if (prefix.trim().length === 0) {
      throw new Error('prefix cannot be empty');
    }

    this.prefix = prefix;
    /**
     * @private
     */

    this.count = 0;
  }

  next() {
    return "".concat(this.prefix).concat(this.count++);
  }

}

module.exports = VariablePool;

/***/ }),

/***/ "./node_modules/scratch-vm/src/dispatch/central-dispatch.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-vm/src/dispatch/central-dispatch.js ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const SharedDispatch = __webpack_require__(/*! ./shared-dispatch */ "./node_modules/scratch-vm/src/dispatch/shared-dispatch.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * This class serves as the central broker for message dispatch. It expects to operate on the main thread / Window and
 * it must be informed of any Worker threads which will participate in the messaging system. From any context in the
 * messaging system, the dispatcher's "call" method can call any method on any "service" provided in any participating
 * context. The dispatch system will forward function arguments and return values across worker boundaries as needed.
 * @see {WorkerDispatch}
 */


class CentralDispatch extends SharedDispatch {
  constructor() {
    super();
    /**
     * Map of channel name to worker or local service provider.
     * If the entry is a Worker, the service is provided by an object on that worker.
     * Otherwise, the service is provided locally and methods on the service will be called directly.
     * @see {setService}
     * @type {object.<Worker|object>}
     */

    this.services = {};
    /**
     * The constructor we will use to recognize workers.
     * @type {Function}
     */

    this.workerClass = typeof Worker === 'undefined' ? null : Worker;
    /**
     * List of workers attached to this dispatcher.
     * @type {Array}
     */

    this.workers = [];
  }
  /**
   * Synchronously call a particular method on a particular service provided locally.
   * Calling this function on a remote service will fail.
   * @param {string} service - the name of the service.
   * @param {string} method - the name of the method.
   * @param {*} [args] - the arguments to be copied to the method, if any.
   * @returns {*} - the return value of the service method.
   */


  callSync(service, method) {
    const {
      provider,
      isRemote
    } = this._getServiceProvider(service);

    if (provider) {
      if (isRemote) {
        throw new Error("Cannot use 'callSync' on remote provider for service ".concat(service, "."));
      }

      for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
        args[_key - 2] = arguments[_key];
      }

      return provider[method].apply(provider, args);
    }

    throw new Error("Provider not found for service: ".concat(service));
  }
  /**
   * Synchronously set a local object as the global provider of the specified service.
   * WARNING: Any method on the provider can be called from any worker within the dispatch system.
   * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'.
   * @param {object} provider - a local object which provides this service.
   */


  setServiceSync(service, provider) {
    if (this.services.hasOwnProperty(service)) {
      log.warn("Central dispatch replacing existing service provider for ".concat(service));
    }

    this.services[service] = provider;
  }
  /**
   * Set a local object as the global provider of the specified service.
   * WARNING: Any method on the provider can be called from any worker within the dispatch system.
   * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'.
   * @param {object} provider - a local object which provides this service.
   * @returns {Promise} - a promise which will resolve once the service is registered.
   */


  setService(service, provider) {
    /** Return a promise for consistency with {@link WorkerDispatch#setService} */
    try {
      this.setServiceSync(service, provider);
      return Promise.resolve();
    } catch (e) {
      return Promise.reject(e);
    }
  }
  /**
   * Add a worker to the message dispatch system. The worker must implement a compatible message dispatch framework.
   * The dispatcher will immediately attempt to "handshake" with the worker.
   * @param {Worker} worker - the worker to add into the dispatch system.
   */


  addWorker(worker) {
    if (this.workers.indexOf(worker) === -1) {
      this.workers.push(worker);
      worker.onmessage = this._onMessage.bind(this, worker);

      this._remoteCall(worker, 'dispatch', 'handshake').catch(e => {
        log.error("Could not handshake with worker: ".concat(e));
      });
    } else {
      log.warn('Central dispatch ignoring attempt to add duplicate worker');
    }
  }
  /**
   * Fetch the service provider object for a particular service name.
   * @override
   * @param {string} service - the name of the service to look up
   * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found
   * @protected
   */


  _getServiceProvider(service) {
    const provider = this.services[service];
    return provider && {
      provider,
      isRemote: Boolean(this.workerClass && provider instanceof this.workerClass || provider.isRemote)
    };
  }
  /**
   * Handle a call message sent to the dispatch service itself
   * @override
   * @param {Worker} worker - the worker which sent the message.
   * @param {DispatchCallMessage} message - the message to be handled.
   * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate
   * @protected
   */


  _onDispatchMessage(worker, message) {
    let promise;

    switch (message.method) {
      case 'setService':
        promise = this.setService(message.args[0], worker);
        break;

      default:
        log.error("Central dispatch received message for unknown method: ".concat(message.method));
    }

    return promise;
  }

}

module.exports = new CentralDispatch();

/***/ }),

/***/ "./node_modules/scratch-vm/src/dispatch/shared-dispatch.js":
/*!*****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/dispatch/shared-dispatch.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * @typedef {object} DispatchCallMessage - a message to the dispatch system representing a service method call
 * @property {*} responseId - send a response message with this response ID. See {@link DispatchResponseMessage}
 * @property {string} service - the name of the service to be called
 * @property {string} method - the name of the method to be called
 * @property {Array|undefined} args - the arguments to be passed to the method
 */

/**
 * @typedef {object} DispatchResponseMessage - a message to the dispatch system representing the results of a call
 * @property {*} responseId - a copy of the response ID from the call which generated this response
 * @property {*|undefined} error - if this is truthy, then it contains results from a failed call (such as an exception)
 * @property {*|undefined} result - if error is not truthy, then this contains the return value of the call (if any)
 */

/**
 * @typedef {DispatchCallMessage|DispatchResponseMessage} DispatchMessage
 * Any message to the dispatch system.
 */

/**
 * The SharedDispatch class is responsible for dispatch features shared by
 * {@link CentralDispatch} and {@link WorkerDispatch}.
 */


class SharedDispatch {
  constructor() {
    /**
     * List of callback registrations for promises waiting for a response from a call to a service on another
     * worker. A callback registration is an array of [resolve,reject] Promise functions.
     * Calls to local services don't enter this list.
     * @type {Array.<Function[]>}
     */
    this.callbacks = [];
    /**
     * The next response ID to be used.
     * @type {int}
     */

    this.nextResponseId = 0;
  }
  /**
   * Call a particular method on a particular service, regardless of whether that service is provided locally or on
   * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone
   * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be
   * transferred to the worker, and they should not be used after this call.
   * @example
   *      dispatcher.call('vm', 'setData', 'cat', 42);
   *      // this finds the worker for the 'vm' service, then on that worker calls:
   *      vm.setData('cat', 42);
   * @param {string} service - the name of the service.
   * @param {string} method - the name of the method.
   * @param {*} [args] - the arguments to be copied to the method, if any.
   * @returns {Promise} - a promise for the return value of the service method.
   */


  call(service, method) {
    for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
      args[_key - 2] = arguments[_key];
    }

    return this.transferCall(service, method, null, ...args);
  }
  /**
   * Call a particular method on a particular service, regardless of whether that service is provided locally or on
   * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone
   * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be
   * transferred to the worker, and they should not be used after this call.
   * @example
   *      dispatcher.transferCall('vm', 'setData', [myArrayBuffer], 'cat', myArrayBuffer);
   *      // this finds the worker for the 'vm' service, transfers `myArrayBuffer` to it, then on that worker calls:
   *      vm.setData('cat', myArrayBuffer);
   * @param {string} service - the name of the service.
   * @param {string} method - the name of the method.
   * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.
   * @param {*} [args] - the arguments to be copied to the method, if any.
   * @returns {Promise} - a promise for the return value of the service method.
   */


  transferCall(service, method, transfer) {
    try {
      const {
        provider,
        isRemote
      } = this._getServiceProvider(service);

      if (provider) {
        for (var _len2 = arguments.length, args = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) {
          args[_key2 - 3] = arguments[_key2];
        }

        if (isRemote) {
          return this._remoteTransferCall(provider, service, method, transfer, ...args);
        }

        const result = provider[method].apply(provider, args);
        return Promise.resolve(result);
      }

      return Promise.reject(new Error("Service not found: ".concat(service)));
    } catch (e) {
      return Promise.reject(e);
    }
  }
  /**
   * Check if a particular service lives on another worker.
   * @param {string} service - the service to check.
   * @returns {boolean} - true if the service is remote (calls must cross a Worker boundary), false otherwise.
   * @private
   */


  _isRemoteService(service) {
    return this._getServiceProvider(service).isRemote;
  }
  /**
   * Like {@link call}, but force the call to be posted through a particular communication channel.
   * @param {object} provider - send the call through this object's `postMessage` function.
   * @param {string} service - the name of the service.
   * @param {string} method - the name of the method.
   * @param {*} [args] - the arguments to be copied to the method, if any.
   * @returns {Promise} - a promise for the return value of the service method.
   */


  _remoteCall(provider, service, method) {
    for (var _len3 = arguments.length, args = new Array(_len3 > 3 ? _len3 - 3 : 0), _key3 = 3; _key3 < _len3; _key3++) {
      args[_key3 - 3] = arguments[_key3];
    }

    return this._remoteTransferCall(provider, service, method, null, ...args);
  }
  /**
   * Like {@link transferCall}, but force the call to be posted through a particular communication channel.
   * @param {object} provider - send the call through this object's `postMessage` function.
   * @param {string} service - the name of the service.
   * @param {string} method - the name of the method.
   * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.
   * @param {*} [args] - the arguments to be copied to the method, if any.
   * @returns {Promise} - a promise for the return value of the service method.
   */


  _remoteTransferCall(provider, service, method, transfer) {
    for (var _len4 = arguments.length, args = new Array(_len4 > 4 ? _len4 - 4 : 0), _key4 = 4; _key4 < _len4; _key4++) {
      args[_key4 - 4] = arguments[_key4];
    }

    return new Promise((resolve, reject) => {
      const responseId = this._storeCallbacks(resolve, reject);
      /** @TODO: remove this hack! this is just here so we don't try to send `util` to a worker */
      // tw: upstream's logic is broken
      // Args is actually a 3 length list of [args, util, real block info]
      // We only want to send args. The others will throw errors when they try to be cloned


      if (args.length > 0 && typeof args[args.length - 1].func === 'function') {
        args.pop();
        args.pop();
      }

      if (transfer) {
        provider.postMessage({
          service,
          method,
          responseId,
          args
        }, transfer);
      } else {
        provider.postMessage({
          service,
          method,
          responseId,
          args
        });
      }
    });
  }
  /**
   * Store callback functions pending a response message.
   * @param {Function} resolve - function to call if the service method returns.
   * @param {Function} reject - function to call if the service method throws.
   * @returns {*} - a unique response ID for this set of callbacks. See {@link _deliverResponse}.
   * @protected
   */


  _storeCallbacks(resolve, reject) {
    const responseId = this.nextResponseId++;
    this.callbacks[responseId] = [resolve, reject];
    return responseId;
  }
  /**
   * Deliver call response from a worker. This should only be called as the result of a message from a worker.
   * @param {int} responseId - the response ID of the callback set to call.
   * @param {DispatchResponseMessage} message - the message containing the response value(s).
   * @protected
   */


  _deliverResponse(responseId, message) {
    try {
      const [resolve, reject] = this.callbacks[responseId];
      delete this.callbacks[responseId];

      if (message.error) {
        reject(message.error);
      } else {
        resolve(message.result);
      }
    } catch (e) {
      log.error("Dispatch callback failed: ".concat(e));
    }
  }
  /**
   * Handle a message event received from a connected worker.
   * @param {Worker} worker - the worker which sent the message, or the global object if running in a worker.
   * @param {MessageEvent} event - the message event to be handled.
   * @protected
   */


  _onMessage(worker, event) {
    /** @type {DispatchMessage} */
    const message = event.data;
    message.args = message.args || [];
    let promise;

    if (message.service) {
      if (message.service === 'dispatch') {
        promise = this._onDispatchMessage(worker, message);
      } else {
        promise = this.call(message.service, message.method, ...message.args);
      }
    } else if (typeof message.responseId === 'undefined') {
      log.error("Dispatch caught malformed message from a worker: ".concat(JSON.stringify(event)));
    } else {
      this._deliverResponse(message.responseId, message);
    }

    if (promise) {
      if (typeof message.responseId === 'undefined') {
        log.error("Dispatch message missing required response ID: ".concat(JSON.stringify(event)));
      } else {
        promise.then(result => worker.postMessage({
          responseId: message.responseId,
          result
        }), error => worker.postMessage({
          responseId: message.responseId,
          error: "".concat(error)
        }));
      }
    }
  }
  /**
   * Fetch the service provider object for a particular service name.
   * @abstract
   * @param {string} service - the name of the service to look up
   * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found
   * @protected
   */


  _getServiceProvider(service) {
    throw new Error("Could not get provider for ".concat(service, ": _getServiceProvider not implemented"));
  }
  /**
   * Handle a call message sent to the dispatch service itself
   * @abstract
   * @param {Worker} worker - the worker which sent the message.
   * @param {DispatchCallMessage} message - the message to be handled.
   * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate
   * @private
   */


  _onDispatchMessage(worker, message) {
    throw new Error("Unimplemented dispatch message handler cannot handle ".concat(message.method, " method"));
  }

}

module.exports = SharedDispatch;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/adapter.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/adapter.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const mutationAdapter = __webpack_require__(/*! ./mutation-adapter */ "./node_modules/scratch-vm/src/engine/mutation-adapter.js");

const html = __webpack_require__(/*! htmlparser2 */ "./src/scaffolding/htmlparser2/index.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");
/**
 * Convert and an individual block DOM to the representation tree.
 * Based on Blockly's `domToBlockHeadless_`.
 * @param {Element} blockDOM DOM tree for an individual block.
 * @param {object} blocks Collection of blocks to add to.
 * @param {boolean} isTopBlock Whether blocks at this level are "top blocks."
 * @param {?string} parent Parent block ID.
 * @return {undefined}
 */


const domToBlock = function domToBlock(blockDOM, blocks, isTopBlock, parent) {
  if (!blockDOM.attribs.id) {
    blockDOM.attribs.id = uid();
  } // Block skeleton.


  const block = {
    id: blockDOM.attribs.id,
    // Block ID
    opcode: blockDOM.attribs.type,
    // For execution, "event_whengreenflag".
    inputs: {},
    // Inputs to this block and the blocks they point to.
    fields: {},
    // Fields on this block and their values.
    next: null,
    // Next block in the stack, if one exists.
    topLevel: isTopBlock,
    // If this block starts a stack.
    parent: parent,
    // Parent block ID, if available.
    shadow: blockDOM.name === 'shadow',
    // If this represents a shadow/slot.
    x: blockDOM.attribs.x,
    // X position of script, if top-level.
    y: blockDOM.attribs.y // Y position of script, if top-level.

  }; // Add the block to the representation tree.

  blocks[block.id] = block; // Process XML children and find enclosed blocks, fields, etc.

  for (let i = 0; i < blockDOM.children.length; i++) {
    const xmlChild = blockDOM.children[i]; // Enclosed blocks and shadows

    let childBlockNode = null;
    let childShadowNode = null;

    for (let j = 0; j < xmlChild.children.length; j++) {
      const grandChildNode = xmlChild.children[j];

      if (!grandChildNode.name) {
        // Non-XML tag node.
        continue;
      }

      const grandChildNodeName = grandChildNode.name.toLowerCase();

      if (grandChildNodeName === 'block') {
        childBlockNode = grandChildNode;
      } else if (grandChildNodeName === 'shadow') {
        childShadowNode = grandChildNode;
      }
    } // Use shadow block only if there's no real block node.


    if (!childBlockNode && childShadowNode) {
      childBlockNode = childShadowNode;
    } // Not all Blockly-type blocks are handled here,
    // as we won't be using all of them for Scratch.


    switch (xmlChild.name.toLowerCase()) {
      case 'field':
        {
          // Add the field to this block.
          const fieldName = xmlChild.attribs.name; // Add id in case it is a variable field

          const fieldId = xmlChild.attribs.id;
          let fieldData = '';

          if (xmlChild.children.length > 0 && xmlChild.children[0].data) {
            fieldData = xmlChild.children[0].data;
          } else {
            // If the child of the field with a data property
            // doesn't exist, set the data to an empty string.
            fieldData = '';
          }

          block.fields[fieldName] = {
            name: fieldName,
            id: fieldId,
            value: fieldData
          };
          const fieldVarType = xmlChild.attribs.variabletype;

          if (typeof fieldVarType === 'string') {
            block.fields[fieldName].variableType = fieldVarType;
          }

          break;
        }

      case 'comment':
        {
          block.comment = xmlChild.attribs.id;
          break;
        }

      case 'value':
      case 'statement':
        {
          // Recursively generate block structure for input block.
          domToBlock(childBlockNode, blocks, false, block.id);

          if (childShadowNode && childBlockNode !== childShadowNode) {
            // Also generate the shadow block.
            domToBlock(childShadowNode, blocks, false, block.id);
          } // Link this block's input to the child block.


          const inputName = xmlChild.attribs.name;
          block.inputs[inputName] = {
            name: inputName,
            block: childBlockNode.attribs.id,
            shadow: childShadowNode ? childShadowNode.attribs.id : null
          };
          break;
        }

      case 'next':
        {
          if (!childBlockNode || !childBlockNode.attribs) {
            // Invalid child block.
            continue;
          } // Recursively generate block structure for next block.


          domToBlock(childBlockNode, blocks, false, block.id); // Link next block to this block.

          block.next = childBlockNode.attribs.id;
          break;
        }

      case 'mutation':
        {
          block.mutation = mutationAdapter(xmlChild);
          break;
        }
    }
  }
};
/**
 * Convert outer blocks DOM from a Blockly CREATE event
 * to a usable form for the Scratch runtime.
 * This structure is based on Blockly xml.js:`domToWorkspace` and `domToBlock`.
 * @param {Element} blocksDOM DOM tree for this event.
 * @return {Array.<object>} Usable list of blocks from this CREATE event.
 */


const domToBlocks = function domToBlocks(blocksDOM) {
  // At this level, there could be multiple blocks adjacent in the DOM tree.
  const blocks = {};

  for (let i = 0; i < blocksDOM.length; i++) {
    const block = blocksDOM[i];

    if (!block.name || !block.attribs) {
      continue;
    }

    const tagName = block.name.toLowerCase();

    if (tagName === 'block' || tagName === 'shadow') {
      domToBlock(block, blocks, true, null);
    }
  } // Flatten blocks object into a list.


  const blocksList = [];

  for (const b in blocks) {
    if (!blocks.hasOwnProperty(b)) continue;
    blocksList.push(blocks[b]);
  }

  return blocksList;
};
/**
 * Adapter between block creation events and block representation which can be
 * used by the Scratch runtime.
 * @param {object} e `Blockly.events.create` or `Blockly.events.endDrag`
 * @return {Array.<object>} List of blocks from this CREATE event.
 */


const adapter = function adapter(e) {
  // Validate input
  if (typeof e !== 'object') return;
  if (typeof e.xml !== 'object') return;
  return domToBlocks(html.parseDOM(e.xml.outerHTML, {
    decodeEntities: true
  }));
};

module.exports = adapter;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/block-utility.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/block-utility.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Thread = __webpack_require__(/*! ./thread */ "./node_modules/scratch-vm/src/engine/thread.js");

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");
/**
 * @fileoverview
 * Interface provided to block primitive functions for interacting with the
 * runtime, thread, target, and convenient methods.
 */


class BlockUtility {
  constructor() {
    let sequencer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
    let thread = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;

    /**
     * A sequencer block primitives use to branch or start procedures with
     * @type {?Sequencer}
     */
    this.sequencer = sequencer;
    /**
     * The block primitives thread with the block's target, stackFrame and
     * modifiable status.
     * @type {?Thread}
     */

    this.thread = thread;
    this._nowObj = {
      now: () => this.sequencer.runtime.currentMSecs
    };
  }
  /**
   * The target the primitive is working on.
   * @type {Target}
   */


  get target() {
    return this.thread.target;
  }
  /**
   * The runtime the block primitive is running in.
   * @type {Runtime}
   */


  get runtime() {
    return this.sequencer.runtime;
  }
  /**
   * Use the runtime's currentMSecs value as a timestamp value for now
   * This is useful in some cases where we need compatibility with Scratch 2
   * @type {function}
   */


  get nowObj() {
    if (this.runtime) {
      return this._nowObj;
    }

    return null;
  }
  /**
   * The stack frame used by loop and other blocks to track internal state.
   * @type {object}
   */


  get stackFrame() {
    const frame = this.thread.peekStackFrame();

    if (frame.executionContext === null) {
      frame.executionContext = {};
    }

    return frame.executionContext;
  }
  /**
   * Check the stack timer and return a boolean based on whether it has finished or not.
   * @return {boolean} - true if the stack timer has finished.
   */


  stackTimerFinished() {
    const timeElapsed = this.stackFrame.timer.timeElapsed();

    if (timeElapsed < this.stackFrame.duration) {
      return false;
    }

    return true;
  }
  /**
   * Check if the stack timer needs initialization.
   * @return {boolean} - true if the stack timer needs to be initialized.
   */


  stackTimerNeedsInit() {
    return !this.stackFrame.timer;
  }
  /**
   * Create and start a stack timer
   * @param {number} duration - a duration in milliseconds to set the timer for.
   */


  startStackTimer(duration) {
    if (this.nowObj) {
      this.stackFrame.timer = new Timer(this.nowObj);
    } else {
      this.stackFrame.timer = new Timer();
    }

    this.stackFrame.timer.start();
    this.stackFrame.duration = duration;
  }
  /**
   * Set the thread to yield.
   */


  yield() {
    this.thread.status = Thread.STATUS_YIELD;
  }
  /**
   * Set the thread to yield until the next tick of the runtime.
   */


  yieldTick() {
    this.thread.status = Thread.STATUS_YIELD_TICK;
  }
  /**
   * Start a branch in the current block.
   * @param {number} branchNum Which branch to step to (i.e., 1, 2).
   * @param {boolean} isLoop Whether this block is a loop.
   */


  startBranch(branchNum, isLoop) {
    this.sequencer.stepToBranch(this.thread, branchNum, isLoop);
  }
  /**
   * Stop all threads.
   */


  stopAll() {
    this.sequencer.runtime.stopAll();
  }
  /**
   * Stop threads other on this target other than the thread holding the
   * executed block.
   */


  stopOtherTargetThreads() {
    this.sequencer.runtime.stopForTarget(this.thread.target, this.thread);
  }
  /**
   * Stop this thread.
   */


  stopThisScript() {
    this.thread.stopThisScript();
  }
  /**
   * Start a specified procedure on this thread.
   * @param {string} procedureCode Procedure code for procedure to start.
   */


  startProcedure(procedureCode) {
    this.sequencer.stepToProcedure(this.thread, procedureCode);
  }
  /**
   * Get names and ids of parameters for the given procedure.
   * @param {string} procedureCode Procedure code for procedure to query.
   * @return {Array.<string>} List of param names for a procedure.
   */


  getProcedureParamNamesAndIds(procedureCode) {
    return this.thread.target.blocks.getProcedureParamNamesAndIds(procedureCode);
  }
  /**
   * Get names, ids, and defaults of parameters for the given procedure.
   * @param {string} procedureCode Procedure code for procedure to query.
   * @return {Array.<string>} List of param names for a procedure.
   */


  getProcedureParamNamesIdsAndDefaults(procedureCode) {
    return this.thread.target.blocks.getProcedureParamNamesIdsAndDefaults(procedureCode);
  }
  /**
   * Initialize procedure parameters in the thread before pushing parameters.
   */


  initParams() {
    this.thread.initParams();
  }
  /**
   * Store a procedure parameter value by its name.
   * @param {string} paramName The procedure's parameter name.
   * @param {*} paramValue The procedure's parameter value.
   */


  pushParam(paramName, paramValue) {
    this.thread.pushParam(paramName, paramValue);
  }
  /**
   * Retrieve the stored parameter value for a given parameter name.
   * @param {string} paramName The procedure's parameter name.
   * @return {*} The parameter's current stored value.
   */


  getParam(paramName) {
    return this.thread.getParam(paramName);
  }
  /**
   * Start all relevant hats.
   * @param {!string} requestedHat Opcode of hats to start.
   * @param {object=} optMatchFields Optionally, fields to match on the hat.
   * @param {Target=} optTarget Optionally, a target to restrict to.
   * @return {Array.<Thread>} List of threads started by this function.
   */


  startHats(requestedHat, optMatchFields, optTarget) {
    // Store thread and sequencer to ensure we can return to the calling block's context.
    // startHats may execute further blocks and dirty the BlockUtility's execution context
    // and confuse the calling block when we return to it.
    const callerThread = this.thread;
    const callerSequencer = this.sequencer;
    const result = this.sequencer.runtime.startHats(requestedHat, optMatchFields, optTarget); // Restore thread and sequencer to prior values before we return to the calling block.

    this.thread = callerThread;
    this.sequencer = callerSequencer;
    return result;
  }
  /**
   * Query a named IO device.
   * @param {string} device The name of like the device, like keyboard.
   * @param {string} func The name of the device's function to query.
   * @param {Array.<*>} args Arguments to pass to the device's function.
   * @return {*} The expected output for the device's function.
   */


  ioQuery(device, func, args) {
    // Find the I/O device and execute the query/function call.
    if (this.sequencer.runtime.ioDevices[device] && this.sequencer.runtime.ioDevices[device][func]) {
      const devObject = this.sequencer.runtime.ioDevices[device];
      return devObject[func].apply(devObject, args);
    }
  }

}

module.exports = BlockUtility;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/blocks-execute-cache.js":
/*!********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/blocks-execute-cache.js ***!
  \********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * Access point for private method shared between blocks.js and execute.js for
 * caching execute information.
 */

/**
 * A private method shared with execute to build an object containing the block
 * information execute needs and that is reset when other cached Blocks info is
 * reset.
 * @param {Blocks} blocks Blocks containing the expected blockId
 * @param {string} blockId blockId for the desired execute cache
 */
exports.getCached = function () {
  throw new Error('blocks.js has not initialized BlocksExecuteCache');
}; // Call after the default throwing getCached is assigned for Blocks to replace.


__webpack_require__(/*! ./blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/blocks-runtime-cache.js":
/*!********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/blocks-runtime-cache.js ***!
  \********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * The BlocksRuntimeCache caches data about the top block of scripts so that
 * Runtime can iterate a targeted opcode and iterate the returned set faster.
 * Many top blocks need to match fields as well as opcode, since that matching
 * compares strings in uppercase we can go ahead and uppercase the cached value
 * so we don't need to in the future.
 */

/**
 * A set of cached data about the top block of a script.
 * @param {Blocks} container - Container holding the block and related data
 * @param {string} blockId - Id for whose block data is cached in this instance
 */
class RuntimeScriptCache {
  constructor(container, blockId) {
    /**
     * Container with block data for blockId.
     * @type {Blocks}
     */
    this.container = container;
    /**
     * ID for block this instance caches.
     * @type {string}
     */

    this.blockId = blockId;
    const block = container.getBlock(blockId);
    const fields = container.getFields(block);
    /**
     * Formatted fields or fields of input blocks ready for comparison in
     * runtime.
     *
     * This is a clone of parts of the targeted blocks. Changes to these
     * clones are limited to copies under RuntimeScriptCache and will not
     * appear in the original blocks in their container. This copy is
     * modified changing the case of strings to uppercase. These uppercase
     * values will be compared later by the VM.
     * @type {object}
     */

    this.fieldsOfInputs = Object.assign({}, fields);

    if (Object.keys(fields).length === 0) {
      const inputs = container.getInputs(block);

      for (const input in inputs) {
        if (!inputs.hasOwnProperty(input)) continue;
        const id = inputs[input].block;
        const inputBlock = container.getBlock(id);
        const inputFields = container.getFields(inputBlock);
        Object.assign(this.fieldsOfInputs, inputFields);
      }
    }

    for (const key in this.fieldsOfInputs) {
      const field = this.fieldsOfInputs[key] = Object.assign({}, this.fieldsOfInputs[key]);

      if (field.value.toUpperCase) {
        field.value = field.value.toUpperCase();
      }
    }
  }

}
/**
 * Get an array of scripts from a block container prefiltered to match opcode.
 * @param {Blocks} container - Container of blocks
 * @param {string} opcode - Opcode to filter top blocks by
 */


exports.getScripts = function () {
  throw new Error('blocks.js has not initialized BlocksRuntimeCache');
};
/**
 * Exposed RuntimeScriptCache class used by integration in blocks.js.
 * @private
 */


exports._RuntimeScriptCache = RuntimeScriptCache;

__webpack_require__(/*! ./blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/blocks.js":
/*!******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/blocks.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const adapter = __webpack_require__(/*! ./adapter */ "./node_modules/scratch-vm/src/engine/adapter.js");

const mutationAdapter = __webpack_require__(/*! ./mutation-adapter */ "./node_modules/scratch-vm/src/engine/mutation-adapter.js");

const xmlEscape = __webpack_require__(/*! ../util/xml-escape */ "./node_modules/scratch-vm/src/util/xml-escape.js");

const MonitorRecord = __webpack_require__(/*! ./monitor-record */ "./node_modules/scratch-vm/src/engine/monitor-record.js");

const Clone = __webpack_require__(/*! ../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const {
  Map
} = __webpack_require__(/*! immutable */ "./node_modules/immutable/dist/immutable.js");

const BlocksExecuteCache = __webpack_require__(/*! ./blocks-execute-cache */ "./node_modules/scratch-vm/src/engine/blocks-execute-cache.js");

const BlocksRuntimeCache = __webpack_require__(/*! ./blocks-runtime-cache */ "./node_modules/scratch-vm/src/engine/blocks-runtime-cache.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const Variable = __webpack_require__(/*! ./variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const getMonitorIdForBlockWithArgs = __webpack_require__(/*! ../util/get-monitor-id */ "./node_modules/scratch-vm/src/util/get-monitor-id.js");
/**
 * @fileoverview
 * Store and mutate the VM block representation,
 * and handle updates from Scratch Blocks events.
 */

/**
 * Create a block container.
 * @param {Runtime} runtime The runtime this block container operates within
 * @param {boolean} optNoGlow Optional flag to indicate that blocks in this container
 * should not request glows. This does not affect glows when clicking on a block to execute it.
 */


class Blocks {
  constructor(runtime, optNoGlow) {
    this.runtime = runtime;
    /**
     * All blocks in the workspace.
     * Keys are block IDs, values are metadata about the block.
     * @type {Object.<string, Object>}
     */

    this._blocks = {};
    /**
     * All top-level scripts in the workspace.
     * A list of block IDs that represent scripts (i.e., first block in script).
     * @type {Array.<String>}
     */

    this._scripts = [];
    /**
     * Runtime Cache
     * @type {{inputs: {}, procedureParamNames: {}, procedureDefinitions: {}}}
     * @private
     */

    Object.defineProperty(this, '_cache', {
      writable: true,
      enumerable: false
    });
    this._cache = {
      /**
       * Cache block inputs by block id
       * @type {object.<string, !Array.<object>>}
       */
      inputs: {},

      /**
       * Cache procedure Param Names by block id
       * @type {object.<string, ?Array.<string>>}
       */
      procedureParamNames: {},

      /**
       * Cache procedure definitions by block id
       * @type {object.<string, ?string>}
       */
      procedureDefinitions: {},

      /**
       * A cache for execute to use and store on. Only available to
       * execute.
       * @type {object.<string, object>}
       */
      _executeCached: {},

      /**
       * A cache of block IDs and targets to start threads on as they are
       * actively monitored.
       * @type {Array<{blockId: string, target: Target}>}
       */
      _monitored: null,

      /**
       * A cache of hat opcodes to collection of theads to execute.
       * @type {object.<string, object>}
       */
      scripts: {},

      /**
       * tw: A cache of top block (usually hat, but not always) opcodes to compiled scripts.
       * @type {object.<string, object>}
       */
      compiledScripts: {},

      /**
       * tw: A cache of procedure code opcodes to a parsed intermediate representation
       * @type {object.<string, object>}
       */
      compiledProcedures: {},

      /**
       * tw: Whether populateProcedureCache has been run
       */
      proceduresPopulated: false
    };
    /**
     * Flag which indicates that blocks in this container should not glow.
     * Blocks will still glow when clicked on, but this flag is used to control
     * whether the blocks in this container can request a glow as part of
     * a running stack. E.g. the flyout block container and the monitor block container
     * should not be able to request a glow, but blocks containers belonging to
     * sprites should.
     * @type {boolean}
     */

    this.forceNoGlow = optNoGlow || false;
  }
  /**
   * Get the cached compilation result of a block.
   * @param {string} blockId ID of the top block.
   * @returns {{success: boolean; value: any}|null} Cached success or error, or null if there is no cached value.
   */


  getCachedCompileResult(blockId) {
    if (this._cache.compiledScripts.hasOwnProperty(blockId)) {
      return this._cache.compiledScripts[blockId];
    }

    return null;
  }
  /**
   * Set the cached compilation result of a script.
   * @param {string} blockId ID of the top block.
   * @param {*} value The compilation result to store.
   */


  cacheCompileResult(blockId, value) {
    this._cache.compiledScripts[blockId] = {
      success: true,
      value: value
    };
  }
  /**
   * Set the cached error of a script.
   * @param {string} blockId ID of the top block.
   * @param {*} error The error to store.
   */


  cacheCompileError(blockId, error) {
    this._cache.compiledScripts[blockId] = {
      success: false,
      value: error
    };
  }
  /**
   * Blockly inputs that represent statements/branch.
   * are prefixed with this string.
   * @const{string}
   */


  static get BRANCH_INPUT_PREFIX() {
    return 'SUBSTACK';
  }
  /**
   * Provide an object with metadata for the requested block ID.
   * @param {!string} blockId ID of block we have stored.
   * @return {?object} Metadata about the block, if it exists.
   */


  getBlock(blockId) {
    return this._blocks[blockId];
  }
  /**
   * Get all known top-level blocks that start scripts.
   * @return {Array.<string>} List of block IDs.
   */


  getScripts() {
    return this._scripts;
  }
  /**
    * Get the next block for a particular block
    * @param {?string} id ID of block to get the next block for
    * @return {?string} ID of next block in the sequence
    */


  getNextBlock(id) {
    const block = this._blocks[id];
    return typeof block === 'undefined' ? null : block.next;
  }
  /**
   * Get the branch for a particular C-shaped block.
   * @param {?string} id ID for block to get the branch for.
   * @param {?number} branchNum Which branch to select (e.g. for if-else).
   * @return {?string} ID of block in the branch.
   */


  getBranch(id, branchNum) {
    const block = this._blocks[id];
    if (typeof block === 'undefined') return null;
    if (!branchNum) branchNum = 1;
    let inputName = Blocks.BRANCH_INPUT_PREFIX;

    if (branchNum > 1) {
      inputName += branchNum;
    } // Empty C-block?


    const input = block.inputs[inputName];
    return typeof input === 'undefined' ? null : input.block;
  }
  /**
   * Get the opcode for a particular block
   * @param {?object} block The block to query
   * @return {?string} the opcode corresponding to that block
   */


  getOpcode(block) {
    return typeof block === 'undefined' ? null : block.opcode;
  }
  /**
   * Get all fields and their values for a block.
   * @param {?object} block The block to query.
   * @return {?object} All fields and their values.
   */


  getFields(block) {
    return typeof block === 'undefined' ? null : block.fields;
  }
  /**
   * Get all non-branch inputs for a block.
   * @param {?object} block the block to query.
   * @return {?Array.<object>} All non-branch inputs and their associated blocks.
   */


  getInputs(block) {
    if (typeof block === 'undefined') return null;
    let inputs = this._cache.inputs[block.id];

    if (typeof inputs !== 'undefined') {
      return inputs;
    }

    inputs = {};

    for (const input in block.inputs) {
      // Ignore blocks prefixed with branch prefix.
      if (input.substring(0, Blocks.BRANCH_INPUT_PREFIX.length) !== Blocks.BRANCH_INPUT_PREFIX) {
        inputs[input] = block.inputs[input];
      }
    }

    this._cache.inputs[block.id] = inputs;
    return inputs;
  }
  /**
   * Get mutation data for a block.
   * @param {?object} block The block to query.
   * @return {?object} Mutation for the block.
   */


  getMutation(block) {
    return typeof block === 'undefined' ? null : block.mutation;
  }
  /**
   * Get the top-level script for a given block.
   * @param {?string} id ID of block to query.
   * @return {?string} ID of top-level script block.
   */


  getTopLevelScript(id) {
    let block = this._blocks[id];
    if (typeof block === 'undefined') return null;

    while (block.parent !== null) {
      block = this._blocks[block.parent];
    }

    return block.id;
  }
  /**
   * Get the procedure definition for a given name.
   * @param {?string} name Name of procedure to query.
   * @return {?string} ID of procedure definition.
   */


  getProcedureDefinition(name) {
    const blockID = this._cache.procedureDefinitions[name];

    if (typeof blockID !== 'undefined') {
      return blockID;
    }

    for (const id in this._blocks) {
      if (!this._blocks.hasOwnProperty(id)) continue;
      const block = this._blocks[id];

      if (block.opcode === 'procedures_definition') {
        // tw: make sure that populateProcedureCache is kept up to date with this method
        const internal = this._getCustomBlockInternal(block);

        if (internal && internal.mutation.proccode === name) {
          this._cache.procedureDefinitions[name] = id; // The outer define block id

          return id;
        }
      }
    }

    this._cache.procedureDefinitions[name] = null;
    return null;
  }
  /**
   * Get names and ids of parameters for the given procedure.
   * @param {?string} name Name of procedure to query.
   * @return {?Array.<string>} List of param names for a procedure.
   */


  getProcedureParamNamesAndIds(name) {
    return this.getProcedureParamNamesIdsAndDefaults(name).slice(0, 2);
  }
  /**
   * Get names, ids, and defaults of parameters for the given procedure.
   * @param {?string} name Name of procedure to query.
   * @return {?Array.<string>} List of param names for a procedure.
   */


  getProcedureParamNamesIdsAndDefaults(name) {
    const cachedNames = this._cache.procedureParamNames[name];

    if (typeof cachedNames !== 'undefined') {
      return cachedNames;
    }

    for (const id in this._blocks) {
      if (!this._blocks.hasOwnProperty(id)) continue;
      const block = this._blocks[id];

      if (block.opcode === 'procedures_prototype' && block.mutation.proccode === name) {
        // tw: make sure that populateProcedureCache is kept up to date with this method
        const names = JSON.parse(block.mutation.argumentnames);
        const ids = JSON.parse(block.mutation.argumentids);
        const defaults = JSON.parse(block.mutation.argumentdefaults);
        this._cache.procedureParamNames[name] = [names, ids, defaults];
        return this._cache.procedureParamNames[name];
      }
    }

    const addonBlock = this.runtime.getAddonBlock(name);

    if (addonBlock) {
      this._cache.procedureParamNames[name] = addonBlock.namesIdsDefaults;
      return addonBlock.namesIdsDefaults;
    }

    this._cache.procedureParamNames[name] = null;
    return null;
  }
  /**
   * tw: Setup the procedureParamNames and procedureDefinitions caches all at once.
   * This makes subsequent calls to these methods faster.
   */


  populateProcedureCache() {
    if (this._cache.proceduresPopulated) {
      return;
    }

    for (const id in this._blocks) {
      if (!this._blocks.hasOwnProperty(id)) continue;
      const block = this._blocks[id];

      if (block.opcode === 'procedures_prototype') {
        const name = block.mutation.proccode;

        if (!this._cache.procedureParamNames[name]) {
          const names = JSON.parse(block.mutation.argumentnames);
          const ids = JSON.parse(block.mutation.argumentids);
          const defaults = JSON.parse(block.mutation.argumentdefaults);
          this._cache.procedureParamNames[name] = [names, ids, defaults];
        }

        continue;
      }

      if (block.opcode === 'procedures_definition') {
        const internal = this._getCustomBlockInternal(block);

        if (internal) {
          const name = internal.mutation.proccode;

          if (!this._cache.procedureDefinitions[name]) {
            this._cache.procedureDefinitions[name] = id;
          }

          continue;
        }
      }
    }

    this._cache.proceduresPopulated = true;
  }

  duplicate() {
    const newBlocks = new Blocks(this.runtime, this.forceNoGlow);
    newBlocks._blocks = Clone.simple(this._blocks);
    newBlocks._scripts = Clone.simple(this._scripts);
    return newBlocks;
  } // ---------------------------------------------------------------------

  /**
   * Create event listener for blocks, variables, and comments. Handles validation and
   * serves as a generic adapter between the blocks, variables, and the
   * runtime interface.
   * @param {object} e Blockly "block" or "variable" event
   */


  blocklyListen(e) {
    // Validate event
    if (typeof e !== 'object') return;

    if (typeof e.blockId !== 'string' && typeof e.varId !== 'string' && typeof e.commentId !== 'string') {
      return;
    }

    const stage = this.runtime.getTargetForStage();
    const editingTarget = this.runtime.getEditingTarget(); // UI event: clicked scripts toggle in the runtime.

    if (e.element === 'stackclick') {
      this.runtime.toggleScript(e.blockId, {
        stackClick: true
      });
      return;
    } // Block create/update/destroy


    switch (e.type) {
      case 'create':
        {
          const newBlocks = adapter(e); // A create event can create many blocks. Add them all.

          for (let i = 0; i < newBlocks.length; i++) {
            this.createBlock(newBlocks[i]);
          }

          break;
        }

      case 'change':
        this.changeBlock({
          id: e.blockId,
          element: e.element,
          name: e.name,
          value: e.newValue
        });
        break;

      case 'move':
        this.moveBlock({
          id: e.blockId,
          oldParent: e.oldParentId,
          oldInput: e.oldInputName,
          newParent: e.newParentId,
          newInput: e.newInputName,
          newCoordinate: e.newCoordinate
        });
        break;

      case 'dragOutside':
        this.runtime.emitBlockDragUpdate(e.isOutside);
        break;

      case 'endDrag':
        this.runtime.emitBlockDragUpdate(false
        /* areBlocksOverGui */
        ); // Drag blocks onto another sprite

        if (e.isOutside) {
          const newBlocks = adapter(e);
          this.runtime.emitBlockEndDrag(newBlocks, e.blockId);
        }

        break;

      case 'delete':
        // Don't accept delete events for missing blocks,
        // or shadow blocks being obscured.
        if (!this._blocks.hasOwnProperty(e.blockId) || this._blocks[e.blockId].shadow) {
          return;
        } // Inform any runtime to forget about glows on this script.


        if (this._blocks[e.blockId].topLevel) {
          this.runtime.quietGlow(e.blockId);
        }

        this.deleteBlock(e.blockId);
        break;

      case 'var_create':
        this.resetCache(); // tw: more aggressive cache resetting
        // Check if the variable being created is global or local
        // If local, create a local var on the current editing target, as long
        // as there are no conflicts, and the current target is actually a sprite
        // If global or if the editing target is not present or we somehow got
        // into a state where a local var was requested for the stage,
        // create a stage (global) var after checking for name conflicts
        // on all the sprites.

        if (e.isLocal && editingTarget && !editingTarget.isStage && !e.isCloud) {
          if (!editingTarget.lookupVariableById(e.varId)) {
            editingTarget.createVariable(e.varId, e.varName, e.varType);
            this.emitProjectChanged();
          }
        } else {
          if (stage.lookupVariableById(e.varId)) {
            // Do not re-create a variable if it already exists
            return;
          } // Check for name conflicts in all of the targets


          const allTargets = this.runtime.targets.filter(t => t.isOriginal);

          for (const target of allTargets) {
            if (target.lookupVariableByNameAndType(e.varName, e.varType, true)) {
              return;
            }
          }

          stage.createVariable(e.varId, e.varName, e.varType, e.isCloud);
          this.emitProjectChanged();
        }

        break;

      case 'var_rename':
        if (editingTarget && editingTarget.variables.hasOwnProperty(e.varId)) {
          // This is a local variable, rename on the current target
          editingTarget.renameVariable(e.varId, e.newName); // Update all the blocks on the current target that use
          // this variable

          editingTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);
        } else {
          // This is a global variable
          stage.renameVariable(e.varId, e.newName); // Update all blocks on all targets that use the renamed variable

          const targets = this.runtime.targets;

          for (let i = 0; i < targets.length; i++) {
            const currTarget = targets[i];
            currTarget.blocks.updateBlocksAfterVarRename(e.varId, e.newName);
          }
        }

        this.emitProjectChanged();
        break;

      case 'var_delete':
        {
          this.resetCache(); // tw: more aggressive cache resetting

          const target = editingTarget && editingTarget.variables.hasOwnProperty(e.varId) ? editingTarget : stage;
          target.deleteVariable(e.varId);
          this.emitProjectChanged();
          break;
        }

      case 'comment_create':
        this.resetCache(); // tw: comments can affect compilation

        if (this.runtime.getEditingTarget()) {
          const currTarget = this.runtime.getEditingTarget();
          currTarget.createComment(e.commentId, e.blockId, e.text, e.xy.x, e.xy.y, e.width, e.height, e.minimized);

          if (currTarget.comments[e.commentId].x === null && currTarget.comments[e.commentId].y === null) {
            // Block comments imported from 2.0 projects are imported with their
            // x and y coordinates set to null so that scratch-blocks can
            // auto-position them. If we are receiving a create event for these
            // comments, then the auto positioning should have taken place.
            // Update the x and y position of these comments to match the
            // one from the event.
            currTarget.comments[e.commentId].x = e.xy.x;
            currTarget.comments[e.commentId].y = e.xy.y;
          }
        }

        this.emitProjectChanged();
        break;

      case 'comment_change':
        this.resetCache(); // tw: comments can affect compilation

        if (this.runtime.getEditingTarget()) {
          const currTarget = this.runtime.getEditingTarget();

          if (!currTarget.comments.hasOwnProperty(e.commentId)) {
            log.warn("Cannot change comment with id ".concat(e.commentId, " because it does not exist."));
            return;
          }

          const comment = currTarget.comments[e.commentId];
          const change = e.newContents_;

          if (change.hasOwnProperty('minimized')) {
            comment.minimized = change.minimized;
          }

          if (change.hasOwnProperty('width') && change.hasOwnProperty('height')) {
            comment.width = change.width;
            comment.height = change.height;
          }

          if (change.hasOwnProperty('text')) {
            comment.text = change.text;
          }

          this.emitProjectChanged();
        }

        break;

      case 'comment_move':
        if (this.runtime.getEditingTarget()) {
          const currTarget = this.runtime.getEditingTarget();

          if (currTarget && !currTarget.comments.hasOwnProperty(e.commentId)) {
            log.warn("Cannot change comment with id ".concat(e.commentId, " because it does not exist."));
            return;
          }

          const comment = currTarget.comments[e.commentId];
          const newCoord = e.newCoordinate_;
          comment.x = newCoord.x;
          comment.y = newCoord.y;
          this.emitProjectChanged();
        }

        break;

      case 'comment_delete':
        this.resetCache(); // tw: comments can affect compilation

        if (this.runtime.getEditingTarget()) {
          const currTarget = this.runtime.getEditingTarget();

          if (!currTarget.comments.hasOwnProperty(e.commentId)) {
            // If we're in this state, we have probably received
            // a delete event from a workspace that we switched from
            // (e.g. a delete event for a comment on sprite a's workspace
            // when switching from sprite a to sprite b)
            return;
          }

          delete currTarget.comments[e.commentId];

          if (e.blockId) {
            const block = currTarget.blocks.getBlock(e.blockId);

            if (!block) {
              log.warn("Could not find block referenced by comment with id: ".concat(e.commentId));
              return;
            }

            delete block.comment;
          }

          this.emitProjectChanged();
        }

        break;
    }
  } // ---------------------------------------------------------------------

  /**
   * Reset all runtime caches.
   */


  resetCache() {
    this._cache.inputs = {};
    this._cache.procedureParamNames = {};
    this._cache.procedureDefinitions = {};
    this._cache._executeCached = {};
    this._cache._monitored = null;
    this._cache.scripts = {};
    this._cache.compiledScripts = {};
    this._cache.compiledProcedures = {};
    this._cache.proceduresPopulated = false;
  }
  /**
   * Emit a project changed event if this is a block container
   * that can affect the project state.
   */


  emitProjectChanged() {
    if (!this.forceNoGlow) {
      this.runtime.emitProjectChanged();
    }
  }
  /**
   * Block management: create blocks and scripts from a `create` event
   * @param {!object} block Blockly create event to be processed
   */


  createBlock(block) {
    // Does the block already exist?
    // Could happen, e.g., for an unobscured shadow.
    if (this._blocks.hasOwnProperty(block.id)) {
      return;
    } // Create new block.


    this._blocks[block.id] = block; // Push block id to scripts array.
    // Blocks are added as a top-level stack if they are marked as a top-block
    // (if they were top-level XML in the event).

    if (block.topLevel) {
      this._addScript(block.id);
    }

    this.resetCache(); // A new block was actually added to the block container,
    // emit a project changed event

    this.emitProjectChanged();
  }
  /**
   * Block management: change block field values
   * @param {!object} args Blockly change event to be processed
   */


  changeBlock(args) {
    // Validate
    if (['field', 'mutation', 'checkbox'].indexOf(args.element) === -1) return;
    let block = this._blocks[args.id];
    if (typeof block === 'undefined') return;

    switch (args.element) {
      case 'field':
        // TODO when the field of a monitored block changes,
        // update the checkbox in the flyout based on whether
        // a monitor for that current combination of selected parameters exists
        // e.g.
        // 1. check (current [v year])
        // 2. switch dropdown in flyout block to (current [v minute])
        // 3. the checkbox should become unchecked if we're not already
        //    monitoring current minute
        // Update block value
        if (!block.fields[args.name]) return;

        if (args.name === 'VARIABLE' || args.name === 'LIST' || args.name === 'BROADCAST_OPTION') {
          // Get variable name using the id in args.value.
          const variable = this.runtime.getEditingTarget().lookupVariableById(args.value);

          if (variable) {
            block.fields[args.name].value = variable.name;
            block.fields[args.name].id = args.value;
          }
        } else {
          // Changing the value in a dropdown
          block.fields[args.name].value = args.value; // The selected item in the sensing of block menu needs to change based on the
          // selected target.  Set it to the first item in the menu list.
          // TODO: (#1787)

          if (block.opcode === 'sensing_of_object_menu') {
            if (block.fields.OBJECT.value === '_stage_') {
              this._blocks[block.parent].fields.PROPERTY.value = 'backdrop #';
            } else {
              this._blocks[block.parent].fields.PROPERTY.value = 'x position';
            }

            this.runtime.requestBlocksUpdate();
          }

          const flyoutBlock = block.shadow && block.parent ? this._blocks[block.parent] : block;

          if (flyoutBlock.isMonitored) {
            this.runtime.requestUpdateMonitor(Map({
              id: flyoutBlock.id,
              params: this._getBlockParams(flyoutBlock)
            }));
          }
        }

        break;

      case 'mutation':
        block.mutation = mutationAdapter(args.value);
        break;

      case 'checkbox':
        {
          // A checkbox usually has a one to one correspondence with the monitor
          // block but in the case of monitored reporters that have arguments,
          // map the old id to a new id, creating a new monitor block if necessary
          if (block.fields && Object.keys(block.fields).length > 0 && block.opcode !== 'data_variable' && block.opcode !== 'data_listcontents') {
            // This block has an argument which needs to get separated out into
            // multiple monitor blocks with ids based on the selected argument
            const newId = getMonitorIdForBlockWithArgs(block.id, block.fields); // Note: we're not just constantly creating a longer and longer id everytime we check
            // the checkbox because we're using the id of the block in the flyout as the base
            // check if a block with the new id already exists, otherwise create

            let newBlock = this.runtime.monitorBlocks.getBlock(newId);

            if (!newBlock) {
              newBlock = JSON.parse(JSON.stringify(block));
              newBlock.id = newId;
              this.runtime.monitorBlocks.createBlock(newBlock);
            }

            block = newBlock; // Carry on through the rest of this code with newBlock
          }

          const wasMonitored = block.isMonitored;
          block.isMonitored = args.value; // Variable blocks may be sprite specific depending on the owner of the variable

          let isSpriteLocalVariable = false;

          if (block.opcode === 'data_variable') {
            isSpriteLocalVariable = !this.runtime.getTargetForStage().variables[block.fields.VARIABLE.id];
          } else if (block.opcode === 'data_listcontents') {
            isSpriteLocalVariable = !this.runtime.getTargetForStage().variables[block.fields.LIST.id];
          }

          const isSpriteSpecific = isSpriteLocalVariable || this.runtime.monitorBlockInfo.hasOwnProperty(block.opcode) && this.runtime.monitorBlockInfo[block.opcode].isSpriteSpecific;

          if (isSpriteSpecific) {
            // If creating a new sprite specific monitor, the only possible target is
            // the current editing one b/c you cannot dynamically create monitors.
            // Also, do not change the targetId if it has already been assigned
            block.targetId = block.targetId || this.runtime.getEditingTarget().id;
          } else {
            block.targetId = null;
          }

          if (wasMonitored && !block.isMonitored) {
            this.runtime.requestHideMonitor(block.id);
          } else if (!wasMonitored && block.isMonitored) {
            // Tries to show the monitor for specified block. If it doesn't exist, add the monitor.
            if (!this.runtime.requestShowMonitor(block.id)) {
              this.runtime.requestAddMonitor(MonitorRecord({
                id: block.id,
                targetId: block.targetId,
                spriteName: block.targetId ? this.runtime.getTargetById(block.targetId).getName() : null,
                opcode: block.opcode,
                params: this._getBlockParams(block),
                // @todo(vm#565) for numerical values with decimals, some countries use comma
                value: '',
                mode: block.opcode === 'data_listcontents' ? 'list' : 'default'
              }));
            }
          }

          break;
        }
    }

    this.emitProjectChanged();
    this.resetCache();
  }
  /**
   * Block management: move blocks from parent to parent
   * @param {!object} e Blockly move event to be processed
   */


  moveBlock(e) {
    if (!this._blocks.hasOwnProperty(e.id)) {
      return;
    }

    const block = this._blocks[e.id]; // Track whether a change actually occurred
    // ignoring changes like routine re-positioning
    // of a block when loading a workspace

    let didChange = false; // Move coordinate changes.

    if (e.newCoordinate) {
      didChange = block.x !== e.newCoordinate.x || block.y !== e.newCoordinate.y;
      block.x = e.newCoordinate.x;
      block.y = e.newCoordinate.y;
    } // Remove from any old parent.


    if (typeof e.oldParent !== 'undefined') {
      const oldParent = this._blocks[e.oldParent];

      if (typeof e.oldInput !== 'undefined' && oldParent.inputs[e.oldInput].block === e.id) {
        // This block was connected to the old parent's input.
        oldParent.inputs[e.oldInput].block = null;
      } else if (oldParent.next === e.id) {
        // This block was connected to the old parent's next connection.
        oldParent.next = null;
      }

      this._blocks[e.id].parent = null;
      didChange = true;
    } // Is this block a top-level block?


    if (typeof e.newParent === 'undefined') {
      this._addScript(e.id);
    } else {
      // Remove script, if one exists.
      this._deleteScript(e.id); // Otherwise, try to connect it in its new place.


      if (typeof e.newInput === 'undefined') {
        // Moved to the new parent's next connection.
        this._blocks[e.newParent].next = e.id;
      } else {
        // Moved to the new parent's input.
        // Don't obscure the shadow block.
        let oldShadow = null;

        if (this._blocks[e.newParent].inputs.hasOwnProperty(e.newInput)) {
          oldShadow = this._blocks[e.newParent].inputs[e.newInput].shadow;
        } // If the block being attached is itself a shadow, make sure to set
        // both block and shadow to that blocks ID. This happens when adding
        // inputs to a custom procedure.


        if (this._blocks[e.id].shadow) oldShadow = e.id;
        this._blocks[e.newParent].inputs[e.newInput] = {
          name: e.newInput,
          block: e.id,
          shadow: oldShadow
        };
      }

      this._blocks[e.id].parent = e.newParent;
      didChange = true;
    }

    this.resetCache();
    if (didChange) this.emitProjectChanged();
  }
  /**
   * Block management: run all blocks.
   * @param {!object} runtime Runtime to run all blocks in.
   */


  runAllMonitored(runtime) {
    if (this._cache._monitored === null) {
      this._cache._monitored = Object.keys(this._blocks).filter(blockId => this.getBlock(blockId).isMonitored).map(blockId => {
        const targetId = this.getBlock(blockId).targetId;
        return {
          blockId,
          target: targetId ? runtime.getTargetById(targetId) : null
        };
      });
    }

    const monitored = this._cache._monitored;

    for (let i = 0; i < monitored.length; i++) {
      const {
        blockId,
        target
      } = monitored[i];
      runtime.addMonitorScript(blockId, target);
    }
  }
  /**
   * Block management: delete blocks and their associated scripts. Does nothing if a block
   * with the given ID does not exist.
   * @param {!string} blockId Id of block to delete
   */


  deleteBlock(blockId) {
    // @todo In runtime, stop threads running on this script.
    // Get block
    const block = this._blocks[blockId];

    if (!block) {
      // No block with the given ID exists
      return;
    } // Delete children


    if (block.next !== null) {
      this.deleteBlock(block.next);
    } // Delete inputs (including branches)


    for (const input in block.inputs) {
      // If it's null, the block in this input moved away.
      if (block.inputs[input].block !== null) {
        this.deleteBlock(block.inputs[input].block);
      } // Delete obscured shadow blocks.


      if (block.inputs[input].shadow !== null && block.inputs[input].shadow !== block.inputs[input].block) {
        this.deleteBlock(block.inputs[input].shadow);
      }
    } // Delete any script starting with this block.


    this._deleteScript(blockId); // Delete block itself.


    delete this._blocks[blockId];
    this.resetCache();
    this.emitProjectChanged();
  }
  /**
   * Returns a map of all references to variables or lists from blocks
   * in this block container.
   * @param {Array<object>} optBlocks Optional list of blocks to constrain the search to.
   * This is useful for getting variable/list references for a stack of blocks instead
   * of all blocks on the workspace
   * @param {?boolean} optIncludeBroadcast Optional whether to include broadcast fields.
   * @return {object} A map of variable ID to a list of all variable references
   * for that ID. A variable reference contains the field referencing that variable
   * and also the type of the variable being referenced.
   */


  getAllVariableAndListReferences(optBlocks, optIncludeBroadcast) {
    const blocks = optBlocks ? optBlocks : this._blocks;
    const allReferences = Object.create(null);

    for (const blockId in blocks) {
      let varOrListField = null;
      let varType = null;

      if (blocks[blockId].fields.VARIABLE) {
        varOrListField = blocks[blockId].fields.VARIABLE;
        varType = Variable.SCALAR_TYPE;
      } else if (blocks[blockId].fields.LIST) {
        varOrListField = blocks[blockId].fields.LIST;
        varType = Variable.LIST_TYPE;
      } else if (optIncludeBroadcast && blocks[blockId].fields.BROADCAST_OPTION) {
        varOrListField = blocks[blockId].fields.BROADCAST_OPTION;
        varType = Variable.BROADCAST_MESSAGE_TYPE;
      }

      if (varOrListField) {
        const currVarId = varOrListField.id;

        if (allReferences[currVarId]) {
          allReferences[currVarId].push({
            referencingField: varOrListField,
            type: varType
          });
        } else {
          allReferences[currVarId] = [{
            referencingField: varOrListField,
            type: varType
          }];
        }
      }
    }

    return allReferences;
  }
  /**
   * Keep blocks up to date after a variable gets renamed.
   * @param {string} varId The id of the variable that was renamed
   * @param {string} newName The new name of the variable that was renamed
   */


  updateBlocksAfterVarRename(varId, newName) {
    const blocks = this._blocks;

    for (const blockId in blocks) {
      let varOrListField = null;

      if (blocks[blockId].fields.VARIABLE) {
        varOrListField = blocks[blockId].fields.VARIABLE;
      } else if (blocks[blockId].fields.LIST) {
        varOrListField = blocks[blockId].fields.LIST;
      }

      if (varOrListField) {
        const currFieldId = varOrListField.id;

        if (varId === currFieldId) {
          varOrListField.value = newName;
        }
      }
    }
  }
  /**
   * Keep blocks up to date after they are shared between targets.
   * @param {boolean} isStage If the new target is a stage.
   */


  updateTargetSpecificBlocks(isStage) {
    const blocks = this._blocks;

    for (const blockId in blocks) {
      if (isStage && blocks[blockId].opcode === 'event_whenthisspriteclicked') {
        blocks[blockId].opcode = 'event_whenstageclicked';
      } else if (!isStage && blocks[blockId].opcode === 'event_whenstageclicked') {
        blocks[blockId].opcode = 'event_whenthisspriteclicked';
      }
    }
  }
  /**
   * Update blocks after a sound, costume, or backdrop gets renamed.
   * Any block referring to the old name of the asset should get updated
   * to refer to the new name.
   * @param {string} oldName The old name of the asset that was renamed.
   * @param {string} newName The new name of the asset that was renamed.
   * @param {string} assetType String representation of the kind of asset
   * that was renamed. This can be one of 'sprite','costume', 'sound', or
   * 'backdrop'.
   */


  updateAssetName(oldName, newName, assetType) {
    let getAssetField;

    if (assetType === 'costume') {
      getAssetField = this._getCostumeField.bind(this);
    } else if (assetType === 'sound') {
      getAssetField = this._getSoundField.bind(this);
    } else if (assetType === 'backdrop') {
      getAssetField = this._getBackdropField.bind(this);
    } else if (assetType === 'sprite') {
      getAssetField = this._getSpriteField.bind(this);
    } else {
      return;
    }

    const blocks = this._blocks;

    for (const blockId in blocks) {
      const assetField = getAssetField(blockId);

      if (assetField && assetField.value === oldName) {
        assetField.value = newName;
      }
    }

    this.resetCache();
  }
  /**
   * Update sensing_of blocks after a variable gets renamed.
   * @param {string} oldName The old name of the variable that was renamed.
   * @param {string} newName The new name of the variable that was renamed.
   * @param {string} targetName The name of the target the variable belongs to.
   * @return {boolean} Returns true if any of the blocks were updated.
   */


  updateSensingOfReference(oldName, newName, targetName) {
    const blocks = this._blocks;
    let blockUpdated = false;

    for (const blockId in blocks) {
      const block = blocks[blockId];

      if (block.opcode === 'sensing_of' && block.fields.PROPERTY.value === oldName && // If block and shadow are different, it means a block is inserted to OBJECT, and should be ignored.
      block.inputs.OBJECT.block === block.inputs.OBJECT.shadow) {
        const inputBlock = this.getBlock(block.inputs.OBJECT.block);

        if (inputBlock.fields.OBJECT.value === targetName) {
          block.fields.PROPERTY.value = newName;
          blockUpdated = true;
        }
      }
    }

    if (blockUpdated) this.resetCache();
    return blockUpdated;
  }
  /**
   * Helper function to retrieve a costume menu field from a block given its id.
   * @param {string} blockId A unique identifier for a block
   * @return {?object} The costume menu field of the block with the given block id.
   * Null if either a block with the given id doesn't exist or if a costume menu field
   * does not exist on the block with the given id.
   */


  _getCostumeField(blockId) {
    const block = this.getBlock(blockId);

    if (block && block.fields.hasOwnProperty('COSTUME')) {
      return block.fields.COSTUME;
    }

    return null;
  }
  /**
   * Helper function to retrieve a sound menu field from a block given its id.
   * @param {string} blockId A unique identifier for a block
   * @return {?object} The sound menu field of the block with the given block id.
   * Null, if either a block with the given id doesn't exist or if a sound menu field
   * does not exist on the block with the given id.
   */


  _getSoundField(blockId) {
    const block = this.getBlock(blockId);

    if (block && block.fields.hasOwnProperty('SOUND_MENU')) {
      return block.fields.SOUND_MENU;
    }

    return null;
  }
  /**
   * Helper function to retrieve a backdrop menu field from a block given its id.
   * @param {string} blockId A unique identifier for a block
   * @return {?object} The backdrop menu field of the block with the given block id.
   * Null, if either a block with the given id doesn't exist or if a backdrop menu field
   * does not exist on the block with the given id.
   */


  _getBackdropField(blockId) {
    const block = this.getBlock(blockId);

    if (block && block.fields.hasOwnProperty('BACKDROP')) {
      return block.fields.BACKDROP;
    }

    return null;
  }
  /**
   * Helper function to retrieve a sprite menu field from a block given its id.
   * @param {string} blockId A unique identifier for a block
   * @return {?object} The sprite menu field of the block with the given block id.
   * Null, if either a block with the given id doesn't exist or if a sprite menu field
   * does not exist on the block with the given id.
   */


  _getSpriteField(blockId) {
    const block = this.getBlock(blockId);

    if (!block) {
      return null;
    }

    const spriteMenuNames = ['TOWARDS', 'TO', 'OBJECT', 'VIDEOONMENU2', 'DISTANCETOMENU', 'TOUCHINGOBJECTMENU', 'CLONE_OPTION'];

    for (let i = 0; i < spriteMenuNames.length; i++) {
      const menuName = spriteMenuNames[i];

      if (block.fields.hasOwnProperty(menuName)) {
        return block.fields[menuName];
      }
    }

    return null;
  } // ---------------------------------------------------------------------

  /**
   * Encode all of `this._blocks` as an XML string usable
   * by a Blockly/scratch-blocks workspace.
   * @param {object<string, Comment>} comments Map of comments referenced by id
   * @return {string} String of XML representing this object's blocks.
   */


  toXML(comments) {
    return this._scripts.map(script => this.blockToXML(script, comments)).join();
  }
  /**
   * Recursively encode an individual block and its children
   * into a Blockly/scratch-blocks XML string.
   * @param {!string} blockId ID of block to encode.
   * @param {object<string, Comment>} comments Map of comments referenced by id
   * @return {string} String of XML representing this block and any children.
   */


  blockToXML(blockId, comments) {
    const block = this._blocks[blockId]; // block should exist, but currently some blocks' next property point
    // to a blockId for non-existent blocks. Until we track down that behavior,
    // this early exit allows the project to load.

    if (!block) return; // Encode properties of this block.

    const tagName = block.shadow ? 'shadow' : 'block';
    let xmlString = "<".concat(tagName, "\n                id=\"").concat(block.id, "\"\n                type=\"").concat(block.opcode, "\"\n                ").concat(block.topLevel ? "x=\"".concat(block.x, "\" y=\"").concat(block.y, "\"") : '', "\n            >");
    const commentId = block.comment;

    if (commentId) {
      if (comments) {
        if (comments.hasOwnProperty(commentId)) {
          xmlString += comments[commentId].toXML();
        } else {
          log.warn("Could not find comment with id: ".concat(commentId, " in provided comment descriptions."));
        }
      } else {
        log.warn("Cannot serialize comment with id: ".concat(commentId, "; no comment descriptions provided."));
      }
    } // Add any mutation. Must come before inputs.


    if (block.mutation) {
      xmlString += this.mutationToXML(block.mutation);
    } // Add any inputs on this block.


    for (const input in block.inputs) {
      if (!block.inputs.hasOwnProperty(input)) continue;
      const blockInput = block.inputs[input]; // Only encode a value tag if the value input is occupied.

      if (blockInput.block || blockInput.shadow) {
        xmlString += "<value name=\"".concat(blockInput.name, "\">");

        if (blockInput.block) {
          xmlString += this.blockToXML(blockInput.block, comments);
        }

        if (blockInput.shadow && blockInput.shadow !== blockInput.block) {
          // Obscured shadow.
          xmlString += this.blockToXML(blockInput.shadow, comments);
        }

        xmlString += '</value>';
      }
    } // Add any fields on this block.


    for (const field in block.fields) {
      if (!block.fields.hasOwnProperty(field)) continue;
      const blockField = block.fields[field];
      xmlString += "<field name=\"".concat(blockField.name, "\"");
      const fieldId = blockField.id;

      if (fieldId) {
        xmlString += " id=\"".concat(fieldId, "\"");
      }

      const varType = blockField.variableType;

      if (typeof varType === 'string') {
        xmlString += " variabletype=\"".concat(varType, "\"");
      }

      let value = blockField.value;

      if (typeof value === 'string') {
        value = xmlEscape(blockField.value);
      }

      xmlString += ">".concat(value, "</field>");
    } // Add blocks connected to the next connection.


    if (block.next) {
      xmlString += "<next>".concat(this.blockToXML(block.next, comments), "</next>");
    }

    xmlString += "</".concat(tagName, ">");
    return xmlString;
  }
  /**
   * Recursively encode a mutation object to XML.
   * @param {!object} mutation Object representing a mutation.
   * @return {string} XML string representing a mutation.
   */


  mutationToXML(mutation) {
    let mutationString = "<".concat(mutation.tagName);

    for (const prop in mutation) {
      if (prop === 'children' || prop === 'tagName') continue;
      let mutationValue = typeof mutation[prop] === 'string' ? xmlEscape(mutation[prop]) : mutation[prop]; // Handle dynamic extension blocks

      if (prop === 'blockInfo') {
        mutationValue = xmlEscape(JSON.stringify(mutation[prop]));
      }

      mutationString += " ".concat(prop, "=\"").concat(mutationValue, "\"");
    }

    mutationString += '>';

    for (let i = 0; i < mutation.children.length; i++) {
      mutationString += this.mutationToXML(mutation.children[i]);
    }

    mutationString += "</".concat(mutation.tagName, ">");
    return mutationString;
  } // ---------------------------------------------------------------------

  /**
   * Helper to serialize block fields and input fields for reporting new monitors
   * @param {!object} block Block to be paramified.
   * @return {!object} object of param key/values.
   */


  _getBlockParams(block) {
    const params = {};

    for (const key in block.fields) {
      params[key] = block.fields[key].value;
    }

    for (const inputKey in block.inputs) {
      const inputBlock = this._blocks[block.inputs[inputKey].block];

      for (const key in inputBlock.fields) {
        params[key] = inputBlock.fields[key].value;
      }
    }

    return params;
  }
  /**
   * Helper to get the corresponding internal procedure definition block
   * @param {!object} defineBlock Outer define block.
   * @return {!object} internal definition block which has the mutation.
   */


  _getCustomBlockInternal(defineBlock) {
    if (defineBlock.inputs && defineBlock.inputs.custom_block) {
      return this._blocks[defineBlock.inputs.custom_block.block];
    }
  }
  /**
   * Helper to add a stack to `this._scripts`.
   * @param {?string} topBlockId ID of block that starts the script.
   */


  _addScript(topBlockId) {
    const i = this._scripts.indexOf(topBlockId);

    if (i > -1) return; // Already in scripts.

    this._scripts.push(topBlockId); // Update `topLevel` property on the top block.


    this._blocks[topBlockId].topLevel = true;
  }
  /**
   * Helper to remove a script from `this._scripts`.
   * @param {?string} topBlockId ID of block that starts the script.
   */


  _deleteScript(topBlockId) {
    const i = this._scripts.indexOf(topBlockId);

    if (i > -1) this._scripts.splice(i, 1); // Update `topLevel` property on the top block.

    if (this._blocks[topBlockId]) this._blocks[topBlockId].topLevel = false;
  }

}
/**
 * A private method shared with execute to build an object containing the block
 * information execute needs and that is reset when other cached Blocks info is
 * reset.
 * @param {Blocks} blocks Blocks containing the expected blockId
 * @param {string} blockId blockId for the desired execute cache
 * @param {function} CacheType constructor for cached block information
 * @return {object} execute cache object
 */


BlocksExecuteCache.getCached = function (blocks, blockId, CacheType) {
  let cached = blocks._cache._executeCached[blockId];

  if (typeof cached !== 'undefined') {
    return cached;
  }

  const block = blocks.getBlock(blockId);
  if (typeof block === 'undefined') return null;

  if (typeof CacheType === 'undefined') {
    cached = {
      id: blockId,
      opcode: blocks.getOpcode(block),
      fields: blocks.getFields(block),
      inputs: blocks.getInputs(block),
      mutation: blocks.getMutation(block)
    };
  } else {
    cached = new CacheType(blocks, {
      id: blockId,
      opcode: blocks.getOpcode(block),
      fields: blocks.getFields(block),
      inputs: blocks.getInputs(block),
      mutation: blocks.getMutation(block)
    });
  }

  blocks._cache._executeCached[blockId] = cached;
  return cached;
};
/**
 * Cache class constructor for runtime. Used to consider what threads should
 * start based on hat data.
 * @type {function}
 */


const RuntimeScriptCache = BlocksRuntimeCache._RuntimeScriptCache;
/**
 * Get an array of scripts from a block container prefiltered to match opcode.
 * @param {Blocks} blocks - Container of blocks
 * @param {string} opcode - Opcode to filter top blocks by
 * @returns {Array.<RuntimeScriptCache>} - Array of RuntimeScriptCache cache
 *   objects
 */

BlocksRuntimeCache.getScripts = function (blocks, opcode) {
  let scripts = blocks._cache.scripts[opcode];

  if (!scripts) {
    scripts = blocks._cache.scripts[opcode] = [];
    const allScripts = blocks._scripts;

    for (let i = 0; i < allScripts.length; i++) {
      const topBlockId = allScripts[i];
      const block = blocks.getBlock(topBlockId);

      if (block.opcode === opcode) {
        scripts.push(new RuntimeScriptCache(blocks, topBlockId));
      }
    }
  }

  return scripts;
};

module.exports = Blocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/comment.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/comment.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * Object representing a Scratch Comment (block or workspace).
 */
const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const xmlEscape = __webpack_require__(/*! ../util/xml-escape */ "./node_modules/scratch-vm/src/util/xml-escape.js");

class Comment {
  /**
   * @param {string} id Id of the comment.
   * @param {string} text Text content of the comment.
   * @param {number} x X position of the comment on the workspace.
   * @param {number} y Y position of the comment on the workspace.
   * @param {number} width The width of the comment when it is full size.
   * @param {number} height The height of the comment when it is full size.
   * @param {boolean} minimized Whether the comment is minimized.
   * @constructor
   */
  constructor(id, text, x, y, width, height, minimized) {
    this.id = id || uid();
    this.text = text;
    this.x = x;
    this.y = y;
    this.width = Math.max(Number(width), Comment.MIN_WIDTH);
    this.height = Math.max(Number(height), Comment.MIN_HEIGHT);
    this.minimized = minimized || false;
    this.blockId = null;
  }

  toXML() {
    return "<comment id=\"".concat(this.id, "\" x=\"").concat(this.x, "\" y=\"").concat(this.y, "\" w=\"").concat(this.width, "\" h=\"").concat(this.height, "\" pinned=\"").concat(this.blockId !== null, "\" minimized=\"").concat(this.minimized, "\">").concat(xmlEscape(this.text), "</comment>");
  } // TODO choose min and defaults for width and height


  static get MIN_WIDTH() {
    return 20;
  }

  static get MIN_HEIGHT() {
    return 20;
  }

  static get DEFAULT_WIDTH() {
    return 100;
  }

  static get DEFAULT_HEIGHT() {
    return 100;
  }

}

module.exports = Comment;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/execute.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/execute.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const BlockUtility = __webpack_require__(/*! ./block-utility */ "./node_modules/scratch-vm/src/engine/block-utility.js");

const BlocksExecuteCache = __webpack_require__(/*! ./blocks-execute-cache */ "./node_modules/scratch-vm/src/engine/blocks-execute-cache.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const Thread = __webpack_require__(/*! ./thread */ "./node_modules/scratch-vm/src/engine/thread.js");

const {
  Map
} = __webpack_require__(/*! immutable */ "./node_modules/immutable/dist/immutable.js");

const cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");
/**
 * Single BlockUtility instance reused by execute for every pritimive ran.
 * @const
 */


const blockUtility = new BlockUtility();
/**
 * Profiler frame name for block functions.
 * @const {string}
 */

const blockFunctionProfilerFrame = 'blockFunction';
/**
 * Profiler frame ID for 'blockFunction'.
 * @type {number}
 */

let blockFunctionProfilerId = -1;
/**
 * Utility function to determine if a value is a Promise.
 * @param {*} value Value to check for a Promise.
 * @return {boolean} True if the value appears to be a Promise.
 */

const isPromise = function isPromise(value) {
  return value !== null && typeof value === 'object' && typeof value.then === 'function';
};
/**
 * Handle any reported value from the primitive, either directly returned
 * or after a promise resolves.
 * @param {*} resolvedValue Value eventually returned from the primitive.
 * @param {!Sequencer} sequencer Sequencer stepping the thread for the ran
 * primitive.
 * @param {!Thread} thread Thread containing the primitive.
 * @param {!string} currentBlockId Id of the block in its thread for value from
 * the primitive.
 * @param {!string} opcode opcode used to identify a block function primitive.
 * @param {!boolean} isHat Is the current block a hat?
 */
// @todo move this to callback attached to the thread when we have performance
// metrics (dd)


const handleReport = function handleReport(resolvedValue, sequencer, thread, blockCached, lastOperation) {
  const currentBlockId = blockCached.id;
  const opcode = blockCached.opcode;
  const isHat = blockCached._isHat;
  thread.pushReportedValue(resolvedValue);

  if (isHat) {
    // Hat predicate was evaluated.
    if (sequencer.runtime.getIsEdgeActivatedHat(opcode)) {
      // If this is an edge-activated hat, only proceed if the value is
      // true and used to be false, or the stack was activated explicitly
      // via stack click
      if (!thread.stackClick) {
        const hasOldEdgeValue = thread.target.hasEdgeActivatedValue(currentBlockId);
        const oldEdgeValue = thread.target.updateEdgeActivatedValue(currentBlockId, resolvedValue);
        const edgeWasActivated = hasOldEdgeValue ? !oldEdgeValue && resolvedValue : resolvedValue;

        if (edgeWasActivated) {
          // TW: Resume the thread if we were paused for a promise.
          thread.status = Thread.STATUS_RUNNING;
        } else {
          sequencer.retireThread(thread);
        }
      }
    } else if (!resolvedValue) {
      // Not an edge-activated hat: retire the thread
      // if predicate was false.
      sequencer.retireThread(thread);
    }
  } else {
    // In a non-hat, report the value visually if necessary if
    // at the top of the thread stack.
    if (lastOperation && typeof resolvedValue !== 'undefined' && thread.atStackTop()) {
      if (thread.stackClick) {
        sequencer.runtime.visualReport(currentBlockId, resolvedValue);
      }

      if (thread.updateMonitor) {
        const targetId = sequencer.runtime.monitorBlocks.getBlock(currentBlockId).targetId;

        if (targetId && !sequencer.runtime.getTargetById(targetId)) {
          // Target no longer exists
          return;
        }

        sequencer.runtime.requestUpdateMonitor(Map({
          id: currentBlockId,
          spriteName: targetId ? sequencer.runtime.getTargetById(targetId).getName() : null,
          value: resolvedValue
        }));
      }
    } // Finished any yields.


    thread.status = Thread.STATUS_RUNNING;
  }
};

const handlePromise = (primitiveReportedValue, sequencer, thread, blockCached, lastOperation) => {
  if (thread.status === Thread.STATUS_RUNNING) {
    // Primitive returned a promise; automatically yield thread.
    thread.status = Thread.STATUS_PROMISE_WAIT;
  } // Promise handlers


  primitiveReportedValue.then(resolvedValue => {
    handleReport(resolvedValue, sequencer, thread, blockCached, lastOperation); // If it's a command block or a top level reporter in a stackClick.
    // TW: Don't mangle the stack when we just finished executing a hat block.
    // Hat block is always the top and first block of the script. There are no loops to find.

    if (lastOperation && !blockCached._isHat) {
      let stackFrame;
      let nextBlockId;

      do {
        // In the case that the promise is the last block in the current thread stack
        // We need to pop out repeatedly until we find the next block.
        const popped = thread.popStack();

        if (popped === null) {
          return;
        }

        nextBlockId = thread.target.blocks.getNextBlock(popped);

        if (nextBlockId !== null) {
          // A next block exists so break out this loop
          break;
        } // Investigate the next block and if not in a loop,
        // then repeat and pop the next item off the stack frame


        stackFrame = thread.peekStackFrame();
      } while (stackFrame !== null && !stackFrame.isLoop);

      thread.pushStack(nextBlockId);
    }
  }, rejectionReason => {
    // Promise rejected: the primitive had some error.
    // Log it and proceed.
    log.warn('Primitive rejected promise: ', rejectionReason);
    thread.status = Thread.STATUS_RUNNING;
    thread.popStack();
  });
};
/**
 * A execute.js internal representation of a block to reduce the time spent in
 * execute as the same blocks are called the most.
 *
 * With the help of the Blocks class create a mutable copy of block
 * information. The members of BlockCached derived values of block information
 * that does not need to be reevaluated until a change in Blocks. Since Blocks
 * handles where the cache instance is stored, it drops all cache versions of a
 * block when any change happens to it. This way we can quickly execute blocks
 * and keep perform the right action according to the current block information
 * in the editor.
 *
 * @param {Blocks} blockContainer the related Blocks instance
 * @param {object} cached default set of cached values
 */


class BlockCached {
  constructor(blockContainer, cached) {
    /**
     * Block id in its parent set of blocks.
     * @type {string}
     */
    this.id = cached.id;
    /**
     * Block operation code for this block.
     * @type {string}
     */

    this.opcode = cached.opcode;
    /**
     * Original block object containing argument values for static fields.
     * @type {object}
     */

    this.fields = cached.fields;
    /**
     * Original block object containing argument values for executable inputs.
     * @type {object}
     */

    this.inputs = cached.inputs;
    /**
     * Procedure mutation.
     * @type {?object}
     */

    this.mutation = cached.mutation;
    /**
     * The profiler the block is configured with.
     * @type {?Profiler}
     */

    this._profiler = null;
    /**
     * Profiler information frame.
     * @type {?ProfilerFrame}
     */

    this._profilerFrame = null;
    /**
     * Is the opcode a hat (event responder) block.
     * @type {boolean}
     */

    this._isHat = false;
    /**
     * The block opcode's implementation function.
     * @type {?function}
     */

    this._blockFunction = null;
    /**
     * Is the block function defined for this opcode?
     * @type {boolean}
     */

    this._definedBlockFunction = false;
    /**
     * Is this block a block with no function but a static value to return.
     * @type {boolean}
     */

    this._isShadowBlock = false;
    /**
     * The static value of this block if it is a shadow block.
     * @type {?any}
     */

    this._shadowValue = null;
    /**
     * A copy of the block's fields that may be modified.
     * @type {object}
     */

    this._fields = Object.assign({}, this.fields);
    /**
     * A copy of the block's inputs that may be modified.
     * @type {object}
     */

    this._inputs = Object.assign({}, this.inputs);
    /**
     * An arguments object for block implementations. All executions of this
     * specific block will use this objecct.
     * @type {object}
     */

    this._argValues = {
      mutation: this.mutation
    };
    /**
     * The inputs key the parent refers to this BlockCached by.
     * @type {string}
     */

    this._parentKey = null;
    /**
     * The target object where the parent wants the resulting value stored
     * with _parentKey as the key.
     * @type {object}
     */

    this._parentValues = null;
    /**
     * A sequence of non-shadow operations that can must be performed. This
     * list recreates the order this block and its children are executed.
     * Since the order is always the same we can safely store that order
     * and iterate over the operations instead of dynamically walking the
     * tree every time.
     * @type {Array<BlockCached>}
     */

    this._ops = [];
    const {
      runtime
    } = blockUtility.sequencer;
    const {
      opcode,
      fields,
      inputs
    } = this; // Assign opcode isHat and blockFunction data to avoid dynamic lookups.

    this._isHat = runtime.getIsHat(opcode);
    this._blockFunction = runtime.getOpcodeFunction(opcode);
    this._definedBlockFunction = typeof this._blockFunction !== 'undefined'; // Store the current shadow value if there is a shadow value.

    const fieldKeys = Object.keys(fields);
    this._isShadowBlock = !this._definedBlockFunction && fieldKeys.length === 1 && Object.keys(inputs).length === 0;
    this._shadowValue = this._isShadowBlock && fields[fieldKeys[0]].value; // Store the static fields onto _argValues.

    for (const fieldName in fields) {
      if (fieldName === 'VARIABLE' || fieldName === 'LIST' || fieldName === 'BROADCAST_OPTION') {
        this._argValues[fieldName] = {
          id: fields[fieldName].id,
          name: fields[fieldName].value
        };
      } else {
        this._argValues[fieldName] = fields[fieldName].value;
      }
    } // Remove custom_block. It is not part of block execution.


    delete this._inputs.custom_block;

    if ('BROADCAST_INPUT' in this._inputs) {
      // BROADCAST_INPUT is called BROADCAST_OPTION in the args and is an
      // object with an unchanging shape.
      this._argValues.BROADCAST_OPTION = {
        id: null,
        name: null
      }; // We can go ahead and compute BROADCAST_INPUT if it is a shadow
      // value.

      const broadcastInput = this._inputs.BROADCAST_INPUT;

      if (broadcastInput.block === broadcastInput.shadow) {
        // Shadow dropdown menu is being used.
        // Get the appropriate information out of it.
        const shadow = blockContainer.getBlock(broadcastInput.shadow);
        const broadcastField = shadow.fields.BROADCAST_OPTION;
        this._argValues.BROADCAST_OPTION.id = broadcastField.id;
        this._argValues.BROADCAST_OPTION.name = broadcastField.value; // Evaluating BROADCAST_INPUT here we do not need to do so
        // later.

        delete this._inputs.BROADCAST_INPUT;
      }
    } // Cache all input children blocks in the operation lists. The
    // operations can later be run in the order they appear in correctly
    // executing the operations quickly in a flat loop instead of needing to
    // recursivly iterate them.


    for (const inputName in this._inputs) {
      const input = this._inputs[inputName];

      if (input.block) {
        const inputCached = BlocksExecuteCache.getCached(blockContainer, input.block, BlockCached);

        if (inputCached._isHat) {
          continue;
        }

        this._ops.push(...inputCached._ops);

        inputCached._parentKey = inputName;
        inputCached._parentValues = this._argValues; // Shadow values are static and do not change, go ahead and
        // store their value on args.

        if (inputCached._isShadowBlock) {
          this._argValues[inputName] = inputCached._shadowValue;
        }
      }
    } // The final operation is this block itself. At the top most block is a
    // command block or a block that is being run as a monitor.


    if (this._definedBlockFunction) {
      this._ops.push(this);
    }
  }

}
/**
 * Initialize a BlockCached instance so its command/hat
 * block and reporters can be profiled during execution.
 * @param {Profiler} profiler - The profiler that is currently enabled.
 * @param {BlockCached} blockCached - The blockCached instance to profile.
 */


const _prepareBlockProfiling = function _prepareBlockProfiling(profiler, blockCached) {
  blockCached._profiler = profiler;

  if (blockFunctionProfilerId === -1) {
    blockFunctionProfilerId = profiler.idByName(blockFunctionProfilerFrame);
  }

  const ops = blockCached._ops;

  for (let i = 0; i < ops.length; i++) {
    ops[i]._profilerFrame = profiler.frame(blockFunctionProfilerId, ops[i].opcode);
  }
};
/**
 * Execute a block.
 * @param {!Sequencer} sequencer Which sequencer is executing.
 * @param {!Thread} thread Thread which to read and execute.
 */


const execute = function execute(sequencer, thread) {
  const runtime = sequencer.runtime; // store sequencer and thread so block functions can access them through
  // convenience methods.

  blockUtility.sequencer = sequencer;
  blockUtility.thread = thread; // Current block to execute is the one on the top of the stack.

  const currentBlockId = thread.peekStack();
  const currentStackFrame = thread.peekStackFrame();
  let blockContainer = thread.blockContainer;
  let blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached);

  if (blockCached === null) {
    blockContainer = runtime.flyoutBlocks;
    blockCached = BlocksExecuteCache.getCached(blockContainer, currentBlockId, BlockCached); // Stop if block or target no longer exists.

    if (blockCached === null) {
      // No block found: stop the thread; script no longer exists.
      sequencer.retireThread(thread);
      return;
    }
  }

  const ops = blockCached._ops;
  const length = ops.length;
  let i = 0;

  if (currentStackFrame.reported !== null) {
    const reported = currentStackFrame.reported; // Reinstate all the previous values.

    for (; i < reported.length; i++) {
      const {
        opCached: oldOpCached,
        inputValue
      } = reported[i];
      const opCached = ops.find(op => op.id === oldOpCached);

      if (opCached) {
        const inputName = opCached._parentKey;
        const argValues = opCached._parentValues;

        if (inputName === 'BROADCAST_INPUT') {
          // Something is plugged into the broadcast input.
          // Cast it to a string. We don't need an id here.
          argValues.BROADCAST_OPTION.id = null;
          argValues.BROADCAST_OPTION.name = cast.toString(inputValue);
        } else {
          argValues[inputName] = inputValue;
        }
      }
    } // Find the last reported block that is still in the set of operations.
    // This way if the last operation was removed, we'll find the next
    // candidate. If an earlier block that was performed was removed then
    // we'll find the index where the last operation is now.


    if (reported.length > 0) {
      const lastExisting = reported.reverse().find(report => ops.find(op => op.id === report.opCached));

      if (lastExisting) {
        i = ops.findIndex(opCached => opCached.id === lastExisting.opCached) + 1;
      } else {
        i = 0;
      }
    } // The reporting block must exist and must be the next one in the sequence of operations.


    if (thread.justReported !== null && ops[i] && ops[i].id === currentStackFrame.reporting) {
      const opCached = ops[i];
      const inputValue = thread.justReported;
      thread.justReported = null;
      const inputName = opCached._parentKey;
      const argValues = opCached._parentValues;

      if (inputName === 'BROADCAST_INPUT') {
        // Something is plugged into the broadcast input.
        // Cast it to a string. We don't need an id here.
        argValues.BROADCAST_OPTION.id = null;
        argValues.BROADCAST_OPTION.name = cast.toString(inputValue);
      } else {
        argValues[inputName] = inputValue;
      }

      i += 1;
    }

    currentStackFrame.reporting = null;
    currentStackFrame.reported = null;
  }

  const start = i;

  for (; i < length; i++) {
    const lastOperation = i === length - 1;
    const opCached = ops[i];
    const blockFunction = opCached._blockFunction; // Update values for arguments (inputs).

    const argValues = opCached._argValues; // Fields are set during opCached initialization.
    // Blocks should glow when a script is starting,
    // not after it has finished (see #1404).
    // Only blocks in blockContainers that don't forceNoGlow
    // should request a glow.

    if (!blockContainer.forceNoGlow) {
      thread.requestScriptGlowInFrame = true;
    } // Inputs are set during previous steps in the loop.


    const primitiveReportedValue = blockFunction(argValues, blockUtility); // If it's a promise, wait until promise resolves.

    if (isPromise(primitiveReportedValue)) {
      handlePromise(primitiveReportedValue, sequencer, thread, opCached, lastOperation); // Store the already reported values. They will be thawed into the
      // future versions of the same operations by block id. The reporting
      // operation if it is promise waiting will set its parent value at
      // that time.

      thread.justReported = null;
      currentStackFrame.reporting = ops[i].id;
      currentStackFrame.reported = ops.slice(0, i).map(reportedCached => {
        const inputName = reportedCached._parentKey;
        const reportedValues = reportedCached._parentValues;

        if (inputName === 'BROADCAST_INPUT') {
          return {
            opCached: reportedCached.id,
            inputValue: reportedValues[inputName].BROADCAST_OPTION.name
          };
        }

        return {
          opCached: reportedCached.id,
          inputValue: reportedValues[inputName]
        };
      }); // We are waiting for a promise. Stop running this set of operations
      // and continue them later after thawing the reported values.

      break;
    } else if (thread.status === Thread.STATUS_RUNNING) {
      if (lastOperation) {
        handleReport(primitiveReportedValue, sequencer, thread, opCached, lastOperation);
      } else {
        // By definition a block that is not last in the list has a
        // parent.
        const inputName = opCached._parentKey;
        const parentValues = opCached._parentValues;

        if (inputName === 'BROADCAST_INPUT') {
          // Something is plugged into the broadcast input.
          // Cast it to a string. We don't need an id here.
          parentValues.BROADCAST_OPTION.id = null;
          parentValues.BROADCAST_OPTION.name = cast.toString(primitiveReportedValue);
        } else {
          parentValues[inputName] = primitiveReportedValue;
        }
      }
    }
  }

  if (runtime.profiler !== null) {
    if (blockCached._profiler !== runtime.profiler) {
      _prepareBlockProfiling(runtime.profiler, blockCached);
    } // Determine the index that is after the last executed block. `i` is
    // currently the block that was just executed. `i + 1` will be the block
    // after that. `length` with the min call makes sure we don't try to
    // reference an operation outside of the set of operations.


    const end = Math.min(i + 1, length);

    for (let p = start; p < end; p++) {
      ops[p]._profilerFrame.count += 1;
    }
  }
};

module.exports = execute;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/monitor-record.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/monitor-record.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const {
  Record
} = __webpack_require__(/*! immutable */ "./node_modules/immutable/dist/immutable.js");

const MonitorRecord = Record({
  id: null,
  // Block Id

  /** Present only if the monitor is sprite-specific, such as x position */
  spriteName: null,

  /** Present only if the monitor is sprite-specific, such as x position */
  targetId: null,
  opcode: null,
  value: null,
  params: null,
  mode: 'default',
  sliderMin: 0,
  sliderMax: 100,
  isDiscrete: true,
  x: null,
  // (x: null, y: null) Indicates that the monitor should be auto-positioned
  y: null,
  width: 0,
  height: 0,
  visible: true
});
module.exports = MonitorRecord;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/mutation-adapter.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/mutation-adapter.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const html = __webpack_require__(/*! htmlparser2 */ "./src/scaffolding/htmlparser2/index.js");

const decodeHtml = __webpack_require__(/*! decode-html */ "./node_modules/decode-html/index.js");
/**
 * Convert a part of a mutation DOM to a mutation VM object, recursively.
 * @param {object} dom DOM object for mutation tag.
 * @return {object} Object representing useful parts of this mutation.
 */


const mutatorTagToObject = function mutatorTagToObject(dom) {
  const obj = Object.create(null);
  obj.tagName = dom.name;
  obj.children = [];

  for (const prop in dom.attribs) {
    if (prop === 'xmlns') continue;
    obj[prop] = decodeHtml(dom.attribs[prop]); // Note: the capitalization of block info in the following lines is important.
    // The lowercase is read in from xml which normalizes case. The VM uses camel case everywhere else.

    if (prop === 'blockinfo') {
      obj.blockInfo = JSON.parse(obj.blockinfo);
      delete obj.blockinfo;
    }
  }

  for (let i = 0; i < dom.children.length; i++) {
    obj.children.push(mutatorTagToObject(dom.children[i]));
  }

  return obj;
};
/**
 * Adapter between mutator XML or DOM and block representation which can be
 * used by the Scratch runtime.
 * @param {(object|string)} mutation Mutation XML string or DOM.
 * @return {object} Object representing the mutation.
 */


const mutationAdpater = function mutationAdpater(mutation) {
  let mutationParsed; // Check if the mutation is already parsed; if not, parse it.

  if (typeof mutation === 'object') {
    mutationParsed = mutation;
  } else {
    mutationParsed = html.parseDOM(mutation)[0];
  }

  return mutatorTagToObject(mutationParsed);
};

module.exports = mutationAdpater;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/profiler.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/profiler.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview
 * A way to profile Scratch internal performance. Like what blocks run during a
 * step? How much time do they take? How much time is spent inbetween blocks?
 *
 * Profiler aims for to spend as little time inside its functions while
 * recording. For this it has a simple internal record structure that records a
 * series of values for each START and STOP event in a single array. This lets
 * all the values be pushed in one call for the array. This simplicity allows
 * the contents of the start() and stop() calls to be inlined in areas that are
 * called frequently enough to want even greater performance from Profiler so
 * what is recorded better reflects on the profiled code and not Profiler
 * itself.
 */

/**
 * The next id returned for a new profile'd function.
 * @type {number}
 */
let nextId = 0;
/**
 * The mapping of names to ids.
 * @const {Object.<string, number>}
 */

const profilerNames = {};
/**
 * The START event identifier in Profiler records.
 * @const {number}
 */

const START = 0;
/**
 * The STOP event identifier in Profiler records.
 * @const {number}
 */

const STOP = 1;
/**
 * The number of cells used in the records array by a START event.
 * @const {number}
 */

const START_SIZE = 4;
/**
 * The number of cells used in the records array by a STOP event.
 * @const {number}
 */

const STOP_SIZE = 2;
/**
 * Stored reference to Performance instance provided by the Browser.
 * @const {Performance}
 */

const performance = typeof window === 'object' && window.performance;
/**
 * Callback handle called by Profiler for each frame it decodes from its
 * records.
 * @callback FrameCallback
 * @param {ProfilerFrame} frame
 */

/**
 * A set of information about a frame of execution that was recorded.
 */

class ProfilerFrame {
  /**
   * @param {number} depth Depth of the frame in the recorded stack.
   */
  constructor(depth) {
    /**
     * The numeric id of a record symbol like Runtime._step or
     * blockFunction.
     * @type {number}
     */
    this.id = -1;
    /**
     * The amount of time spent inside the recorded frame and any deeper
     * frames.
     * @type {number}
     */

    this.totalTime = 0;
    /**
     * The amount of time spent only inside this record frame. Not
     * including time in any deeper frames.
     * @type {number}
     */

    this.selfTime = 0;
    /**
     * An arbitrary argument for the recorded frame. For example a block
     * function might record its opcode as an argument.
     * @type {*}
     */

    this.arg = null;
    /**
     * The depth of the recorded frame. This can help compare recursive
     * funtions that are recorded. Each level of recursion with have a
     * different depth value.
     * @type {number}
     */

    this.depth = depth;
    /**
     * A summarized count of the number of calls to this frame.
     * @type {number}
     */

    this.count = 0;
  }

}

class Profiler {
  /**
   * @param {FrameCallback} onFrame a handle called for each recorded frame.
   * The passed frame value may not be stored as it'll be updated with later
   * frame information. Any information that is further stored by the handler
   * should make copies or reduce the information.
   */
  constructor() {
    let onFrame = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};

    /**
     * A series of START and STOP values followed by arguments. After
     * recording is complete the full set of records is reported back by
     * stepping through the series to connect the relative START and STOP
     * information.
     * @type {Array.<*>}
     */
    this.records = [];
    /**
     * An array of frames incremented on demand instead as part of start
     * and stop.
     * @type {Array.<ProfilerFrame>}
     */

    this.increments = [];
    /**
     * An array of profiler frames separated by counter argument. Generally
     * for Scratch these frames are separated by block function opcode.
     * This tracks each time an opcode is called.
     * @type {Array.<ProfilerFrame>}
     */

    this.counters = [];
    /**
     * A frame with no id or argument.
     * @type {ProfilerFrame}
     */

    this.nullFrame = new ProfilerFrame(-1);
    /**
     * A cache of ProfilerFrames to reuse when reporting the recorded
     * frames in records.
     * @type {Array.<ProfilerFrame>}
     */

    this._stack = [new ProfilerFrame(0)];
    /**
     * A callback handle called with each decoded frame when reporting back
     * all the recorded times.
     * @type {FrameCallback}
     */

    this.onFrame = onFrame;
    /**
     * A reference to the START record id constant.
     * @const {number}
     */

    this.START = START;
    /**
     * A reference to the STOP record id constant.
     * @const {number}
     */

    this.STOP = STOP;
  }
  /**
   * Start recording a frame of time for an id and optional argument.
   * @param {number} id The id returned by idByName for a name symbol like
   * Runtime._step.
   * @param {?*} arg An arbitrary argument value to store with the frame.
   */


  start(id, arg) {
    this.records.push(START, id, arg, performance.now());
  }
  /**
   * Stop the current frame.
   */


  stop() {
    this.records.push(STOP, performance.now());
  }
  /**
   * Increment the number of times this symbol is called.
   * @param {number} id The id returned by idByName for a name symbol.
   */


  increment(id) {
    if (!this.increments[id]) {
      this.increments[id] = new ProfilerFrame(-1);
      this.increments[id].id = id;
    }

    this.increments[id].count += 1;
  }
  /**
   * Find or create a ProfilerFrame-like object whose counter can be
   * incremented outside of the Profiler.
   * @param {number} id The id returned by idByName for a name symbol.
   * @param {*} arg The argument for a frame that identifies it in addition
   *   to the id.
   * @return {{count: number}} A ProfilerFrame-like whose count should be
   *   incremented for each call.
   */


  frame(id, arg) {
    for (let i = 0; i < this.counters.length; i++) {
      if (this.counters[i].id === id && this.counters[i].arg === arg) {
        return this.counters[i];
      }
    }

    const newCounter = new ProfilerFrame(-1);
    newCounter.id = id;
    newCounter.arg = arg;
    this.counters.push(newCounter);
    return newCounter;
  }
  /**
   * Decode records and report all frames to `this.onFrame`.
   */


  reportFrames() {
    const stack = this._stack;
    let depth = 1; // Step through the records and initialize Frame instances from the
    // START and STOP events. START and STOP events are separated by events
    // for deeper frames run by higher frames. Frames are stored on a stack
    // and reinitialized for each START event. When a stop event is reach
    // the Frame for the current depth has its final values stored and its
    // passed to the current onFrame callback. This way Frames are "pushed"
    // for each START event and "popped" for each STOP and handed to an
    // outside handle to any desired reduction of the collected data.

    for (let i = 0; i < this.records.length;) {
      if (this.records[i] === START) {
        if (depth >= stack.length) {
          stack.push(new ProfilerFrame(depth));
        } // Store id, arg, totalTime, and initialize selfTime.


        const frame = stack[depth++];
        frame.id = this.records[i + 1];
        frame.arg = this.records[i + 2]; // totalTime is first set as the time recorded by this START
        // event. Once the STOP event is reached the stored start time
        // is subtracted from the recorded stop time. The resulting
        // difference is the actual totalTime, and replaces the start
        // time in frame.totalTime.
        //
        // totalTime is used this way as a convenient member to store a
        // value between the two events without needing additional
        // members on the Frame or in a shadow map.

        frame.totalTime = this.records[i + 3]; // selfTime is decremented until we reach the STOP event for
        // this frame. totalTime will be added to it then to get the
        // time difference.

        frame.selfTime = 0;
        i += START_SIZE;
      } else if (this.records[i] === STOP) {
        const now = this.records[i + 1];
        const frame = stack[--depth]; // totalTime is the difference between the start event time
        // stored in totalTime and the stop event time pulled from this
        // record.

        frame.totalTime = now - frame.totalTime; // selfTime is the difference of this frame's totalTime and the
        // sum of totalTime of deeper frames.

        frame.selfTime += frame.totalTime; // Remove this frames totalTime from the parent's selfTime.

        stack[depth - 1].selfTime -= frame.totalTime; // This frame occured once.

        frame.count = 1;
        this.onFrame(frame);
        i += STOP_SIZE;
      } else {
        this.records.length = 0;
        throw new Error('Unable to decode Profiler records.');
      }
    }

    for (let j = 0; j < this.increments.length; j++) {
      if (this.increments[j] && this.increments[j].count > 0) {
        this.onFrame(this.increments[j]);
        this.increments[j].count = 0;
      }
    }

    for (let k = 0; k < this.counters.length; k++) {
      if (this.counters[k].count > 0) {
        this.onFrame(this.counters[k]);
        this.counters[k].count = 0;
      }
    }

    this.records.length = 0;
  }
  /**
   * Lookup or create an id for a frame name.
   * @param {string} name The name to return an id for.
   * @return {number} The id for the passed name.
   */


  idByName(name) {
    return Profiler.idByName(name);
  }
  /**
   * Reverse lookup the name from a given frame id.
   * @param {number} id The id to search for.
   * @return {string} The name for the given id.
   */


  nameById(id) {
    return Profiler.nameById(id);
  }
  /**
   * Lookup or create an id for a frame name.
   * @static
   * @param {string} name The name to return an id for.
   * @return {number} The id for the passed name.
   */


  static idByName(name) {
    if (typeof profilerNames[name] !== 'number') {
      profilerNames[name] = nextId++;
    }

    return profilerNames[name];
  }
  /**
   * Reverse lookup the name from a given frame id.
   * @static
   * @param {number} id The id to search for.
   * @return {string} The name for the given id.
   */


  static nameById(id) {
    for (const name in profilerNames) {
      if (profilerNames[name] === id) {
        return name;
      }
    }

    return null;
  }
  /**
   * Profiler is only available on platforms with the Performance API.
   * @return {boolean} Can the Profiler run in this browser?
   */


  static available() {
    return typeof window === 'object' && typeof window.performance !== 'undefined';
  }

}
/**
 * A reference to the START record id constant.
 * @const {number}
 */


Profiler.START = START;
/**
 * A reference to the STOP record id constant.
 * @const {number}
 */

Profiler.STOP = STOP;
module.exports = Profiler;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/runtime.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/runtime.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

const EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");

const {
  OrderedMap
} = __webpack_require__(/*! immutable */ "./node_modules/immutable/dist/immutable.js");

const ExtendedJSON = __webpack_require__(/*! @turbowarp/json */ "./node_modules/@turbowarp/json/src/index.js");

const ArgumentType = __webpack_require__(/*! ../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const Blocks = __webpack_require__(/*! ./blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

const BlocksRuntimeCache = __webpack_require__(/*! ./blocks-runtime-cache */ "./node_modules/scratch-vm/src/engine/blocks-runtime-cache.js");

const BlockType = __webpack_require__(/*! ../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Profiler = __webpack_require__(/*! ./profiler */ "./node_modules/scratch-vm/src/engine/profiler.js");

const Sequencer = __webpack_require__(/*! ./sequencer */ "./node_modules/scratch-vm/src/engine/sequencer.js");

const execute = __webpack_require__(/*! ./execute.js */ "./node_modules/scratch-vm/src/engine/execute.js");

const ScratchBlocksConstants = __webpack_require__(/*! ./scratch-blocks-constants */ "./node_modules/scratch-vm/src/engine/scratch-blocks-constants.js");

const TargetType = __webpack_require__(/*! ../extension-support/target-type */ "./node_modules/scratch-vm/src/extension-support/target-type.js");

const Thread = __webpack_require__(/*! ./thread */ "./node_modules/scratch-vm/src/engine/thread.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const maybeFormatMessage = __webpack_require__(/*! ../util/maybe-format-message */ "./node_modules/scratch-vm/src/util/maybe-format-message.js");

const StageLayering = __webpack_require__(/*! ./stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

const Variable = __webpack_require__(/*! ./variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const xmlEscape = __webpack_require__(/*! ../util/xml-escape */ "./node_modules/scratch-vm/src/util/xml-escape.js");

const ScratchLinkWebSocket = __webpack_require__(/*! ../util/scratch-link-websocket */ "./node_modules/scratch-vm/src/util/scratch-link-websocket.js"); // Virtual I/O devices.


const Clock = __webpack_require__(/*! ../io/clock */ "./node_modules/scratch-vm/src/io/clock.js");

const Cloud = __webpack_require__(/*! ../io/cloud */ "./node_modules/scratch-vm/src/io/cloud.js");

const Keyboard = __webpack_require__(/*! ../io/keyboard */ "./node_modules/scratch-vm/src/io/keyboard.js");

const Mouse = __webpack_require__(/*! ../io/mouse */ "./node_modules/scratch-vm/src/io/mouse.js");

const MouseWheel = __webpack_require__(/*! ../io/mouseWheel */ "./node_modules/scratch-vm/src/io/mouseWheel.js");

const UserData = __webpack_require__(/*! ../io/userData */ "./node_modules/scratch-vm/src/io/userData.js");

const Video = __webpack_require__(/*! ../io/video */ "./node_modules/scratch-vm/src/io/video.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const defaultBlockPackages = {
  scratch3_control: __webpack_require__(/*! ../blocks/scratch3_control */ "./node_modules/scratch-vm/src/blocks/scratch3_control.js"),
  scratch3_event: __webpack_require__(/*! ../blocks/scratch3_event */ "./node_modules/scratch-vm/src/blocks/scratch3_event.js"),
  scratch3_looks: __webpack_require__(/*! ../blocks/scratch3_looks */ "./node_modules/scratch-vm/src/blocks/scratch3_looks.js"),
  scratch3_motion: __webpack_require__(/*! ../blocks/scratch3_motion */ "./node_modules/scratch-vm/src/blocks/scratch3_motion.js"),
  scratch3_operators: __webpack_require__(/*! ../blocks/scratch3_operators */ "./node_modules/scratch-vm/src/blocks/scratch3_operators.js"),
  scratch3_sound: __webpack_require__(/*! ../blocks/scratch3_sound */ "./node_modules/scratch-vm/src/blocks/scratch3_sound.js"),
  scratch3_sensing: __webpack_require__(/*! ../blocks/scratch3_sensing */ "./node_modules/scratch-vm/src/blocks/scratch3_sensing.js"),
  scratch3_data: __webpack_require__(/*! ../blocks/scratch3_data */ "./node_modules/scratch-vm/src/blocks/scratch3_data.js"),
  scratch3_procedures: __webpack_require__(/*! ../blocks/scratch3_procedures */ "./node_modules/scratch-vm/src/blocks/scratch3_procedures.js")
};

const interpolate = __webpack_require__(/*! ./tw-interpolate */ "./node_modules/scratch-vm/src/engine/tw-interpolate.js");

const FrameLoop = __webpack_require__(/*! ./tw-frame-loop */ "./node_modules/scratch-vm/src/engine/tw-frame-loop.js");

const defaultExtensionColors = ['#0FBD8C', '#0DA57A', '#0B8E69'];
const COMMENT_CONFIG_MAGIC = ' // _twconfig_';
/**
 * Information used for converting Scratch argument types into scratch-blocks data.
 * @type {object.<ArgumentType, {shadowType: string, fieldType: string}>}
 */

const ArgumentTypeMap = (() => {
  const map = {};
  map[ArgumentType.ANGLE] = {
    shadow: {
      type: 'math_angle',
      // We specify fieldNames here so that we can pick
      // create and populate a field with the defaultValue
      // specified in the extension.
      // When the `fieldName` property is not specified,
      // the <field></field> will be left out of the XML and
      // the scratch-blocks defaults for that field will be
      // used instead (e.g. default of 0 for number fields)
      fieldName: 'NUM'
    }
  };
  map[ArgumentType.COLOR] = {
    shadow: {
      type: 'colour_picker',
      fieldName: 'COLOUR'
    }
  };
  map[ArgumentType.NUMBER] = {
    shadow: {
      type: 'math_number',
      fieldName: 'NUM'
    }
  };
  map[ArgumentType.STRING] = {
    shadow: {
      type: 'text',
      fieldName: 'TEXT'
    }
  };
  map[ArgumentType.BOOLEAN] = {
    check: 'Boolean'
  };
  map[ArgumentType.MATRIX] = {
    shadow: {
      type: 'matrix',
      fieldName: 'MATRIX'
    }
  };
  map[ArgumentType.NOTE] = {
    shadow: {
      type: 'note',
      fieldName: 'NOTE'
    }
  };
  map[ArgumentType.IMAGE] = {
    // Inline images are weird because they're not actually "arguments".
    // They are more analagous to the label on a block.
    fieldType: 'field_image'
  };
  return map;
})();
/**
 * A pair of functions used to manage the cloud variable limit,
 * to be used when adding (or attempting to add) or removing a cloud variable.
 * @typedef {object} CloudDataManager
 * @property {function} canAddCloudVariable A function to call to check that
 * a cloud variable can be added.
 * @property {function} addCloudVariable A function to call to track a new
 * cloud variable on the runtime.
 * @property {function} removeCloudVariable A function to call when
 * removing an existing cloud variable.
 * @property {function} hasCloudVariables A function to call to check that
 * the runtime has any cloud variables.
 * @property {function} getNumberOfCloudVariables A function that returns the
 * number of cloud variables in the project.
 */

/**
 * Creates and manages cloud variable limit in a project,
 * and returns two functions to be used to add a new
 * cloud variable (while checking that it can be added)
 * and remove an existing cloud variable.
 * These are to be called whenever attempting to create or delete
 * a cloud variable.
 * @param {Object} cloudOptions
 * @param {number} cloudOptions.limit Maximum number of cloud variables
 * @return {CloudDataManager} The functions to be used when adding or removing a
 * cloud variable.
 */


const cloudDataManager = cloudOptions => {
  let count = 0;

  const canAddCloudVariable = () => count < cloudOptions.limit;

  const addCloudVariable = () => {
    count++;
  };

  const removeCloudVariable = () => {
    count--;
  };

  const hasCloudVariables = () => count > 0;

  const getNumberOfCloudVariables = () => count;

  return {
    canAddCloudVariable,
    addCloudVariable,
    removeCloudVariable,
    hasCloudVariables,
    getNumberOfCloudVariables
  };
};
/**
 * Numeric ID for Runtime._step in Profiler instances.
 * @type {number}
 */


let stepProfilerId = -1;
/**
 * Numeric ID for Sequencer.stepThreads in Profiler instances.
 * @type {number}
 */

let stepThreadsProfilerId = -1;
/**
 * Numeric ID for RenderWebGL.draw in Profiler instances.
 * @type {number}
 */

let rendererDrawProfilerId = -1;
/**
 * Manages targets, scripts, and the sequencer.
 * @constructor
 */

class Runtime extends EventEmitter {
  constructor() {
    super();
    /**
     * Target management and storage.
     * @type {Array.<!Target>}
     */

    this.targets = [];
    /**
     * Targets in reverse order of execution. Shares its order with drawables.
     * @type {Array.<!Target>}
     */

    this.executableTargets = [];
    /**
     * A list of threads that are currently running in the VM.
     * Threads are added when execution starts and pruned when execution ends.
     * @type {Array.<Thread>}
     */

    this.threads = [];
    this.threadMap = new Map();
    /** @type {!Sequencer} */

    this.sequencer = new Sequencer(this);
    /**
     * Storage container for flyout blocks.
     * These will execute on `_editingTarget.`
     * @type {!Blocks}
     */

    this.flyoutBlocks = new Blocks(this, true
    /* force no glow */
    );
    /**
     * Storage container for monitor blocks.
     * These will execute on a target maybe
     * @type {!Blocks}
     */

    this.monitorBlocks = new Blocks(this, true
    /* force no glow */
    );
    /**
     * Currently known editing target for the VM.
     * @type {?Target}
     */

    this._editingTarget = null;
    /**
     * Map to look up a block primitive's implementation function by its opcode.
     * This is a two-step lookup: package name first, then primitive name.
     * @type {Object.<string, Function>}
     */

    this._primitives = {};
    /**
     * Map to look up all block information by extended opcode.
     * @type {Array.<CategoryInfo>}
     * @private
     */

    this._blockInfo = [];
    /**
     * Map to look up hat blocks' metadata.
     * Keys are opcode for hat, values are metadata objects.
     * @type {Object.<string, Object>}
     */

    this._hats = {};
    /**
     * A list of script block IDs that were glowing during the previous frame.
     * @type {!Array.<!string>}
     */

    this._scriptGlowsPreviousFrame = [];
    /**
     * Number of non-monitor threads running during the previous frame.
     * @type {number}
     */

    this._nonMonitorThreadCount = 0;
    /**
     * All threads that finished running and were removed from this.threads
     * by behaviour in Sequencer.stepThreads.
     * @type {Array<Thread>}
     */

    this._lastStepDoneThreads = null;
    /**
     * Currently known number of clones, used to enforce clone limit.
     * @type {number}
     */

    this._cloneCounter = 0;
    /**
     * Flag to emit a targets update at the end of a step. When target data
     * changes, this flag is set to true.
     * @type {boolean}
     */

    this._refreshTargets = false;
    /**
     * Map to look up all monitor block information by opcode.
     * @type {object}
     * @private
     */

    this.monitorBlockInfo = {};
    /**
     * Ordered map of all monitors, which are MonitorReporter objects.
     */

    this._monitorState = OrderedMap({});
    /**
     * Monitor state from last tick
     */

    this._prevMonitorState = OrderedMap({});
    /**
     * Whether the project is in "turbo mode."
     * @type {Boolean}
     */

    this.turboMode = false;
    /**
     * tw: Responsible for managing the VM's many timers.
     */

    this.frameLoop = new FrameLoop(this);
    /**
     * Current length of a step.
     * Changes as mode switches, and used by the sequencer to calculate
     * WORK_TIME.
     * @type {!number}
     */

    this.currentStepTime = 1000 / 30; // Set an intial value for this.currentMSecs

    this.updateCurrentMSecs();
    /**
     * Whether any primitive has requested a redraw.
     * Affects whether `Sequencer.stepThreads` will yield
     * after stepping each thread.
     * Reset on every frame.
     * @type {boolean}
     */

    this.redrawRequested = false; // Register all given block packages.

    this._registerBlockPackages(); // Register and initialize "IO devices", containers for processing
    // I/O related data.

    /** @type {Object.<string, Object>} */


    this.ioDevices = {
      clock: new Clock(this),
      cloud: new Cloud(this),
      keyboard: new Keyboard(this),
      mouse: new Mouse(this),
      mouseWheel: new MouseWheel(this),
      userData: new UserData(),
      video: new Video(this)
    };
    /**
     * A list of extensions, used to manage hardware connection.
     */

    this.peripheralExtensions = {};
    /**
     * A runtime profiler that records timed events for later playback to
     * diagnose Scratch performance.
     * @type {Profiler}
     */

    this.profiler = null;
    this.cloudOptions = {
      limit: 10
    };
    const newCloudDataManager = cloudDataManager(this.cloudOptions);
    /**
     * Check wether the runtime has any cloud data.
     * @type {function}
     * @return {boolean} Whether or not the runtime currently has any
     * cloud variables.
     */

    this.hasCloudData = newCloudDataManager.hasCloudVariables;
    /**
     * A function which checks whether a new cloud variable can be added
     * to the runtime.
     * @type {function}
     * @return {boolean} Whether or not a new cloud variable can be added
     * to the runtime.
     */

    this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable;
    /**
     * A function which returns the number of cloud variables in the runtime.
     * @returns {number}
     */

    this.getNumberOfCloudVariables = newCloudDataManager.getNumberOfCloudVariables;
    /**
     * A function that tracks a new cloud variable in the runtime,
     * updating the cloud variable limit. Calling this function will
     * emit a cloud data update event if this is the first cloud variable
     * being added.
     * @type {function}
     */

    this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager);
    /**
     * A function which updates the runtime's cloud variable limit
     * when removing a cloud variable and emits a cloud update event
     * if the last of the cloud variables is being removed.
     * @type {function}
     */

    this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);
    /**
     * A string representing the origin of the current project from outside of the
     * Scratch community, such as CSFirst.
     * @type {?string}
     */

    this.origin = null;
    this._stageTarget = null;
    this.addonBlocks = {};
    this.stageWidth = Runtime.STAGE_WIDTH;
    this.stageHeight = Runtime.STAGE_HEIGHT;
    this.runtimeOptions = {
      maxClones: Runtime.MAX_CLONES,
      miscLimits: true,
      fencing: true
    };
    this.compilerOptions = {
      enabled: true,
      warpTimer: false
    };
    this.debug = false;
    this._lastStepTime = Date.now();
    this.interpolationEnabled = false;
    this._defaultStoredSettings = this._generateAllProjectOptions();
    /**
     * TW: We support a "packaged runtime" mode. This can be used when:
     *  - there will never be an editor attached such as scratch-gui or scratch-blocks
     *  - the project will never be exported with saveProjectSb3()
     *  - original costume and sound data is not needed
     * In this mode, the runtime is able to discard large amounts of data and avoid some processing
     * to make projects load faster and use less memory.
     * This is not designed to protect projects from copying as someone can still copy the data that
     * gets fed into the runtime in the first place.
     * This mode is used by the TurboWarp Packager.
     */

    this.isPackaged = false;
    /**
     * Contains information about the external communication methods that the scripts inside the project
     * can use to send data from inside the project to an external server.
     * Do not update this directly. Use Runtime.setExternalCommunicationMethod() instead.
     */

    this.externalCommunicationMethods = {
      cloudVariables: false,
      customExtensions: false
    };
    this.on(Runtime.HAS_CLOUD_DATA_UPDATE, enabled => {
      this.setExternalCommunicationMethod('cloudVariables', enabled);
    });
    /**
     * If set to true, features such as reading colors from the user's webcam will be disabled
     * when the project has access to any external communication method to protect user privacy.
     * Requires TurboWarp/scratch-render.
     * Do not update this directly. Use Runtime.setEnforcePrivacy() instead.
     */

    this.enforcePrivacy = true;
  }
  /**
   * Width of the stage, in pixels.
   * @const {number}
   */


  static get STAGE_WIDTH() {
    // tw: stage size is set per-runtime, this is only the initial value
    return 480;
  }
  /**
   * Height of the stage, in pixels.
   * @const {number}
   */


  static get STAGE_HEIGHT() {
    // tw: stage size is set per-runtime, this is only the initial value
    return 360;
  }
  /**
   * Event name for glowing a script.
   * @const {string}
   */


  static get SCRIPT_GLOW_ON() {
    return 'SCRIPT_GLOW_ON';
  }
  /**
   * Event name for unglowing a script.
   * @const {string}
   */


  static get SCRIPT_GLOW_OFF() {
    return 'SCRIPT_GLOW_OFF';
  }
  /**
   * Event name for glowing a block.
   * @const {string}
   */


  static get BLOCK_GLOW_ON() {
    return 'BLOCK_GLOW_ON';
  }
  /**
   * Event name for unglowing a block.
   * @const {string}
   */


  static get BLOCK_GLOW_OFF() {
    return 'BLOCK_GLOW_OFF';
  }
  /**
   * Event name for a cloud data update
   * to this project.
   * @const {string}
   */


  static get HAS_CLOUD_DATA_UPDATE() {
    return 'HAS_CLOUD_DATA_UPDATE';
  }
  /**
   * Event name for turning on turbo mode.
   * @const {string}
   */


  static get TURBO_MODE_ON() {
    return 'TURBO_MODE_ON';
  }
  /**
   * Event name for turning off turbo mode.
   * @const {string}
   */


  static get TURBO_MODE_OFF() {
    return 'TURBO_MODE_OFF';
  }
  /**
   * Event name for runtime options changing.
   * @const {string}
   */


  static get RUNTIME_OPTIONS_CHANGED() {
    return 'RUNTIME_OPTIONS_CHANGED';
  }
  /**
   * Event name for compiler options changing.
   * @const {string}
   */


  static get COMPILER_OPTIONS_CHANGED() {
    return 'COMPILER_OPTIONS_CHANGED';
  }
  /**
   * Event name for framerate changing.
   * @const {string}
   */


  static get FRAMERATE_CHANGED() {
    return 'FRAMERATE_CHANGED';
  }
  /**
   * Event name for interpolation changing.
   * @const {string}
   */


  static get INTERPOLATION_CHANGED() {
    return 'INTERPOLATION_CHANGED';
  }
  /**
   * Event name for stage size changing.
   * @const {string}
   */


  static get STAGE_SIZE_CHANGED() {
    return 'STAGE_SIZE_CHANGED';
  }
  /**
   * Event name for compiler errors.
   * @const {string}
   */


  static get COMPILE_ERROR() {
    return 'COMPILE_ERROR';
  }
  /**
   * Event name when the project is started (threads may not necessarily be
   * running).
   * @const {string}
   */


  static get PROJECT_START() {
    return 'PROJECT_START';
  }
  /**
   * Event name when threads start running.
   * Used by the UI to indicate running status.
   * @const {string}
   */


  static get PROJECT_RUN_START() {
    return 'PROJECT_RUN_START';
  }
  /**
   * Event name when threads stop running
   * Used by the UI to indicate not-running status.
   * @const {string}
   */


  static get PROJECT_RUN_STOP() {
    return 'PROJECT_RUN_STOP';
  }
  /**
   * Event name for project being stopped or restarted by the user.
   * Used by blocks that need to reset state.
   * @const {string}
   */


  static get PROJECT_STOP_ALL() {
    return 'PROJECT_STOP_ALL';
  }
  /**
   * Event name for target being stopped by a stop for target call.
   * Used by blocks that need to stop individual targets.
   * @const {string}
   */


  static get STOP_FOR_TARGET() {
    return 'STOP_FOR_TARGET';
  }
  /**
   * Event name for visual value report.
   * @const {string}
   */


  static get VISUAL_REPORT() {
    return 'VISUAL_REPORT';
  }
  /**
   * Event name for project loaded report.
   * @const {string}
   */


  static get PROJECT_LOADED() {
    return 'PROJECT_LOADED';
  }
  /**
   * Event name for report that a change was made that can be saved
   * @const {string}
   */


  static get PROJECT_CHANGED() {
    return 'PROJECT_CHANGED';
  }
  /**
   * Event name for report that a change was made to an extension in the toolbox.
   * @const {string}
   */


  static get TOOLBOX_EXTENSIONS_NEED_UPDATE() {
    return 'TOOLBOX_EXTENSIONS_NEED_UPDATE';
  }
  /**
   * Event name for targets update report.
   * @const {string}
   */


  static get TARGETS_UPDATE() {
    return 'TARGETS_UPDATE';
  }
  /**
   * Event name for monitors update.
   * @const {string}
   */


  static get MONITORS_UPDATE() {
    return 'MONITORS_UPDATE';
  }
  /**
   * Event name for block drag update.
   * @const {string}
   */


  static get BLOCK_DRAG_UPDATE() {
    return 'BLOCK_DRAG_UPDATE';
  }
  /**
   * Event name for block drag end.
   * @const {string}
   */


  static get BLOCK_DRAG_END() {
    return 'BLOCK_DRAG_END';
  }
  /**
   * Event name for reporting that an extension was added.
   * @const {string}
   */


  static get EXTENSION_ADDED() {
    return 'EXTENSION_ADDED';
  }
  /**
   * Event name for reporting that an extension as asked for a custom field to be added
   * @const {string}
   */


  static get EXTENSION_FIELD_ADDED() {
    return 'EXTENSION_FIELD_ADDED';
  }
  /**
   * Event name for updating the available set of peripheral devices.
   * This causes the peripheral connection modal to update a list of
   * available peripherals.
   * @const {string}
   */


  static get PERIPHERAL_LIST_UPDATE() {
    return 'PERIPHERAL_LIST_UPDATE';
  }
  /**
   * Event name for when the user picks a bluetooth device to connect to
   * via Companion Device Manager (CDM)
   * @const {string}
   */


  static get USER_PICKED_PERIPHERAL() {
    return 'USER_PICKED_PERIPHERAL';
  }
  /**
   * Event name for reporting that a peripheral has connected.
   * This causes the status button in the blocks menu to indicate 'connected'.
   * @const {string}
   */


  static get PERIPHERAL_CONNECTED() {
    return 'PERIPHERAL_CONNECTED';
  }
  /**
   * Event name for reporting that a peripheral has been intentionally disconnected.
   * This causes the status button in the blocks menu to indicate 'disconnected'.
   * @const {string}
   */


  static get PERIPHERAL_DISCONNECTED() {
    return 'PERIPHERAL_DISCONNECTED';
  }
  /**
   * Event name for reporting that a peripheral has encountered a request error.
   * This causes the peripheral connection modal to switch to an error state.
   * @const {string}
   */


  static get PERIPHERAL_REQUEST_ERROR() {
    return 'PERIPHERAL_REQUEST_ERROR';
  }
  /**
   * Event name for reporting that a peripheral connection has been lost.
   * This causes a 'peripheral connection lost' error alert to display.
   * @const {string}
   */


  static get PERIPHERAL_CONNECTION_LOST_ERROR() {
    return 'PERIPHERAL_CONNECTION_LOST_ERROR';
  }
  /**
   * Event name for reporting that a peripheral has not been discovered.
   * This causes the peripheral connection modal to show a timeout state.
   * @const {string}
   */


  static get PERIPHERAL_SCAN_TIMEOUT() {
    return 'PERIPHERAL_SCAN_TIMEOUT';
  }
  /**
   * Event name to indicate that the microphone is being used to stream audio.
   * @const {string}
   */


  static get MIC_LISTENING() {
    return 'MIC_LISTENING';
  }
  /**
   * Event name for reporting that blocksInfo was updated.
   * @const {string}
   */


  static get BLOCKSINFO_UPDATE() {
    return 'BLOCKSINFO_UPDATE';
  }
  /**
   * Event name when the runtime tick loop has been started.
   * @const {string}
   */


  static get RUNTIME_STARTED() {
    return 'RUNTIME_STARTED';
  }
  /**
   * Event name when the runtime tick loop has been stopped.
   * @const {string}
   */


  static get RUNTIME_STOPPED() {
    return 'RUNTIME_STOPPED';
  }
  /**
   * Event name when the runtime dispose has been called.
   * @const {string}
   */


  static get RUNTIME_DISPOSED() {
    return 'RUNTIME_DISPOSED';
  }
  /**
   * Event name for reporting that a block was updated and needs to be rerendered.
   * @const {string}
   */


  static get BLOCKS_NEED_UPDATE() {
    return 'BLOCKS_NEED_UPDATE';
  }
  /**
   * How rapidly we try to step threads by default, in ms.
   */


  static get THREAD_STEP_INTERVAL() {
    // tw: not used, only exists for compatibility
    return 1000 / 60;
  }
  /**
   * In compatibility mode, how rapidly we try to step threads, in ms.
   */


  static get THREAD_STEP_INTERVAL_COMPATIBILITY() {
    // tw: not used, only exists for compatibility
    return 1000 / 30;
  }
  /**
   * How many clones can be created at a time.
   * @const {number}
   */


  static get MAX_CLONES() {
    // tw: clone limit is set per-runtime in runtimeOptions, this is only the initial value
    return 300;
  } // -----------------------------------------------------------------------------
  // -----------------------------------------------------------------------------
  // Helper function for initializing the addCloudVariable function


  _initializeAddCloudVariable(newCloudDataManager) {
    // The addCloudVariable function
    return () => {
      const hadCloudVarsBefore = this.hasCloudData();
      newCloudDataManager.addCloudVariable();

      if (!hadCloudVarsBefore && this.hasCloudData()) {
        this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, true);
      }
    };
  } // Helper function for initializing the removeCloudVariable function


  _initializeRemoveCloudVariable(newCloudDataManager) {
    return () => {
      const hadCloudVarsBefore = this.hasCloudData();
      newCloudDataManager.removeCloudVariable();

      if (hadCloudVarsBefore && !this.hasCloudData()) {
        this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false);
      }
    };
  }
  /**
   * Register default block packages with this runtime.
   * @todo Prefix opcodes with package name.
   * @private
   */


  _registerBlockPackages() {
    for (const packageName in defaultBlockPackages) {
      if (defaultBlockPackages.hasOwnProperty(packageName)) {
        // @todo pass a different runtime depending on package privilege?
        const packageObject = new defaultBlockPackages[packageName](this); // Collect primitives from package.

        if (packageObject.getPrimitives) {
          const packagePrimitives = packageObject.getPrimitives();

          for (const op in packagePrimitives) {
            if (packagePrimitives.hasOwnProperty(op)) {
              this._primitives[op] = packagePrimitives[op].bind(packageObject);
            }
          }
        } // Collect hat metadata from package.


        if (packageObject.getHats) {
          const packageHats = packageObject.getHats();

          for (const hatName in packageHats) {
            if (packageHats.hasOwnProperty(hatName)) {
              this._hats[hatName] = packageHats[hatName];
            }
          }
        } // Collect monitored from package.


        if (packageObject.getMonitored) {
          this.monitorBlockInfo = Object.assign({}, this.monitorBlockInfo, packageObject.getMonitored());
        }

        this.compilerRegisterExtension(packageName, packageObject);
      }
    }
  }

  compilerRegisterExtension(name, extensionObject) {
    this["ext_".concat(name)] = extensionObject;
  }

  getMonitorState() {
    return this._monitorState;
  }
  /**
   * Generate an extension-specific menu ID.
   * @param {string} menuName - the name of the menu.
   * @param {string} extensionId - the ID of the extension hosting the menu.
   * @returns {string} - the constructed ID.
   * @private
   */


  _makeExtensionMenuId(menuName, extensionId) {
    return "".concat(extensionId, "_menu_").concat(xmlEscape(menuName));
  }
  /**
   * Create a context ("args") object for use with `formatMessage` on messages which might be target-specific.
   * @param {Target} [target] - the target to use as context. If a target is not provided, default to the current
   * editing target or the stage.
   */


  makeMessageContextForTarget(target) {
    const context = {};
    target = target || this.getEditingTarget() || this.getTargetForStage();

    if (target) {
      context.targetType = target.isStage ? TargetType.STAGE : TargetType.SPRITE;
    }
  }
  /**
   * Register the primitives provided by an extension.
   * @param {ExtensionMetadata} extensionInfo - information about the extension (id, blocks, etc.)
   * @private
   */


  _registerExtensionPrimitives(extensionInfo) {
    const categoryInfo = {
      id: extensionInfo.id,
      name: maybeFormatMessage(extensionInfo.name),
      showStatusButton: extensionInfo.showStatusButton,
      blockIconURI: extensionInfo.blockIconURI,
      menuIconURI: extensionInfo.menuIconURI
    };

    if (extensionInfo.color1) {
      categoryInfo.color1 = extensionInfo.color1;
      categoryInfo.color2 = extensionInfo.color2;
      categoryInfo.color3 = extensionInfo.color3;
    } else {
      categoryInfo.color1 = defaultExtensionColors[0];
      categoryInfo.color2 = defaultExtensionColors[1];
      categoryInfo.color3 = defaultExtensionColors[2];
    }

    this._blockInfo.push(categoryInfo);

    this._fillExtensionCategory(categoryInfo, extensionInfo);

    for (const fieldTypeName in categoryInfo.customFieldTypes) {
      if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) {
        const fieldTypeInfo = categoryInfo.customFieldTypes[fieldTypeName]; // Emit events for custom field types from extension

        this.emit(Runtime.EXTENSION_FIELD_ADDED, {
          name: "field_".concat(fieldTypeInfo.extendedName),
          implementation: fieldTypeInfo.fieldImplementation
        });
      }
    }

    this.emit(Runtime.EXTENSION_ADDED, categoryInfo);
  }
  /**
   * Reregister the primitives for an extension
   * @param  {ExtensionMetadata} extensionInfo - new info (results of running getInfo) for an extension
   * @private
   */


  _refreshExtensionPrimitives(extensionInfo) {
    const categoryInfo = this._blockInfo.find(info => info.id === extensionInfo.id);

    if (categoryInfo) {
      categoryInfo.name = maybeFormatMessage(extensionInfo.name);

      this._fillExtensionCategory(categoryInfo, extensionInfo);

      this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo);
    }
  }
  /**
   * Read extension information, convert menus, blocks and custom field types
   * and store the results in the provided category object.
   * @param {CategoryInfo} categoryInfo - the category to be filled
   * @param {ExtensionMetadata} extensionInfo - the extension metadata to read
   * @private
   */


  _fillExtensionCategory(categoryInfo, extensionInfo) {
    categoryInfo.blocks = [];
    categoryInfo.customFieldTypes = {};
    categoryInfo.menus = [];
    categoryInfo.menuInfo = {};

    for (const menuName in extensionInfo.menus) {
      if (extensionInfo.menus.hasOwnProperty(menuName)) {
        const menuInfo = extensionInfo.menus[menuName];

        const convertedMenu = this._buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo);

        categoryInfo.menus.push(convertedMenu);
        categoryInfo.menuInfo[menuName] = menuInfo;
      }
    }

    for (const fieldTypeName in extensionInfo.customFieldTypes) {
      if (extensionInfo.customFieldTypes.hasOwnProperty(fieldTypeName)) {
        const fieldType = extensionInfo.customFieldTypes[fieldTypeName];

        const fieldTypeInfo = this._buildCustomFieldInfo(fieldTypeName, fieldType, extensionInfo.id, categoryInfo);

        categoryInfo.customFieldTypes[fieldTypeName] = fieldTypeInfo;
      }
    }

    if (extensionInfo.docsURI) {
      try {
        const url = new URL(extensionInfo.docsURI);

        if (url.protocol !== 'http:' && url.protocol !== 'https:') {
          throw new Error('invalid protocol');
        }

        const xml = '<button ' + "text=\"".concat(xmlEscape(maybeFormatMessage({
          // note: this translation is hardcoded in translation upload scripts
          id: 'tw.blocks.openDocs',
          default: 'Open Documentation',
          description: 'Button to open extensions docsURI'
        })), "\" ") + 'callbackKey="OPEN_DOCUMENTATION" ' + "web-class=\"docs-uri-".concat(xmlEscape(extensionInfo.docsURI), "\"></button>");
        const block = {
          info: {},
          xml
        };
        categoryInfo.blocks.push(block);
      } catch (e) {
        log.warn('cannot create docsURI button', e);
      }
    }

    for (const blockInfo of extensionInfo.blocks) {
      try {
        const convertedBlock = this._convertForScratchBlocks(blockInfo, categoryInfo);

        categoryInfo.blocks.push(convertedBlock);

        if (convertedBlock.json) {
          const opcode = convertedBlock.json.type;

          if (blockInfo.blockType !== BlockType.EVENT) {
            this._primitives[opcode] = convertedBlock.info.func;
          }

          if (blockInfo.blockType === BlockType.EVENT || blockInfo.blockType === BlockType.HAT) {
            this._hats[opcode] = {
              edgeActivated: blockInfo.isEdgeActivated,
              restartExistingThreads: blockInfo.shouldRestartExistingThreads
            };
          }
        }
      } catch (e) {
        log.error('Error parsing block: ', {
          block: blockInfo,
          error: e
        });
      }
    }
  }
  /**
   * Convert the given extension menu items into the scratch-blocks style of list of pairs.
   * If the menu is dynamic (e.g. the passed in argument is a function), return the input unmodified.
   * @param {object} menuItems - an array of menu items or a function to retrieve such an array
   * @returns {object} - an array of 2 element arrays or the original input function
   * @private
   */


  _convertMenuItems(menuItems) {
    if (typeof menuItems !== 'function') {
      const extensionMessageContext = this.makeMessageContextForTarget();
      return menuItems.map(item => {
        const formattedItem = maybeFormatMessage(item, extensionMessageContext);

        switch (typeof formattedItem) {
          case 'string':
            return [formattedItem, formattedItem];

          case 'object':
            return [maybeFormatMessage(item.text, extensionMessageContext), item.value];

          default:
            throw new Error("Can't interpret menu item: ".concat(JSON.stringify(item)));
        }
      });
    }

    return menuItems;
  }
  /**
   * Build the scratch-blocks JSON for a menu. Note that scratch-blocks treats menus as a special kind of block.
   * @param {string} menuName - the name of the menu
   * @param {object} menuInfo - a description of this menu and its items
   * @property {*} items - an array of menu items or a function to retrieve such an array
   * @property {boolean} [acceptReporters] - if true, allow dropping reporters onto this menu
   * @param {CategoryInfo} categoryInfo - the category for this block
   * @returns {object} - a JSON-esque object ready for scratch-blocks' consumption
   * @private
   */


  _buildMenuForScratchBlocks(menuName, menuInfo, categoryInfo) {
    const menuId = this._makeExtensionMenuId(menuName, categoryInfo.id);

    const menuItems = this._convertMenuItems(menuInfo.items);

    return {
      json: {
        message0: '%1',
        type: menuId,
        inputsInline: true,
        output: 'String',
        colour: categoryInfo.color1,
        colourSecondary: categoryInfo.color2,
        colourTertiary: categoryInfo.color3,
        outputShape: menuInfo.acceptReporters ? ScratchBlocksConstants.OUTPUT_SHAPE_ROUND : ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE,
        args0: [{
          type: 'field_dropdown',
          name: menuName,
          options: menuItems
        }]
      }
    };
  }

  _buildCustomFieldInfo(fieldName, fieldInfo, extensionId, categoryInfo) {
    const extendedName = "".concat(extensionId, "_").concat(fieldName);
    return {
      fieldName: fieldName,
      extendedName: extendedName,
      argumentTypeInfo: {
        shadow: {
          type: extendedName,
          fieldName: "field_".concat(extendedName)
        }
      },
      scratchBlocksDefinition: this._buildCustomFieldTypeForScratchBlocks(extendedName, fieldInfo.output, fieldInfo.outputShape, categoryInfo),
      fieldImplementation: fieldInfo.implementation
    };
  }
  /**
   * Build the scratch-blocks JSON needed for a fieldType.
   * Custom field types need to be namespaced to the extension so that extensions can't interfere with each other
   * @param  {string} fieldName - The name of the field
   * @param {string} output - The output of the field
   * @param {number} outputShape - Shape of the field (from ScratchBlocksConstants)
   * @param {object} categoryInfo - The category the field belongs to (Used to set its colors)
   * @returns {object} - Object to be inserted into scratch-blocks
   */


  _buildCustomFieldTypeForScratchBlocks(fieldName, output, outputShape, categoryInfo) {
    return {
      json: {
        type: fieldName,
        message0: '%1',
        inputsInline: true,
        output: output,
        colour: categoryInfo.color1,
        colourSecondary: categoryInfo.color2,
        colourTertiary: categoryInfo.color3,
        outputShape: outputShape,
        args0: [{
          name: "field_".concat(fieldName),
          type: "field_".concat(fieldName)
        }]
      }
    };
  }
  /**
   * Convert ExtensionBlockMetadata into data ready for scratch-blocks.
   * @param {ExtensionBlockMetadata} blockInfo - the block info to convert
   * @param {CategoryInfo} categoryInfo - the category for this block
   * @returns {ConvertedBlockInfo} - the converted & original block information
   * @private
   */


  _convertForScratchBlocks(blockInfo, categoryInfo) {
    if (blockInfo === '---') {
      return this._convertSeparatorForScratchBlocks(blockInfo);
    }

    if (blockInfo.blockType === BlockType.BUTTON) {
      return this._convertButtonForScratchBlocks(blockInfo);
    }

    return this._convertBlockForScratchBlocks(blockInfo, categoryInfo);
  }
  /**
   * Convert ExtensionBlockMetadata into scratch-blocks JSON & XML, and generate a proxy function.
   * @param {ExtensionBlockMetadata} blockInfo - the block to convert
   * @param {CategoryInfo} categoryInfo - the category for this block
   * @returns {ConvertedBlockInfo} - the converted & original block information
   * @private
   */


  _convertBlockForScratchBlocks(blockInfo, categoryInfo) {
    const extendedOpcode = "".concat(categoryInfo.id, "_").concat(blockInfo.opcode);
    const blockJSON = {
      type: extendedOpcode,
      inputsInline: true,
      category: categoryInfo.name,
      colour: categoryInfo.color1,
      colourSecondary: categoryInfo.color2,
      colourTertiary: categoryInfo.color3
    };
    const context = {
      // TODO: store this somewhere so that we can map args appropriately after translation.
      // This maps an arg name to its relative position in the original (usually English) block text.
      // When displaying a block in another language we'll need to run a `replace` action similar to the one
      // below, but each `[ARG]` will need to be replaced with the number in this map.
      argsMap: {},
      blockJSON,
      categoryInfo,
      blockInfo,
      inputList: []
    }; // If an icon for the extension exists, prepend it to each block, with a vertical separator.
    // We can overspecify an icon for each block, but if no icon exists on a block, fall back to
    // the category block icon.

    const iconURI = blockInfo.blockIconURI || categoryInfo.blockIconURI;

    if (iconURI) {
      blockJSON.extensions = ['scratch_extension'];
      blockJSON.message0 = '%1 %2';
      const iconJSON = {
        type: 'field_image',
        src: iconURI,
        width: 40,
        height: 40
      };
      const separatorJSON = {
        type: 'field_vertical_separator'
      };
      blockJSON.args0 = [iconJSON, separatorJSON];
    }

    switch (blockInfo.blockType) {
      case BlockType.COMMAND:
        blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;
        blockJSON.previousStatement = null; // null = available connection; undefined = hat

        if (!blockInfo.isTerminal) {
          blockJSON.nextStatement = null; // null = available connection; undefined = terminal
        }

        break;

      case BlockType.REPORTER:
        blockJSON.output = 'String'; // TODO: distinguish number & string here?

        blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_ROUND;
        break;

      case BlockType.BOOLEAN:
        blockJSON.output = 'Boolean';
        blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_HEXAGONAL;
        break;

      case BlockType.HAT:
      case BlockType.EVENT:
        if (!blockInfo.hasOwnProperty('isEdgeActivated')) {
          // if absent, this property defaults to true
          blockInfo.isEdgeActivated = true;
        }

        blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;
        blockJSON.nextStatement = null; // null = available connection; undefined = terminal

        break;

      case BlockType.CONDITIONAL:
      case BlockType.LOOP:
        blockInfo.branchCount = blockInfo.branchCount || 1;
        blockJSON.outputShape = ScratchBlocksConstants.OUTPUT_SHAPE_SQUARE;
        blockJSON.previousStatement = null; // null = available connection; undefined = hat

        if (!blockInfo.isTerminal) {
          blockJSON.nextStatement = null; // null = available connection; undefined = terminal
        }

        break;
    }

    const blockText = Array.isArray(blockInfo.text) ? blockInfo.text : [blockInfo.text];
    let inTextNum = 0; // text for the next block "arm" is blockText[inTextNum]

    let inBranchNum = 0; // how many branches have we placed into the JSON so far?

    let outLineNum = 0; // used for scratch-blocks `message${outLineNum}` and `args${outLineNum}`

    const convertPlaceholders = this._convertPlaceholders.bind(this, context);

    const extensionMessageContext = this.makeMessageContextForTarget(); // alternate between a block "arm" with text on it and an open slot for a substack

    while (inTextNum < blockText.length || inBranchNum < blockInfo.branchCount) {
      if (inTextNum < blockText.length) {
        context.outLineNum = outLineNum;
        const lineText = maybeFormatMessage(blockText[inTextNum], extensionMessageContext);
        const convertedText = lineText.replace(/\[(.+?)]/g, convertPlaceholders);

        if (blockJSON["message".concat(outLineNum)]) {
          blockJSON["message".concat(outLineNum)] += convertedText;
        } else {
          blockJSON["message".concat(outLineNum)] = convertedText;
        }

        ++inTextNum;
        ++outLineNum;
      }

      if (inBranchNum < blockInfo.branchCount) {
        blockJSON["message".concat(outLineNum)] = '%1';
        blockJSON["args".concat(outLineNum)] = [{
          type: 'input_statement',
          name: "SUBSTACK".concat(inBranchNum > 0 ? inBranchNum + 1 : '')
        }];
        ++inBranchNum;
        ++outLineNum;
      }
    }

    if (blockInfo.blockType === BlockType.REPORTER) {
      if (!blockInfo.disableMonitor && context.inputList.length === 0) {
        blockJSON.checkboxInFlyout = true;
      }
    } else if (blockInfo.blockType === BlockType.LOOP) {
      // Add icon to the bottom right of a loop block
      blockJSON["lastDummyAlign".concat(outLineNum)] = 'RIGHT';
      blockJSON["message".concat(outLineNum)] = '%1';
      blockJSON["args".concat(outLineNum)] = [{
        type: 'field_image',
        src: './static/blocks-media/repeat.svg',
        // TODO: use a constant or make this configurable?
        width: 24,
        height: 24,
        alt: '*',
        // TODO remove this since we don't use collapsed blocks in scratch
        flip_rtl: true
      }];
      ++outLineNum;
    }

    const mutation = blockInfo.isDynamic ? "<mutation blockInfo=\"".concat(xmlEscape(JSON.stringify(blockInfo)), "\"/>") : '';
    const inputs = context.inputList.join('');
    const blockXML = "<block type=\"".concat(extendedOpcode, "\">").concat(mutation).concat(inputs, "</block>");
    return {
      info: context.blockInfo,
      json: context.blockJSON,
      xml: blockXML
    };
  }
  /**
   * Generate a separator between blocks categories or sub-categories.
   * @param {ExtensionBlockMetadata} blockInfo - the block to convert
   * @param {CategoryInfo} categoryInfo - the category for this block
   * @returns {ConvertedBlockInfo} - the converted & original block information
   * @private
   */


  _convertSeparatorForScratchBlocks(blockInfo) {
    return {
      info: blockInfo,
      xml: '<sep gap="36"/>'
    };
  }
  /**
   * Convert a button for scratch-blocks. A button has no opcode but specifies a callback name in the `func` field.
   * @param {ExtensionBlockMetadata} buttonInfo - the button to convert
   * @property {string} func - the callback name
   * @param {CategoryInfo} categoryInfo - the category for this button
   * @returns {ConvertedBlockInfo} - the converted & original button information
   * @private
   */


  _convertButtonForScratchBlocks(buttonInfo) {
    // for now we only support these pre-defined callbacks handled in scratch-blocks
    const supportedCallbackKeys = ['MAKE_A_LIST', 'MAKE_A_PROCEDURE', 'MAKE_A_VARIABLE'];

    if (supportedCallbackKeys.indexOf(buttonInfo.func) < 0) {
      log.error("Custom button callbacks not supported yet: ".concat(buttonInfo.func));
    }

    const extensionMessageContext = this.makeMessageContextForTarget();
    const buttonText = maybeFormatMessage(buttonInfo.text, extensionMessageContext);
    return {
      info: buttonInfo,
      xml: "<button text=\"".concat(buttonText, "\" callbackKey=\"").concat(buttonInfo.func, "\"></button>")
    };
  }
  /**
   * Helper for _convertPlaceholdes which handles inline images which are a specialized case of block "arguments".
   * @param {object} argInfo Metadata about the inline image as specified by the extension
   * @return {object} JSON blob for a scratch-blocks image field.
   * @private
   */


  _constructInlineImageJson(argInfo) {
    if (!argInfo.dataURI) {
      log.warn('Missing data URI in extension block with argument type IMAGE');
    }

    return {
      type: 'field_image',
      src: argInfo.dataURI || '',
      // TODO these probably shouldn't be hardcoded...?
      width: 24,
      height: 24,
      // Whether or not the inline image should be flipped horizontally
      // in RTL languages. Defaults to false, indicating that the
      // image will not be flipped.
      flip_rtl: argInfo.flipRTL || false
    };
  }
  /**
   * Helper for _convertForScratchBlocks which handles linearization of argument placeholders. Called as a callback
   * from string#replace. In addition to the return value the JSON and XML items in the context will be filled.
   * @param {object} context - information shared with _convertForScratchBlocks about the block, etc.
   * @param {string} match - the overall string matched by the placeholder regex, including brackets: '[FOO]'.
   * @param {string} placeholder - the name of the placeholder being matched: 'FOO'.
   * @return {string} scratch-blocks placeholder for the argument: '%1'.
   * @private
   */


  _convertPlaceholders(context, match, placeholder) {
    // Sanitize the placeholder to ensure valid XML
    placeholder = placeholder.replace(/[<"&]/, '_'); // Determine whether the argument type is one of the known standard field types

    const argInfo = context.blockInfo.arguments[placeholder] || {};
    let argTypeInfo = ArgumentTypeMap[argInfo.type] || {}; // Field type not a standard field type, see if extension has registered custom field type

    if (!ArgumentTypeMap[argInfo.type] && context.categoryInfo.customFieldTypes[argInfo.type]) {
      argTypeInfo = context.categoryInfo.customFieldTypes[argInfo.type].argumentTypeInfo;
    } // Start to construct the scratch-blocks style JSON defining how the block should be
    // laid out


    let argJSON; // Most field types are inputs (slots on the block that can have other blocks plugged into them)
    // check if this is not one of those cases. E.g. an inline image on a block.

    if (argTypeInfo.fieldType === 'field_image') {
      argJSON = this._constructInlineImageJson(argInfo);
    } else {
      // Construct input value
      // Layout a block argument (e.g. an input slot on the block)
      argJSON = {
        type: 'input_value',
        name: placeholder
      };
      const defaultValue = typeof argInfo.defaultValue === 'undefined' ? '' : xmlEscape(maybeFormatMessage(argInfo.defaultValue, this.makeMessageContextForTarget()).toString());

      if (argTypeInfo.check) {
        // Right now the only type of 'check' we have specifies that the
        // input slot on the block accepts Boolean reporters, so it should be
        // shaped like a hexagon
        argJSON.check = argTypeInfo.check;
      }

      let valueName;
      let shadowType;
      let fieldName;

      if (argInfo.menu) {
        const menuInfo = context.categoryInfo.menuInfo[argInfo.menu];

        if (menuInfo.acceptReporters) {
          valueName = placeholder;
          shadowType = this._makeExtensionMenuId(argInfo.menu, context.categoryInfo.id);
          fieldName = argInfo.menu;
        } else {
          argJSON.type = 'field_dropdown';
          argJSON.options = this._convertMenuItems(menuInfo.items);
          valueName = null;
          shadowType = null;
          fieldName = placeholder;
        }
      } else {
        valueName = placeholder;
        shadowType = argTypeInfo.shadow && argTypeInfo.shadow.type || null;
        fieldName = argTypeInfo.shadow && argTypeInfo.shadow.fieldName || null;
      } // <value> is the ScratchBlocks name for a block input.


      if (valueName) {
        context.inputList.push("<value name=\"".concat(placeholder, "\">"));
      } // The <shadow> is a placeholder for a reporter and is visible when there's no reporter in this input.
      // Boolean inputs don't need to specify a shadow in the XML.


      if (shadowType) {
        context.inputList.push("<shadow type=\"".concat(shadowType, "\">"));
      } // A <field> displays a dynamic value: a user-editable text field, a drop-down menu, etc.
      // Leave out the field if defaultValue or fieldName are not specified


      if (defaultValue && fieldName) {
        context.inputList.push("<field name=\"".concat(fieldName, "\">").concat(defaultValue, "</field>"));
      }

      if (shadowType) {
        context.inputList.push('</shadow>');
      }

      if (valueName) {
        context.inputList.push('</value>');
      }
    }

    const argsName = "args".concat(context.outLineNum);
    const blockArgs = context.blockJSON[argsName] = context.blockJSON[argsName] || [];
    if (argJSON) blockArgs.push(argJSON);
    const argNum = blockArgs.length;
    context.argsMap[placeholder] = argNum;
    return "%".concat(argNum);
  }
  /**
   * @returns {Array.<object>} scratch-blocks XML for each category of extension blocks, in category order.
   * @param {?Target} [target] - the active editing target (optional)
   * @property {string} id - the category / extension ID
   * @property {string} xml - the XML text for this category, starting with `<category>` and ending with `</category>`
   */


  getBlocksXML(target) {
    return this._blockInfo.map(categoryInfo => {
      const {
        name,
        color1,
        color2
      } = categoryInfo; // Filter out blocks that aren't supposed to be shown on this target, as determined by the block info's
      // `hideFromPalette` and `filter` properties.

      const paletteBlocks = categoryInfo.blocks.filter(block => {
        let blockFilterIncludesTarget = true; // If an editing target is not passed, include all blocks
        // If the block info doesn't include a `filter` property, always include it

        if (target && block.info.filter) {
          blockFilterIncludesTarget = block.info.filter.includes(target.isStage ? TargetType.STAGE : TargetType.SPRITE);
        } // If the block info's `hideFromPalette` is true, then filter out this block


        return blockFilterIncludesTarget && !block.info.hideFromPalette;
      });
      const colorXML = "colour=\"".concat(color1, "\" secondaryColour=\"").concat(color2, "\""); // Use a menu icon if there is one. Otherwise, use the block icon. If there's no icon,
      // the category menu will show its default colored circle.

      let menuIconURI = '';

      if (categoryInfo.menuIconURI) {
        menuIconURI = categoryInfo.menuIconURI;
      } else if (categoryInfo.blockIconURI) {
        menuIconURI = categoryInfo.blockIconURI;
      }

      const menuIconXML = menuIconURI ? "iconURI=\"".concat(menuIconURI, "\"") : '';
      let statusButtonXML = '';

      if (categoryInfo.showStatusButton) {
        statusButtonXML = 'showStatusButton="true"';
      }

      return {
        id: categoryInfo.id,
        xml: "<category name=\"".concat(name, "\" id=\"").concat(categoryInfo.id, "\" ").concat(statusButtonXML, " ").concat(colorXML, " ").concat(menuIconXML, ">").concat(paletteBlocks.map(block => block.xml).join(''), "</category>")
      };
    });
  }
  /**
   * @returns {Array.<string>} - an array containing the scratch-blocks JSON information for each dynamic block.
   */


  getBlocksJSON() {
    return this._blockInfo.reduce((result, categoryInfo) => result.concat(categoryInfo.blocks.map(blockInfo => blockInfo.json)), []);
  }
  /**
   * Get a scratch link socket.
   * @param {string} type Either BLE or BT
   * @returns {ScratchLinkSocket} The scratch link socket.
   */


  getScratchLinkSocket(type) {
    const factory = this._linkSocketFactory || this._defaultScratchLinkSocketFactory;
    return factory(type);
  }
  /**
   * Configure how ScratchLink sockets are created. Factory must consume a "type" parameter
   * either BT or BLE.
   * @param {Function} factory The new factory for creating ScratchLink sockets.
   */


  configureScratchLinkSocketFactory(factory) {
    this._linkSocketFactory = factory;
  }
  /**
   * The default scratch link socket creator, using websockets to the installed device manager.
   * @param {string} type Either BLE or BT
   * @returns {ScratchLinkSocket} The new scratch link socket (a WebSocket object)
   */


  _defaultScratchLinkSocketFactory(type) {
    return new ScratchLinkWebSocket(type);
  }
  /**
   * Register an extension that communications with a hardware peripheral by id,
   * to have access to it and its peripheral functions in the future.
   * @param {string} extensionId - the id of the extension.
   * @param {object} extension - the extension to register.
   */


  registerPeripheralExtension(extensionId, extension) {
    this.peripheralExtensions[extensionId] = extension;
  }
  /**
   * Tell the specified extension to scan for a peripheral.
   * @param {string} extensionId - the id of the extension.
   */


  scanForPeripheral(extensionId) {
    if (this.peripheralExtensions[extensionId]) {
      this.peripheralExtensions[extensionId].scan();
    }
  }
  /**
   * Connect to the extension's specified peripheral.
   * @param {string} extensionId - the id of the extension.
   * @param {number} peripheralId - the id of the peripheral.
   */


  connectPeripheral(extensionId, peripheralId) {
    if (this.peripheralExtensions[extensionId]) {
      this.peripheralExtensions[extensionId].connect(peripheralId);
    }
  }
  /**
   * Disconnect from the extension's connected peripheral.
   * @param {string} extensionId - the id of the extension.
   */


  disconnectPeripheral(extensionId) {
    if (this.peripheralExtensions[extensionId]) {
      this.peripheralExtensions[extensionId].disconnect();
    }
  }
  /**
   * Returns whether the extension has a currently connected peripheral.
   * @param {string} extensionId - the id of the extension.
   * @return {boolean} - whether the extension has a connected peripheral.
   */


  getPeripheralIsConnected(extensionId) {
    let isConnected = false;

    if (this.peripheralExtensions[extensionId]) {
      isConnected = this.peripheralExtensions[extensionId].isConnected();
    }

    return isConnected;
  }
  /**
   * Emit an event to indicate that the microphone is being used to stream audio.
   * @param {boolean} listening - true if the microphone is currently listening.
   */


  emitMicListening(listening) {
    this.emit(Runtime.MIC_LISTENING, listening);
  }
  /**
   * Retrieve the function associated with the given opcode.
   * @param {!string} opcode The opcode to look up.
   * @return {Function} The function which implements the opcode.
   */


  getOpcodeFunction(opcode) {
    return this._primitives[opcode];
  }
  /**
   * Return whether an opcode represents a hat block.
   * @param {!string} opcode The opcode to look up.
   * @return {boolean} True if the op is known to be a hat.
   */


  getIsHat(opcode) {
    return this._hats.hasOwnProperty(opcode);
  }
  /**
   * Return whether an opcode represents an edge-activated hat block.
   * @param {!string} opcode The opcode to look up.
   * @return {boolean} True if the op is known to be a edge-activated hat.
   */


  getIsEdgeActivatedHat(opcode) {
    return this._hats.hasOwnProperty(opcode) && this._hats[opcode].edgeActivated;
  }
  /**
   * Attach the audio engine
   * @param {!AudioEngine} audioEngine The audio engine to attach
   */


  attachAudioEngine(audioEngine) {
    this.audioEngine = audioEngine;
  }
  /**
   * Attach the renderer
   * @param {!RenderWebGL} renderer The renderer to attach
   */


  attachRenderer(renderer) {
    this.renderer = renderer;
    this.renderer.setLayerGroupOrdering(StageLayering.LAYER_GROUPS);
    this.renderer.offscreenTouching = !this.runtimeOptions.fencing;
    this.updatePrivacy();
  }
  /**
   * Set the bitmap adapter for the VM/runtime, which converts scratch 2
   * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2)
   * @param {!function} bitmapAdapter The adapter to attach
   */


  attachV2BitmapAdapter(bitmapAdapter) {
    this.v2BitmapAdapter = bitmapAdapter;
  }
  /**
   * Attach the storage module
   * @param {!ScratchStorage} storage The storage module to attach
   */


  attachStorage(storage) {
    this.storage = storage;

    if (this.isPackaged) {
      // In packaged runtime mode, generating real asset IDs is a waste of time.
      // We do still want to preserve every asset having a unique ID.
      const originalCreateAsset = storage.createAsset;
      let assetIdCounter = 0; // eslint-disable-next-line no-unused-vars

      storage.createAsset = function packagedCreateAsset(assetType, dataFormat, data, assetId, generateId) {
        if (!assetId) {
          assetId = (++assetIdCounter).toString();
        }

        return originalCreateAsset.call(this, assetType, dataFormat, data, assetId, // Never generate real asset ID
        false);
      };
    }
  } // -----------------------------------------------------------------------------
  // -----------------------------------------------------------------------------

  /**
   * Create a thread and push it to the list of threads.
   * @param {!string} id ID of block that starts the stack.
   * @param {!Target} target Target to run thread on.
   * @param {?object} opts optional arguments
   * @param {?boolean} opts.stackClick true if the script was activated by clicking on the stack
   * @param {?boolean} opts.updateMonitor true if the script should update a monitor value
   * @return {!Thread} The newly created thread.
   */


  _pushThread(id, target, opts) {
    const thread = new Thread(id);
    thread.target = target;
    thread.stackClick = Boolean(opts && opts.stackClick);
    thread.updateMonitor = Boolean(opts && opts.updateMonitor);
    thread.blockContainer = thread.updateMonitor ? this.monitorBlocks : target.blocks;
    thread.pushStack(id);
    this.threads.push(thread);

    if (!thread.stackClick && !thread.updateMonitor) {
      this.threadMap.set(thread.getId(), thread);
    } // tw: compile new threads. Do not attempt to compile monitor threads.


    if (!(opts && opts.updateMonitor) && this.compilerOptions.enabled) {
      thread.tryCompile();
    }

    return thread;
  }
  /**
   * Stop a thread: stop running it immediately, and remove it from the thread list later.
   * @param {!Thread} thread Thread object to remove from actives
   */


  _stopThread(thread) {
    // Mark the thread for later removal
    thread.isKilled = true; // Inform sequencer to stop executing that thread.

    this.sequencer.retireThread(thread);
  }
  /**
   * Restart a thread in place, maintaining its position in the list of threads.
   * This is used by `startHats` to and is necessary to ensure 2.0-like execution order.
   * Test project: https://scratch.mit.edu/projects/130183108/
   * @param {!Thread} thread Thread object to restart.
   * @return {Thread} The restarted thread.
   */


  _restartThread(thread) {
    const newThread = new Thread(thread.topBlock);
    newThread.target = thread.target;
    newThread.stackClick = thread.stackClick;
    newThread.updateMonitor = thread.updateMonitor;
    newThread.blockContainer = thread.blockContainer;
    newThread.pushStack(thread.topBlock); // tw: when a thread is restarted, we have to check whether the previous script was attempted to be compiled.

    if (thread.triedToCompile && this.compilerOptions.enabled) {
      newThread.tryCompile();
    }

    if (!newThread.stackClick && !newThread.updateMonitor) {
      this.threadMap.set(newThread.getId(), newThread);
    }

    const i = this.threads.indexOf(thread);

    if (i > -1) {
      this.threads[i] = newThread;
      return newThread;
    }

    this.threads.push(thread);
    return thread;
  }

  emitCompileError(target, error) {
    this.emit(Runtime.COMPILE_ERROR, target, error);
  }
  /**
   * Return whether a thread is currently active/running.
   * @param {?Thread} thread Thread object to check.
   * @return {boolean} True if the thread is active/running.
   */


  isActiveThread(thread) {
    return thread.stack.length > 0 && thread.status !== Thread.STATUS_DONE && this.threads.indexOf(thread) > -1;
  }
  /**
   * Return whether a thread is waiting for more information or done.
   * @param {?Thread} thread Thread object to check.
   * @return {boolean} True if the thread is waiting
   */


  isWaitingThread(thread) {
    return thread.status === Thread.STATUS_PROMISE_WAIT || thread.status === Thread.STATUS_YIELD_TICK || !this.isActiveThread(thread);
  }
  /**
   * Toggle a script.
   * @param {!string} topBlockId ID of block that starts the script.
   * @param {?object} opts optional arguments to toggle script
   * @param {?string} opts.target target ID for target to run script on. If not supplied, uses editing target.
   * @param {?boolean} opts.stackClick true if the user activated the stack by clicking, false if not. This
   *     determines whether we show a visual report when turning on the script.
   */


  toggleScript(topBlockId, opts) {
    opts = Object.assign({
      target: this._editingTarget,
      stackClick: false
    }, opts); // Remove any existing thread.

    for (let i = 0; i < this.threads.length; i++) {
      // Toggling a script that's already running turns it off
      if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE) {
        const blockContainer = opts.target.blocks;
        const opcode = blockContainer.getOpcode(blockContainer.getBlock(topBlockId));

        if (this.getIsEdgeActivatedHat(opcode) && this.threads[i].stackClick !== opts.stackClick) {
          // Allow edge activated hat thread stack click to coexist with
          // edge activated hat thread that runs every frame
          continue;
        }

        this._stopThread(this.threads[i]);

        return;
      }
    } // Otherwise add it.


    this._pushThread(topBlockId, opts.target, opts);
  }
  /**
   * Enqueue a script that when finished will update the monitor for the block.
   * @param {!string} topBlockId ID of block that starts the script.
   * @param {?Target} optTarget target Target to run script on. If not supplied, uses editing target.
   */


  addMonitorScript(topBlockId, optTarget) {
    if (!optTarget) optTarget = this._editingTarget;

    for (let i = 0; i < this.threads.length; i++) {
      // Don't re-add the script if it's already running
      if (this.threads[i].topBlock === topBlockId && this.threads[i].status !== Thread.STATUS_DONE && this.threads[i].updateMonitor) {
        return;
      }
    } // Otherwise add it.


    this._pushThread(topBlockId, optTarget, {
      updateMonitor: true
    });
  }
  /**
   * Run a function `f` for all scripts in a workspace.
   * `f` will be called with two parameters:
   *  - the top block ID of the script.
   *  - the target that owns the script.
   * @param {!Function} f Function to call for each script.
   * @param {Target=} optTarget Optionally, a target to restrict to.
   */


  allScriptsDo(f, optTarget) {
    let targets = this.executableTargets;

    if (optTarget) {
      targets = [optTarget];
    }

    for (let t = targets.length - 1; t >= 0; t--) {
      const target = targets[t];
      const scripts = target.blocks.getScripts();

      for (let j = 0; j < scripts.length; j++) {
        const topBlockId = scripts[j];
        f(topBlockId, target);
      }
    }
  }

  allScriptsByOpcodeDo(opcode, f, optTarget) {
    let targets = this.executableTargets;

    if (optTarget) {
      targets = [optTarget];
    }

    for (let t = targets.length - 1; t >= 0; t--) {
      const target = targets[t];
      const scripts = BlocksRuntimeCache.getScripts(target.blocks, opcode);

      for (let j = 0; j < scripts.length; j++) {
        f(scripts[j], target);
      }
    }
  }
  /**
   * Start all relevant hats.
   * @param {!string} requestedHatOpcode Opcode of hats to start.
   * @param {object=} optMatchFields Optionally, fields to match on the hat.
   * @param {Target=} optTarget Optionally, a target to restrict to.
   * @return {Array.<Thread>} List of threads started by this function.
   */


  startHats(requestedHatOpcode, optMatchFields, optTarget) {
    if (!this._hats.hasOwnProperty(requestedHatOpcode)) {
      // No known hat with this opcode.
      return;
    }

    const instance = this;
    const newThreads = []; // Look up metadata for the relevant hat.

    const hatMeta = instance._hats[requestedHatOpcode];

    for (const opts in optMatchFields) {
      if (!optMatchFields.hasOwnProperty(opts)) continue;
      optMatchFields[opts] = optMatchFields[opts].toUpperCase();
    } // tw: By assuming that all new threads will not interfere with eachother, we can optimize the loops
    // inside the allScriptsByOpcodeDo callback below.


    const startingThreadListLength = this.threads.length; // Consider all scripts, looking for hats with opcode `requestedHatOpcode`.

    this.allScriptsByOpcodeDo(requestedHatOpcode, (script, target) => {
      const {
        blockId: topBlockId,
        fieldsOfInputs: hatFields
      } = script; // Match any requested fields.
      // For example: ensures that broadcasts match.
      // This needs to happen before the block is evaluated
      // (i.e., before the predicate can be run) because "broadcast and wait"
      // needs to have a precise collection of started threads.

      for (const matchField in optMatchFields) {
        if (hatFields[matchField].value !== optMatchFields[matchField]) {
          // Field mismatch.
          return;
        }
      }

      if (hatMeta.restartExistingThreads) {
        // If `restartExistingThreads` is true, we should stop
        // any existing threads starting with the top block.
        const existingThread = this.threadMap.get(Thread.getIdFromTargetAndBlock(target, topBlockId));

        if (existingThread) {
          newThreads.push(this._restartThread(existingThread));
          return;
        }
      } else {
        // If `restartExistingThreads` is false, we should
        // give up if any threads with the top block are running.
        for (let j = 0; j < startingThreadListLength; j++) {
          if (this.threads[j].target === target && this.threads[j].topBlock === topBlockId && // stack click threads and hat threads can coexist
          !this.threads[j].stackClick && this.threads[j].status !== Thread.STATUS_DONE) {
            // Some thread is already running.
            return;
          }
        }
      } // Start the thread with this top block.


      newThreads.push(this._pushThread(topBlockId, target));
    }, optTarget); // For compatibility with Scratch 2, edge triggered hats need to be processed before
    // threads are stepped. See ScratchRuntime.as for original implementation

    newThreads.forEach(thread => {
      // tw: do not step compiled threads, the hat block can't be executed
      if (!thread.isCompiled) {
        execute(this.sequencer, thread);
        thread.goToNextBlock();
      }
    });
    return newThreads;
  }
  /**
   * Dispose all targets. Return to clean state.
   */


  dispose() {
    this.stopAll(); // Deleting each target's variable's monitors.

    this.targets.forEach(target => {
      if (target.isOriginal) target.deleteMonitors();
    });
    this.targets.map(this.disposeTarget, this); // tw: explicitly emit a MONITORS_UPDATE instead of relying on implicit behavior of _step()

    const emptyMonitorState = OrderedMap({});

    if (!emptyMonitorState.equals(this._monitorState)) {
      this._monitorState = emptyMonitorState;
      this.emit(Runtime.MONITORS_UPDATE, this._monitorState);
    }

    this.emit(Runtime.RUNTIME_DISPOSED);
    this.ioDevices.clock.resetProjectTimer(); // @todo clear out extensions? turboMode? etc.
    // *********** Cloud *******************
    // If the runtime currently has cloud data,
    // emit a has cloud data update event resetting
    // it to false

    if (this.hasCloudData()) {
      this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, false);
    }

    this.ioDevices.cloud.clear(); // Reset runtime cloud data info

    const newCloudDataManager = cloudDataManager(this.cloudOptions);
    this.hasCloudData = newCloudDataManager.hasCloudVariables;
    this.canAddCloudVariable = newCloudDataManager.canAddCloudVariable;
    this.getNumberOfCloudVariables = newCloudDataManager.getNumberOfCloudVariables;
    this.addCloudVariable = this._initializeAddCloudVariable(newCloudDataManager);
    this.removeCloudVariable = this._initializeRemoveCloudVariable(newCloudDataManager);
  }
  /**
   * Add a target to the runtime. This tracks the sprite pane
   * ordering of the target. The target still needs to be put
   * into the correct execution order after calling this function.
   * @param {Target} target target to add
   */


  addTarget(target) {
    this.targets.push(target);
    this.executableTargets.push(target);

    if (target.isStage && !this._stageTarget) {
      this._stageTarget = target;
    }
  }
  /**
   * Move a target in the execution order by a relative amount.
   *
   * A positve number will make the target execute earlier. A negative number
   * will make the target execute later in the order.
   *
   * @param {Target} executableTarget target to move
   * @param {number} delta number of positions to move target by
   * @returns {number} new position in execution order
   */


  moveExecutable(executableTarget, delta) {
    const oldIndex = this.executableTargets.indexOf(executableTarget);
    this.executableTargets.splice(oldIndex, 1);
    let newIndex = oldIndex + delta;

    if (newIndex > this.executableTargets.length) {
      newIndex = this.executableTargets.length;
    }

    if (newIndex <= 0) {
      if (this.executableTargets.length > 0 && this.executableTargets[0].isStage) {
        newIndex = 1;
      } else {
        newIndex = 0;
      }
    }

    this.executableTargets.splice(newIndex, 0, executableTarget);
    return newIndex;
  }
  /**
   * Set a target to execute at a specific position in the execution order.
   *
   * Infinity will set the target to execute first. 0 will set the target to
   * execute last (before the stage).
   *
   * @param {Target} executableTarget target to move
   * @param {number} newIndex position in execution order to place the target
   * @returns {number} new position in the execution order
   */


  setExecutablePosition(executableTarget, newIndex) {
    const oldIndex = this.executableTargets.indexOf(executableTarget);
    return this.moveExecutable(executableTarget, newIndex - oldIndex);
  }
  /**
   * Remove a target from the execution set.
   * @param {Target} executableTarget target to remove
   */


  removeExecutable(executableTarget) {
    const oldIndex = this.executableTargets.indexOf(executableTarget);

    if (oldIndex > -1) {
      this.executableTargets.splice(oldIndex, 1);
    }
  }
  /**
   * Dispose of a target.
   * @param {!Target} disposingTarget Target to dispose of.
   */


  disposeTarget(disposingTarget) {
    this.targets = this.targets.filter(target => {
      if (disposingTarget !== target) return true; // Allow target to do dispose actions.

      target.dispose(); // Remove from list of targets.

      return false;
    });

    if (this._stageTarget === disposingTarget) {
      this._stageTarget = null;
    }
  }
  /**
   * Stop any threads acting on the target.
   * @param {!Target} target Target to stop threads for.
   * @param {Thread=} optThreadException Optional thread to skip.
   */


  stopForTarget(target, optThreadException) {
    // Emit stop event to allow blocks to clean up any state.
    this.emit(Runtime.STOP_FOR_TARGET, target, optThreadException); // Stop any threads on the target.

    for (let i = 0; i < this.threads.length; i++) {
      if (this.threads[i] === optThreadException) {
        continue;
      }

      if (this.threads[i].target === target) {
        this._stopThread(this.threads[i]);
      }
    }
  }
  /**
   * Start all threads that start with the green flag.
   */


  greenFlag() {
    this.stopAll();
    this.emit(Runtime.PROJECT_START);
    this.updateCurrentMSecs();
    this.ioDevices.clock.resetProjectTimer();
    this.targets.forEach(target => target.clearEdgeActivatedValues()); // Inform all targets of the green flag.

    for (let i = 0; i < this.targets.length; i++) {
      this.targets[i].onGreenFlag();
    }

    this.startHats('event_whenflagclicked');
  }
  /**
   * Stop "everything."
   */


  stopAll() {
    // Emit stop event to allow blocks to clean up any state.
    this.emit(Runtime.PROJECT_STOP_ALL); // Dispose all clones.

    const newTargets = [];

    for (let i = 0; i < this.targets.length; i++) {
      this.targets[i].onStopAll();

      if (this.targets[i].hasOwnProperty('isOriginal') && !this.targets[i].isOriginal) {
        this.targets[i].dispose();
      } else {
        newTargets.push(this.targets[i]);
      }
    }

    this.targets = newTargets; // Dispose of the active thread.

    if (this.sequencer.activeThread !== null) {
      this._stopThread(this.sequencer.activeThread);
    } // Remove all remaining threads from executing in the next tick.


    this.threads = [];
    this.threadMap.clear();
  }

  _renderInterpolatedPositions() {
    const frameStarted = this._lastStepTime;
    const now = Date.now();
    const timeSinceStart = now - frameStarted;
    const progressInFrame = Math.min(1, Math.max(0, timeSinceStart / this.currentStepTime));
    interpolate.interpolate(this, progressInFrame);

    if (this.renderer) {
      this.renderer.draw();
    }
  }

  updateThreadMap() {
    this.threadMap.clear();

    for (const thread of this.threads) {
      if (!thread.stackClick && !thread.updateMonitor) {
        this.threadMap.set(thread.getId(), thread);
      }
    }
  }
  /**
   * Repeatedly run `sequencer.stepThreads` and filter out
   * inactive threads after each iteration.
   */


  _step() {
    if (this.interpolationEnabled) {
      interpolate.setupInitialState(this);
    }

    if (this.profiler !== null) {
      if (stepProfilerId === -1) {
        stepProfilerId = this.profiler.idByName('Runtime._step');
      }

      this.profiler.start(stepProfilerId);
    } // Clean up threads that were told to stop during or since the last step


    this.threads = this.threads.filter(thread => !thread.isKilled);
    this.updateThreadMap(); // Find all edge-activated hats, and add them to threads to be evaluated.

    for (const hatType in this._hats) {
      if (!this._hats.hasOwnProperty(hatType)) continue;
      const hat = this._hats[hatType];

      if (hat.edgeActivated) {
        this.startHats(hatType);
      }
    }

    this.redrawRequested = false;

    this._pushMonitors();

    if (this.profiler !== null) {
      if (stepThreadsProfilerId === -1) {
        stepThreadsProfilerId = this.profiler.idByName('Sequencer.stepThreads');
      }

      this.profiler.start(stepThreadsProfilerId);
    }

    const doneThreads = this.sequencer.stepThreads();

    if (this.profiler !== null) {
      this.profiler.stop();
    }

    this._updateGlows(doneThreads); // Add done threads so that even if a thread finishes within 1 frame, the green
    // flag will still indicate that a script ran.


    this._emitProjectRunStatus(this.threads.length + doneThreads.length - this._getMonitorThreadCount([...this.threads, ...doneThreads])); // Store threads that completed this iteration for testing and other
    // internal purposes.


    this._lastStepDoneThreads = doneThreads;

    if (this.renderer) {
      // @todo: Only render when this.redrawRequested or clones rendered.
      if (this.profiler !== null) {
        if (rendererDrawProfilerId === -1) {
          rendererDrawProfilerId = this.profiler.idByName('RenderWebGL.draw');
        }

        this.profiler.start(rendererDrawProfilerId);
      } // tw: do not draw if document is hidden or a rAF loop is running
      // Checking for the animation frame loop is more reliable than using
      // interpolationEnabled in some edge cases


      if (!document.hidden && !this.frameLoop._interpolationAnimation) {
        this.renderer.draw();
      }

      if (this.profiler !== null) {
        this.profiler.stop();
      }
    }

    if (this._refreshTargets) {
      this.emit(Runtime.TARGETS_UPDATE, false
      /* Don't emit project changed */
      );
      this._refreshTargets = false;
    }

    if (!this._prevMonitorState.equals(this._monitorState)) {
      this.emit(Runtime.MONITORS_UPDATE, this._monitorState);
      this._prevMonitorState = this._monitorState;
    }

    if (this.profiler !== null) {
      this.profiler.stop();
      this.profiler.reportFrames();
    }

    if (this.interpolationEnabled) {
      this._lastStepTime = Date.now();
    }
  }
  /**
   * Get the number of threads in the given array that are monitor threads (threads
   * that update monitor values, and don't count as running a script).
   * @param {!Array.<Thread>} threads The set of threads to look through.
   * @return {number} The number of monitor threads in threads.
   */


  _getMonitorThreadCount(threads) {
    let count = 0;
    threads.forEach(thread => {
      if (thread.updateMonitor) count++;
    });
    return count;
  }
  /**
   * Queue monitor blocks to sequencer to be run.
   */


  _pushMonitors() {
    this.monitorBlocks.runAllMonitored(this);
  }
  /**
   * Set the current editing target known by the runtime.
   * @param {!Target} editingTarget New editing target.
   */


  setEditingTarget(editingTarget) {
    const oldEditingTarget = this._editingTarget;
    this._editingTarget = editingTarget; // Script glows must be cleared.

    this._scriptGlowsPreviousFrame = [];

    this._updateGlows();

    if (oldEditingTarget !== this._editingTarget) {
      this.requestToolboxExtensionsUpdate();
    }
  }
  /**
   * Set whether we are in 30 TPS compatibility mode.
   * @param {boolean} compatibilityModeOn True iff in compatibility mode.
   */


  setCompatibilityMode(compatibilityModeOn) {
    // tw: "compatibility mode" is replaced with a generic framerate setter,
    // but this method is kept for compatibility
    if (compatibilityModeOn) {
      this.setFramerate(30);
    } else {
      this.setFramerate(60);
    }
  }
  /**
   * tw: Change runtime target frames per second
   * @param {number} framerate Target frames per second
   */


  setFramerate(framerate) {
    // Setting framerate to anything greater than this is unnecessary and can break the sequencer
    // Additionally, the JS spec says intervals can't run more than once every 4ms (250/s) anyways
    if (framerate > 250) framerate = 250; // Convert negative framerates to 1FPS
    // Note that 0 is a special value which means "matching device screen refresh rate"

    if (framerate < 0) framerate = 1;
    this.frameLoop.setFramerate(framerate);
    this.emit(Runtime.FRAMERATE_CHANGED, framerate);
  }
  /**
   * tw: Enable or disable interpolation.
   * @param {boolean} interpolationEnabled True if interpolation should be enabled.
   */


  setInterpolation(interpolationEnabled) {
    this.interpolationEnabled = interpolationEnabled;
    this.frameLoop.setInterpolation(this.interpolationEnabled);
    this.emit(Runtime.INTERPOLATION_CHANGED, interpolationEnabled);
  }
  /**
   * tw: Update runtime options
   * @param {*} runtimeOptions New options
   */


  setRuntimeOptions(runtimeOptions) {
    this.runtimeOptions = Object.assign({}, this.runtimeOptions, runtimeOptions);
    this.emit(Runtime.RUNTIME_OPTIONS_CHANGED, this.runtimeOptions);

    if (this.renderer) {
      this.renderer.offscreenTouching = !this.runtimeOptions.fencing;
    }
  }
  /**
   * tw: Update compiler options
   * @param {*} compilerOptions New options
   */


  setCompilerOptions(compilerOptions) {
    this.compilerOptions = Object.assign({}, this.compilerOptions, compilerOptions);
    this.resetAllCaches();
    this.emit(Runtime.COMPILER_OPTIONS_CHANGED, this.compilerOptions);
  }
  /**
   * Change width and height of stage. This will also inform the renderer of the new stage size.
   * @param {number} width New stage width
   * @param {number} height New stage height
   */


  setStageSize(width, height) {
    width = Math.round(Math.max(1, width));
    height = Math.round(Math.max(1, height));

    if (this.stageWidth !== width || this.stageHeight !== height) {
      const deltaX = width - this.stageWidth;
      const deltaY = height - this.stageHeight; // Preserve monitor location relative to the center of the stage

      if (this._monitorState.size > 0) {
        const offsetX = deltaX / 2;
        const offsetY = deltaY / 2;

        for (const monitor of this._monitorState.valueSeq()) {
          const newMonitor = monitor.set('x', monitor.get('x') + offsetX).set('y', monitor.get('y') + offsetY);
          this.requestUpdateMonitor(newMonitor);
        }

        this.emit(Runtime.MONITORS_UPDATE, this._monitorState);
      }

      this.stageWidth = width;
      this.stageHeight = height;

      if (this.renderer) {
        this.renderer.setStageSize(-width / 2, width / 2, -height / 2, height / 2);
      }
    }

    this.emit(Runtime.STAGE_SIZE_CHANGED, width, height);
  }

  setInEditor(inEditor) {// no-op
  }
  /**
   * TW: Enable "packaged runtime" mode. This is a one-way operation.
   */


  convertToPackagedRuntime() {
    if (this.storage) {
      throw new Error('convertToPackagedRuntime must be called before attachStorage');
    }

    this.isPackaged = true;
  }
  /**
   * tw: Reset the cache of all block containers.
   */


  resetAllCaches() {
    for (const target of this.targets) {
      if (target.isOriginal) {
        target.blocks.resetCache();
      }
    }

    this.flyoutBlocks.resetCache();
    this.monitorBlocks.resetCache();
  }
  /**
   * Add an "addon block"
   * @param {object} options Options object
   * @param {string} options.procedureCode The ID of the block
   * @param {function} options.callback The callback, called with (args, BlockUtility). May return a promise.
   * @param {string[]} options.arguments Names of the arguments accepted
   * @param {boolean} [hidden] True to not include this block in the block palette
   */


  addAddonBlock(options) {
    const procedureCode = options.procedureCode;
    const names = options.arguments;
    const ids = options.arguments.map((_, i) => "arg".concat(i));
    const defaults = options.arguments.map(() => '');
    this.addonBlocks[procedureCode] = _objectSpread({
      namesIdsDefaults: [names, ids, defaults]
    }, options);

    if (!options.hidden) {
      const ID = 'a-b';

      let blockInfo = this._blockInfo.find(i => i.id === ID);

      if (!blockInfo) {
        // eslint-disable-next-line max-len
        const ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48"><path d="M14.92 1.053A13.835 13.835 0 0 0 1.052 14.919v18.162a13.835 13.835 0 0 0 13.866 13.866h18.162a13.835 13.835 0 0 0 13.866-13.866V14.919A13.835 13.835 0 0 0 33.081 1.053zm16.6 12.746L41.72 24 31.52 34.201l-3.276-3.275L35.17 24l-6.926-6.926Zm-15.116.073 3.278 3.278L12.83 24l6.926 6.926L16.48 34.2 6.28 24Z" style="fill:#29beb8;fill-opacity:1;stroke:none;stroke-width:1.51371;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"/></svg>';
        blockInfo = {
          id: ID,
          name: 'Addons',
          color1: '#29beb8',
          color2: '#3aa8a4',
          color3: '#3aa8a4',
          menuIconURI: "data:image/svg+xml;,".concat(encodeURIComponent(ICON)),
          blocks: [],
          customFieldTypes: {},
          menus: []
        };

        this._blockInfo.unshift(blockInfo);
      }

      blockInfo.blocks.push({
        info: {},
        xml: '<block type="procedures_call" gap="16"><mutation generateshadows="true" warp="false"' + " proccode=\"".concat(xmlEscape(procedureCode), "\"") + " argumentnames=\"".concat(xmlEscape(JSON.stringify(names)), "\"") + " argumentids=\"".concat(xmlEscape(JSON.stringify(ids)), "\"") + " argumentdefaults=\"".concat(xmlEscape(JSON.stringify(defaults)), "\"") + '></mutation></block>'
      });
    }

    this.resetAllCaches();
  }

  getAddonBlock(procedureCode) {
    if (Object.prototype.hasOwnProperty.call(this.addonBlocks, procedureCode)) {
      return this.addonBlocks[procedureCode];
    }

    return null;
  }

  findProjectOptionsComment() {
    const target = this.getTargetForStage();
    const comments = target.comments;

    for (const comment of Object.values(comments)) {
      if (comment.text.includes(COMMENT_CONFIG_MAGIC)) {
        return comment;
      }
    }

    return null;
  }

  parseProjectOptions() {
    const comment = this.findProjectOptionsComment();
    if (!comment) return;
    const lineWithMagic = comment.text.split('\n').find(i => i.endsWith(COMMENT_CONFIG_MAGIC));

    if (!lineWithMagic) {
      log.warn('Config comment does not contain valid line');
      return;
    }

    const jsonText = lineWithMagic.substr(0, lineWithMagic.length - COMMENT_CONFIG_MAGIC.length);
    let parsed;

    try {
      parsed = ExtendedJSON.parse(jsonText);

      if (!parsed || typeof parsed !== 'object') {
        throw new Error('Invalid object');
      }
    } catch (e) {
      log.warn('Config comment has invalid JSON', e);
      return;
    }

    if (typeof parsed.framerate === 'number') {
      this.setFramerate(parsed.framerate);
    }

    if (parsed.turbo) {
      this.turboMode = true;
      this.emit(Runtime.TURBO_MODE_ON);
    }

    if (parsed.interpolation) {
      this.setInterpolation(true);
    }

    if (parsed.runtimeOptions) {
      this.setRuntimeOptions(parsed.runtimeOptions);
    }

    if (parsed.hq && this.renderer) {
      this.renderer.setUseHighQualityRender(true);
    }

    const storedWidth = +parsed.width || this.stageWidth;
    const storedHeight = +parsed.height || this.stageHeight;

    if (storedWidth !== this.stageWidth || storedHeight !== this.stageHeight) {
      this.setStageSize(storedWidth, storedHeight);
    }
  }

  _generateAllProjectOptions() {
    return {
      framerate: this.frameLoop.framerate,
      runtimeOptions: this.runtimeOptions,
      interpolation: this.interpolationEnabled,
      turbo: this.turboMode,
      hq: this.renderer ? this.renderer.useHighQualityRender : false,
      width: this.stageWidth,
      height: this.stageHeight
    };
  }

  generateDifferingProjectOptions() {
    const difference = (oldObject, newObject) => {
      const result = {};

      for (const key of Object.keys(newObject)) {
        const newValue = newObject[key];
        const oldValue = oldObject[key];

        if (typeof newValue === 'object' && newValue) {
          const valueDiffering = difference(oldValue, newValue);

          if (Object.keys(valueDiffering).length > 0) {
            result[key] = valueDiffering;
          }
        } else if (newValue !== oldValue) {
          result[key] = newValue;
        }
      }

      return result;
    };

    return difference(this._defaultStoredSettings, this._generateAllProjectOptions());
  }

  storeProjectOptions() {
    const options = this.generateDifferingProjectOptions(); // TODO: translate

    const text = "Configuration for https://turbowarp.org/\nYou can move, resize, and minimize this comment, but don't edit it by hand. This comment can be deleted to remove the stored settings.\n".concat(ExtendedJSON.stringify(options)).concat(COMMENT_CONFIG_MAGIC);
    const existingComment = this.findProjectOptionsComment();

    if (existingComment) {
      existingComment.text = text;
    } else {
      const target = this.getTargetForStage(); // TODO: smarter position logic

      target.createComment(uid(), null, text, 50, 50, 350, 170, false);
    }

    this.emitProjectChanged();
  }
  /**
   * Eagerly (re)compile all scripts within this project.
   */


  precompile() {
    this.allScriptsDo((topBlockId, target) => {
      const topBlock = target.blocks.getBlock(topBlockId);

      if (this.getIsHat(topBlock.opcode)) {
        const thread = new Thread(topBlockId);
        thread.target = target;
        thread.blockContainer = target.blocks;
        thread.tryCompile();
      }
    });
  }

  enableDebug() {
    this.resetAllCaches();
    this.debug = true;
  }
  /**
   * Emit glows/glow clears for scripts after a single tick.
   * Looks at `this.threads` and notices which have turned on/off new glows.
   * @param {Array.<Thread>=} optExtraThreads Optional list of inactive threads.
   */


  _updateGlows(optExtraThreads) {
    const searchThreads = [];
    searchThreads.push.apply(searchThreads, this.threads);

    if (optExtraThreads) {
      searchThreads.push.apply(searchThreads, optExtraThreads);
    } // Set of scripts that request a glow this frame.


    const requestedGlowsThisFrame = []; // Final set of scripts glowing during this frame.

    const finalScriptGlows = []; // Find all scripts that should be glowing.

    for (let i = 0; i < searchThreads.length; i++) {
      const thread = searchThreads[i];
      const target = thread.target;

      if (target === this._editingTarget) {
        const blockForThread = thread.blockGlowInFrame;

        if (thread.requestScriptGlowInFrame || thread.stackClick) {
          let script = target.blocks.getTopLevelScript(blockForThread);

          if (!script) {
            // Attempt to find in flyout blocks.
            script = this.flyoutBlocks.getTopLevelScript(blockForThread);
          }

          if (script) {
            requestedGlowsThisFrame.push(script);
          }
        }
      }
    } // Compare to previous frame.


    for (let j = 0; j < this._scriptGlowsPreviousFrame.length; j++) {
      const previousFrameGlow = this._scriptGlowsPreviousFrame[j];

      if (requestedGlowsThisFrame.indexOf(previousFrameGlow) < 0) {
        // Glow turned off.
        this.glowScript(previousFrameGlow, false);
      } else {
        // Still glowing.
        finalScriptGlows.push(previousFrameGlow);
      }
    }

    for (let k = 0; k < requestedGlowsThisFrame.length; k++) {
      const currentFrameGlow = requestedGlowsThisFrame[k];

      if (this._scriptGlowsPreviousFrame.indexOf(currentFrameGlow) < 0) {
        // Glow turned on.
        this.glowScript(currentFrameGlow, true);
        finalScriptGlows.push(currentFrameGlow);
      }
    }

    this._scriptGlowsPreviousFrame = finalScriptGlows;
  }
  /**
   * Emit run start/stop after each tick. Emits when `this.threads.length` goes
   * between non-zero and zero
   *
   * @param {number} nonMonitorThreadCount The new nonMonitorThreadCount
   */


  _emitProjectRunStatus(nonMonitorThreadCount) {
    if (this._nonMonitorThreadCount === 0 && nonMonitorThreadCount > 0) {
      this.emit(Runtime.PROJECT_RUN_START);
    }

    if (this._nonMonitorThreadCount > 0 && nonMonitorThreadCount === 0) {
      this.emit(Runtime.PROJECT_RUN_STOP);
    }

    this._nonMonitorThreadCount = nonMonitorThreadCount;
  }
  /**
   * "Quiet" a script's glow: stop the VM from generating glow/unglow events
   * about that script. Use when a script has just been deleted, but we may
   * still be tracking glow data about it.
   * @param {!string} scriptBlockId Id of top-level block in script to quiet.
   */


  quietGlow(scriptBlockId) {
    const index = this._scriptGlowsPreviousFrame.indexOf(scriptBlockId);

    if (index > -1) {
      this._scriptGlowsPreviousFrame.splice(index, 1);
    }
  }
  /**
   * Emit feedback for block glowing (used in the sequencer).
   * @param {?string} blockId ID for the block to update glow
   * @param {boolean} isGlowing True to turn on glow; false to turn off.
   */


  glowBlock(blockId, isGlowing) {
    if (isGlowing) {
      this.emit(Runtime.BLOCK_GLOW_ON, {
        id: blockId
      });
    } else {
      this.emit(Runtime.BLOCK_GLOW_OFF, {
        id: blockId
      });
    }
  }
  /**
   * Emit feedback for script glowing.
   * @param {?string} topBlockId ID for the top block to update glow
   * @param {boolean} isGlowing True to turn on glow; false to turn off.
   */


  glowScript(topBlockId, isGlowing) {
    if (isGlowing) {
      this.emit(Runtime.SCRIPT_GLOW_ON, {
        id: topBlockId
      });
    } else {
      this.emit(Runtime.SCRIPT_GLOW_OFF, {
        id: topBlockId
      });
    }
  }
  /**
   * Emit whether blocks are being dragged over gui
   * @param {boolean} areBlocksOverGui True if blocks are dragged out of blocks workspace, false otherwise
   */


  emitBlockDragUpdate(areBlocksOverGui) {
    this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui);
  }
  /**
   * Emit event to indicate that the block drag has ended with the blocks outside the blocks workspace
   * @param {Array.<object>} blocks The set of blocks dragged to the GUI
   * @param {string} topBlockId The original id of the top block being dragged
   */


  emitBlockEndDrag(blocks, topBlockId) {
    this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId);
  }
  /**
   * Emit value for reporter to show in the blocks.
   * @param {string} blockId ID for the block.
   * @param {string} value Value to show associated with the block.
   */


  visualReport(blockId, value) {
    this.emit(Runtime.VISUAL_REPORT, {
      id: blockId,
      value: String(value)
    });
  }
  /**
   * Add a monitor to the state. If the monitor already exists in the state,
   * updates those properties that are defined in the given monitor record.
   * @param {!MonitorRecord} monitor Monitor to add.
   */


  requestAddMonitor(monitor) {
    const id = monitor.get('id');

    if (!this.requestUpdateMonitor(monitor)) {
      // update monitor if it exists in the state
      // if the monitor did not exist in the state, add it
      this._monitorState = this._monitorState.set(id, monitor);
    }
  }
  /**
   * Update a monitor in the state and report success/failure of update.
   * @param {!Map} monitor Monitor values to update. Values on the monitor with overwrite
   *     values on the old monitor with the same ID. If a value isn't defined on the new monitor,
   *     the old monitor will keep its old value.
   * @return {boolean} true if monitor exists in the state and was updated, false if it did not exist.
   */


  requestUpdateMonitor(monitor) {
    const id = monitor.get('id');

    if (this._monitorState.has(id)) {
      this._monitorState = // Use mergeWith here to prevent undefined values from overwriting existing ones
      this._monitorState.set(id, this._monitorState.get(id).mergeWith((prev, next) => {
        if (typeof next === 'undefined' || next === null) {
          return prev;
        }

        return next;
      }, monitor));
      return true;
    }

    return false;
  }
  /**
   * Removes a monitor from the state. Does nothing if the monitor already does
   * not exist in the state.
   * @param {!string} monitorId ID of the monitor to remove.
   */


  requestRemoveMonitor(monitorId) {
    this._monitorState = this._monitorState.delete(monitorId);
  }
  /**
   * Hides a monitor and returns success/failure of action.
   * @param {!string} monitorId ID of the monitor to hide.
   * @return {boolean} true if monitor exists and was updated, false otherwise
   */


  requestHideMonitor(monitorId) {
    return this.requestUpdateMonitor(new Map([['id', monitorId], ['visible', false]]));
  }
  /**
   * Shows a monitor and returns success/failure of action.
   * not exist in the state.
   * @param {!string} monitorId ID of the monitor to show.
   * @return {boolean} true if monitor exists and was updated, false otherwise
   */


  requestShowMonitor(monitorId) {
    return this.requestUpdateMonitor(new Map([['id', monitorId], ['visible', true]]));
  }
  /**
   * Removes all monitors with the given target ID from the state. Does nothing if
   * the monitor already does not exist in the state.
   * @param {!string} targetId Remove all monitors with given target ID.
   */


  requestRemoveMonitorByTargetId(targetId) {
    this._monitorState = this._monitorState.filterNot(value => value.targetId === targetId);
  }
  /**
   * Get a target by its id.
   * @param {string} targetId Id of target to find.
   * @return {?Target} The target, if found.
   */


  getTargetById(targetId) {
    for (let i = 0; i < this.targets.length; i++) {
      const target = this.targets[i];

      if (target.id === targetId) {
        return target;
      }
    }
  }
  /**
   * Get the first original (non-clone-block-created) sprite given a name.
   * @param {string} spriteName Name of sprite to look for.
   * @return {?Target} Target representing a sprite of the given name.
   */


  getSpriteTargetByName(spriteName) {
    for (let i = 0; i < this.targets.length; i++) {
      const target = this.targets[i];

      if (target.isStage) {
        continue;
      }

      if (target.sprite && target.sprite.name === spriteName) {
        return target;
      }
    }
  }
  /**
   * Get a target by its drawable id.
   * @param {number} drawableID drawable id of target to find
   * @return {?Target} The target, if found
   */


  getTargetByDrawableId(drawableID) {
    for (let i = 0; i < this.targets.length; i++) {
      const target = this.targets[i];
      if (target.drawableID === drawableID) return target;
    }
  }
  /**
   * Update the clone counter to track how many clones are created.
   * @param {number} changeAmount How many clones have been created/destroyed.
   */


  changeCloneCounter(changeAmount) {
    this._cloneCounter += changeAmount;
  }
  /**
   * Return whether there are clones available.
   * @return {boolean} True until the number of clones hits runtimeOptions.maxClones
   */


  clonesAvailable() {
    return this._cloneCounter < this.runtimeOptions.maxClones;
  }
  /**
   * Report that the project has loaded in the Virtual Machine.
   */


  emitProjectLoaded() {
    this.emit(Runtime.PROJECT_LOADED);
  }
  /**
   * Report that the project has changed in a way that would affect serialization
   */


  emitProjectChanged() {
    this.emit(Runtime.PROJECT_CHANGED);
  }
  /**
   * Report that a new target has been created, possibly by cloning an existing target.
   * @param {Target} newTarget - the newly created target.
   * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
   * @fires Runtime#targetWasCreated
   */


  fireTargetWasCreated(newTarget, sourceTarget) {
    this.emit('targetWasCreated', newTarget, sourceTarget);
  }
  /**
   * Report that a clone target is being removed.
   * @param {Target} target - the target being removed
   * @fires Runtime#targetWasRemoved
   */


  fireTargetWasRemoved(target) {
    this.emit('targetWasRemoved', target);
  }
  /**
   * Get a target representing the Scratch stage, if one exists.
   * @return {?Target} The target, if found.
   */


  getTargetForStage() {
    if (this._stageTarget) {
      return this._stageTarget;
    }

    for (let i = 0; i < this.targets.length; i++) {
      const target = this.targets[i];

      if (target.isStage) {
        this._stageTarget = target;
        return target;
      }
    }
  }
  /**
   * Get the editing target.
   * @return {?Target} The editing target.
   */


  getEditingTarget() {
    return this._editingTarget;
  }

  getAllVarNamesOfType(varType) {
    let varNames = [];

    for (const target of this.targets) {
      const targetVarNames = target.getAllVariableNamesInScopeByType(varType, true);
      varNames = varNames.concat(targetVarNames);
    }

    return varNames;
  }
  /**
   * Get the label or label function for an opcode
   * @param {string} extendedOpcode - the opcode you want a label for
   * @return {object} - object with label and category
   * @property {string} category - the category for this opcode
   * @property {Function} [labelFn] - function to generate the label for this opcode
   * @property {string} [label] - the label for this opcode if `labelFn` is absent
   */


  getLabelForOpcode(extendedOpcode) {
    const [category, opcode] = StringUtil.splitFirst(extendedOpcode, '_');
    if (!(category && opcode)) return;

    const categoryInfo = this._blockInfo.find(ci => ci.id === category);

    if (!categoryInfo) return;
    const block = categoryInfo.blocks.find(b => b.info.opcode === opcode);
    if (!block) return; // TODO: we may want to format the label in a locale-specific way.

    return {
      category: 'extension',
      // This assumes that all extensions have the same monitor color.
      label: "".concat(categoryInfo.name, ": ").concat(block.info.text)
    };
  }
  /**
   * Create a new global variable avoiding conflicts with other variable names.
   * @param {string} variableName The desired variable name for the new global variable.
   * This can be turned into a fresh name as necessary.
   * @param {string} optVarId An optional ID to use for the variable. A new one will be generated
   * if a falsey value for this parameter is provided.
   * @param {string} optVarType The type of the variable to create. Defaults to Variable.SCALAR_TYPE.
   * @return {Variable} The new variable that was created.
   */


  createNewGlobalVariable(variableName, optVarId, optVarType) {
    const varType = typeof optVarType === 'string' ? optVarType : Variable.SCALAR_TYPE;
    const allVariableNames = this.getAllVarNamesOfType(varType);
    const newName = StringUtil.unusedName(variableName, allVariableNames);
    const variable = new Variable(optVarId || uid(), newName, varType);
    const stage = this.getTargetForStage();
    stage.variables[variable.id] = variable;
    return variable;
  }
  /**
   * Tell the runtime to request a redraw.
   * Use after a clone/sprite has completed some visible operation on the stage.
   */


  requestRedraw() {
    this.redrawRequested = true;
  }
  /**
   * Emit a targets update at the end of the step if the provided target is
   * the original sprite
   * @param {!Target} target Target requesting the targets update
   */


  requestTargetsUpdate(target) {
    if (!target.isOriginal) return;
    this._refreshTargets = true;
  }
  /**
   * Emit an event that indicates that the blocks on the workspace need updating.
   */


  requestBlocksUpdate() {
    this.emit(Runtime.BLOCKS_NEED_UPDATE);
  }
  /**
   * Emit an event that indicates that the toolbox extension blocks need updating.
   */


  requestToolboxExtensionsUpdate() {
    this.emit(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE);
  }
  /**
   * Set up timers to repeatedly step in a browser.
   */


  start() {
    // Do not start if we are already running
    if (this.frameLoop.running) return;
    this.frameLoop.start();
    this.emit(Runtime.RUNTIME_STARTED);
  }
  /**
   * tw: Stop the tick loop
   * Note: This only stops the loop. It will not stop any threads the next time the VM starts
   */


  stop() {
    if (!this.frameLoop.running) {
      return;
    }

    this.frameLoop.stop();
    this.emit(Runtime.RUNTIME_STOPPED);
  }
  /**
   * Turn on profiling.
   * @param {Profiler/FrameCallback} onFrame A callback handle passed a
   * profiling frame when the profiler reports its collected data.
   */


  enableProfiling(onFrame) {
    if (Profiler.available()) {
      this.profiler = new Profiler(onFrame);
    }
  }
  /**
   * Turn off profiling.
   */


  disableProfiling() {
    this.profiler = null;
  }
  /**
   * Update a millisecond timestamp value that is saved on the Runtime.
   * This value is helpful in certain instances for compatibility with Scratch 2,
   * which sometimes uses a `currentMSecs` timestamp value in Interpreter.as
   */


  updateCurrentMSecs() {
    this.currentMSecs = Date.now();
  }

  updatePrivacy() {
    const enforceRestrictions = this.enforcePrivacy && Object.values(this.externalCommunicationMethods).some(i => i);

    if (this.renderer && this.renderer.setPrivateSkinAccess) {
      this.renderer.setPrivateSkinAccess(!enforceRestrictions);
    }
  }
  /**
   * @param {boolean} enabled True if restrictions should be enforced to protect user privacy.
   */


  setEnforcePrivacy(enabled) {
    this.enforcePrivacy = enabled;
    this.updatePrivacy();
  }
  /**
   * @param {string} method Name of the method in Runtime.externalCommunicationMethods
   * @param {boolean} enabled True if the feature is enabled.
   */


  setExternalCommunicationMethod(method, enabled) {
    if (!Object.prototype.hasOwnProperty.call(this.externalCommunicationMethods, method)) {
      throw new Error("Unknown method: ".concat(method));
    }

    this.externalCommunicationMethods[method] = enabled;
    this.updatePrivacy();
  }

}
/**
 * Event fired after a new target has been created, possibly by cloning an existing target.
 *
 * @event Runtime#targetWasCreated
 * @param {Target} newTarget - the newly created target.
 * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
 */


module.exports = Runtime;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/scratch-blocks-constants.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/scratch-blocks-constants.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * These constants are copied from scratch-blocks/core/constants.js
 * @TODO find a way to require() these straight from scratch-blocks... maybe make a scratch-blocks/dist/constants.js?
 * @readonly
 * @enum {int}
 */
const ScratchBlocksConstants = {
  /**
   * ENUM for output shape: hexagonal (booleans/predicates).
   * @const
   */
  OUTPUT_SHAPE_HEXAGONAL: 1,

  /**
   * ENUM for output shape: rounded (numbers).
   * @const
   */
  OUTPUT_SHAPE_ROUND: 2,

  /**
   * ENUM for output shape: squared (any/all values; strings).
   * @const
   */
  OUTPUT_SHAPE_SQUARE: 3
};
module.exports = ScratchBlocksConstants;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/sequencer.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/sequencer.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");

const Thread = __webpack_require__(/*! ./thread */ "./node_modules/scratch-vm/src/engine/thread.js");

const execute = __webpack_require__(/*! ./execute.js */ "./node_modules/scratch-vm/src/engine/execute.js");

const compilerExecute = __webpack_require__(/*! ../compiler/jsexecute */ "./node_modules/scratch-vm/src/compiler/jsexecute.js");
/**
 * Profiler frame name for stepping a single thread.
 * @const {string}
 */


const stepThreadProfilerFrame = 'Sequencer.stepThread';
/**
 * Profiler frame name for the inner loop of stepThreads.
 * @const {string}
 */

const stepThreadsInnerProfilerFrame = 'Sequencer.stepThreads#inner';
/**
 * Profiler frame name for execute.
 * @const {string}
 */

const executeProfilerFrame = 'execute';
/**
 * Profiler frame ID for stepThreadProfilerFrame.
 * @type {number}
 */

let stepThreadProfilerId = -1;
/**
 * Profiler frame ID for stepThreadsInnerProfilerFrame.
 * @type {number}
 */

let stepThreadsInnerProfilerId = -1;
/**
 * Profiler frame ID for executeProfilerFrame.
 * @type {number}
 */

let executeProfilerId = -1;

class Sequencer {
  constructor(runtime) {
    /**
     * A utility timer for timing thread sequencing.
     * @type {!Timer}
     */
    this.timer = new Timer();
    /**
     * Reference to the runtime owning this sequencer.
     * @type {!Runtime}
     */

    this.runtime = runtime;
    this.activeThread = null;
  }
  /**
   * Time to run a warp-mode thread, in ms.
   * @type {number}
   */


  static get WARP_TIME() {
    return 500;
  }
  /**
   * Step through all threads in `this.runtime.threads`, running them in order.
   * @return {Array.<!Thread>} List of inactive threads after stepping.
   */


  stepThreads() {
    // Work time is 75% of the thread stepping interval.
    const WORK_TIME = 0.75 * this.runtime.currentStepTime; // For compatibility with Scatch 2, update the millisecond clock
    // on the Runtime once per step (see Interpreter.as in Scratch 2
    // for original use of `currentMSecs`)

    this.runtime.updateCurrentMSecs(); // Start counting toward WORK_TIME.

    this.timer.start(); // Count of active threads.

    let numActiveThreads = Infinity; // Whether `stepThreads` has run through a full single tick.

    let ranFirstTick = false;
    const doneThreads = []; // Conditions for continuing to stepping threads:
    // 1. We must have threads in the list, and some must be active.
    // 2. Time elapsed must be less than WORK_TIME.
    // 3. Either turbo mode, or no redraw has been requested by a primitive.

    while (this.runtime.threads.length > 0 && numActiveThreads > 0 && this.timer.timeElapsed() < WORK_TIME && (this.runtime.turboMode || !this.runtime.redrawRequested)) {
      if (this.runtime.profiler !== null) {
        if (stepThreadsInnerProfilerId === -1) {
          stepThreadsInnerProfilerId = this.runtime.profiler.idByName(stepThreadsInnerProfilerFrame);
        }

        this.runtime.profiler.start(stepThreadsInnerProfilerId);
      }

      numActiveThreads = 0;
      let stoppedThread = false; // Attempt to run each thread one time.

      const threads = this.runtime.threads;

      for (let i = 0; i < threads.length; i++) {
        const activeThread = this.activeThread = threads[i]; // Check if the thread is done so it is not executed.

        if (activeThread.stack.length === 0 || activeThread.status === Thread.STATUS_DONE) {
          // Finished with this thread.
          stoppedThread = true;
          continue;
        }

        if (activeThread.status === Thread.STATUS_YIELD_TICK && !ranFirstTick) {
          // Clear single-tick yield from the last call of `stepThreads`.
          activeThread.status = Thread.STATUS_RUNNING;
        }

        if (activeThread.status === Thread.STATUS_RUNNING || activeThread.status === Thread.STATUS_YIELD) {
          // Normal-mode thread: step.
          if (this.runtime.profiler !== null) {
            if (stepThreadProfilerId === -1) {
              stepThreadProfilerId = this.runtime.profiler.idByName(stepThreadProfilerFrame);
            } // Increment the number of times stepThread is called.


            this.runtime.profiler.increment(stepThreadProfilerId);
          }

          this.stepThread(activeThread);
          activeThread.warpTimer = null;

          if (activeThread.isKilled) {
            i--; // if the thread is removed from the list (killed), do not increase index
          }
        }

        if (activeThread.status === Thread.STATUS_RUNNING) {
          numActiveThreads++;
        } // Check if the thread completed while it just stepped to make
        // sure we remove it before the next iteration of all threads.


        if (activeThread.stack.length === 0 || activeThread.status === Thread.STATUS_DONE) {
          // Finished with this thread.
          stoppedThread = true;
        }
      } // We successfully ticked once. Prevents running STATUS_YIELD_TICK
      // threads on the next tick.


      ranFirstTick = true;

      if (this.runtime.profiler !== null) {
        this.runtime.profiler.stop();
      } // Filter inactive threads from `this.runtime.threads`.


      if (stoppedThread) {
        let nextActiveThread = 0;

        for (let i = 0; i < this.runtime.threads.length; i++) {
          const thread = this.runtime.threads[i];

          if (thread.stack.length !== 0 && thread.status !== Thread.STATUS_DONE) {
            this.runtime.threads[nextActiveThread] = thread;
            nextActiveThread++;
          } else {
            this.runtime.threadMap.delete(thread.getId());
            doneThreads.push(thread);
          }
        }

        this.runtime.threads.length = nextActiveThread;
      }
    }

    this.activeThread = null;
    return doneThreads;
  }
  /**
   * Step the requested thread for as long as necessary.
   * @param {!Thread} thread Thread object to step.
   */


  stepThread(thread) {
    if (thread.isCompiled) {
      compilerExecute(thread);
      return;
    }

    let currentBlockId = thread.peekStack();

    if (!currentBlockId) {
      // A "null block" - empty branch.
      thread.popStack(); // Did the null follow a hat block?

      if (thread.stack.length === 0) {
        thread.status = Thread.STATUS_DONE;
        return;
      }
    } // Save the current block ID to notice if we did control flow.


    while (currentBlockId = thread.peekStack()) {
      let isWarpMode = thread.peekStackFrame().warpMode;

      if (isWarpMode && !thread.warpTimer) {
        // Initialize warp-mode timer if it hasn't been already.
        // This will start counting the thread toward `Sequencer.WARP_TIME`.
        thread.warpTimer = new Timer();
        thread.warpTimer.start();
      } // Execute the current block.


      if (this.runtime.profiler !== null) {
        if (executeProfilerId === -1) {
          executeProfilerId = this.runtime.profiler.idByName(executeProfilerFrame);
        } // Increment the number of times execute is called.


        this.runtime.profiler.increment(executeProfilerId);
      }

      if (thread.target === null) {
        this.retireThread(thread);
      } else {
        execute(this, thread);
      }

      thread.blockGlowInFrame = currentBlockId; // If the thread has yielded or is waiting, yield to other threads.

      if (thread.status === Thread.STATUS_YIELD) {
        // Mark as running for next iteration.
        thread.status = Thread.STATUS_RUNNING; // In warp mode, yielded blocks are re-executed immediately.

        if (isWarpMode && thread.warpTimer.timeElapsed() <= Sequencer.WARP_TIME) {
          continue;
        }

        return;
      } else if (thread.status === Thread.STATUS_PROMISE_WAIT) {
        // A promise was returned by the primitive. Yield the thread
        // until the promise resolves. Promise resolution should reset
        // thread.status to Thread.STATUS_RUNNING.
        return;
      } else if (thread.status === Thread.STATUS_YIELD_TICK) {
        // stepThreads will reset the thread to Thread.STATUS_RUNNING
        return;
      } // If no control flow has happened, switch to next block.


      if (thread.peekStack() === currentBlockId) {
        thread.goToNextBlock();
      } // If no next block has been found at this point, look on the stack.


      while (!thread.peekStack()) {
        thread.popStack();

        if (thread.stack.length === 0) {
          // No more stack to run!
          thread.status = Thread.STATUS_DONE;
          return;
        }

        const stackFrame = thread.peekStackFrame();
        isWarpMode = stackFrame.warpMode;

        if (stackFrame.isLoop) {
          // The current level of the stack is marked as a loop.
          // Return to yield for the frame/tick in general.
          // Unless we're in warp mode - then only return if the
          // warp timer is up.
          if (!isWarpMode || thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
            // Don't do anything to the stack, since loops need
            // to be re-executed.
            return;
          } // Don't go to the next block for this level of the stack,
          // since loops need to be re-executed.


          continue;
        } else if (stackFrame.waitingReporter) {
          // This level of the stack was waiting for a value.
          // This means a reporter has just returned - so don't go
          // to the next block for this level of the stack.
          return;
        } // Get next block of existing block on the stack.


        thread.goToNextBlock();
      }
    }
  }
  /**
   * Step a thread into a block's branch.
   * @param {!Thread} thread Thread object to step to branch.
   * @param {number} branchNum Which branch to step to (i.e., 1, 2).
   * @param {boolean} isLoop Whether this block is a loop.
   */


  stepToBranch(thread, branchNum, isLoop) {
    if (!branchNum) {
      branchNum = 1;
    }

    const currentBlockId = thread.peekStack();
    const branchId = thread.target.blocks.getBranch(currentBlockId, branchNum);
    thread.peekStackFrame().isLoop = isLoop;

    if (branchId) {
      // Push branch ID to the thread's stack.
      thread.pushStack(branchId);
    } else {
      thread.pushStack(null);
    }
  }
  /**
   * Step a procedure.
   * @param {!Thread} thread Thread object to step to procedure.
   * @param {!string} procedureCode Procedure code of procedure to step to.
   */


  stepToProcedure(thread, procedureCode) {
    const definition = thread.target.blocks.getProcedureDefinition(procedureCode);

    if (!definition) {
      return;
    } // Check if the call is recursive.
    // If so, set the thread to yield after pushing.


    const isRecursive = thread.isRecursiveCall(procedureCode); // To step to a procedure, we put its definition on the stack.
    // Execution for the thread will proceed through the definition hat
    // and on to the main definition of the procedure.
    // When that set of blocks finishes executing, it will be popped
    // from the stack by the sequencer, returning control to the caller.

    thread.pushStack(definition); // In known warp-mode threads, only yield when time is up.

    if (thread.peekStackFrame().warpMode && thread.warpTimer.timeElapsed() > Sequencer.WARP_TIME) {
      thread.status = Thread.STATUS_YIELD;
    } else {
      // Look for warp-mode flag on definition, and set the thread
      // to warp-mode if needed.
      const definitionBlock = thread.target.blocks.getBlock(definition);
      const innerBlock = thread.target.blocks.getBlock(definitionBlock.inputs.custom_block.block);
      let doWarp = false;

      if (innerBlock && innerBlock.mutation) {
        const warp = innerBlock.mutation.warp;

        if (typeof warp === 'boolean') {
          doWarp = warp;
        } else if (typeof warp === 'string') {
          doWarp = JSON.parse(warp);
        }
      }

      if (doWarp) {
        thread.peekStackFrame().warpMode = true;
      } else if (isRecursive) {
        // In normal-mode threads, yield any time we have a recursive call.
        thread.status = Thread.STATUS_YIELD;
      }
    }
  }
  /**
   * Retire a thread in the middle, without considering further blocks.
   * @param {!Thread} thread Thread object to retire.
   */


  retireThread(thread) {
    thread.stack = [];
    thread.stackFrame = [];
    thread.requestScriptGlowInFrame = false;
    thread.status = Thread.STATUS_DONE;

    if (thread.isCompiled) {
      thread.procedures = null;
      thread.generator = null;
    }
  }

}

module.exports = Sequencer;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/stage-layering.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/stage-layering.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class StageLayering {
  static get BACKGROUND_LAYER() {
    return 'background';
  }

  static get VIDEO_LAYER() {
    return 'video';
  }

  static get PEN_LAYER() {
    return 'pen';
  }

  static get SPRITE_LAYER() {
    return 'sprite';
  } // Order of layer groups relative to each other,


  static get LAYER_GROUPS() {
    return [StageLayering.BACKGROUND_LAYER, StageLayering.VIDEO_LAYER, StageLayering.PEN_LAYER, StageLayering.SPRITE_LAYER];
  }

}

module.exports = StageLayering;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/target.js":
/*!******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/target.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");

const Blocks = __webpack_require__(/*! ./blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const Comment = __webpack_require__(/*! ../engine/comment */ "./node_modules/scratch-vm/src/engine/comment.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const {
  Map
} = __webpack_require__(/*! immutable */ "./node_modules/immutable/dist/immutable.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const VariableUtil = __webpack_require__(/*! ../util/variable-util */ "./node_modules/scratch-vm/src/util/variable-util.js");
/**
 * @fileoverview
 * A Target is an abstract "code-running" object for the Scratch VM.
 * Examples include sprites/clones or potentially physical-world devices.
 */


class Target extends EventEmitter {
  /**
   * @param {Runtime} runtime Reference to the runtime.
   * @param {?Blocks} blocks Blocks instance for the blocks owned by this target.
   * @constructor
   */
  constructor(runtime, blocks) {
    super();

    if (!blocks) {
      blocks = new Blocks(runtime);
    }
    /**
     * Reference to the runtime.
     * @type {Runtime}
     */


    this.runtime = runtime;
    /**
     * A unique ID for this target.
     * @type {string}
     */

    this.id = uid();
    /**
     * Blocks run as code for this target.
     * @type {!Blocks}
     */

    this.blocks = blocks;
    /**
     * Dictionary of variables and their values for this target.
     * Key is the variable id.
     * @type {Object.<string,*>}
     */

    this.variables = {};
    /**
     * Dictionary of comments for this target.
     * Key is the comment id.
     * @type {Object.<string,*>}
     */

    this.comments = {};
    /**
     * Dictionary of custom state for this target.
     * This can be used to store target-specific custom state for blocks which need it.
     * TODO: do we want to persist this in SB3 files?
     * @type {Object.<string,*>}
     */

    this._customState = {};
    /**
     * Currently known values for edge-activated hats.
     * Keys are block ID for the hat; values are the currently known values.
     * @type {Object.<string, *>}
     */

    this._edgeActivatedHatValues = {};
  }
  /**
   * Called when the project receives a "green flag."
   * @abstract
   */


  onGreenFlag() {}
  /**
   * Return a human-readable name for this target.
   * Target implementations should override this.
   * @abstract
   * @returns {string} Human-readable name for the target.
   */


  getName() {
    return this.id;
  }
  /**
   * Update an edge-activated hat block value.
   * @param {!string} blockId ID of hat to store value for.
   * @param {*} newValue Value to store for edge-activated hat.
   * @return {*} The old value for the edge-activated hat.
   */


  updateEdgeActivatedValue(blockId, newValue) {
    const oldValue = this._edgeActivatedHatValues[blockId];
    this._edgeActivatedHatValues[blockId] = newValue;
    return oldValue;
  }

  hasEdgeActivatedValue(blockId) {
    return this._edgeActivatedHatValues.hasOwnProperty(blockId);
  }
  /**
   * Clear all edge-activaed hat values.
   */


  clearEdgeActivatedValues() {
    this._edgeActivatedHatValues = {};
  }
  /**
   * Look up a variable object, first by id, and then by name if the id is not found.
   * Create a new variable if both lookups fail.
   * @param {string} id Id of the variable.
   * @param {string} name Name of the variable.
   * @return {!Variable} Variable object.
   */


  lookupOrCreateVariable(id, name) {
    let variable = this.lookupVariableById(id);
    if (variable) return variable;
    variable = this.lookupVariableByNameAndType(name, Variable.SCALAR_TYPE);
    if (variable) return variable; // No variable with this name exists - create it locally.

    const newVariable = new Variable(id, name, Variable.SCALAR_TYPE, false);
    this.variables[id] = newVariable;
    return newVariable;
  }
  /**
   * Look up a broadcast message object with the given id and return it
   * if it exists.
   * @param {string} id Id of the variable.
   * @param {string} name Name of the variable.
   * @return {?Variable} Variable object.
   */


  lookupBroadcastMsg(id, name) {
    let broadcastMsg;

    if (id) {
      broadcastMsg = this.lookupVariableById(id);
    } else if (name) {
      broadcastMsg = this.lookupBroadcastByInputValue(name);
    } else {
      log.error('Cannot find broadcast message if neither id nor name are provided.');
    }

    if (broadcastMsg) {
      if (name && broadcastMsg.name.toLowerCase() !== name.toLowerCase()) {
        log.error("Found broadcast message with id: ".concat(id, ", but") + "its name, ".concat(broadcastMsg.name, " did not match expected name ").concat(name, "."));
      }

      if (broadcastMsg.type !== Variable.BROADCAST_MESSAGE_TYPE) {
        log.error("Found variable with id: ".concat(id, ", but its type ").concat(broadcastMsg.type) + "did not match expected type ".concat(Variable.BROADCAST_MESSAGE_TYPE));
      }

      return broadcastMsg;
    }
  }
  /**
   * Look up a broadcast message with the given name and return the variable
   * if it exists. Does not create a new broadcast message variable if
   * it doesn't exist.
   * @param {string} name Name of the variable.
   * @return {?Variable} Variable object.
   */


  lookupBroadcastByInputValue(name) {
    const vars = this.variables;

    for (const propName in vars) {
      if (vars[propName].type === Variable.BROADCAST_MESSAGE_TYPE && vars[propName].name.toLowerCase() === name.toLowerCase()) {
        return vars[propName];
      }
    }
  }
  /**
   * Look up a variable object.
   * Search begins for local variables; then look for globals.
   * @param {string} id Id of the variable.
   * @param {string} name Name of the variable.
   * @return {!Variable} Variable object.
   */


  lookupVariableById(id) {
    // If we have a local copy, return it.
    if (this.variables.hasOwnProperty(id)) {
      return this.variables[id];
    } // If the stage has a global copy, return it.


    if (this.runtime && !this.isStage) {
      const stage = this.runtime.getTargetForStage();

      if (stage && stage.variables.hasOwnProperty(id)) {
        return stage.variables[id];
      }
    }
  }
  /**
   * Look up a variable object by its name and variable type.
   * Search begins with local variables; then global variables if a local one
   * was not found.
   * @param {string} name Name of the variable.
   * @param {string} type Type of the variable. Defaults to Variable.SCALAR_TYPE.
   * @param {?bool} skipStage Optional flag to skip checking the stage
   * @return {?Variable} Variable object if found, or null if not.
   */


  lookupVariableByNameAndType(name, type, skipStage) {
    if (typeof name !== 'string') return;
    if (typeof type !== 'string') type = Variable.SCALAR_TYPE;
    skipStage = skipStage || false;

    for (const varId in this.variables) {
      const currVar = this.variables[varId];

      if (currVar.name === name && currVar.type === type) {
        return currVar;
      }
    }

    if (!skipStage && this.runtime && !this.isStage) {
      const stage = this.runtime.getTargetForStage();

      if (stage) {
        for (const varId in stage.variables) {
          const currVar = stage.variables[varId];

          if (currVar.name === name && currVar.type === type) {
            return currVar;
          }
        }
      }
    }

    return null;
  }
  /**
  * Look up a list object for this target, and create it if one doesn't exist.
  * Search begins for local lists; then look for globals.
  * @param {!string} id Id of the list.
  * @param {!string} name Name of the list.
  * @return {!Varible} Variable object representing the found/created list.
   */


  lookupOrCreateList(id, name) {
    let list = this.lookupVariableById(id);
    if (list) return list;
    list = this.lookupVariableByNameAndType(name, Variable.LIST_TYPE);
    if (list) return list; // No variable with this name exists - create it locally.

    const newList = new Variable(id, name, Variable.LIST_TYPE, false);
    this.variables[id] = newList;
    return newList;
  }
  /**
   * Creates a variable with the given id and name and adds it to the
   * dictionary of variables.
   * @param {string} id Id of variable
   * @param {string} name Name of variable.
   * @param {string} type Type of variable, '', 'broadcast_msg', or 'list'
   * @param {boolean} isCloud Whether the variable to create has the isCloud flag set.
   * Additional checks are made that the variable can be created as a cloud variable.
   */


  createVariable(id, name, type, isCloud) {
    if (!this.variables.hasOwnProperty(id)) {
      const newVariable = new Variable(id, name, type, false);

      if (isCloud && this.isStage && this.runtime.canAddCloudVariable()) {
        newVariable.isCloud = true;
        this.runtime.addCloudVariable();
        this.runtime.ioDevices.cloud.requestCreateVariable(newVariable);
      }

      this.variables[id] = newVariable;
    }
  }
  /**
   * Creates a comment with the given properties.
   * @param {string} id Id of the comment.
   * @param {string} blockId Optional id of the block the comment is attached
   * to if it is a block comment.
   * @param {string} text The text the comment contains.
   * @param {number} x The x coordinate of the comment on the workspace.
   * @param {number} y The y coordinate of the comment on the workspace.
   * @param {number} width The width of the comment when it is full size
   * @param {number} height The height of the comment when it is full size
   * @param {boolean} minimized Whether the comment is minimized.
   */


  createComment(id, blockId, text, x, y, width, height, minimized) {
    if (!this.comments.hasOwnProperty(id)) {
      const newComment = new Comment(id, text, x, y, width, height, minimized);

      if (blockId) {
        newComment.blockId = blockId;
        const blockWithComment = this.blocks.getBlock(blockId);

        if (blockWithComment) {
          blockWithComment.comment = id;
        } else {
          log.warn("Could not find block with id ".concat(blockId, " associated with commentId: ").concat(id));
        }
      }

      this.comments[id] = newComment;
    }
  }
  /**
   * Renames the variable with the given id to newName.
   * @param {string} id Id of variable to rename.
   * @param {string} newName New name for the variable.
   */


  renameVariable(id, newName) {
    if (this.variables.hasOwnProperty(id)) {
      const variable = this.variables[id];

      if (variable.id === id) {
        const oldName = variable.name;
        variable.name = newName;

        if (this.runtime) {
          if (variable.isCloud && this.isStage) {
            this.runtime.ioDevices.cloud.requestRenameVariable(oldName, newName);
          }

          if (variable.type === Variable.SCALAR_TYPE) {
            // sensing__of may be referencing to this variable.
            // Change the reference.
            let blockUpdated = false;
            this.runtime.targets.forEach(t => {
              blockUpdated = t.blocks.updateSensingOfReference(oldName, newName, this.isStage ? '_stage_' : this.getName()) || blockUpdated;
            }); // Request workspace change only if sensing_of blocks were actually updated.

            if (blockUpdated) this.runtime.requestBlocksUpdate();
          }

          const blocks = this.runtime.monitorBlocks;
          blocks.changeBlock({
            id: id,
            element: 'field',
            name: variable.type === Variable.LIST_TYPE ? 'LIST' : 'VARIABLE',
            value: id
          }, this.runtime);
          const monitorBlock = blocks.getBlock(variable.id);

          if (monitorBlock) {
            this.runtime.requestUpdateMonitor(Map({
              id: id,
              params: blocks._getBlockParams(monitorBlock)
            }));
          }
        }
      }
    }
  }
  /**
   * Removes the variable with the given id from the dictionary of variables.
   * @param {string} id Id of variable to delete.
   */


  deleteVariable(id) {
    if (this.variables.hasOwnProperty(id)) {
      // Get info about the variable before deleting it
      const deletedVariableName = this.variables[id].name;
      const deletedVariableWasCloud = this.variables[id].isCloud;
      delete this.variables[id];

      if (this.runtime) {
        if (deletedVariableWasCloud && this.isStage) {
          this.runtime.ioDevices.cloud.requestDeleteVariable(deletedVariableName);
          this.runtime.removeCloudVariable();
        }

        this.runtime.monitorBlocks.deleteBlock(id);
        this.runtime.requestRemoveMonitor(id);
      }
    }
  }
  /**
   * Remove this target's monitors from the runtime state and remove the
   * target-specific monitored blocks (e.g. local variables, global variables for the stage, x-position).
   * NOTE: This does not delete any of the stage monitors like backdrop name.
   */


  deleteMonitors() {
    this.runtime.requestRemoveMonitorByTargetId(this.id);
    let targetSpecificMonitorBlockIds;

    if (this.isStage) {
      // This only deletes global variables and not other stage monitors like backdrop number.
      targetSpecificMonitorBlockIds = Object.keys(this.variables);
    } else {
      targetSpecificMonitorBlockIds = Object.keys(this.runtime.monitorBlocks._blocks).filter(key => this.runtime.monitorBlocks._blocks[key].targetId === this.id);
    }

    for (const blockId of targetSpecificMonitorBlockIds) {
      this.runtime.monitorBlocks.deleteBlock(blockId);
    }
  }
  /**
   * Create a clone of the variable with the given id from the dictionary of
   * this target's variables.
   * @param {string} id Id of variable to duplicate.
   * @param {boolean=} optKeepOriginalId Optional flag to keep the original variable ID
   * for the duplicate variable. This is necessary when cloning a sprite, for example.
   * @return {?Variable} The duplicated variable, or null if
   * the original variable was not found.
   */


  duplicateVariable(id, optKeepOriginalId) {
    if (this.variables.hasOwnProperty(id)) {
      const originalVariable = this.variables[id];
      const newVariable = new Variable(optKeepOriginalId ? id : null, // conditionally keep original id or generate a new one
      originalVariable.name, originalVariable.type, originalVariable.isCloud);

      if (newVariable.type === Variable.LIST_TYPE) {
        newVariable.value = originalVariable.value.slice(0);
      } else {
        newVariable.value = originalVariable.value;
      }

      return newVariable;
    }

    return null;
  }
  /**
   * Duplicate the dictionary of this target's variables as part of duplicating.
   * this target or making a clone.
   * @param {object=} optBlocks Optional block container for the target being duplicated.
   * If provided, new variables will be generated with new UIDs and any variable references
   * in this blocks container will be updated to refer to the corresponding new IDs.
   * @return {object} The duplicated dictionary of variables
   */


  duplicateVariables(optBlocks) {
    let allVarRefs;

    if (optBlocks) {
      allVarRefs = optBlocks.getAllVariableAndListReferences();
    }

    return Object.keys(this.variables).reduce((accum, varId) => {
      const newVariable = this.duplicateVariable(varId, !optBlocks);
      accum[newVariable.id] = newVariable;

      if (optBlocks && allVarRefs) {
        const currVarRefs = allVarRefs[varId];

        if (currVarRefs) {
          this.mergeVariables(varId, newVariable.id, currVarRefs);
        }
      }

      return accum;
    }, {});
  }
  /**
   * Post/edit sprite info.
   * @param {object} data An object with sprite info data to set.
   * @abstract
   */


  postSpriteInfo() {}
  /**
   * Retrieve custom state associated with this target and the provided state ID.
   * @param {string} stateId - specify which piece of state to retrieve.
   * @returns {*} the associated state, if any was found.
   */


  getCustomState(stateId) {
    return this._customState[stateId];
  }
  /**
   * Store custom state associated with this target and the provided state ID.
   * @param {string} stateId - specify which piece of state to store on this target.
   * @param {*} newValue - the state value to store.
   */


  setCustomState(stateId, newValue) {
    this._customState[stateId] = newValue;
  }
  /**
   * Call to destroy a target.
   * @abstract
   */


  dispose() {
    this._customState = {};

    if (this.runtime) {
      this.runtime.removeExecutable(this);
    }
  } // Variable Conflict Resolution Helpers

  /**
   * Get the names of all the variables of the given type that are in scope for this target.
   * For targets that are not the stage, this includes any target-specific
   * variables as well as any stage variables unless the skipStage flag is true.
   * For the stage, this is all stage variables.
   * @param {string} type The variable type to search for; defaults to Variable.SCALAR_TYPE
   * @param {?bool} skipStage Optional flag to skip the stage.
   * @return {Array<string>} A list of variable names
   */


  getAllVariableNamesInScopeByType(type, skipStage) {
    if (typeof type !== 'string') type = Variable.SCALAR_TYPE;
    skipStage = skipStage || false;
    const targetVariables = Object.values(this.variables).filter(v => v.type === type).map(variable => variable.name);

    if (skipStage || this.isStage || !this.runtime) {
      return targetVariables;
    }

    const stage = this.runtime.getTargetForStage();
    const stageVariables = stage.getAllVariableNamesInScopeByType(type);
    return targetVariables.concat(stageVariables);
  }
  /**
   * Merge variable references with another variable.
   * @param {string} idToBeMerged ID of the variable whose references need to be updated
   * @param {string} idToMergeWith ID of the variable that the old references should be replaced with
   * @param {?Array<Object>} optReferencesToUpdate Optional context of the change.
   * Defaults to all the blocks in this target.
   * @param {?string} optNewName New variable name to merge with. The old
   * variable name in the references being updated should be replaced with this new name.
   * If this parameter is not provided or is '', no name change occurs.
   */


  mergeVariables(idToBeMerged, idToMergeWith, optReferencesToUpdate, optNewName) {
    const referencesToChange = optReferencesToUpdate || // TODO should there be a separate helper function that traverses the blocks
    // for all references for a given ID instead of doing the below..?
    this.blocks.getAllVariableAndListReferences()[idToBeMerged];
    VariableUtil.updateVariableIdentifiers(referencesToChange, idToMergeWith, optNewName);
  }
  /**
   * Share a local variable (and given references for that variable) to the stage.
   * @param {string} varId The ID of the variable to share.
   * @param {Array<object>} varRefs The list of variable references being shared,
   * that reference the given variable ID. The names and IDs of these variable
   * references will be updated to refer to the new (or pre-existing) global variable.
   */


  shareLocalVariableToStage(varId, varRefs) {
    if (!this.runtime) return;
    const variable = this.variables[varId];

    if (!variable) {
      log.warn("Cannot share a local variable to the stage if it's not local.");
      return;
    }

    const stage = this.runtime.getTargetForStage(); // If a local var is being shared with the stage,
    // sharing will make the variable global, resulting in a conflict
    // with the existing local variable. Preemptively Resolve this conflict
    // by renaming the new global variable.
    // First check if we've already done the local to global transition for this
    // variable. If we have, merge it with the global variable we've already created.

    const varIdForStage = "StageVarFromLocal_".concat(varId);
    let stageVar = stage.lookupVariableById(varIdForStage); // If a global var doesn't already exist, create a new one with a fresh name.
    // Use the ID we created above so that we can lookup this new variable in the
    // future if we decide to share this same variable again.

    if (!stageVar) {
      const varName = variable.name;
      const varType = variable.type;
      const newStageName = "Stage: ".concat(varName);
      stageVar = this.runtime.createNewGlobalVariable(newStageName, varIdForStage, varType);
    } // Update all variable references to use the new name and ID


    this.mergeVariables(varId, stageVar.id, varRefs, stageVar.name);
  }
  /**
   * Share a local variable with a sprite, merging with one of the same name and
   * type if it already exists on the sprite, or create a new one.
   * @param {string} varId Id of the variable to share
   * @param {Target} sprite The sprite to share the variable with
   * @param {Array<object>} varRefs A list of all the variable references currently being shared.
   */


  shareLocalVariableToSprite(varId, sprite, varRefs) {
    if (!this.runtime) return;
    if (this.isStage) return;
    const variable = this.variables[varId];

    if (!variable) {
      log.warn("Tried to call 'shareLocalVariableToSprite' with a non-local variable.");
      return;
    }

    const varName = variable.name;
    const varType = variable.type; // Check if the receiving sprite already has a variable of the same name and type
    // and use the existing variable, otherwise create a new one.

    const existingLocalVar = sprite.lookupVariableByNameAndType(varName, varType);
    let newVarId;

    if (existingLocalVar) {
      newVarId = existingLocalVar.id;
    } else {
      const newVar = new Variable(null, varName, varType);
      newVarId = newVar.id;
      sprite.variables[newVarId] = newVar;
    } // Merge with the local variable on the new sprite.


    this.mergeVariables(varId, newVarId, varRefs);
  }
  /**
   * Given a list of variable referencing fields, shares those variables with
   * the target with the provided id, resolving any variable conflicts that arise
   * using the following rules:
   *
   * If this target is the stage, exit. There are no conflicts that arise
   * from sharing variables from the stage to another sprite. The variables
   * already exist globally, so no further action is needed.
   *
   * If a variable being referenced is a global variable, do nothing. The
   * global variable already exists so no further action is needed.
   *
   * If a variable being referenced is local, and
   * 1) The receiving target is a sprite:
   * create a new local variable or merge with an existing local variable
   * of the same name and type. Update all the referencing fields
   * for the original variable to reference the new variable.
   * 2) The receiving target is the stage:
   * Create a new global variable with a fresh name and update all the referencing
   * fields to reference the new variable.
   *
   * @param {Array<object>} blocks The blocks containing
   * potential conflicting references to variables.
   * @param {Target} receivingTarget The target receiving the variables
   */


  resolveVariableSharingConflictsWithTarget(blocks, receivingTarget) {
    if (this.isStage) return; // Get all the variable references in the given list of blocks

    const allVarListRefs = this.blocks.getAllVariableAndListReferences(blocks); // For all the variables being referenced, check for which ones are local
    // to this target, and resolve conflicts based on whether the receiving target
    // is a sprite (with a conflicting local variable) or whether it is
    // the stage (which cannot have local variables)

    for (const varId in allVarListRefs) {
      const currVar = this.variables[varId];
      if (!currVar) continue; // The current variable is global, there shouldn't be any conflicts here, skip it.
      // Get the list of references for the current variable id

      const currVarListRefs = allVarListRefs[varId];

      if (receivingTarget.isStage) {
        this.shareLocalVariableToStage(varId, currVarListRefs);
      } else {
        this.shareLocalVariableToSprite(varId, receivingTarget, currVarListRefs);
      }
    }
  }
  /**
   * Fixes up variable references in this target avoiding conflicts with
   * pre-existing variables in the same scope.
   * This is used when uploading this target as a new sprite into an existing
   * project, where the new sprite may contain references
   * to variable names that already exist as global variables in the project
   * (and thus are in scope for variable references in the given sprite).
   *
   * If this target has a block that references an existing global variable and that
   * variable *does not* exist in this target (e.g. it was a global variable in the
   * project the sprite was originally exported from), merge the variables. This entails
   * fixing the variable references in this sprite to reference the id of the pre-existing global variable.
   *
   * If this target has a block that references an existing global variable and that
   * variable does exist in the target itself (e.g. it's a local variable in the sprite being uploaded),
   * then the local variable is renamed to distinguish itself from the pre-existing variable.
   * All blocks that reference the local variable will be updated to use the new name.
   */
  // TODO (#1360) This function is too long, add some helpers for the different chunks and cases...


  fixUpVariableReferences() {
    if (!this.runtime) return; // There's no runtime context to conflict with

    if (this.isStage) return; // Stage can't have variable conflicts with itself (and also can't be uploaded)

    const stage = this.runtime.getTargetForStage();
    if (!stage || !stage.variables) return;

    const renameConflictingLocalVar = (id, name, type) => {
      const conflict = stage.lookupVariableByNameAndType(name, type);

      if (conflict) {
        const newName = StringUtil.unusedName("".concat(this.getName(), ": ").concat(name), this.getAllVariableNamesInScopeByType(type));
        this.renameVariable(id, newName);
        return newName;
      }

      return null;
    };

    const allReferences = this.blocks.getAllVariableAndListReferences();
    const unreferencedLocalVarIds = [];

    if (Object.keys(this.variables).length > 0) {
      for (const localVarId in this.variables) {
        if (!this.variables.hasOwnProperty(localVarId)) continue;
        if (!allReferences[localVarId]) unreferencedLocalVarIds.push(localVarId);
      }
    }

    const conflictIdsToReplace = Object.create(null);
    const conflictNamesToReplace = Object.create(null); // Cache the list of all variable names by type so that we don't need to
    // re-calculate this in every iteration of the following loop.

    const varNamesByType = {};

    const allVarNames = type => {
      const namesOfType = varNamesByType[type];
      if (namesOfType) return namesOfType;
      varNamesByType[type] = this.runtime.getAllVarNamesOfType(type);
      return varNamesByType[type];
    };

    for (const varId in allReferences) {
      // We don't care about which var ref we get, they should all have the same var info
      const varRef = allReferences[varId][0];
      const varName = varRef.referencingField.value;
      const varType = varRef.type;

      if (this.lookupVariableById(varId)) {
        // Found a variable with the id in either the target or the stage,
        // figure out which one.
        if (this.variables.hasOwnProperty(varId)) {
          // If the target has the variable, then check whether the stage
          // has one with the same name and type. If it does, then rename
          // this target specific variable so that there is a distinction.
          const newVarName = renameConflictingLocalVar(varId, varName, varType);

          if (newVarName) {
            // We are not calling this.blocks.updateBlocksAfterVarRename
            // here because it will search through all the blocks. We already
            // have access to all the references for this var id.
            allReferences[varId].map(ref => {
              ref.referencingField.value = newVarName;
              return ref;
            });
          }
        }
      } else {
        // We didn't find the referenced variable id anywhere,
        // Treat it as a reference to a global variable (from the original
        // project this sprite was exported from).
        // Check for whether a global variable of the same name and type exists,
        // and if so, track it to merge with the existing global in a second pass of the blocks.
        const existingVar = stage.lookupVariableByNameAndType(varName, varType);

        if (existingVar) {
          if (!conflictIdsToReplace[varId]) {
            conflictIdsToReplace[varId] = existingVar.id;
          }
        } else {
          // A global variable with the same name did not already exist,
          // create a new one such that it does not conflict with any
          // names of local variables of the same type.
          const allNames = allVarNames(varType);
          const freshName = StringUtil.unusedName(varName, allNames);
          stage.createVariable(varId, freshName, varType);

          if (!conflictNamesToReplace[varId]) {
            conflictNamesToReplace[varId] = freshName;
          }
        }
      }
    } // Rename any local variables that were missed above because they aren't
    // referenced by any blocks


    for (const id in unreferencedLocalVarIds) {
      const varId = unreferencedLocalVarIds[id];
      const name = this.variables[varId].name;
      const type = this.variables[varId].type;
      renameConflictingLocalVar(varId, name, type);
    } // Handle global var conflicts with existing global vars (e.g. a sprite is uploaded, and has
    // blocks referencing some variable that the sprite does not own, and this
    // variable conflicts with a global var)
    // In this case, we want to merge the new variable referenes with the
    // existing global variable


    for (const conflictId in conflictIdsToReplace) {
      const existingId = conflictIdsToReplace[conflictId];
      const referencesToUpdate = allReferences[conflictId];
      this.mergeVariables(conflictId, existingId, referencesToUpdate);
    } // Handle global var conflicts existing local vars (e.g a sprite is uploaded,
    // and has blocks referencing some variable that the sprite does not own, and this
    // variable conflcits with another sprite's local var).
    // In this case, we want to go through the variable references and update
    // the name of the variable in that reference.


    for (const conflictId in conflictNamesToReplace) {
      const newName = conflictNamesToReplace[conflictId];
      const referencesToUpdate = allReferences[conflictId];
      referencesToUpdate.map(ref => {
        ref.referencingField.value = newName;
        return ref;
      });
    }
  }

}

module.exports = Target;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/thread.js":
/*!******************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/thread.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Recycle bin for empty stackFrame objects
 * @type Array<_StackFrame>
 */


const _stackFrameFreeList = [];
/**
 * A frame used for each level of the stack. A general purpose
 * place to store a bunch of execution context and parameters
 * @param {boolean} warpMode Whether this level of the stack is warping
 * @constructor
 * @private
 */

class _StackFrame {
  constructor(warpMode) {
    /**
     * Whether this level of the stack is a loop.
     * @type {boolean}
     */
    this.isLoop = false;
    /**
     * Whether this level is in warp mode.  Is set by some legacy blocks and
     * "turbo mode"
     * @type {boolean}
     */

    this.warpMode = warpMode;
    /**
     * Reported value from just executed block.
     * @type {Any}
     */

    this.justReported = null;
    /**
     * The active block that is waiting on a promise.
     * @type {string}
     */

    this.reporting = '';
    /**
     * Persists reported inputs during async block.
     * @type {Object}
     */

    this.reported = null;
    /**
     * Name of waiting reporter.
     * @type {string}
     */

    this.waitingReporter = null;
    /**
     * Procedure parameters.
     * @type {Object}
     */

    this.params = null;
    /**
     * A context passed to block implementations.
     * @type {Object}
     */

    this.executionContext = null;
  }
  /**
   * Reset all properties of the frame to pristine null and false states.
   * Used to recycle.
   * @return {_StackFrame} this
   */


  reset() {
    this.isLoop = false;
    this.warpMode = false;
    this.justReported = null;
    this.reported = null;
    this.waitingReporter = null;
    this.params = null;
    this.executionContext = null;
    return this;
  }
  /**
   * Reuse an active stack frame in the stack.
   * @param {?boolean} warpMode defaults to current warpMode
   * @returns {_StackFrame} this
   */


  reuse() {
    let warpMode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.warpMode;
    this.reset();
    this.warpMode = Boolean(warpMode);
    return this;
  }
  /**
   * Create or recycle a stack frame object.
   * @param {boolean} warpMode Enable warpMode on this frame.
   * @returns {_StackFrame} The clean stack frame with correct warpMode setting.
   */


  static create(warpMode) {
    const stackFrame = _stackFrameFreeList.pop();

    if (typeof stackFrame !== 'undefined') {
      stackFrame.warpMode = Boolean(warpMode);
      return stackFrame;
    }

    return new _StackFrame(warpMode);
  }
  /**
   * Put a stack frame object into the recycle bin for reuse.
   * @param {_StackFrame} stackFrame The frame to reset and recycle.
   */


  static release(stackFrame) {
    if (typeof stackFrame !== 'undefined') {
      _stackFrameFreeList.push(stackFrame.reset());
    }
  }

}
/**
 * A thread is a running stack context and all the metadata needed.
 * @param {?string} firstBlock First block to execute in the thread.
 * @constructor
 */


class Thread {
  constructor(firstBlock) {
    /**
     * ID of top block of the thread
     * @type {!string}
     */
    this.topBlock = firstBlock;
    /**
     * Stack for the thread. When the sequencer enters a control structure,
     * the block is pushed onto the stack so we know where to exit.
     * @type {Array.<string>}
     */

    this.stack = [];
    /**
     * Stack frames for the thread. Store metadata for the executing blocks.
     * @type {Array.<_StackFrame>}
     */

    this.stackFrames = [];
    /**
     * Status of the thread, one of three states (below)
     * @type {number}
     */

    this.status = 0;
    /* Thread.STATUS_RUNNING */

    /**
     * Whether the thread is killed in the middle of execution.
     * @type {boolean}
     */

    this.isKilled = false;
    /**
     * Target of this thread.
     * @type {?Target}
     */

    this.target = null;
    /**
     * The Blocks this thread will execute.
     * @type {Blocks}
     */

    this.blockContainer = null;
    /**
     * Whether the thread requests its script to glow during this frame.
     * @type {boolean}
     */

    this.requestScriptGlowInFrame = false;
    /**
     * Which block ID should glow during this frame, if any.
     * @type {?string}
     */

    this.blockGlowInFrame = null;
    /**
     * A timer for when the thread enters warp mode.
     * Substitutes the sequencer's count toward WORK_TIME on a per-thread basis.
     * @type {?Timer}
     */

    this.warpTimer = null;
    this.justReported = null;
    this.triedToCompile = false;
    this.isCompiled = false; // compiler data
    // these values only make sense if isCompiled == true

    this.timer = null;
    /**
     * The thread's generator.
     * @type {Generator}
     */

    this.generator = null;
    /**
     * @type {Object.<string, import('../compiler/compile').CompiledScript>}
     */

    this.procedures = null;
  }
  /**
   * Thread status for initialized or running thread.
   * This is the default state for a thread - execution should run normally,
   * stepping from block to block.
   * @const
   */


  static get STATUS_RUNNING() {
    return 0; // used by compiler
  }
  /**
   * Threads are in this state when a primitive is waiting on a promise;
   * execution is paused until the promise changes thread status.
   * @const
   */


  static get STATUS_PROMISE_WAIT() {
    return 1; // used by compiler
  }
  /**
   * Thread status for yield.
   * @const
   */


  static get STATUS_YIELD() {
    return 2; // used by compiler
  }
  /**
   * Thread status for a single-tick yield. This will be cleared when the
   * thread is resumed.
   * @const
   */


  static get STATUS_YIELD_TICK() {
    return 3; // used by compiler
  }
  /**
   * Thread status for a finished/done thread.
   * Thread is in this state when there are no more blocks to execute.
   * @const
   */


  static get STATUS_DONE() {
    return 4; // used by compiler
  }
  /**
   * @param {Target} target The target running the thread.
   * @param {string} topBlock ID of the thread's top block.
   * @returns {string} A unique ID for this target and thread.
   */


  static getIdFromTargetAndBlock(target, topBlock) {
    // & should never appear in any IDs, so we can use it as a separator
    return "".concat(target.id, "&").concat(topBlock);
  }

  getId() {
    return Thread.getIdFromTargetAndBlock(this.target, this.topBlock);
  }
  /**
   * Push stack and update stack frames appropriately.
   * @param {string} blockId Block ID to push to stack.
   */


  pushStack(blockId) {
    this.stack.push(blockId); // Push an empty stack frame, if we need one.
    // Might not, if we just popped the stack.

    if (this.stack.length > this.stackFrames.length) {
      const parent = this.stackFrames[this.stackFrames.length - 1];
      this.stackFrames.push(_StackFrame.create(typeof parent !== 'undefined' && parent.warpMode));
    }
  }
  /**
   * Reset the stack frame for use by the next block.
   * (avoids popping and re-pushing a new stack frame - keeps the warpmode the same
   * @param {string} blockId Block ID to push to stack.
   */


  reuseStackForNextBlock(blockId) {
    this.stack[this.stack.length - 1] = blockId;
    this.stackFrames[this.stackFrames.length - 1].reuse();
  }
  /**
   * Pop last block on the stack and its stack frame.
   * @return {string} Block ID popped from the stack.
   */


  popStack() {
    _StackFrame.release(this.stackFrames.pop());

    return this.stack.pop();
  }
  /**
   * Pop back down the stack frame until we hit a procedure call or the stack frame is emptied
   */


  stopThisScript() {
    let blockID = this.peekStack();

    while (blockID !== null) {
      const block = this.target.blocks.getBlock(blockID);

      if (typeof block !== 'undefined' && block.opcode === 'procedures_call') {
        break;
      }

      this.popStack();
      blockID = this.peekStack();
    }

    if (this.stack.length === 0) {
      // Clean up!
      this.requestScriptGlowInFrame = false;
      this.status = Thread.STATUS_DONE;
    }
  }
  /**
   * Get top stack item.
   * @return {?string} Block ID on top of stack.
   */


  peekStack() {
    return this.stack.length > 0 ? this.stack[this.stack.length - 1] : null;
  }
  /**
   * Get top stack frame.
   * @return {?object} Last stack frame stored on this thread.
   */


  peekStackFrame() {
    return this.stackFrames.length > 0 ? this.stackFrames[this.stackFrames.length - 1] : null;
  }
  /**
   * Get stack frame above the current top.
   * @return {?object} Second to last stack frame stored on this thread.
   */


  peekParentStackFrame() {
    return this.stackFrames.length > 1 ? this.stackFrames[this.stackFrames.length - 2] : null;
  }
  /**
   * Push a reported value to the parent of the current stack frame.
   * @param {*} value Reported value to push.
   */


  pushReportedValue(value) {
    this.justReported = typeof value === 'undefined' ? null : value;
  }
  /**
   * Initialize procedure parameters on this stack frame.
   */


  initParams() {
    const stackFrame = this.peekStackFrame();

    if (stackFrame.params === null) {
      stackFrame.params = {};
    }
  }
  /**
   * Add a parameter to the stack frame.
   * Use when calling a procedure with parameter values.
   * @param {!string} paramName Name of parameter.
   * @param {*} value Value to set for parameter.
   */


  pushParam(paramName, value) {
    const stackFrame = this.peekStackFrame();
    stackFrame.params[paramName] = value;
  }
  /**
   * Get a parameter at the lowest possible level of the stack.
   * @param {!string} paramName Name of parameter.
   * @return {*} value Value for parameter.
   */


  getParam(paramName) {
    for (let i = this.stackFrames.length - 1; i >= 0; i--) {
      const frame = this.stackFrames[i];

      if (frame.params === null) {
        continue;
      }

      if (frame.params.hasOwnProperty(paramName)) {
        return frame.params[paramName];
      }

      return null;
    }

    return null;
  }

  getAllparams() {
    const stackFrame = this.peekStackFrame();
    return stackFrame.params;
  }
  /**
   * Whether the current execution of a thread is at the top of the stack.
   * @return {boolean} True if execution is at top of the stack.
   */


  atStackTop() {
    return this.peekStack() === this.topBlock;
  }
  /**
   * Switch the thread to the next block at the current level of the stack.
   * For example, this is used in a standard sequence of blocks,
   * where execution proceeds from one block to the next.
   */


  goToNextBlock() {
    const nextBlockId = this.target.blocks.getNextBlock(this.peekStack());
    this.reuseStackForNextBlock(nextBlockId);
  }
  /**
   * Attempt to determine whether a procedure call is recursive,
   * by examining the stack.
   * @param {!string} procedureCode Procedure code of procedure being called.
   * @return {boolean} True if the call appears recursive.
   */


  isRecursiveCall(procedureCode) {
    let callCount = 5; // Max number of enclosing procedure calls to examine.

    const sp = this.stack.length - 1;

    for (let i = sp - 1; i >= 0; i--) {
      const block = this.target.blocks.getBlock(this.stack[i]);

      if (block.opcode === 'procedures_call' && block.mutation.proccode === procedureCode) {
        return true;
      }

      if (--callCount < 0) return false;
    }

    return false;
  }
  /**
   * Attempt to compile this thread.
   */


  tryCompile() {
    if (!this.blockContainer) {
      return;
    } // importing the compiler here avoids circular dependency issues


    const compile = __webpack_require__(/*! ../compiler/compile */ "./node_modules/scratch-vm/src/compiler/compile.js");

    this.triedToCompile = true;
    const topBlock = this.topBlock; // Flyout blocks are stored in a special block container.

    const blocks = this.blockContainer.getBlock(topBlock) ? this.blockContainer : this.target.runtime.flyoutBlocks;
    const cachedResult = blocks.getCachedCompileResult(topBlock); // If there is a cached error, do not attempt to recompile.

    if (cachedResult && !cachedResult.success) {
      return;
    }

    let result;

    if (cachedResult) {
      result = cachedResult.value;
    } else {
      try {
        result = compile(this);
        blocks.cacheCompileResult(topBlock, result);
      } catch (error) {
        log.error('cannot compile script', this.target.getName(), error);
        blocks.cacheCompileError(topBlock, error);
        this.target.runtime.emitCompileError(this.target, error);
        return;
      }
    }

    this.procedures = {};

    for (const procedureCode of Object.keys(result.procedures)) {
      this.procedures[procedureCode] = result.procedures[procedureCode](this);
    }

    this.generator = result.startingFunction(this)();

    if (!this.blockContainer.forceNoGlow) {
      this.blockGlowInFrame = this.topBlock;
      this.requestScriptGlowInFrame = true;
    }

    this.isCompiled = true;
  }

}

module.exports = Thread;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/tw-frame-loop.js":
/*!*************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/tw-frame-loop.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// Due to the existence of features such as interpolation and "0 FPS" being treated as "screen refresh rate",
// The VM loop logic has become much more complex
// Use setTimeout to polyfill requestAnimationFrame in Node.js environments
const _requestAnimationFrame = typeof requestAnimationFrame === 'function' ? requestAnimationFrame : f => setTimeout(f, 1000 / 60);

const _cancelAnimationFrame = typeof requestAnimationFrame === 'function' ? cancelAnimationFrame : clearTimeout;

const animationFrameWrapper = callback => {
  let id;

  const handle = () => {
    id = _requestAnimationFrame(handle);
    callback();
  };

  const cancel = () => _cancelAnimationFrame(id);

  id = _requestAnimationFrame(handle);
  return {
    cancel
  };
};

class FrameLoop {
  constructor(runtime) {
    this.runtime = runtime;
    this.running = false;
    this.setFramerate(30);
    this.setInterpolation(false);
    this.stepCallback = this.stepCallback.bind(this);
    this.interpolationCallback = this.interpolationCallback.bind(this);
    this._stepInterval = null;
    this._interpolationAnimation = null;
    this._stepAnimation = null;
  }

  setFramerate(fps) {
    this.framerate = fps;

    this._restart();
  }

  setInterpolation(interpolation) {
    this.interpolation = interpolation;

    this._restart();
  }

  stepCallback() {
    this.runtime._step();
  }

  interpolationCallback() {
    this.runtime._renderInterpolatedPositions();
  }

  _restart() {
    if (this.running) {
      this.stop();
      this.start();
    }
  }

  start() {
    this.running = true;

    if (this.framerate === 0) {
      this._stepAnimation = animationFrameWrapper(this.stepCallback);
      this.runtime.currentStepTime = 1000 / 60;
    } else {
      // Interpolation should never be enabled when framerate === 0 as that's just redundant
      if (this.interpolation) {
        this._interpolationAnimation = animationFrameWrapper(this.interpolationCallback);
      }

      this._stepInterval = setInterval(this.stepCallback, 1000 / this.framerate);
      this.runtime.currentStepTime = 1000 / this.framerate;
    }
  }

  stop() {
    this.running = false;
    clearInterval(this._stepInterval);

    if (this._interpolationAnimation) {
      this._interpolationAnimation.cancel();
    }

    if (this._stepAnimation) {
      this._stepAnimation.cancel();
    }

    this._interpolationAnimation = null;
    this._stepAnimation = null;
  }

}

module.exports = FrameLoop;

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/tw-interpolate.js":
/*!**************************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/tw-interpolate.js ***!
  \**************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Prepare the targets of a runtime for interpolation.
 * @param {Runtime} runtime The Runtime with targets to prepare for interpolation.
 */
const setupInitialState = runtime => {
  const renderer = runtime.renderer;

  for (const target of runtime.targets) {
    const directionAndScale = target._getRenderedDirectionAndScale(); // If sprite may have been interpolated in the previous frame, reset its renderer state.


    if (renderer && target.interpolationData) {
      const drawableID = target.drawableID;
      renderer.updateDrawablePosition(drawableID, [target.x, target.y]);
      renderer.updateDrawableDirectionScale(drawableID, directionAndScale.direction, directionAndScale.scale);
      renderer.updateDrawableEffect(drawableID, 'ghost', target.effects.ghost);
    }

    if (target.visible && !target.isStage) {
      target.interpolationData = {
        x: target.x,
        y: target.y,
        direction: directionAndScale.direction,
        scale: directionAndScale.scale,
        costume: target.currentCostume,
        ghost: target.effects.ghost
      };
    } else {
      target.interpolationData = null;
    }
  }
};
/**
 * Interpolate the position of targets.
 * @param {Runtime} runtime The Runtime with targets to interpolate.
 * @param {number} time Relative time in the frame in [0-1].
 */


const interpolate = (runtime, time) => {
  const renderer = runtime.renderer;

  if (!renderer) {
    return;
  }

  for (const target of runtime.targets) {
    // interpolationData is the initial state at the start of the frame (time 0)
    // the state on the target itself is the state at the end of the frame (time 1)
    const interpolationData = target.interpolationData;

    if (!interpolationData) {
      continue;
    } // Don't waste time interpolating sprites that are hidden.


    if (!target.visible) {
      continue;
    }

    const drawableID = target.drawableID; // Position interpolation.

    const xDistance = target.x - interpolationData.x;
    const yDistance = target.y - interpolationData.y;
    const absoluteXDistance = Math.abs(xDistance);
    const absoluteYDistance = Math.abs(yDistance);

    if (absoluteXDistance > 0.1 || absoluteYDistance > 0.1) {
      const drawable = renderer._allDrawables[drawableID]; // Large movements are likely intended to be instantaneous.
      // getAABB is less accurate than getBounds, but it's much faster

      const bounds = drawable.getAABB();
      const tolerance = Math.min(240, Math.max(50, 1.5 * (bounds.width + bounds.height)));
      const distance = Math.sqrt(absoluteXDistance ** 2 + absoluteYDistance ** 2);

      if (distance < tolerance) {
        const newX = interpolationData.x + xDistance * time;
        const newY = interpolationData.y + yDistance * time;
        renderer.updateDrawablePosition(drawableID, [newX, newY]);
      }
    } // Effect interpolation.


    const ghostChange = target.effects.ghost - interpolationData.ghost;
    const absoluteGhostChange = Math.abs(ghostChange); // Large changes are likely intended to be instantaneous.

    if (absoluteGhostChange > 0 && absoluteGhostChange < 25) {
      const newGhost = target.effects.ghost + ghostChange * time;
      renderer.updateDrawableEffect(drawableID, 'ghost', newGhost);
    } // Interpolate scale and direction.


    const costumeUnchanged = interpolationData.costume === target.currentCostume;

    if (costumeUnchanged) {
      let {
        direction,
        scale
      } = target._getRenderedDirectionAndScale();

      let updateDrawableDirectionScale = false; // Interpolate direction.

      if (direction !== interpolationData.direction) {
        // Perfect 90 degree angles should not be interpolated.
        // eg. the foreground tile clones in https://scratch.mit.edu/projects/60917032/
        if (direction % 90 !== 0 || interpolationData.direction % 90 !== 0) {
          const currentRadians = direction * Math.PI / 180;
          const startingRadians = interpolationData.direction * Math.PI / 180;
          direction = Math.atan2(Math.sin(currentRadians) * time + Math.sin(startingRadians) * (1 - time), Math.cos(currentRadians) * time + Math.cos(startingRadians) * (1 - time)) * 180 / Math.PI;
          updateDrawableDirectionScale = true;
        }
      } // Interpolate scale.


      const startingScale = interpolationData.scale;

      if (scale[0] !== startingScale[0] || scale[1] !== startingScale[1]) {
        // Do not interpolate size when the sign of either scale differs.
        if (Math.sign(scale[0]) === Math.sign(startingScale[0]) && Math.sign(scale[1]) === Math.sign(startingScale[1])) {
          const changeX = scale[0] - startingScale[0];
          const changeY = scale[1] - startingScale[1];
          const absoluteChangeX = Math.abs(changeX);
          const absoluteChangeY = Math.abs(changeY); // Large changes are likely intended to be instantaneous.

          if (absoluteChangeX < 100 && absoluteChangeY < 100) {
            scale[0] = startingScale[0] + changeX * time;
            scale[1] = startingScale[1] + changeY * time;
            updateDrawableDirectionScale = true;
          }
        }
      }

      if (updateDrawableDirectionScale) {
        renderer.updateDrawableDirectionScale(drawableID, direction, scale);
      }
    }
  }
};

module.exports = {
  setupInitialState,
  interpolate
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/engine/variable.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-vm/src/engine/variable.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * Object representing a Scratch variable.
 */
const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const xmlEscape = __webpack_require__(/*! ../util/xml-escape */ "./node_modules/scratch-vm/src/util/xml-escape.js");

class Variable {
  /**
   * @param {string} id Id of the variable.
   * @param {string} name Name of the variable.
   * @param {string} type Type of the variable, one of '' or 'list'
   * @param {boolean} isCloud Whether the variable is stored in the cloud.
   * @constructor
   */
  constructor(id, name, type, isCloud) {
    this.id = id || uid();
    this.name = name;
    this.type = type;
    this.isCloud = isCloud;

    switch (this.type) {
      case Variable.SCALAR_TYPE:
        this.value = 0;
        break;

      case Variable.LIST_TYPE:
        this.value = [];
        break;

      case Variable.BROADCAST_MESSAGE_TYPE:
        this.value = this.name;
        break;

      default:
        throw new Error("Invalid variable type: ".concat(this.type));
    }
  }

  toXML(isLocal) {
    isLocal = isLocal === true;
    return "<variable type=\"".concat(this.type, "\" id=\"").concat(this.id, "\" islocal=\"").concat(isLocal, "\" iscloud=\"").concat(this.isCloud, "\">").concat(xmlEscape(this.name), "</variable>");
  }
  /**
   * Type representation for scalar variables.
   * This is currently represented as ''
   * for compatibility with blockly.
   * @const {string}
   */


  static get SCALAR_TYPE() {
    return ''; // used by compiler
  }
  /**
   * Type representation for list variables.
   * @const {string}
   */


  static get LIST_TYPE() {
    return 'list'; // used by compiler
  }
  /**
   * Type representation for list variables.
   * @const {string}
   */


  static get BROADCAST_MESSAGE_TYPE() {
    return 'broadcast_msg';
  }

}

module.exports = Variable;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/argument-type.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/argument-type.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Block argument types
 * @enum {string}
 */
const ArgumentType = {
  /**
   * Numeric value with angle picker
   */
  ANGLE: 'angle',

  /**
   * Boolean value with hexagonal placeholder
   */
  BOOLEAN: 'Boolean',

  /**
   * Numeric value with color picker
   */
  COLOR: 'color',

  /**
   * Numeric value with text field
   */
  NUMBER: 'number',

  /**
   * String value with text field
   */
  STRING: 'string',

  /**
   * String value with matrix field
   */
  MATRIX: 'matrix',

  /**
   * MIDI note number with note picker (piano) field
   */
  NOTE: 'note',

  /**
   * Inline image on block (as part of the label)
   */
  IMAGE: 'image'
};
module.exports = ArgumentType;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/block-type.js":
/*!*********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/block-type.js ***!
  \*********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Types of block
 * @enum {string}
 */
const BlockType = {
  /**
   * Boolean reporter with hexagonal shape
   */
  BOOLEAN: 'Boolean',

  /**
   * A button (not an actual block) for some special action, like making a variable
   */
  BUTTON: 'button',

  /**
   * Command block
   */
  COMMAND: 'command',

  /**
   * Specialized command block which may or may not run a child branch
   * The thread continues with the next block whether or not a child branch ran.
   */
  CONDITIONAL: 'conditional',

  /**
   * Specialized hat block with no implementation function
   * This stack only runs if the corresponding event is emitted by other code.
   */
  EVENT: 'event',

  /**
   * Hat block which conditionally starts a block stack
   */
  HAT: 'hat',

  /**
   * Specialized command block which may or may not run a child branch
   * If a child branch runs, the thread evaluates the loop block again.
   */
  LOOP: 'loop',

  /**
   * General reporter with numeric or string value
   */
  REPORTER: 'reporter'
};
module.exports = BlockType;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/extension-manager.js":
/*!****************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/extension-manager.js ***!
  \****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const dispatch = __webpack_require__(/*! ../dispatch/central-dispatch */ "./node_modules/scratch-vm/src/dispatch/central-dispatch.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const maybeFormatMessage = __webpack_require__(/*! ../util/maybe-format-message */ "./node_modules/scratch-vm/src/util/maybe-format-message.js");

const BlockType = __webpack_require__(/*! ./block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const SecurityManager = __webpack_require__(/*! ./tw-security-manager */ "./node_modules/scratch-vm/src/extension-support/tw-security-manager.js"); // These extensions are currently built into the VM repository but should not be loaded at startup.
// TODO: move these out into a separate repository?
// TODO: change extension spec so that library info, including extension ID, can be collected through static methods


const builtinExtensions = {
  // This is an example that isn't loaded with the other core blocks,
  // but serves as a reference for loading core blocks as extensions.
  coreExample: () => __webpack_require__(/*! ../blocks/scratch3_core_example */ "./node_modules/scratch-vm/src/blocks/scratch3_core_example.js"),
  // These are the non-core built-in extensions.
  pen: () => __webpack_require__(/*! ../extensions/scratch3_pen */ "./node_modules/scratch-vm/src/extensions/scratch3_pen/index.js"),
  wedo2: () => __webpack_require__(/*! ../extensions/scratch3_wedo2 */ "./node_modules/scratch-vm/src/extensions/scratch3_wedo2/index.js"),
  music: () => __webpack_require__(/*! ../extensions/scratch3_music */ "./node_modules/scratch-vm/src/extensions/scratch3_music/index.js"),
  microbit: () => __webpack_require__(/*! ../extensions/scratch3_microbit */ "./node_modules/scratch-vm/src/extensions/scratch3_microbit/index.js"),
  text2speech: () => __webpack_require__(/*! ../extensions/scratch3_text2speech */ "./node_modules/scratch-vm/src/extensions/scratch3_text2speech/index.js"),
  translate: () => __webpack_require__(/*! ../extensions/scratch3_translate */ "./node_modules/scratch-vm/src/extensions/scratch3_translate/index.js"),
  videoSensing: () => __webpack_require__(/*! ../extensions/scratch3_video_sensing */ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/index.js"),
  ev3: () => __webpack_require__(/*! ../extensions/scratch3_ev3 */ "./node_modules/scratch-vm/src/extensions/scratch3_ev3/index.js"),
  makeymakey: () => __webpack_require__(/*! ../extensions/scratch3_makeymakey */ "./node_modules/scratch-vm/src/extensions/scratch3_makeymakey/index.js"),
  boost: () => __webpack_require__(/*! ../extensions/scratch3_boost */ "./node_modules/scratch-vm/src/extensions/scratch3_boost/index.js"),
  gdxfor: () => __webpack_require__(/*! ../extensions/scratch3_gdx_for */ "./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/index.js"),
  // tw: core extension
  tw: () => __webpack_require__(/*! ../extensions/tw */ "./node_modules/scratch-vm/src/extensions/tw/index.js")
};
/**
 * @typedef {object} ArgumentInfo - Information about an extension block argument
 * @property {ArgumentType} type - the type of value this argument can take
 * @property {*|undefined} default - the default value of this argument (default: blank)
 */

/**
 * @typedef {object} ConvertedBlockInfo - Raw extension block data paired with processed data ready for scratch-blocks
 * @property {ExtensionBlockMetadata} info - the raw block info
 * @property {object} json - the scratch-blocks JSON definition for this block
 * @property {string} xml - the scratch-blocks XML definition for this block
 */

/**
 * @typedef {object} CategoryInfo - Information about a block category
 * @property {string} id - the unique ID of this category
 * @property {string} name - the human-readable name of this category
 * @property {string|undefined} blockIconURI - optional URI for the block icon image
 * @property {string} color1 - the primary color for this category, in '#rrggbb' format
 * @property {string} color2 - the secondary color for this category, in '#rrggbb' format
 * @property {string} color3 - the tertiary color for this category, in '#rrggbb' format
 * @property {Array.<ConvertedBlockInfo>} blocks - the blocks, separators, etc. in this category
 * @property {Array.<object>} menus - the menus provided by this category
 */

/**
 * @typedef {object} PendingExtensionWorker - Information about an extension worker still initializing
 * @property {string} extensionURL - the URL of the extension to be loaded by this worker
 * @property {Function} resolve - function to call on successful worker startup
 * @property {Function} reject - function to call on failed worker startup
 */

const createExtensionService = extensionManager => {
  const service = {};
  service.registerExtensionServiceSync = extensionManager.registerExtensionServiceSync.bind(extensionManager);
  service.allocateWorker = extensionManager.allocateWorker.bind(extensionManager);
  service.onWorkerInit = extensionManager.onWorkerInit.bind(extensionManager);
  service.registerExtensionService = extensionManager.registerExtensionService.bind(extensionManager);
  return service;
};

class ExtensionManager {
  constructor(vm) {
    /**
     * The ID number to provide to the next extension worker.
     * @type {int}
     */
    this.nextExtensionWorker = 0;
    /**
     * FIFO queue of extensions which have been requested but not yet loaded in a worker,
     * along with promise resolution functions to call once the worker is ready or failed.
     *
     * @type {Array.<PendingExtensionWorker>}
     */

    this.pendingExtensions = [];
    /**
     * Map of worker ID to workers which have been allocated but have not yet finished initialization.
     * @type {Array.<PendingExtensionWorker>}
     */

    this.pendingWorkers = [];
    /**
     * Map of worker ID to the URL where it was loaded from.
     * @type {Array<string>}
     */

    this.workerURLs = [];
    /**
     * Map of loaded extension URLs/IDs to service names.
     * @type {Map.<string, string>}
     * @private
     */

    this._loadedExtensions = new Map();
    /**
     * Responsible for determining security policies related to custom extensions.
     */

    this.securityManager = new SecurityManager();
    /**
     * @type {VirtualMachine}
     */

    this.vm = vm;
    /**
     * Keep a reference to the runtime so we can construct internal extension objects.
     * TODO: remove this in favor of extensions accessing the runtime as a service.
     * @type {Runtime}
     */

    this.runtime = vm.runtime;
    this.loadingAsyncExtensions = 0;
    this.asyncExtensionsLoadedCallbacks = [];
    dispatch.setService('extensions', createExtensionService(this)).catch(e => {
      log.error("ExtensionManager was unable to register extension service: ".concat(JSON.stringify(e)));
    });
  }
  /**
   * Check whether an extension is registered or is in the process of loading. This is intended to control loading or
   * adding extensions so it may return `true` before the extension is ready to be used. Use the promise returned by
   * `loadExtensionURL` if you need to wait until the extension is truly ready.
   * @param {string} extensionID - the ID of the extension.
   * @returns {boolean} - true if loaded, false otherwise.
   */


  isExtensionLoaded(extensionID) {
    return this._loadedExtensions.has(extensionID);
  }
  /**
   * Determine whether an extension with a given ID is built in to the VM, such as pen.
   * Note that "core extensions" like motion will return false here.
   * @param {string} extensionId
   * @returns {boolean}
   */


  isBuiltinExtension(extensionId) {
    return Object.prototype.hasOwnProperty.call(builtinExtensions, extensionId);
  }
  /**
   * Synchronously load an internal extension (core or non-core) by ID. This call will
   * fail if the provided id is not does not match an internal extension.
   * @param {string} extensionId - the ID of an internal extension
   */


  loadExtensionIdSync(extensionId) {
    if (!this.isBuiltinExtension(extensionId)) {
      log.warn("Could not find extension ".concat(extensionId, " in the built in extensions."));
      return;
    }
    /** @TODO dupe handling for non-builtin extensions. See commit 670e51d33580e8a2e852b3b038bb3afc282f81b9 */


    if (this.isExtensionLoaded(extensionId)) {
      const message = "Rejecting attempt to load a second extension with ID ".concat(extensionId);
      log.warn(message);
      return;
    }

    const extension = builtinExtensions[extensionId]();
    const extensionInstance = new extension(this.runtime);

    const serviceName = this._registerInternalExtension(extensionInstance);

    this._loadedExtensions.set(extensionId, serviceName);

    this.runtime.compilerRegisterExtension(extensionId, extensionInstance);
  }

  _isValidExtensionURL(extensionURL) {
    try {
      const parsedURL = new URL(extensionURL);
      return parsedURL.protocol === 'https:' || parsedURL.protocol === 'http:' || parsedURL.protocol === 'data:' || parsedURL.protocol === 'file:';
    } catch (e) {
      return false;
    }
  }
  /**
   * Load an extension by URL or internal extension ID
   * @param {string} extensionURL - the URL for the extension to load OR the ID of an internal extension
   * @returns {Promise} resolved once the extension is loaded and initialized or rejected on failure
   */


  async loadExtensionURL(extensionURL) {
    if (this.isBuiltinExtension(extensionURL)) {
      this.loadExtensionIdSync(extensionURL);
      return;
    }

    if (this.isExtensionURLLoaded(extensionURL)) {
      // Extension is already loaded.
      return;
    }

    if (!this._isValidExtensionURL(extensionURL)) {
      throw new Error("Invalid extension URL: ".concat(extensionURL));
    }

    this.runtime.setExternalCommunicationMethod('customExtensions', true);
    this.loadingAsyncExtensions++;
    const sandboxMode = await this.securityManager.getSandboxMode(extensionURL);

    if (sandboxMode === 'unsandboxed') {
      const {
        load
      } = __webpack_require__(/*! ./tw-unsandboxed-extension-runner */ "./node_modules/scratch-vm/src/extension-support/tw-unsandboxed-extension-runner.js");

      const extensionObjects = await load(extensionURL, this.vm).catch(error => this._failedLoadingExtensionScript(error));
      const fakeWorkerId = this.nextExtensionWorker++;
      this.workerURLs[fakeWorkerId] = extensionURL;

      for (const extensionObject of extensionObjects) {
        const extensionInfo = extensionObject.getInfo();
        const serviceName = "unsandboxed.".concat(fakeWorkerId, ".").concat(extensionInfo.id);
        dispatch.setServiceSync(serviceName, extensionObject);
        dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName);

        this._loadedExtensions.set(extensionInfo.id, serviceName);
      }

      this._finishedLoadingExtensionScript();

      return;
    }
    /* eslint-disable max-len */


    let ExtensionWorker;

    if (sandboxMode === 'worker') {
      ExtensionWorker = __webpack_require__(/*! worker-loader?name=js/extension-worker/extension-worker.[hash].js!./extension-worker */ "./src/build/inline-worker-loader/worker-loader.js?name=js/extension-worker/extension-worker.[hash].js!./node_modules/scratch-vm/src/extension-support/extension-worker.js");
    } else if (sandboxMode === 'iframe') {
      ExtensionWorker = (await __webpack_require__.e(/*! import() | iframe-extension-worker */ "vendors~iframe-extension-worker").then(__webpack_require__.t.bind(null, /*! ./tw-iframe-extension-worker */ "./node_modules/scratch-vm/src/extension-support/tw-iframe-extension-worker.js", 7))).default;
    } else {
      throw new Error("Invalid sandbox mode: ".concat(sandboxMode));
    }
    /* eslint-enable max-len */


    return new Promise((resolve, reject) => {
      this.pendingExtensions.push({
        extensionURL,
        resolve,
        reject
      });
      dispatch.addWorker(new ExtensionWorker());
    }).catch(error => this._failedLoadingExtensionScript(error));
  }
  /**
   * Wait until all async extensions have loaded
   * @returns {Promise} resolved when all async extensions have loaded
   */


  allAsyncExtensionsLoaded() {
    if (this.loadingAsyncExtensions === 0) {
      return;
    }

    return new Promise((resolve, reject) => {
      this.asyncExtensionsLoadedCallbacks.push({
        resolve,
        reject
      });
    });
  }
  /**
   * Regenerate blockinfo for any loaded extensions
   * @returns {Promise} resolved once all the extensions have been reinitialized
   */


  refreshBlocks() {
    const allPromises = Array.from(this._loadedExtensions.values()).map(serviceName => dispatch.call(serviceName, 'getInfo').then(info => {
      info = this._prepareExtensionInfo(serviceName, info);
      dispatch.call('runtime', '_refreshExtensionPrimitives', info);
    }).catch(e => {
      log.error("Failed to refresh built-in extension primitives: ".concat(JSON.stringify(e)));
    }));
    return Promise.all(allPromises);
  }

  allocateWorker() {
    const id = this.nextExtensionWorker++;
    const workerInfo = this.pendingExtensions.shift();
    this.pendingWorkers[id] = workerInfo;
    this.workerURLs[id] = workerInfo.extensionURL;
    return [id, workerInfo.extensionURL];
  }
  /**
   * Synchronously collect extension metadata from the specified service and begin the extension registration process.
   * @param {string} serviceName - the name of the service hosting the extension.
   */


  registerExtensionServiceSync(serviceName) {
    const info = dispatch.callSync(serviceName, 'getInfo');

    this._registerExtensionInfo(serviceName, info);
  }
  /**
   * Collect extension metadata from the specified service and begin the extension registration process.
   * @param {string} serviceName - the name of the service hosting the extension.
   */


  registerExtensionService(serviceName) {
    dispatch.call(serviceName, 'getInfo').then(info => {
      this._loadedExtensions.set(info.id, serviceName);

      this._registerExtensionInfo(serviceName, info);

      this._finishedLoadingExtensionScript();
    });
  }

  _finishedLoadingExtensionScript() {
    this.loadingAsyncExtensions--;

    if (this.loadingAsyncExtensions === 0) {
      this.asyncExtensionsLoadedCallbacks.forEach(i => i.resolve());
      this.asyncExtensionsLoadedCallbacks = [];
    }
  }

  _failedLoadingExtensionScript(error) {
    // Don't set the current extension counter to 0, otherwise it will go negative if another
    // extension finishes or fails to load.
    this.loadingAsyncExtensions--;
    this.asyncExtensionsLoadedCallbacks.forEach(i => i.reject(error));
    this.asyncExtensionsLoadedCallbacks = []; // Re-throw error so the promise still rejects.

    throw error;
  }
  /**
   * Called by an extension worker to indicate that the worker has finished initialization.
   * @param {int} id - the worker ID.
   * @param {*?} e - the error encountered during initialization, if any.
   */


  onWorkerInit(id, e) {
    const workerInfo = this.pendingWorkers[id];
    delete this.pendingWorkers[id];

    if (e) {
      workerInfo.reject(e);
    } else {
      workerInfo.resolve();
    }
  }
  /**
   * Register an internal (non-Worker) extension object
   * @param {object} extensionObject - the extension object to register
   * @returns {string} The name of the registered extension service
   */


  _registerInternalExtension(extensionObject) {
    const extensionInfo = extensionObject.getInfo();
    const fakeWorkerId = this.nextExtensionWorker++;
    const serviceName = "extension_".concat(fakeWorkerId, "_").concat(extensionInfo.id);
    dispatch.setServiceSync(serviceName, extensionObject);
    dispatch.callSync('extensions', 'registerExtensionServiceSync', serviceName);
    return serviceName;
  }
  /**
   * Sanitize extension info then register its primitives with the VM.
   * @param {string} serviceName - the name of the service hosting the extension
   * @param {ExtensionInfo} extensionInfo - the extension's metadata
   * @private
   */


  _registerExtensionInfo(serviceName, extensionInfo) {
    extensionInfo = this._prepareExtensionInfo(serviceName, extensionInfo);
    dispatch.call('runtime', '_registerExtensionPrimitives', extensionInfo).catch(e => {
      log.error("Failed to register primitives for extension on service ".concat(serviceName, ":"), e);
    });
  }
  /**
   * Modify the provided text as necessary to ensure that it may be used as an attribute value in valid XML.
   * @param {string} text - the text to be sanitized
   * @returns {string} - the sanitized text
   * @private
   */


  _sanitizeID(text) {
    return text.toString().replace(/[<"&]/, '_');
  }
  /**
   * Apply minor cleanup and defaults for optional extension fields.
   * TODO: make the ID unique in cases where two copies of the same extension are loaded.
   * @param {string} serviceName - the name of the service hosting this extension block
   * @param {ExtensionInfo} extensionInfo - the extension info to be sanitized
   * @returns {ExtensionInfo} - a new extension info object with cleaned-up values
   * @private
   */


  _prepareExtensionInfo(serviceName, extensionInfo) {
    extensionInfo = Object.assign({}, extensionInfo);

    if (!/^[a-z0-9]+$/i.test(extensionInfo.id)) {
      throw new Error('Invalid extension id');
    }

    extensionInfo.name = extensionInfo.name || extensionInfo.id;
    extensionInfo.blocks = extensionInfo.blocks || [];
    extensionInfo.targetTypes = extensionInfo.targetTypes || [];
    extensionInfo.blocks = extensionInfo.blocks.reduce((results, blockInfo) => {
      try {
        let result;

        switch (blockInfo) {
          case '---':
            // separator
            result = '---';
            break;

          default:
            // an ExtensionBlockMetadata object
            result = this._prepareBlockInfo(serviceName, blockInfo);
            break;
        }

        results.push(result);
      } catch (e) {
        // TODO: more meaningful error reporting
        log.error("Error processing block: ".concat(e.message, ", Block:\n").concat(JSON.stringify(blockInfo)));
      }

      return results;
    }, []);
    extensionInfo.menus = extensionInfo.menus || {};
    extensionInfo.menus = this._prepareMenuInfo(serviceName, extensionInfo.menus);
    return extensionInfo;
  }
  /**
   * Prepare extension menus. e.g. setup binding for dynamic menu functions.
   * @param {string} serviceName - the name of the service hosting this extension block
   * @param {Array.<MenuInfo>} menus - the menu defined by the extension.
   * @returns {Array.<MenuInfo>} - a menuInfo object with all preprocessing done.
   * @private
   */


  _prepareMenuInfo(serviceName, menus) {
    const menuNames = Object.getOwnPropertyNames(menus);

    for (let i = 0; i < menuNames.length; i++) {
      const menuName = menuNames[i];
      let menuInfo = menus[menuName]; // If the menu description is in short form (items only) then normalize it to general form: an object with
      // its items listed in an `items` property.

      if (!menuInfo.items) {
        menuInfo = {
          items: menuInfo
        };
        menus[menuName] = menuInfo;
      } // If `items` is a string, it should be the name of a function in the extension object. Calling the
      // function should return an array of items to populate the menu when it is opened.


      if (typeof menuInfo.items === 'string') {
        const menuItemFunctionName = menuInfo.items;
        const serviceObject = dispatch.services[serviceName]; // Bind the function here so we can pass a simple item generation function to Scratch Blocks later.

        menuInfo.items = this._getExtensionMenuItems.bind(this, serviceObject, menuItemFunctionName);
      }
    }

    return menus;
  }
  /**
   * Fetch the items for a particular extension menu, providing the target ID for context.
   * @param {object} extensionObject - the extension object providing the menu.
   * @param {string} menuItemFunctionName - the name of the menu function to call.
   * @returns {Array} menu items ready for scratch-blocks.
   * @private
   */


  _getExtensionMenuItems(extensionObject, menuItemFunctionName) {
    // Fetch the items appropriate for the target currently being edited. This assumes that menus only
    // collect items when opened by the user while editing a particular target.
    const editingTarget = this.runtime.getEditingTarget() || this.runtime.getTargetForStage();
    const editingTargetID = editingTarget ? editingTarget.id : null;
    const extensionMessageContext = this.runtime.makeMessageContextForTarget(editingTarget); // TODO: Fix this to use dispatch.call when extensions are running in workers.

    const menuFunc = extensionObject[menuItemFunctionName];
    const menuItems = menuFunc.call(extensionObject, editingTargetID).map(item => {
      item = maybeFormatMessage(item, extensionMessageContext);

      switch (typeof item) {
        case 'object':
          return [maybeFormatMessage(item.text, extensionMessageContext), item.value];

        case 'string':
          return [item, item];

        default:
          return item;
      }
    });

    if (!menuItems || menuItems.length < 1) {
      throw new Error("Extension menu returned no items: ".concat(menuItemFunctionName));
    }

    return menuItems;
  }
  /**
   * Apply defaults for optional block fields.
   * @param {string} serviceName - the name of the service hosting this extension block
   * @param {ExtensionBlockMetadata} blockInfo - the block info from the extension
   * @returns {ExtensionBlockMetadata} - a new block info object which has values for all relevant optional fields.
   * @private
   */


  _prepareBlockInfo(serviceName, blockInfo) {
    blockInfo = Object.assign({}, {
      blockType: BlockType.COMMAND,
      terminal: false,
      blockAllThreads: false,
      arguments: {}
    }, blockInfo);
    blockInfo.opcode = blockInfo.opcode && this._sanitizeID(blockInfo.opcode);
    blockInfo.text = blockInfo.text || blockInfo.opcode;

    switch (blockInfo.blockType) {
      case BlockType.EVENT:
        if (blockInfo.func) {
          log.warn("Ignoring function \"".concat(blockInfo.func, "\" for event block ").concat(blockInfo.opcode));
        }

        break;

      case BlockType.BUTTON:
        if (blockInfo.opcode) {
          log.warn("Ignoring opcode \"".concat(blockInfo.opcode, "\" for button with text: ").concat(blockInfo.text));
        }

        break;

      default:
        {
          if (!blockInfo.opcode) {
            throw new Error('Missing opcode for block');
          }

          const funcName = blockInfo.func ? this._sanitizeID(blockInfo.func) : blockInfo.opcode;
          const getBlockInfo = blockInfo.isDynamic ? args => args && args.mutation && args.mutation.blockInfo : () => blockInfo;

          const callBlockFunc = (() => {
            if (dispatch._isRemoteService(serviceName)) {
              return (args, util, realBlockInfo) => dispatch.call(serviceName, funcName, args, util, realBlockInfo).then(result => {
                // Scratch is only designed to handle these types.
                // If any other value comes in such as undefined, null, an object, etc.
                // we'll convert it to a string to avoid undefined behavior.
                if (typeof result === 'number' || typeof result === 'string' || typeof result === 'boolean') {
                  return result;
                }

                return "".concat(result);
              }) // When an error happens, instead of returning undefined, we'll return a stringified
              // version of the error so that it can be debugged.
              .catch(err => {
                // We want the full error including stack to be printed but the log helper
                // messes with that.
                // eslint-disable-next-line no-console
                console.error('Custom extension block error', err);
                return "".concat(err);
              });
            } // avoid promise latency if we can call direct


            const serviceObject = dispatch.services[serviceName];

            if (!serviceObject[funcName]) {
              // The function might show up later as a dynamic property of the service object
              log.warn("Could not find extension block function called ".concat(funcName));
            }

            return (args, util, realBlockInfo) => serviceObject[funcName](args, util, realBlockInfo);
          })();

          blockInfo.func = (args, util) => {
            const realBlockInfo = getBlockInfo(args); // TODO: filter args using the keys of realBlockInfo.arguments? maybe only if sandboxed?

            return callBlockFunc(args, util, realBlockInfo);
          };

          break;
        }
    }

    return blockInfo;
  }

  getExtensionURLs() {
    const extensionURLs = {};

    for (const [extensionId, serviceName] of this._loadedExtensions.entries()) {
      if (builtinExtensions.hasOwnProperty(extensionId)) {
        continue;
      } // Service names for extension workers are in the format "extension.WORKER_ID.EXTENSION_ID"


      const workerId = +serviceName.split('.')[1];
      const extensionURL = this.workerURLs[workerId];

      if (typeof extensionURL === 'string') {
        extensionURLs[extensionId] = extensionURL;
      }
    }

    return extensionURLs;
  }

  isExtensionURLLoaded(url) {
    return Object.values(this.workerURLs).includes(url);
  }

}

module.exports = ExtensionManager;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/target-type.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/target-type.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Default types of Target supported by the VM
 * @enum {string}
 */
const TargetType = {
  /**
   * Rendered target which can move, change costumes, etc.
   */
  SPRITE: 'sprite',

  /**
   * Rendered target which cannot move but can change backdrops
   */
  STAGE: 'stage'
};
module.exports = TargetType;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js":
/*!**********************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js ***!
  \**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ./argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ./block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const TargetType = __webpack_require__(/*! ./target-type */ "./node_modules/scratch-vm/src/extension-support/target-type.js");

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Scratch = {
  ArgumentType,
  BlockType,
  TargetType,
  Cast
};
module.exports = Scratch;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js":
/*!******************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js ***!
  \******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// ScratchX API Documentation: https://github.com/LLK/scratchx/wiki/
// Global Scratch API from extension-worker.js

/* globals Scratch */
const ArgumentType = __webpack_require__(/*! ./argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ./block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const {
  argumentIndexToId,
  generateExtensionId
} = __webpack_require__(/*! ./tw-scratchx-utilities */ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js");
/**
 * @typedef ScratchXDescriptor
 * @property {unknown[][]} blocks
 * @property {Record<string, unknown[]>} [menus]
 * @property {string} [url]
 * @property {string} [displayName]
 */

/**
 * @typedef ScratchXStatus
 * @property {0|1|2} status 0 is red/error, 1 is yellow/not ready, 2 is green/ready
 * @property {string} msg
 */


const parseScratchXBlockType = type => {
  if (type === '' || type === ' ' || type === 'w') {
    return {
      type: BlockType.COMMAND,
      async: type === 'w'
    };
  }

  if (type === 'r' || type === 'R') {
    return {
      type: BlockType.REPORTER,
      async: type === 'R'
    };
  }

  if (type === 'b') {
    return {
      type: BlockType.BOOLEAN,
      // ScratchX docs don't seem to mention boolean reporters that wait
      async: false
    };
  }

  if (type === 'h') {
    return {
      type: BlockType.HAT,
      async: false
    };
  }

  throw new Error("Unknown ScratchX block type: ".concat(type));
};

const isScratchCompatibleValue = v => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';
/**
 * @param {string} argument ScratchX argument with leading % removed.
 * @param {unknown} defaultValue Default value, if any
 */


const parseScratchXArgument = (argument, defaultValue) => {
  const result = {};
  const hasDefaultValue = isScratchCompatibleValue(defaultValue);

  if (hasDefaultValue) {
    result.defaultValue = defaultValue;
  } // TODO: ScratchX docs don't mention support for boolean arguments?


  if (argument === 's') {
    result.type = ArgumentType.STRING;

    if (!hasDefaultValue) {
      result.defaultValue = '';
    }
  } else if (argument === 'n') {
    result.type = ArgumentType.NUMBER;

    if (!hasDefaultValue) {
      result.defaultValue = 0;
    }
  } else if (argument[0] === 'm') {
    result.type = ArgumentType.STRING;
    const split = argument.split(/\.|:/);
    const menuName = split[1];
    result.menu = menuName;
  } else {
    throw new Error("Unknown ScratchX argument type: ".concat(argument));
  }

  return result;
};

const wrapScratchXFunction = (originalFunction, argumentCount, async) => args => {
  // Convert Scratch 3's argument object to an argument list expected by ScratchX
  const argumentList = [];

  for (let i = 0; i < argumentCount; i++) {
    argumentList.push(args[argumentIndexToId(i)]);
  }

  if (async) {
    return new Promise(resolve => {
      originalFunction(...argumentList, resolve);
    });
  }

  return originalFunction(...argumentList);
};
/**
 * @param {string} name
 * @param {ScratchXDescriptor} descriptor
 * @param {Record<string, () => unknown>} functions
 */


const convert = (name, descriptor, functions) => {
  const extensionId = generateExtensionId(name);
  const info = {
    id: extensionId,
    name: descriptor.displayName || name,
    blocks: [],
    color1: '#4a4a5e',
    color2: '#31323f',
    color3: '#191a21'
  };
  const scratch3Extension = {
    getInfo: () => info,
    _getStatus: functions._getStatus
  };

  if (descriptor.url) {
    info.docsURI = descriptor.url;
  }

  for (const blockDescriptor of descriptor.blocks) {
    if (blockDescriptor.length === 1) {
      // Separator
      info.blocks.push('---');
      continue;
    }

    const scratchXBlockType = blockDescriptor[0];
    const blockText = blockDescriptor[1];
    const functionName = blockDescriptor[2];
    const defaultArgumentValues = blockDescriptor.slice(3);
    let scratchText = '';
    const argumentInfo = [];
    const blockTextParts = blockText.split(/%([\w.:]+)/g);

    for (let i = 0; i < blockTextParts.length; i++) {
      const part = blockTextParts[i];
      const isArgument = i % 2 === 1;

      if (isArgument) {
        parseScratchXArgument(part);
        const argumentIndex = Math.floor(i / 2).toString();
        const argumentDefaultValue = defaultArgumentValues[argumentIndex];
        const argumentId = argumentIndexToId(argumentIndex);
        argumentInfo[argumentId] = parseScratchXArgument(part, argumentDefaultValue);
        scratchText += "[".concat(argumentId, "]");
      } else {
        scratchText += part;
      }
    }

    const scratch3BlockType = parseScratchXBlockType(scratchXBlockType);
    const blockInfo = {
      opcode: functionName,
      blockType: scratch3BlockType.type,
      text: scratchText,
      arguments: argumentInfo
    };
    info.blocks.push(blockInfo);
    const originalFunction = functions[functionName];
    const argumentCount = argumentInfo.length;
    scratch3Extension[functionName] = wrapScratchXFunction(originalFunction, argumentCount, scratch3BlockType.async);
  }

  const menus = descriptor.menus;

  if (menus) {
    const scratch3Menus = {};

    for (const menuName of Object.keys(menus) || {}) {
      const menuItems = menus[menuName];
      const menuInfo = {
        items: menuItems
      };
      scratch3Menus[menuName] = menuInfo;
    }

    info.menus = scratch3Menus;
  }

  return scratch3Extension;
};

const extensionNameToExtension = new Map();

const register = (name, descriptor, functions) => {
  const scratch3Extension = convert(name, descriptor, functions);
  extensionNameToExtension.set(name, scratch3Extension);
  Scratch.extensions.register(scratch3Extension);
};
/**
 * @param {string} extensionName
 * @returns {ScratchXStatus}
 */


const getStatus = extensionName => {
  const extension = extensionNameToExtension.get(extensionName);

  if (extension) {
    return extension._getStatus();
  }

  return {
    status: 0,
    msg: 'does not exist'
  };
};

module.exports = {
  register,
  getStatus,
  // For tests
  convert
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js":
/*!********************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js ***!
  \********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview
 * General ScratchX-related utilities used in multiple places.
 * Changing these functions may break projects.
 */

/**
 * @param {string} scratchXName
 * @returns {string}
 */
const generateExtensionId = scratchXName => {
  const sanitizedName = scratchXName.replace(/[^a-z0-9]/gi, '').toLowerCase();
  return "sbx".concat(sanitizedName);
};
/**
 * @param {number} i 0-indexed index of argument in list
 * @returns {string} Scratch 3 argument name
 */


const argumentIndexToId = i => i.toString();

module.exports = {
  generateExtensionId,
  argumentIndexToId
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/tw-security-manager.js":
/*!******************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/tw-security-manager.js ***!
  \******************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* eslint-disable no-unused-vars */

/**
 * Responsible for determining various policies related to custom extension security.
 * The default implementation attempts to retain compatibility with a vanilla scratch-vm
 * and ensure maximum security. You can manually opt-in to less security by overriding
 * methods. For example:
 * ```js
 * vm.securityManager.getSandboxMode = (url) => {
 *   if (url.startsWith("https://example.com/")) {
 *     return "unsandboxed";
 *   }
 *   return "iframe";
 * };
 * vm.securityManager.canAutomaticallyLoadExtension = (url) => {
 *   return confirm("Automatically load extension: " + url);
 * };
 * ```
 */
class SecurityManager {
  /**
   * Determine the typeof sandbox to use for a certain custom extension.
   * @param {string} extensionURL The URL of the custom extension.
   * @returns {Promise<'worker'|'iframe'|'unsandboxed'>}
   */
  getSandboxMode(extensionURL) {
    // Default to worker for Scratch compatibility
    return Promise.resolve('worker');
  }
  /**
   * Determine whether a custom extension that was stored inside a project may be
   * loaded. You could, for example, ask the user to confirm loading an extension
   * before resolving.
   * @param {string} extensionURL The URL of the custom extension.
   * @returns {Promise<boolean>}
   */


  canLoadExtensionFromProject(extensionURL) {
    // Default to false for security
    return Promise.resolve(false);
  }

}

module.exports = SecurityManager;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extension-support/tw-unsandboxed-extension-runner.js":
/*!******************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extension-support/tw-unsandboxed-extension-runner.js ***!
  \******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {const ScratchCommon = __webpack_require__(/*! ./tw-extension-api-common */ "./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js");

const AsyncLimiter = __webpack_require__(/*! ../util/async-limiter */ "./node_modules/scratch-vm/src/util/async-limiter.js");
/**
 * Sets up the global.Scratch API for an unsandboxed extension.
 * @param {VirtualMachine} vm
 * @returns {Promise<object[]>} Resolves with a list of extension objects when Scratch.extensions.register is called.
 */


const createUnsandboxedExtensionAPI = vm => new Promise(resolve => {
  const extensionObjects = [];

  const register = extensionObject => {
    extensionObjects.push(extensionObject);
    resolve(extensionObjects);
  }; // Create a new copy of global.Scratch for each extension


  global.Scratch = Object.assign({}, global.Scratch || {}, ScratchCommon);
  global.Scratch.vm = vm;
  global.Scratch.renderer = vm.runtime.renderer;
  global.Scratch.extensions = {
    unsandboxed: true,
    register
  };
  global.ScratchExtensions = __webpack_require__(/*! ./tw-scratchx-compatibility-layer */ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js");
});
/**
 * Disable the existing global.Scratch unsandboxed extension APIs.
 * This helps debug poorly designed extensions.
 */


const teardownUnsandboxedExtensionAPI = () => {
  // We can assume global.Scratch already exists.
  global.Scratch.extensions.register = () => {
    throw new Error('Too late to register new extensions.');
  };
};
/**
 * Load an unsandboxed extension from an arbitrary URL. This is dangerous.
 * @param {string} extensionURL
 * @param {Virtualmachine} vm
 * @returns {Promise<object[]>} Resolves with a list of extension objects if the extension was loaded successfully.
 */


const loadUnsandboxedExtension = (extensionURL, vm) => new Promise((resolve, reject) => {
  createUnsandboxedExtensionAPI(vm).then(resolve);
  const script = document.createElement('script');

  script.onerror = () => {
    reject(new Error("Error in unsandboxed script ".concat(extensionURL, ". Check the console for more information.")));
  };

  script.src = extensionURL;
  document.body.appendChild(script);
}).then(objects => {
  teardownUnsandboxedExtensionAPI();
  return objects;
}); // Because loading unsandboxed extensions requires messing with global state (global.Scratch),
// only let one extension load at a time.


const limiter = new AsyncLimiter(loadUnsandboxedExtension, 1);

const load = (extensionURL, vm) => limiter.do(extensionURL, vm);

module.exports = {
  createUnsandboxedExtensionAPI,
  load
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_boost/index.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_boost/index.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const color = __webpack_require__(/*! ../../util/color */ "./node_modules/scratch-vm/src/util/color.js");

const BLE = __webpack_require__(/*! ../../io/ble */ "./node_modules/scratch-vm/src/io/ble.js");

const Base64Util = __webpack_require__(/*! ../../util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const RateLimiter = __webpack_require__(/*! ../../util/rateLimiter.js */ "./node_modules/scratch-vm/src/util/rateLimiter.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * The LEGO Wireless Protocol documentation used to create this extension can be found at:
 * https://lego.github.io/lego-ble-wireless-protocol-docs/index.html
 */

/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const iconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAMAAAC5zwKfAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACpQTFRF////fIel5ufolZ62/2YavsPS+YZOkJmy9/j53+Hk6+zs6N/b6dfO////tDhMHAAAAA50Uk5T/////////////////wBFwNzIAAAA6ElEQVR42uzX2w6DIBAEUGDVtlr//3dLaLwgiwUd2z7MJPJg5EQWiGhGcAxBggQJEiT436CIfqXJPTn3MKNYYMSDFpoAmp24OaYgvwKnFgL2zvVTCwHrMoMi+nUQLFthaNCCa0iwclLkDgYVsQp0mzxuqXgK1MRzoCLWgkPXNN2wI/q6Kvt7u/cX0HtejN8x2sXpnpb8J8D3b0Keuhh3X975M+i0xNVbg3s1TIasgK21bQyGO+s2PykaGMYbge8KrNrssvkOWDXkErB8UuBHETjoYLkKBA8ZfuDkbwVBggQJEiR4MC8BBgDTtMZLx2nFCQAAAABJRU5ErkJggg==';
/**
 * Boost BLE UUIDs.
 * @enum {string}
 */

const BoostBLE = {
  service: '00001623-1212-efde-1623-785feabcd123',
  characteristic: '00001624-1212-efde-1623-785feabcd123',
  sendInterval: 100,
  sendRateMax: 20
};
/**
 * Boost Motor Max Power Add. Defines how much more power than the target speed
 * the motors may supply to reach the target speed faster.
 * Lower number == softer, slower reached target speed.
 * Higher number == harder, faster reached target speed.
 * @constant {number}
 */

const BoostMotorMaxPowerAdd = 10;
/**
 * A time interval to wait (in milliseconds) in between battery check calls.
 * @type {number}
 */

const BoostPingInterval = 5000;
/**
 * The number of continuous samples the color-sensor will evaluate color from.
 * @type {number}
 */

const BoostColorSampleSize = 5;
/**
 * Enum for Boost sensor and actuator types.
 * @readonly
 * @enum {number}
 */

const BoostIO = {
  MOTOR_WEDO: 0x01,
  MOTOR_SYSTEM: 0x02,
  BUTTON: 0x05,
  LIGHT: 0x08,
  VOLTAGE: 0x14,
  CURRENT: 0x15,
  PIEZO: 0x16,
  LED: 0x17,
  TILT_EXTERNAL: 0x22,
  MOTION_SENSOR: 0x23,
  COLOR: 0x25,
  MOTOREXT: 0x26,
  MOTORINT: 0x27,
  TILT: 0x28
};
/**
 * Enum for ids for various output command feedback types on the Boost.
 * @readonly
 * @enum {number}
 */

const BoostPortFeedback = {
  IN_PROGRESS: 0x01,
  COMPLETED: 0x02,
  DISCARDED: 0x04,
  IDLE: 0x08,
  BUSY_OR_FULL: 0x10
};
/**
 * Enum for physical Boost Ports
 * @readonly
 * @enum {number}
 */

const BoostPort10000223OrOlder = {
  A: 55,
  B: 56,
  C: 1,
  D: 2
};
const BoostPort10000224OrNewer = {
  A: 0,
  B: 1,
  C: 2,
  D: 3
}; // Set default port mapping to support the newer firmware

let BoostPort = BoostPort10000224OrNewer;
/**
 * Ids for each color sensor value used by the extension.
 * @readonly
 * @enum {string}
 */

const BoostColor = {
  ANY: 'any',
  NONE: 'none',
  RED: 'red',
  BLUE: 'blue',
  GREEN: 'green',
  YELLOW: 'yellow',
  WHITE: 'white',
  BLACK: 'black'
};
/**
 * Enum for indices for each color sensed by the Boost vision sensor.
 * @readonly
 * @enum {number}
 */

const BoostColorIndex = {
  [BoostColor.NONE]: 255,
  [BoostColor.RED]: 9,
  [BoostColor.BLUE]: 3,
  [BoostColor.GREEN]: 5,
  [BoostColor.YELLOW]: 7,
  [BoostColor.WHITE]: 10,
  [BoostColor.BLACK]: 0
};
/**
 * Enum for Message Types
 * @readonly
 * @enum {number}
 */

const BoostMessage = {
  HUB_PROPERTIES: 0x01,
  HUB_ACTIONS: 0x02,
  HUB_ALERTS: 0x03,
  HUB_ATTACHED_IO: 0x04,
  ERROR: 0x05,
  PORT_INPUT_FORMAT_SETUP_SINGLE: 0x41,
  PORT_INPUT_FORMAT_SETUP_COMBINED: 0x42,
  PORT_INFORMATION: 0x43,
  PORT_MODEINFORMATION: 0x44,
  PORT_VALUE: 0x45,
  PORT_VALUE_COMBINED: 0x46,
  PORT_INPUT_FORMAT: 0x47,
  PORT_INPUT_FORMAT_COMBINED: 0x48,
  OUTPUT: 0x81,
  PORT_FEEDBACK: 0x82
};
/**
 * Enum for Hub Property Types
 * @readonly
 * @enum {number}
 */

const BoostHubProperty = {
  ADVERTISEMENT_NAME: 0x01,
  BUTTON: 0x02,
  FW_VERSION: 0x03,
  HW_VERSION: 0x04,
  RSSI: 0x05,
  BATTERY_VOLTAGE: 0x06,
  BATTERY_TYPE: 0x07,
  MANUFACTURER_NAME: 0x08,
  RADIO_FW_VERSION: 0x09,
  LEGO_WP_VERSION: 0x0A,
  SYSTEM_TYPE_ID: 0x0B,
  HW_NETWORK_ID: 0x0C,
  PRIMARY_MAC: 0x0D,
  SECONDARY_MAC: 0x0E,
  HW_NETWORK_FAMILY: 0x0F
};
/**
 * Enum for Hub Property Operations
 * @readonly
 * @enum {number}
 */

const BoostHubPropertyOperation = {
  SET: 0x01,
  ENABLE_UPDATES: 0x02,
  DISABLE_UPDATES: 0x03,
  RESET: 0x04,
  REQUEST_UPDATE: 0x05,
  UPDATE: 0x06
};
/**
 * Enum for Motor Subcommands (for 0x81)
 * @readonly
 * @enum {number}
 */

const BoostOutputSubCommand = {
  START_POWER: 0x01,
  START_POWER_PAIR: 0x02,
  SET_ACC_TIME: 0x05,
  SET_DEC_TIME: 0x06,
  START_SPEED: 0x07,
  START_SPEED_PAIR: 0x08,
  START_SPEED_FOR_TIME: 0x09,
  START_SPEED_FOR_TIME_PAIR: 0x0A,
  START_SPEED_FOR_DEGREES: 0x0B,
  START_SPEED_FOR_DEGREES_PAIR: 0x0C,
  GO_TO_ABS_POSITION: 0x0D,
  GO_TO_ABS_POSITION_PAIR: 0x0E,
  PRESET_ENCODER: 0x14,
  WRITE_DIRECT_MODE_DATA: 0x51
};
/**
 * Enum for Startup/Completion information for an output command.
 * Startup and completion bytes must be OR'ed to be combined to a single byte.
 * @readonly
 * @enum {number}
 */

const BoostOutputExecution = {
  // Startup information
  BUFFER_IF_NECESSARY: 0x00,
  EXECUTE_IMMEDIATELY: 0x10,
  // Completion information
  NO_ACTION: 0x00,
  COMMAND_FEEDBACK: 0x01
};
/**
 * Enum for Boost Motor end states
 * @readonly
 * @enum {number}
 */

const BoostMotorEndState = {
  FLOAT: 0,
  HOLD: 126,
  BRAKE: 127
};
/**
 * Enum for Boost Motor acceleration/deceleration profiles
 * @readyonly
 * @enum {number}
 */

const BoostMotorProfile = {
  DO_NOT_USE: 0x00,
  ACCELERATION: 0x01,
  DECELERATION: 0x02
};
/**
 * Enum for when Boost IO's are attached/detached
 * @readonly
 * @enum {number}
 */

const BoostIOEvent = {
  ATTACHED: 0x01,
  DETACHED: 0x00,
  ATTACHED_VIRTUAL: 0x02
};
/**
 * Enum for selected sensor modes.
 * @enum {number}
 */

const BoostMode = {
  TILT: 0,
  // angle (pitch/yaw)
  LED: 1,
  // Set LED to accept RGB values
  COLOR: 0,
  // Read indexed colors from Vision Sensor
  MOTOR_SENSOR: 2,
  // Set motors to report their position
  UNKNOWN: 0 // Anything else will use the default mode (mode 0)

};
/**
 * Enum for Boost motor states.
 * @param {number}
 */

const BoostMotorState = {
  OFF: 0,
  ON_FOREVER: 1,
  ON_FOR_TIME: 2,
  ON_FOR_ROTATION: 3
};
/**
 * Helper function for converting a JavaScript number to an INT32-number
 * @param {number} number - a number
 * @return {array} - a 4-byte array of Int8-values representing an INT32-number
 */

const numberToInt32Array = function numberToInt32Array(number) {
  const buffer = new ArrayBuffer(4);
  const dataview = new DataView(buffer);
  dataview.setInt32(0, number);
  return [dataview.getInt8(3), dataview.getInt8(2), dataview.getInt8(1), dataview.getInt8(0)];
};
/**
 * Helper function for converting a regular array to a Little Endian INT32-value
 * @param {Array} array - an array containing UInt8-values
 * @return {number} - a number
 */


const int32ArrayToNumber = function int32ArrayToNumber(array) {
  const i = Uint8Array.from(array);
  const d = new DataView(i.buffer);
  return d.getInt32(0, true);
};
/**
 * Manage power, direction, position, and timers for one Boost motor.
 */


class BoostMotor {
  /**
   * Construct a Boost Motor instance.
   * @param {Boost} parent - the Boost peripheral which owns this motor.
   * @param {int} index - the zero-based index of this motor on its parent peripheral.
   */
  constructor(parent, index) {
    /**
     * The Boost peripheral which owns this motor.
     * @type {Boost}
     * @private
     */
    this._parent = parent;
    /**
     * The zero-based index of this motor on its parent peripheral.
     * @type {int}
     * @private
     */

    this._index = index;
    /**
     * This motor's current direction: 1 for "this way" or -1 for "that way"
     * @type {number}
     * @private
     */

    this._direction = 1;
    /**
     * This motor's current power level, in the range [0,100].
     * @type {number}
     * @private
     */

    this._power = 50;
    /**
     * This motor's current relative position
     * @type {number}
     * @private
     */

    this._position = 0;
    /**
     * Is this motor currently moving?
     * @type {boolean}
     * @private
     */

    this._status = BoostMotorState.OFF;
    /**
     * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
     * the end-of-action handler. Cancel this when changing plans.
     * @type {Object}
     * @private
     */

    this._pendingDurationTimeoutId = null;
    /**
     * The starting time for the pending duration timeout.
     * @type {number}
     * @private
     */

    this._pendingDurationTimeoutStartTime = null;
    /**
     * The delay/duration of the pending duration timeout.
     * @type {number}
     * @private
     */

    this._pendingDurationTimeoutDelay = null;
    /**
     * The target position of a turn-based command.
     * @type {number}
     * @private
     */

    this._pendingRotationDestination = null;
    /**
     * If the motor has been turned on run for a specific rotation, this is the function
     * that will be called once Scratch VM gets a notification from the Move Hub.
     * @type {Object}
     * @private
     */

    this._pendingRotationPromise = null;
    this.turnOff = this.turnOff.bind(this);
  }
  /**
   * @return {int} - this motor's current direction: 1 for "this way" or -1 for "that way"
   */


  get direction() {
    return this._direction;
  }
  /**
   * @param {int} value - this motor's new direction: 1 for "this way" or -1 for "that way"
   */


  set direction(value) {
    if (value < 0) {
      this._direction = -1;
    } else {
      this._direction = 1;
    }
  }
  /**
   * @return {int} - this motor's current power level, in the range [0,100].
   */


  get power() {
    return this._power;
  }
  /**
   * @param {int} value - this motor's new power level, in the range [10,100].
   */


  set power(value) {
    /**
     * Scale the motor power to a range between 10 and 100,
     * to make sure the motors will run with something built onto them.
     */
    if (value === 0) {
      this._power = 0;
    } else {
      this._power = MathUtil.scale(value, 1, 100, 10, 100);
    }
  }
  /**
   * @return {int} - this motor's current position, in the range of [-MIN_INT32,MAX_INT32]
   */


  get position() {
    return this._position;
  }
  /**
   * @param {int} value - set this motor's current position.
   */


  set position(value) {
    this._position = value;
  }
  /**
   * @return {BoostMotorState} - the motor's current state.
   */


  get status() {
    return this._status;
  }
  /**
   * @param {BoostMotorState} value - set this motor's state.
   */


  set status(value) {
    this._clearRotationState();

    this._clearDurationTimeout();

    this._status = value;
  }
  /**
   * @return {number} - time, in milliseconds, of when the pending duration timeout began.
   */


  get pendingDurationTimeoutStartTime() {
    return this._pendingDurationTimeoutStartTime;
  }
  /**
   * @return {number} - delay, in milliseconds, of the pending duration timeout.
   */


  get pendingDurationTimeoutDelay() {
    return this._pendingDurationTimeoutDelay;
  }
  /**
   * @return {number} - target position, in degrees, of the pending rotation.
   */


  get pendingRotationDestination() {
    return this._pendingRotationDestination;
  }
  /**
   * @return {Promise} - the Promise function for the pending rotation.
   */


  get pendingRotationPromise() {
    return this._pendingRotationPromise;
  }
  /**
   * @param {function} func - function to resolve pending rotation Promise
   */


  set pendingRotationPromise(func) {
    this._pendingRotationPromise = func;
  }
  /**
   * Turn this motor on indefinitely
   * @private
   */


  _turnOn() {
    const cmd = this._parent.generateOutputCommand(this._index, BoostOutputExecution.EXECUTE_IMMEDIATELY, BoostOutputSubCommand.START_SPEED, [this.power * this.direction, MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100), BoostMotorProfile.DO_NOT_USE]);

    this._parent.send(BoostBLE.characteristic, cmd);
  }
  /**
   * Turn this motor on indefinitely
   */


  turnOnForever() {
    this.status = BoostMotorState.ON_FOREVER;

    this._turnOn();
  }
  /**
   * Turn this motor on for a specific duration.
   * @param {number} milliseconds - run the motor for this long.
   */


  turnOnFor(milliseconds) {
    milliseconds = Math.max(0, milliseconds);
    this.status = BoostMotorState.ON_FOR_TIME;

    this._turnOn();

    this._setNewDurationTimeout(this.turnOff, milliseconds);
  }
  /**
   * Turn this motor on for a specific rotation in degrees.
   * @param {number} degrees - run the motor for this amount of degrees.
   * @param {number} direction - rotate in this direction
   */


  turnOnForDegrees(degrees, direction) {
    degrees = Math.max(0, degrees);

    const cmd = this._parent.generateOutputCommand(this._index, BoostOutputExecution.EXECUTE_IMMEDIATELY ^ BoostOutputExecution.COMMAND_FEEDBACK, BoostOutputSubCommand.START_SPEED_FOR_DEGREES, [...numberToInt32Array(degrees), this.power * this.direction * direction, MathUtil.clamp(this.power + BoostMotorMaxPowerAdd, 0, 100), BoostMotorEndState.BRAKE, BoostMotorProfile.DO_NOT_USE]);

    this.status = BoostMotorState.ON_FOR_ROTATION;
    this._pendingRotationDestination = this.position + degrees * this.direction * direction;

    this._parent.send(BoostBLE.characteristic, cmd);
  }
  /**
   * Turn this motor off.
   * @param {boolean} [useLimiter=true] - if true, use the rate limiter
   */


  turnOff() {
    let useLimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;

    const cmd = this._parent.generateOutputCommand(this._index, BoostOutputExecution.EXECUTE_IMMEDIATELY, BoostOutputSubCommand.START_POWER, [BoostMotorEndState.FLOAT]);

    this.status = BoostMotorState.OFF;

    this._parent.send(BoostBLE.characteristic, cmd, useLimiter);
  }
  /**
   * Clear the motor action timeout, if any. Safe to call even when there is no pending timeout.
   * @private
   */


  _clearDurationTimeout() {
    if (this._pendingDurationTimeoutId !== null) {
      clearTimeout(this._pendingDurationTimeoutId);
      this._pendingDurationTimeoutId = null;
      this._pendingDurationTimeoutStartTime = null;
      this._pendingDurationTimeoutDelay = null;
    }
  }
  /**
   * Set a new motor action timeout, after clearing an existing one if necessary.
   * @param {Function} callback - to be called at the end of the timeout.
   * @param {int} delay - wait this many milliseconds before calling the callback.
   * @private
   */


  _setNewDurationTimeout(callback, delay) {
    this._clearDurationTimeout();

    const timeoutID = setTimeout(() => {
      if (this._pendingDurationTimeoutId === timeoutID) {
        this._pendingDurationTimeoutId = null;
        this._pendingDurationTimeoutStartTime = null;
        this._pendingDurationTimeoutDelay = null;
      }

      callback();
    }, delay);
    this._pendingDurationTimeoutId = timeoutID;
    this._pendingDurationTimeoutStartTime = Date.now();
    this._pendingDurationTimeoutDelay = delay;
  }
  /**
   * Clear the motor states related to rotation-based commands, if any.
   * Safe to call even when there is no pending promise function.
   * @private
   */


  _clearRotationState() {
    if (this._pendingRotationPromise !== null) {
      this._pendingRotationPromise();

      this._pendingRotationPromise = null;
    }

    this._pendingRotationDestination = null;
  }

}
/**
 * Manage communication with a Boost peripheral over a Bluetooth Low Energy client socket.
 */


class Boost {
  constructor(runtime, extensionId) {
    /**
     * The Scratch 3.0 runtime used to trigger the green flag button.
     * @type {Runtime}
     * @private
     */
    this._runtime = runtime;

    this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));
    /**
     * The id of the extension this peripheral belongs to.
     */


    this._extensionId = extensionId;
    /**
     * A list of the ids of the physical or virtual sensors.
     * @type {string[]}
     * @private
     */

    this._ports = [];
    /**
     * A list of motors registered by the Boost hardware.
     * @type {BoostMotor[]}
     * @private
     */

    this._motors = [];
    /**
     * The most recently received value for each sensor.
     * @type {Object.<string, number>}
     * @private
     */

    this._sensors = {
      tiltX: 0,
      tiltY: 0,
      color: BoostColor.NONE,
      previousColor: BoostColor.NONE
    };
    /**
     * An array of values from the Boost Vision Sensor.
     * @type {Array}
     * @private
     */

    this._colorSamples = [];
    /**
     * The Bluetooth connection socket for reading/writing peripheral data.
     * @type {BLE}
     * @private
     */

    this._ble = null;

    this._runtime.registerPeripheralExtension(extensionId, this);
    /**
     * A rate limiter utility, to help limit the rate at which we send BLE messages
     * over the socket to Scratch Link to a maximum number of sends per second.
     * @type {RateLimiter}
     * @private
     */


    this._rateLimiter = new RateLimiter(BoostBLE.sendRateMax);
    /**
     * An interval id for the battery check interval.
     * @type {number}
     * @private
     */

    this._pingDeviceId = null;
    this.reset = this.reset.bind(this);
    this._onConnect = this._onConnect.bind(this);
    this._onMessage = this._onMessage.bind(this);
    this._pingDevice = this._pingDevice.bind(this);
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.
   */


  get tiltX() {
    return this._sensors.tiltX;
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.
   */


  get tiltY() {
    return this._sensors.tiltY;
  }
  /**
   * @return {number} - the latest color value received from the vision sensor.
   */


  get color() {
    return this._sensors.color;
  }
  /**
   * @return {number} - the previous color value received from the vision sensor.
   */


  get previousColor() {
    return this._sensors.previousColor;
  }
  /**
   * Look up the color id for an index received from the vision sensor.
   * @param {number} index - the color index to look up.
   * @return {BoostColor} the color id for this index.
   */


  boostColorForIndex(index) {
    const colorForIndex = Object.keys(BoostColorIndex).find(key => BoostColorIndex[key] === index);
    return colorForIndex || BoostColor.NONE;
  }
  /**
   * Access a particular motor on this peripheral.
   * @param {int} index - the index of the desired motor.
   * @return {BoostMotor} - the BoostMotor instance, if any, at that index.
   */


  motor(index) {
    return this._motors[index];
  }
  /**
   * Stop all the motors that are currently running.
   */


  stopAllMotors() {
    this._motors.forEach(motor => {
      if (motor) {
        // Send the motor off command without using the rate limiter.
        // This allows the stop button to stop motors even if we are
        // otherwise flooded with commands.
        motor.turnOff(false);
      }
    });
  }
  /**
   * Set the Boost peripheral's LED to a specific color.
   * @param {int} inputRGB - a 24-bit RGB color in 0xRRGGBB format.
   * @return {Promise} - a promise of the completion of the set led send operation.
   */


  setLED(inputRGB) {
    const rgb = [inputRGB >> 16 & 0x000000FF, inputRGB >> 8 & 0x000000FF, inputRGB & 0x000000FF];
    const cmd = this.generateOutputCommand(this._ports.indexOf(BoostIO.LED), BoostOutputExecution.EXECUTE_IMMEDIATELY ^ BoostOutputExecution.COMMAND_FEEDBACK, BoostOutputSubCommand.WRITE_DIRECT_MODE_DATA, [BoostMode.LED, ...rgb]);
    return this.send(BoostBLE.characteristic, cmd);
  }
  /**
   * Sets the input mode of the LED to RGB.
   * @return {Promise} - a promise returned by the send operation.
   */


  setLEDMode() {
    const cmd = this.generateInputCommand(this._ports.indexOf(BoostIO.LED), BoostMode.LED, 0, false);
    return this.send(BoostBLE.characteristic, cmd);
  }
  /**
   * Stop the motors on the Boost peripheral.
   */


  stopAll() {
    if (!this.isConnected()) return;
    this.stopAllMotors();
  }
  /**
   * Called by the runtime when user wants to scan for a Boost peripheral.
   */


  scan() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this._ble = new BLE(this._runtime, this._extensionId, {
      filters: [{
        services: [BoostBLE.service],
        manufacturerData: {
          0x0397: {
            dataPrefix: [0x00, 0x40],
            mask: [0x00, 0xFF]
          }
        }
      }],
      optionalServices: []
    }, this._onConnect, this.reset);
  }
  /**
   * Called by the runtime when user wants to connect to a certain Boost peripheral.
   * @param {number} id - the id of the peripheral to connect to.
   */


  connect(id) {
    if (this._ble) {
      this._ble.connectPeripheral(id);
    }
  }
  /**
   * Disconnects from the current BLE socket and resets state.
   */


  disconnect() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this.reset();
  }
  /**
   * Reset all the state and timeout/interval ids.
   */


  reset() {
    this._ports = [];
    this._motors = [];
    this._sensors = {
      tiltX: 0,
      tiltY: 0,
      color: BoostColor.NONE,
      previousColor: BoostColor.NONE
    };

    if (this._pingDeviceId) {
      window.clearInterval(this._pingDeviceId);
      this._pingDeviceId = null;
    }
  }
  /**
   * Called by the runtime to detect whether the Boost peripheral is connected.
   * @return {boolean} - the connected state.
   */


  isConnected() {
    let connected = false;

    if (this._ble) {
      connected = this._ble.isConnected();
    }

    return connected;
  }
  /**
   * Write a message to the Boost peripheral BLE socket.
   * @param {number} uuid - the UUID of the characteristic to write to
   * @param {Array} message - the message to write.
   * @param {boolean} [useLimiter=true] - if true, use the rate limiter
   * @return {Promise} - a promise result of the write operation
   */


  send(uuid, message) {
    let useLimiter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
    if (!this.isConnected()) return Promise.resolve();

    if (useLimiter) {
      if (!this._rateLimiter.okayToSend()) return Promise.resolve();
    }

    return this._ble.write(BoostBLE.service, uuid, Base64Util.uint8ArrayToBase64(message), 'base64');
  }
  /**
   * Generate a Boost 'Output Command' in the byte array format
   * (COMMON HEADER, PORT ID, EXECUTION BYTE, SUBCOMMAND ID, PAYLOAD).
   *
   * Payload is accepted as an array since these vary across different subcommands.
   *
   * @param  {number} portID - the port (Connect ID) to send a command to.
   * @param  {number} execution - Byte containing startup/completion information
   * @param  {number} subCommand - the id of the subcommand byte.
   * @param  {array}  payload    - the list of bytes to send as subcommand payload
   * @return {array}            - a generated output command.
   */


  generateOutputCommand(portID, execution, subCommand, payload) {
    const hubID = 0x00;
    const command = [hubID, BoostMessage.OUTPUT, portID, execution, subCommand, ...payload];
    command.unshift(command.length + 1); // Prepend payload with length byte;

    return command;
  }
  /**
   * Generate a Boost 'Input Command' in the byte array format
   * (COMMAND ID, COMMAND TYPE, CONNECT ID, TYPE ID, MODE, DELTA INTERVAL (4 BYTES),
   * UNIT, NOTIFICATIONS ENABLED).
   *
   * This sends a command to the Boost that sets that input format
   * of the specified inputs and sets value change notifications.
   *
   * @param  {number}  portID           - the port (Connect ID) to send a command to.
   * @param  {number}  mode                - the mode of the input sensor.
   * @param  {number}  delta               - the delta change needed to trigger notification.
   * @param  {boolean} enableNotifications - whether to enable notifications.
   * @return {array}                       - a generated input command.
   */


  generateInputCommand(portID, mode, delta, enableNotifications) {
    const command = [0x00, // Hub ID
    BoostMessage.PORT_INPUT_FORMAT_SETUP_SINGLE, portID, mode].concat(numberToInt32Array(delta)).concat([enableNotifications]);
    command.unshift(command.length + 1); // Prepend payload with length byte;

    return command;
  }
  /**
   * Starts reading data from peripheral after BLE has connected.
   * @private
   */


  _onConnect() {
    this._ble.startNotifications(BoostBLE.service, BoostBLE.characteristic, this._onMessage);

    this._pingDeviceId = window.setInterval(this._pingDevice, BoostPingInterval); // Send a request for firmware version.

    setTimeout(() => {
      const command = [0x00, // Hub ID
      BoostMessage.HUB_PROPERTIES, BoostHubProperty.FW_VERSION, BoostHubPropertyOperation.REQUEST_UPDATE];
      command.unshift(command.length + 1);
      this.send(BoostBLE.characteristic, command, false);
    }, 500);
  }
  /**
   * Process the sensor data from the incoming BLE characteristic.
   * @param {object} base64 - the incoming BLE data.
   * @private
   */


  _onMessage(base64) {
    const data = Base64Util.base64ToUint8Array(base64);
    /**
     * First three bytes are the common header:
     * 0: Length of message
     * 1: Hub ID (always 0x00 at the moment, unused)
     * 2: Message Type
     * 3: Port ID
     * We base our switch-case on Message Type
     */

    const messageType = data[2];
    const portID = data[3];

    switch (messageType) {
      case BoostMessage.HUB_PROPERTIES:
        {
          const property = data[3];

          switch (property) {
            case BoostHubProperty.FW_VERSION:
              {
                // Establish firmware version 1.0.00.0224 as a 32-bit signed integer (little endian)
                const fwVersion10000224 = int32ArrayToNumber([0x24, 0x02, 0x00, 0x10]);
                const fwHub = int32ArrayToNumber(data.slice(5, data.length));

                if (fwHub < fwVersion10000224) {
                  BoostPort = BoostPort10000223OrOlder;
                  log.info('Move Hub firmware older than version 1.0.00.0224 detected. Using old port mapping.');
                } else {
                  BoostPort = BoostPort10000224OrNewer;
                }

                break;
              }
          }

          break;
        }

      case BoostMessage.HUB_ATTACHED_IO:
        {
          // IO Attach/Detach events
          const event = data[4];
          const typeId = data[5];

          switch (event) {
            case BoostIOEvent.ATTACHED:
              this._registerSensorOrMotor(portID, typeId);

              break;

            case BoostIOEvent.DETACHED:
              this._clearPort(portID);

              break;

            case BoostIOEvent.ATTACHED_VIRTUAL:
            default:
          }

          break;
        }

      case BoostMessage.PORT_VALUE:
        {
          const type = this._ports[portID];

          switch (type) {
            case BoostIO.TILT:
              this._sensors.tiltX = data[4];
              this._sensors.tiltY = data[5];
              break;

            case BoostIO.COLOR:
              this._colorSamples.unshift(data[4]);

              if (this._colorSamples.length > BoostColorSampleSize) {
                this._colorSamples.pop();

                if (this._colorSamples.every((v, i, arr) => v === arr[0])) {
                  this._sensors.previousColor = this._sensors.color;
                  this._sensors.color = this.boostColorForIndex(this._colorSamples[0]);
                } else {
                  this._sensors.color = BoostColor.NONE;
                }
              } else {
                this._sensors.color = BoostColor.NONE;
              }

              break;

            case BoostIO.MOTOREXT:
            case BoostIO.MOTORINT:
              this.motor(portID).position = int32ArrayToNumber(data.slice(4, 8));
              break;

            case BoostIO.CURRENT:
            case BoostIO.VOLTAGE:
            case BoostIO.LED:
              break;

            default:
              log.warn("Unknown sensor value! Type: ".concat(type));
          }

          break;
        }

      case BoostMessage.PORT_FEEDBACK:
        {
          const feedback = data[4];
          const motor = this.motor(portID);

          if (motor) {
            // Makes sure that commands resolve both when they actually complete and when they fail
            const isBusy = feedback & BoostPortFeedback.IN_PROGRESS;
            const commandCompleted = feedback & (BoostPortFeedback.COMPLETED ^ BoostPortFeedback.DISCARDED);

            if (!isBusy && commandCompleted) {
              if (motor.status === BoostMotorState.ON_FOR_ROTATION) {
                motor.status = BoostMotorState.OFF;
              }
            }
          }

          break;
        }

      case BoostMessage.ERROR:
        log.warn("Error reported by hub: ".concat(data));
        break;
    }
  }
  /**
   * Ping the Boost hub. If the Boost hub has disconnected
   * for some reason, the BLE socket will get an error back and automatically
   * close the socket.
   * @private
   */


  _pingDevice() {
    this._ble.read(BoostBLE.service, BoostBLE.characteristic, false);
  }
  /**
   * Register a new sensor or motor connected at a port.  Store the type of
   * sensor or motor internally, and then register for notifications on input
   * values if it is a sensor.
   * @param {number} portID - the port to register a sensor or motor on.
   * @param {number} type - the type ID of the sensor or motor
   * @private
   */


  _registerSensorOrMotor(portID, type) {
    // Record which port is connected to what type of device
    this._ports[portID] = type; // Record motor port

    if (type === BoostIO.MOTORINT || type === BoostIO.MOTOREXT) {
      this._motors[portID] = new BoostMotor(this, portID);
    } // Set input format for tilt or distance sensor


    let mode = null;
    let delta = 1;

    switch (type) {
      case BoostIO.MOTORINT:
      case BoostIO.MOTOREXT:
        mode = BoostMode.MOTOR_SENSOR;
        break;

      case BoostIO.COLOR:
        mode = BoostMode.COLOR;
        delta = 0;
        break;

      case BoostIO.LED:
        mode = BoostMode.LED;
        /**
         * Sets the LED to blue to give an indication on the hub
         * that it has connected successfully.
         */

        this.setLEDMode();
        this.setLED(0x0000FF);
        break;

      case BoostIO.TILT:
        mode = BoostMode.TILT;
        break;

      default:
        mode = BoostMode.UNKNOWN;
    }

    const cmd = this.generateInputCommand(portID, mode, delta, true // Receive feedback
    );
    this.send(BoostBLE.characteristic, cmd);
  }
  /**
   * Clear the sensors or motors present on the ports.
   * @param {number} portID - the port to clear.
   * @private
   */


  _clearPort(portID) {
    const type = this._ports[portID];

    if (type === BoostIO.TILT) {
      this._sensors.tiltX = this._sensors.tiltY = 0;
    }

    if (type === BoostIO.COLOR) {
      this._sensors.color = BoostColor.NONE;
    }

    this._ports[portID] = 'none';
    this._motors[portID] = null;
  }

}
/**
 * Enum for motor specification.
 * @readonly
 * @enum {string}
 */


const BoostMotorLabel = {
  A: 'A',
  B: 'B',
  C: 'C',
  D: 'D',
  AB: 'AB',
  ALL: 'ABCD'
};
/**
 * Enum for motor direction specification.
 * @readonly
 * @enum {string}
 */

const BoostMotorDirection = {
  FORWARD: 'this way',
  BACKWARD: 'that way',
  REVERSE: 'reverse'
};
/**
 * Enum for tilt sensor direction.
 * @readonly
 * @enum {string}
 */

const BoostTiltDirection = {
  UP: 'up',
  DOWN: 'down',
  LEFT: 'left',
  RIGHT: 'right',
  ANY: 'any'
};
/**
 * Scratch 3.0 blocks to interact with a LEGO Boost peripheral.
 */

class Scratch3BoostBlocks {
  /**
   * @return {string} - the ID of this extension.
   */
  static get EXTENSION_ID() {
    return 'boost';
  }
  /**
   * @return {number} - the tilt sensor counts as "tilted" if its tilt angle meets or exceeds this threshold.
   */


  static get TILT_THRESHOLD() {
    return 15;
  }
  /**
   * Construct a set of Boost blocks.
   * @param {Runtime} runtime - the Scratch 3.0 runtime.
   */


  constructor(runtime) {
    /**
     * The Scratch 3.0 runtime.
     * @type {Runtime}
     */
    this.runtime = runtime; // Create a new Boost peripheral instance

    this._peripheral = new Boost(this.runtime, Scratch3BoostBlocks.EXTENSION_ID);
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: Scratch3BoostBlocks.EXTENSION_ID,
      name: 'BOOST',
      blockIconURI: iconURI,
      showStatusButton: true,
      blocks: [{
        opcode: 'motorOnFor',
        text: formatMessage({
          id: 'boost.motorOnFor',
          default: 'turn motor [MOTOR_ID] for [DURATION] seconds',
          description: 'turn a motor on for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.A
          },
          DURATION: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        }
      }, {
        opcode: 'motorOnForRotation',
        text: formatMessage({
          id: 'boost.motorOnForRotation',
          default: 'turn motor [MOTOR_ID] for [ROTATION] rotations',
          description: 'turn a motor on for rotation'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.A
          },
          ROTATION: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        }
      }, {
        opcode: 'motorOn',
        text: formatMessage({
          id: 'boost.motorOn',
          default: 'turn motor [MOTOR_ID] on',
          description: 'turn a motor on indefinitely'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.A
          }
        }
      }, {
        opcode: 'motorOff',
        text: formatMessage({
          id: 'boost.motorOff',
          default: 'turn motor [MOTOR_ID] off',
          description: 'turn a motor off'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.A
          }
        }
      }, {
        opcode: 'setMotorPower',
        text: formatMessage({
          id: 'boost.setMotorPower',
          default: 'set motor [MOTOR_ID] speed to [POWER] %',
          description: 'set the motor\'s speed without turning it on'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.ALL
          },
          POWER: {
            type: ArgumentType.NUMBER,
            defaultValue: 100
          }
        }
      }, {
        opcode: 'setMotorDirection',
        text: formatMessage({
          id: 'boost.setMotorDirection',
          default: 'set motor [MOTOR_ID] direction [MOTOR_DIRECTION]',
          description: 'set the motor\'s turn direction without turning it on'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: BoostMotorLabel.A
          },
          MOTOR_DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_DIRECTION',
            defaultValue: BoostMotorDirection.FORWARD
          }
        }
      }, {
        opcode: 'getMotorPosition',
        text: formatMessage({
          id: 'boost.getMotorPosition',
          default: 'motor [MOTOR_REPORTER_ID] position',
          description: 'the position returned by the motor'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          MOTOR_REPORTER_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_REPORTER_ID',
            defaultValue: BoostMotorLabel.A
          }
        }
      }, {
        opcode: 'whenColor',
        text: formatMessage({
          id: 'boost.whenColor',
          default: 'when [COLOR] brick seen',
          description: 'check for when color'
        }),
        blockType: BlockType.HAT,
        arguments: {
          COLOR: {
            type: ArgumentType.STRING,
            menu: 'COLOR',
            defaultValue: BoostColor.ANY
          }
        }
      }, {
        opcode: 'seeingColor',
        text: formatMessage({
          id: 'boost.seeingColor',
          default: 'seeing [COLOR] brick?',
          description: 'is the color sensor seeing a certain color?'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          COLOR: {
            type: ArgumentType.STRING,
            menu: 'COLOR',
            defaultValue: BoostColor.ANY
          }
        }
      }, {
        opcode: 'whenTilted',
        text: formatMessage({
          id: 'boost.whenTilted',
          default: 'when tilted [TILT_DIRECTION_ANY]',
          description: 'check when tilted in a certain direction'
        }),
        func: 'isTilted',
        blockType: BlockType.HAT,
        arguments: {
          TILT_DIRECTION_ANY: {
            type: ArgumentType.STRING,
            menu: 'TILT_DIRECTION_ANY',
            defaultValue: BoostTiltDirection.ANY
          }
        }
      }, {
        opcode: 'getTiltAngle',
        text: formatMessage({
          id: 'boost.getTiltAngle',
          default: 'tilt angle [TILT_DIRECTION]',
          description: 'the angle returned by the tilt sensor'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          TILT_DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'TILT_DIRECTION',
            defaultValue: BoostTiltDirection.UP
          }
        }
      }, {
        opcode: 'setLightHue',
        text: formatMessage({
          id: 'boost.setLightHue',
          default: 'set light color to [HUE]',
          description: 'set the LED color'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          HUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        }
      }],
      menus: {
        MOTOR_ID: {
          acceptReporters: true,
          items: [{
            text: 'A',
            value: BoostMotorLabel.A
          }, {
            text: 'B',
            value: BoostMotorLabel.B
          }, {
            text: 'C',
            value: BoostMotorLabel.C
          }, {
            text: 'D',
            value: BoostMotorLabel.D
          }, {
            text: 'AB',
            value: BoostMotorLabel.AB
          }, {
            text: 'ABCD',
            value: BoostMotorLabel.ALL
          }]
        },
        MOTOR_REPORTER_ID: {
          acceptReporters: true,
          items: [{
            text: 'A',
            value: BoostMotorLabel.A
          }, {
            text: 'B',
            value: BoostMotorLabel.B
          }, {
            text: 'C',
            value: BoostMotorLabel.C
          }, {
            text: 'D',
            value: BoostMotorLabel.D
          }]
        },
        MOTOR_DIRECTION: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'boost.motorDirection.forward',
              default: 'this way',
              description: 'label for forward element in motor direction menu for LEGO Boost extension'
            }),
            value: BoostMotorDirection.FORWARD
          }, {
            text: formatMessage({
              id: 'boost.motorDirection.backward',
              default: 'that way',
              description: 'label for backward element in motor direction menu for LEGO Boost extension'
            }),
            value: BoostMotorDirection.BACKWARD
          }, {
            text: formatMessage({
              id: 'boost.motorDirection.reverse',
              default: 'reverse',
              description: 'label for reverse element in motor direction menu for LEGO Boost extension'
            }),
            value: BoostMotorDirection.REVERSE
          }]
        },
        TILT_DIRECTION: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'boost.tiltDirection.up',
              default: 'up',
              description: 'label for up element in tilt direction menu for LEGO Boost extension'
            }),
            value: BoostTiltDirection.UP
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.down',
              default: 'down',
              description: 'label for down element in tilt direction menu for LEGO Boost extension'
            }),
            value: BoostTiltDirection.DOWN
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.left',
              default: 'left',
              description: 'label for left element in tilt direction menu for LEGO Boost extension'
            }),
            value: BoostTiltDirection.LEFT
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.right',
              default: 'right',
              description: 'label for right element in tilt direction menu for LEGO Boost extension'
            }),
            value: BoostTiltDirection.RIGHT
          }]
        },
        TILT_DIRECTION_ANY: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'boost.tiltDirection.up',
              default: 'up'
            }),
            value: BoostTiltDirection.UP
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.down',
              default: 'down'
            }),
            value: BoostTiltDirection.DOWN
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.left',
              default: 'left'
            }),
            value: BoostTiltDirection.LEFT
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.right',
              default: 'right'
            }),
            value: BoostTiltDirection.RIGHT
          }, {
            text: formatMessage({
              id: 'boost.tiltDirection.any',
              default: 'any',
              description: 'label for any element in tilt direction menu for LEGO Boost extension'
            }),
            value: BoostTiltDirection.ANY
          }]
        },
        COLOR: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'boost.color.red',
              default: 'red',
              description: 'the color red'
            }),
            value: BoostColor.RED
          }, {
            text: formatMessage({
              id: 'boost.color.blue',
              default: 'blue',
              description: 'the color blue'
            }),
            value: BoostColor.BLUE
          }, {
            text: formatMessage({
              id: 'boost.color.green',
              default: 'green',
              description: 'the color green'
            }),
            value: BoostColor.GREEN
          }, {
            text: formatMessage({
              id: 'boost.color.yellow',
              default: 'yellow',
              description: 'the color yellow'
            }),
            value: BoostColor.YELLOW
          }, {
            text: formatMessage({
              id: 'boost.color.white',
              default: 'white',
              desription: 'the color white'
            }),
            value: BoostColor.WHITE
          }, {
            text: formatMessage({
              id: 'boost.color.black',
              default: 'black',
              description: 'the color black'
            }),
            value: BoostColor.BLACK
          }, {
            text: formatMessage({
              id: 'boost.color.any',
              default: 'any color',
              description: 'any color'
            }),
            value: BoostColor.ANY
          }]
        }
      }
    };
  }
  /**
   * Turn specified motor(s) on for a specified duration.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to activate.
   * @property {int} DURATION - the amount of time to run the motors.
   * @return {Promise} - a promise which will resolve at the end of the duration.
   */


  motorOnFor(args) {
    // TODO: cast args.MOTOR_ID?
    let durationMS = Cast.toNumber(args.DURATION) * 1000;
    durationMS = MathUtil.clamp(durationMS, 0, 15000);
    return new Promise(resolve => {
      this._forEachMotor(args.MOTOR_ID, motorIndex => {
        const motor = this._peripheral.motor(motorIndex);

        if (motor) motor.turnOnFor(durationMS);
      }); // Run for some time even when no motor is connected


      setTimeout(resolve, durationMS);
    });
  }
  /**
   * Turn specified motor(s) on for a specified rotation in full rotations.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to activate.
   * @property {int} ROTATION - the amount of full rotations to turn the motors.
   * @return {Promise} - a promise which will resolve at the end of the duration.
   */


  motorOnForRotation(args) {
    // TODO: cast args.MOTOR_ID?
    let degrees = Cast.toNumber(args.ROTATION) * 360; // TODO: Clamps to 100 rotations. Consider changing.

    const sign = Math.sign(degrees);
    degrees = Math.abs(MathUtil.clamp(degrees, -360000, 360000));
    const motors = [];

    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      motors.push(motorIndex);
    });
    /**
     * Checks that the motors given in args.MOTOR_ID exist,
     * and maps a promise for each of the motor-commands to an array.
     */


    const promises = motors.map(portID => {
      const motor = this._peripheral.motor(portID);

      if (motor) {
        // to avoid a hanging block if power is 0, return an immediately resolving promise.
        if (motor.power === 0) return Promise.resolve();
        return new Promise(resolve => {
          motor.turnOnForDegrees(degrees, sign);
          motor.pendingRotationPromise = resolve;
        });
      }

      return null;
    });
    /**
     * Make sure all promises are resolved, i.e. all motor-commands have completed.
     * To prevent the block from returning a value, an empty function is added to the .then
     */

    return Promise.all(promises).then(() => {});
  }
  /**
   * Turn specified motor(s) on indefinitely.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to activate.
   * @return {Promise} - a Promise that resolves after some delay.
   */


  motorOn(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) motor.turnOnForever();
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BoostBLE.sendInterval);
    });
  }
  /**
   * Turn specified motor(s) off.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to deactivate.
   * @return {Promise} - a Promise that resolves after some delay.
   */


  motorOff(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) motor.turnOff();
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BoostBLE.sendInterval);
    });
  }
  /**
   * Set the power level of the specified motor(s).
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to be affected.
   * @property {int} POWER - the new power level for the motor(s).
   * @return {Promise} - returns a promise to make sure the block yields.
   */


  setMotorPower(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);

        switch (motor.status) {
          case BoostMotorState.ON_FOREVER:
            motor.turnOnForever();
            break;

          case BoostMotorState.ON_FOR_TIME:
            motor.turnOnFor(motor.pendingDurationTimeoutStartTime + motor.pendingDurationTimeoutDelay - Date.now());
            break;
        }
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BoostBLE.sendInterval);
    });
  }
  /**
   * Set the direction of rotation for specified motor(s).
   * If the direction is 'reverse' the motor(s) will be reversed individually.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to be affected.
   * @property {MotorDirection} MOTOR_DIRECTION - the new direction for the motor(s).
   * @return {Promise} - returns a promise to make sure the block yields.
   */


  setMotorDirection(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        switch (args.MOTOR_DIRECTION) {
          case BoostMotorDirection.FORWARD:
            motor.direction = 1;
            break;

          case BoostMotorDirection.BACKWARD:
            motor.direction = -1;
            break;

          case BoostMotorDirection.REVERSE:
            motor.direction = -motor.direction;
            break;

          default:
            log.warn("Unknown motor direction in setMotorDirection: ".concat(args.DIRECTION));
            break;
        } // keep the motor on if it's running, and update the pending timeout if needed


        if (motor) {
          switch (motor.status) {
            case BoostMotorState.ON_FOREVER:
              motor.turnOnForever();
              break;

            case BoostMotorState.ON_FOR_TIME:
              motor.turnOnFor(motor.pendingDurationTimeoutStartTime + motor.pendingDurationTimeoutDelay - Date.now());
              break;
          }
        }
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BoostBLE.sendInterval);
    });
  }
  /**
   * @param {object} args - the block's arguments.
   * @return {number} - returns the motor's position.
   */


  getMotorPosition(args) {
    let portID = null;

    switch (args.MOTOR_REPORTER_ID) {
      case BoostMotorLabel.A:
        portID = BoostPort.A;
        break;

      case BoostMotorLabel.B:
        portID = BoostPort.B;
        break;

      case BoostMotorLabel.C:
        portID = BoostPort.C;
        break;

      case BoostMotorLabel.D:
        portID = BoostPort.D;
        break;

      default:
        log.warn('Asked for a motor position that doesnt exist!');
        return false;
    }

    if (portID !== null && this._peripheral.motor(portID)) {
      let val = this._peripheral.motor(portID).position; // Boost motor A position direction is reversed by design
      // so we have to reverse the position here


      if (portID === BoostPort.A) {
        val *= -1;
      }

      return MathUtil.wrapClamp(val, 0, 360);
    }

    return 0;
  }
  /**
   * Call a callback for each motor indexed by the provided motor ID.
   * @param {MotorID} motorID - the ID specifier.
   * @param {Function} callback - the function to call with the numeric motor index for each motor.
   * @private
   */


  _forEachMotor(motorID, callback) {
    let motors;

    switch (motorID) {
      case BoostMotorLabel.A:
        motors = [BoostPort.A];
        break;

      case BoostMotorLabel.B:
        motors = [BoostPort.B];
        break;

      case BoostMotorLabel.C:
        motors = [BoostPort.C];
        break;

      case BoostMotorLabel.D:
        motors = [BoostPort.D];
        break;

      case BoostMotorLabel.AB:
        motors = [BoostPort.A, BoostPort.B];
        break;

      case BoostMotorLabel.ALL:
        motors = [BoostPort.A, BoostPort.B, BoostPort.C, BoostPort.D];
        break;

      default:
        log.warn("Invalid motor ID: ".concat(motorID));
        motors = [];
        break;
    }

    for (const index of motors) {
      callback(index);
    }
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  whenTilted(args) {
    return this._isTilted(args.TILT_DIRECTION_ANY);
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  isTilted(args) {
    return this._isTilted(args.TILT_DIRECTION_ANY);
  }
  /**
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION - the direction (up, down, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).
   */


  getTiltAngle(args) {
    return this._getTiltAngle(args.TILT_DIRECTION);
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {TiltDirection} direction - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   * @private
   */


  _isTilted(direction) {
    switch (direction) {
      case BoostTiltDirection.ANY:
        return Math.abs(this._peripheral.tiltX) >= Scratch3BoostBlocks.TILT_THRESHOLD || Math.abs(this._peripheral.tiltY) >= Scratch3BoostBlocks.TILT_THRESHOLD;

      default:
        return this._getTiltAngle(direction) >= Scratch3BoostBlocks.TILT_THRESHOLD;
    }
  }
  /**
   * @param {TiltDirection} direction - the direction (up, down, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).
   * @private
   */


  _getTiltAngle(direction) {
    switch (direction) {
      case BoostTiltDirection.UP:
        return this._peripheral.tiltY > 90 ? 256 - this._peripheral.tiltY : -this._peripheral.tiltY;

      case BoostTiltDirection.DOWN:
        return this._peripheral.tiltY > 90 ? this._peripheral.tiltY - 256 : this._peripheral.tiltY;

      case BoostTiltDirection.LEFT:
        return this._peripheral.tiltX > 90 ? this._peripheral.tiltX - 256 : this._peripheral.tiltX;

      case BoostTiltDirection.RIGHT:
        return this._peripheral.tiltX > 90 ? 256 - this._peripheral.tiltX : -this._peripheral.tiltX;

      default:
        log.warn("Unknown tilt direction in _getTiltAngle: ".concat(direction));
    }
  }
  /**
   * Edge-triggering hat function, for when the vision sensor is detecting
   * a certain color.
   * @param {object} args - the block's arguments.
   * @return {boolean} - true when the color sensor senses the specified color.
   */


  whenColor(args) {
    if (args.COLOR === BoostColor.ANY) {
      // For "any" color, return true if the color is not "none", and
      // the color is different from the previous color detected. This
      // allows the hat to trigger when the color changes from one color
      // to another.
      return this._peripheral.color !== BoostColor.NONE && this._peripheral.color !== this._peripheral.previousColor;
    }

    return args.COLOR === this._peripheral.color;
  }
  /**
   * A boolean reporter function, for whether the vision sensor is detecting
   * a certain color.
   * @param {object} args - the block's arguments.
   * @return {boolean} - true when the color sensor senses the specified color.
   */


  seeingColor(args) {
    if (args.COLOR === BoostColor.ANY) {
      return this._peripheral.color !== BoostColor.NONE;
    }

    return args.COLOR === this._peripheral.color;
  }
  /**
   * Set the LED's hue.
   * @param {object} args - the block's arguments.
   * @property {number} HUE - the hue to set, in the range [0,100].
   * @return {Promise} - a Promise that resolves after some delay.
   */


  setLightHue(args) {
    // Convert from [0,100] to [0,360]
    let inputHue = Cast.toNumber(args.HUE);
    inputHue = MathUtil.wrapClamp(inputHue, 0, 100);
    const hue = inputHue * 360 / 100;
    const rgbObject = color.hsvToRgb({
      h: hue,
      s: 1,
      v: 1
    });
    const rgbDecimal = color.rgbToDecimal(rgbObject);
    this._peripheral._led = inputHue;

    this._peripheral.setLED(rgbDecimal);

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BoostBLE.sendInterval);
    });
  }

}

module.exports = Scratch3BoostBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_ev3/index.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_ev3/index.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const uid = __webpack_require__(/*! ../../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const BT = __webpack_require__(/*! ../../io/bt */ "./node_modules/scratch-vm/src/io/bt.js");

const Base64Util = __webpack_require__(/*! ../../util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const RateLimiter = __webpack_require__(/*! ../../util/rateLimiter.js */ "./node_modules/scratch-vm/src/util/rateLimiter.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/svg+xml;base64, ';
/**
 * String with Ev3 expected pairing pin.
 * @readonly
 */

const Ev3PairingPin = '1234';
/**
 * A maximum number of BT message sends per second, to be enforced by the rate limiter.
 * @type {number}
 */

const BTSendRateMax = 40;
/**
 * Enum for Ev3 parameter encodings of various argument and return values.
 * Found in the 'EV3 Firmware Developer Kit', section4, page 9, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 *
 * The format for these values is:
 * 0xxxxxxx for Short Format
 * 1ttt-bbb for Long Format
 *
 * @readonly
 * @enum {number}
 */

const Ev3Encoding = {
  ONE_BYTE: 0x81,
  // = 0b1000-001, "1 byte to follow"
  TWO_BYTES: 0x82,
  // = 0b1000-010, "2 bytes to follow"
  FOUR_BYTES: 0x83,
  // = 0b1000-011, "4 bytes to follow"
  GLOBAL_VARIABLE_ONE_BYTE: 0xE1,
  // = 0b1110-001, "1 byte to follow"
  GLOBAL_CONSTANT_INDEX_0: 0x20,
  // = 0b00100000
  GLOBAL_VARIABLE_INDEX_0: 0x60 // = 0b01100000

};
/**
 * Enum for Ev3 direct command types.
 * Found in the 'EV3 Communication Developer Kit', section 4, page 24, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 * @readonly
 * @enum {number}
 */

const Ev3Command = {
  DIRECT_COMMAND_REPLY: 0x00,
  DIRECT_COMMAND_NO_REPLY: 0x80,
  DIRECT_REPLY: 0x02
};
/**
 * Enum for Ev3 commands opcodes.
 * Found in the 'EV3 Firmware Developer Kit', section 4, page 10, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 * @readonly
 * @enum {number}
 */

const Ev3Opcode = {
  OPOUTPUT_STEP_SPEED: 0xAE,
  OPOUTPUT_TIME_SPEED: 0xAF,
  OPOUTPUT_STOP: 0xA3,
  OPOUTPUT_RESET: 0xA2,
  OPOUTPUT_STEP_SYNC: 0xB0,
  OPOUTPUT_TIME_SYNC: 0xB1,
  OPOUTPUT_GET_COUNT: 0xB3,
  OPSOUND: 0x94,
  OPSOUND_CMD_TONE: 1,
  OPSOUND_CMD_STOP: 0,
  OPINPUT_DEVICE_LIST: 0x98,
  OPINPUT_READSI: 0x9D
};
/**
 * Enum for Ev3 values used as arguments to various opcodes.
 * Found in the 'EV3 Firmware Developer Kit', section4, page 10-onwards, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 * @readonly
 * @enum {number}
 */

const Ev3Args = {
  LAYER: 0,
  // always 0, chained EV3s not supported
  COAST: 0,
  BRAKE: 1,
  RAMP: 50,
  // time in milliseconds
  DO_NOT_CHANGE_TYPE: 0,
  MAX_DEVICES: 32 // 'Normally 32' from pg. 46

};
/**
 * Enum for Ev3 device type numbers.
 * Found in the 'EV3 Firmware Developer Kit', section 5, page 100, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 * @readonly
 * @enum {string}
 */

const Ev3Device = {
  29: 'color',
  30: 'ultrasonic',
  32: 'gyro',
  16: 'touch',
  8: 'mediumMotor',
  7: 'largeMotor',
  126: 'none',
  125: 'none'
};
/**
 * Enum for Ev3 device modes.
 * Found in the 'EV3 Firmware Developer Kit', section 5, page 100, at
 * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
 * @readonly
 * @enum {number}
 */

const Ev3Mode = {
  touch: 0,
  // touch
  color: 1,
  // ambient
  ultrasonic: 1,
  // inch
  none: 0
};
/**
 * Enum for Ev3 device labels used in the Scratch blocks/UI.
 * @readonly
 * @enum {string}
 */

const Ev3Label = {
  touch: 'button',
  color: 'brightness',
  ultrasonic: 'distance'
};
/**
 * Manage power, direction, and timers for one EV3 motor.
 */

class EV3Motor {
  /**
   * Construct a EV3 Motor instance, which could be of type 'largeMotor' or
   * 'mediumMotor'.
   *
   * @param {EV3} parent - the EV3 peripheral which owns this motor.
   * @param {int} index - the zero-based index of this motor on its parent peripheral.
   * @param {string} type - the type of motor (i.e. 'largeMotor' or 'mediumMotor').
   */
  constructor(parent, index, type) {
    /**
     * The EV3 peripheral which owns this motor.
     * @type {EV3}
     * @private
     */
    this._parent = parent;
    /**
     * The zero-based index of this motor on its parent peripheral.
     * @type {int}
     * @private
     */

    this._index = index;
    /**
     * The type of EV3 motor this could be: 'largeMotor' or 'mediumMotor'.
     * @type {string}
     * @private
     */

    this._type = type;
    /**
     * This motor's current direction: 1 for "clockwise" or -1 for "counterclockwise"
     * @type {number}
     * @private
     */

    this._direction = 1;
    /**
     * This motor's current power level, in the range [0,100].
     * @type {number}
     * @private
     */

    this._power = 50;
    /**
     * This motor's current position, in the range [0,360].
     * @type {number}
     * @private
     */

    this._position = 0;
    /**
     * An ID for the current coast command, to help override multiple coast
     * commands sent in succession.
     * @type {number}
     * @private
     */

    this._commandID = null;
    /**
     * A delay, in milliseconds, to add to coasting, to make sure that a brake
     * first takes effect if one was sent.
     * @type {number}
     * @private
     */

    this._coastDelay = 1000;
  }
  /**
   * @return {string} - this motor's type: 'largeMotor' or 'mediumMotor'
   */


  get type() {
    return this._type;
  }
  /**
   * @param {string} value - this motor's new type: 'largeMotor' or 'mediumMotor'
   */


  set type(value) {
    this._type = value;
  }
  /**
   * @return {int} - this motor's current direction: 1 for "clockwise" or -1 for "counterclockwise"
   */


  get direction() {
    return this._direction;
  }
  /**
   * @param {int} value - this motor's new direction: 1 for "clockwise" or -1 for "counterclockwise"
   */


  set direction(value) {
    if (value < 0) {
      this._direction = -1;
    } else {
      this._direction = 1;
    }
  }
  /**
   * @return {int} - this motor's current power level, in the range [0,100].
   */


  get power() {
    return this._power;
  }
  /**
   * @param {int} value - this motor's new power level, in the range [0,100].
   */


  set power(value) {
    this._power = value;
  }
  /**
   * @return {int} - this motor's current position, in the range [-inf,inf].
   */


  get position() {
    return this._position;
  }
  /**
   * @param {int} array - this motor's new position, in the range [0,360].
   */


  set position(array) {
    // tachoValue from Paula
    let value = array[0] + array[1] * 256 + array[2] * 256 * 256 + array[3] * 256 * 256 * 256;

    if (value > 0x7fffffff) {
      value = value - 0x100000000;
    }

    this._position = value;
  }
  /**
   * Turn this motor on for a specific duration.
   * Found in the 'EV3 Firmware Developer Kit', page 56, at
   * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
   *
   * Opcode arguments:
   * (Data8) LAYER – Specify chain layer number [0 - 3]
   * (Data8) NOS – Output bit field [0x00 – 0x0F]
   * (Data8) SPEED – Power level, [-100 – 100]
   * (Data32) STEP1 – Time in milliseconds for ramp up
   * (Data32) STEP2 – Time in milliseconds for continues run
   * (Data32) STEP3 – Time in milliseconds for ramp down
   * (Data8) BRAKE - Specify break level [0: Float, 1: Break]
   *
   * @param {number} milliseconds - run the motor for this long.
   */


  turnOnFor(milliseconds) {
    if (this._power === 0) return;

    const port = this._portMask(this._index);

    let n = milliseconds;
    let speed = this._power * this._direction;
    const ramp = Ev3Args.RAMP;
    let byteCommand = [];
    byteCommand[0] = Ev3Opcode.OPOUTPUT_TIME_SPEED; // If speed is less than zero, make it positive and multiply the input
    // value by -1

    if (speed < 0) {
      speed = -1 * speed;
      n = -1 * n;
    } // If the input value is less than 0


    const dir = n < 0 ? 0x100 - speed : speed; // step negative or positive

    n = Math.abs(n); // Setup motor run duration and ramping behavior

    let rampup = ramp;
    let rampdown = ramp;
    let run = n - ramp * 2;

    if (run < 0) {
      rampup = Math.floor(n / 2);
      run = 0;
      rampdown = n - rampup;
    } // Generate motor command values


    const runcmd = this._runValues(run);

    byteCommand = byteCommand.concat([Ev3Args.LAYER, port, Ev3Encoding.ONE_BYTE, dir & 0xff, Ev3Encoding.ONE_BYTE, rampup]).concat(runcmd.concat([Ev3Encoding.ONE_BYTE, rampdown, Ev3Args.BRAKE]));

    const cmd = this._parent.generateCommand(Ev3Command.DIRECT_COMMAND_NO_REPLY, byteCommand);

    this._parent.send(cmd);

    this.coastAfter(milliseconds);
  }
  /**
   * Set the motor to coast after a specified amount of time.
   * @param {number} time - the time in milliseconds.
   */


  coastAfter(time) {
    if (this._power === 0) return; // Set the motor command id to check before starting coast

    const commandId = uid();
    this._commandID = commandId; // Send coast message

    setTimeout(() => {
      // Do not send coast if another motor command changed the command id.
      if (this._commandID === commandId) {
        this.coast();
        this._commandID = null;
      }
    }, time + this._coastDelay); // add a delay so the brake takes effect
  }
  /**
   * Set the motor to coast.
   */


  coast() {
    if (this._power === 0) return;

    const cmd = this._parent.generateCommand(Ev3Command.DIRECT_COMMAND_NO_REPLY, [Ev3Opcode.OPOUTPUT_STOP, Ev3Args.LAYER, this._portMask(this._index), // port output bit field
    Ev3Args.COAST]);

    this._parent.send(cmd, false); // don't use rate limiter to ensure motor stops

  }
  /**
   * Generate motor run values for a given input.
   * @param  {number} run - run input.
   * @return {array} - run values as a byte array.
   */


  _runValues(run) {
    // If run duration is less than max 16-bit integer
    if (run < 0x7fff) {
      return [Ev3Encoding.TWO_BYTES, run & 0xff, run >> 8 & 0xff];
    } // Run forever


    return [Ev3Encoding.FOUR_BYTES, run & 0xff, run >> 8 & 0xff, run >> 16 & 0xff, run >> 24 & 0xff];
  }
  /**
   * Return a port value for the EV3 that is in the format for 'output bit field'
   * as 1/2/4/8, generally needed for motor ports, instead of the typical 0/1/2/3.
   * The documentation in the 'EV3 Firmware Developer Kit' for motor port arguments
   * is sometimes mistaken, but we believe motor ports are mostly addressed this way.
   * @param {number} port - the port number to convert to an 'output bit field'.
   * @return {number} - the converted port number.
   */


  _portMask(port) {
    return Math.pow(2, port);
  }

}

class EV3 {
  constructor(runtime, extensionId) {
    /**
     * The Scratch 3.0 runtime used to trigger the green flag button.
     * @type {Runtime}
     * @private
     */
    this._runtime = runtime;

    this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));
    /**
     * The id of the extension this peripheral belongs to.
     */


    this._extensionId = extensionId;
    /**
     * A list of the names of the sensors connected in ports 1,2,3,4.
     * @type {string[]}
     * @private
     */

    this._sensorPorts = [];
    /**
     * A list of the names of the motors connected in ports A,B,C,D.
     * @type {string[]}
     * @private
     */

    this._motorPorts = [];
    /**
     * The state of all sensor values.
     * @type {string[]}
     * @private
     */

    this._sensors = {
      distance: 0,
      brightness: 0,
      buttons: [0, 0, 0, 0]
    };
    /**
     * The motors which this EV3 could possibly have connected.
     * @type {string[]}
     * @private
     */

    this._motors = [null, null, null, null];
    /**
     * The polling interval, in milliseconds.
     * @type {number}
     * @private
     */

    this._pollingInterval = 150;
    /**
     * The polling interval ID.
     * @type {number}
     * @private
     */

    this._pollingIntervalID = null;
    /**
     * The counter keeping track of polling cycles.
     * @type {string[]}
     * @private
     */

    this._pollingCounter = 0;
    /**
     * The Bluetooth socket connection for reading/writing peripheral data.
     * @type {BT}
     * @private
     */

    this._bt = null;

    this._runtime.registerPeripheralExtension(extensionId, this);
    /**
     * A rate limiter utility, to help limit the rate at which we send BT messages
     * over the socket to Scratch Link to a maximum number of sends per second.
     * @type {RateLimiter}
     * @private
     */


    this._rateLimiter = new RateLimiter(BTSendRateMax);
    this.reset = this.reset.bind(this);
    this._onConnect = this._onConnect.bind(this);
    this._onMessage = this._onMessage.bind(this);
    this._pollValues = this._pollValues.bind(this);
  }

  get distance() {
    let value = this._sensors.distance > 100 ? 100 : this._sensors.distance;
    value = value < 0 ? 0 : value;
    value = Math.round(100 * value) / 100;
    return value;
  }

  get brightness() {
    return this._sensors.brightness;
  }
  /**
   * Access a particular motor on this peripheral.
   * @param {int} index - the zero-based index of the desired motor.
   * @return {EV3Motor} - the EV3Motor instance, if any, at that index.
   */


  motor(index) {
    return this._motors[index];
  }

  isButtonPressed(port) {
    return this._sensors.buttons[port] === 1;
  }

  beep(freq, time) {
    const cmd = this.generateCommand(Ev3Command.DIRECT_COMMAND_NO_REPLY, [Ev3Opcode.OPSOUND, Ev3Opcode.OPSOUND_CMD_TONE, Ev3Encoding.ONE_BYTE, 2, Ev3Encoding.TWO_BYTES, freq, freq >> 8, Ev3Encoding.TWO_BYTES, time, time >> 8]);
    this.send(cmd);
  }

  stopAll() {
    this.stopAllMotors();
    this.stopSound();
  }

  stopSound() {
    const cmd = this.generateCommand(Ev3Command.DIRECT_COMMAND_NO_REPLY, [Ev3Opcode.OPSOUND, Ev3Opcode.OPSOUND_CMD_STOP]);
    this.send(cmd, false); // don't use rate limiter to ensure sound stops
  }

  stopAllMotors() {
    this._motors.forEach(motor => {
      if (motor) {
        motor.coast();
      }
    });
  }
  /**
   * Called by the runtime when user wants to scan for an EV3 peripheral.
   */


  scan() {
    if (this._bt) {
      this._bt.disconnect();
    }

    this._bt = new BT(this._runtime, this._extensionId, {
      majorDeviceClass: 8,
      minorDeviceClass: 1
    }, this._onConnect, this.reset, this._onMessage);
  }
  /**
   * Called by the runtime when user wants to connect to a certain EV3 peripheral.
   * @param {number} id - the id of the peripheral to connect to.
   */


  connect(id) {
    if (this._bt) {
      this._bt.connectPeripheral(id, Ev3PairingPin);
    }
  }
  /**
   * Called by the runtime when user wants to disconnect from the EV3 peripheral.
   */


  disconnect() {
    if (this._bt) {
      this._bt.disconnect();
    }

    this.reset();
  }
  /**
   * Reset all the state and timeout/interval ids.
   */


  reset() {
    this._sensorPorts = [];
    this._motorPorts = [];
    this._sensors = {
      distance: 0,
      brightness: 0,
      buttons: [0, 0, 0, 0]
    };
    this._motors = [null, null, null, null];

    if (this._pollingIntervalID) {
      window.clearInterval(this._pollingIntervalID);
      this._pollingIntervalID = null;
    }
  }
  /**
   * Called by the runtime to detect whether the EV3 peripheral is connected.
   * @return {boolean} - the connected state.
   */


  isConnected() {
    let connected = false;

    if (this._bt) {
      connected = this._bt.isConnected();
    }

    return connected;
  }
  /**
   * Send a message to the peripheral BT socket.
   * @param {Uint8Array} message - the message to send.
   * @param {boolean} [useLimiter=true] - if true, use the rate limiter
   * @return {Promise} - a promise result of the send operation.
   */


  send(message) {
    let useLimiter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    if (!this.isConnected()) return Promise.resolve();

    if (useLimiter) {
      if (!this._rateLimiter.okayToSend()) return Promise.resolve();
    }

    return this._bt.sendMessage({
      message: Base64Util.uint8ArrayToBase64(message),
      encoding: 'base64'
    });
  }
  /**
   * Genrates direct commands that are sent to the EV3 as a single or compounded byte arrays.
   * See 'EV3 Communication Developer Kit', section 4, page 24 at
   * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits.
   *
   * Direct commands are one of two types:
   * DIRECT_COMMAND_NO_REPLY = a direct command where no reply is expected
   * DIRECT_COMMAND_REPLY = a direct command where a reply is expected, and the
   * number and length of returned values needs to be specified.
   *
   * The direct command byte array sent takes the following format:
   * Byte 0 - 1: Command size, Little Endian. Command size not including these 2 bytes
   * Byte 2 - 3: Message counter, Little Endian. Forth running counter
   * Byte 4:     Command type. Either DIRECT_COMMAND_REPLY or DIRECT_COMMAND_NO_REPLY
   * Byte 5 - 6: Reservation (allocation) of global and local variables using a compressed format
   *             (globals reserved in byte 5 and the 2 lsb of byte 6, locals reserved in the upper
   *             6 bits of byte 6) – see documentation for more details.
   * Byte 7 - n: Byte codes as a single command or compound commands (I.e. more commands composed
   *             as a small program)
   *
   * @param {number} type - the direct command type.
   * @param {string} byteCommands - a compound array of EV3 Opcode + arguments.
   * @param {number} allocation - the allocation of global and local vars needed for replies.
   * @return {array} - generated complete command byte array, with header and compounded commands.
   */


  generateCommand(type, byteCommands) {
    let allocation = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
    // Header (Bytes 0 - 6)
    let command = [];
    command[2] = 0; // Message counter unused for now

    command[3] = 0; // Message counter unused for now

    command[4] = type;
    command[5] = allocation & 0xFF;
    command[6] = allocation >> 8 && 0xFF; // Bytecodes (Bytes 7 - n)

    command = command.concat(byteCommands); // Calculate command length minus first two header bytes

    const len = command.length - 2;
    command[0] = len & 0xFF;
    command[1] = len >> 8 && 0xFF;
    return command;
  }
  /**
   * When the EV3 peripheral connects, start polling for sensor and motor values.
   * @private
   */


  _onConnect() {
    this._pollingIntervalID = window.setInterval(this._pollValues, this._pollingInterval);
  }
  /**
   * Poll the EV3 for sensor and motor input values, based on the list of
   * known connected sensors and motors. This is sent as many compound commands
   * in a direct command, with a reply expected.
   *
   * See 'EV3 Firmware Developer Kit', section 4.8, page 46, at
   * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits
   * for a list of polling/input device commands and their arguments.
   *
   * @private
   */


  _pollValues() {
    if (!this.isConnected()) {
      window.clearInterval(this._pollingIntervalID);
      return;
    }

    const cmds = []; // compound command

    let allocation = 0;
    let sensorCount = 0; // Reset the list of devices every 20 counts

    if (this._pollingCounter % 20 === 0) {
      // GET DEVICE LIST
      cmds[0] = Ev3Opcode.OPINPUT_DEVICE_LIST;
      cmds[1] = Ev3Encoding.ONE_BYTE;
      cmds[2] = Ev3Args.MAX_DEVICES;
      cmds[3] = Ev3Encoding.GLOBAL_VARIABLE_INDEX_0;
      cmds[4] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;
      cmds[5] = Ev3Encoding.GLOBAL_CONSTANT_INDEX_0; // Command and payload lengths

      allocation = 33;
      this._updateDevices = true;
    } else {
      // GET SENSOR VALUES FOR CONNECTED SENSORS
      let index = 0;

      for (let i = 0; i < 4; i++) {
        if (this._sensorPorts[i] !== 'none') {
          cmds[index + 0] = Ev3Opcode.OPINPUT_READSI;
          cmds[index + 1] = Ev3Args.LAYER;
          cmds[index + 2] = i; // PORT

          cmds[index + 3] = Ev3Args.DO_NOT_CHANGE_TYPE;
          cmds[index + 4] = Ev3Mode[this._sensorPorts[i]];
          cmds[index + 5] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;
          cmds[index + 6] = sensorCount * 4; // GLOBAL INDEX

          index += 7;
        }

        sensorCount++;
      } // GET MOTOR POSITION VALUES, EVEN IF NO MOTOR PRESENT


      for (let i = 0; i < 4; i++) {
        cmds[index + 0] = Ev3Opcode.OPOUTPUT_GET_COUNT;
        cmds[index + 1] = Ev3Args.LAYER;
        cmds[index + 2] = i; // PORT (incorrectly specified as 'Output bit field' in LEGO docs)

        cmds[index + 3] = Ev3Encoding.GLOBAL_VARIABLE_ONE_BYTE;
        cmds[index + 4] = sensorCount * 4; // GLOBAL INDEX

        index += 5;
        sensorCount++;
      } // Command and payload lengths


      allocation = sensorCount * 4;
    }

    const cmd = this.generateCommand(Ev3Command.DIRECT_COMMAND_REPLY, cmds, allocation);
    this.send(cmd);
    this._pollingCounter++;
  }
  /**
   * Message handler for incoming EV3 reply messages, either a list of connected
   * devices (sensors and motors) or the values of the connected sensors and motors.
   *
   * See 'EV3 Communication Developer Kit', section 4.1, page 24 at
   * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits
   * for more details on direct reply formats.
   *
   * The direct reply byte array sent takes the following format:
   * Byte 0 – 1: Reply size, Little Endian. Reply size not including these 2 bytes
   * Byte 2 – 3: Message counter, Little Endian. Equals the Direct Command
   * Byte 4:     Reply type. Either DIRECT_REPLY or DIRECT_REPLY_ERROR
   * Byte 5 - n: Resonse buffer. I.e. the content of the by the Command reserved global variables.
   *             I.e. if the command reserved 64 bytes, these bytes will be placed in the reply
   *             packet as the bytes 5 to 68.
   *
   * See 'EV3 Firmware Developer Kit', section 4.8, page 56 at
   * https://education.lego.com/en-us/support/mindstorms-ev3/developer-kits
   * for direct response buffer formats for various commands.
   *
   * @param {object} params - incoming message parameters
   * @private
   */


  _onMessage(params) {
    const message = params.message;
    const data = Base64Util.base64ToUint8Array(message);

    if (data[4] !== Ev3Command.DIRECT_REPLY) {
      return;
    }

    if (this._updateDevices) {
      // PARSE DEVICE LIST
      for (let i = 0; i < 4; i++) {
        const deviceType = Ev3Device[data[i + 5]]; // if returned device type is null, use 'none'

        this._sensorPorts[i] = deviceType ? deviceType : 'none';
      }

      for (let i = 0; i < 4; i++) {
        const deviceType = Ev3Device[data[i + 21]]; // if returned device type is null, use 'none'

        this._motorPorts[i] = deviceType ? deviceType : 'none';
      }

      for (let m = 0; m < 4; m++) {
        const type = this._motorPorts[m];

        if (type !== 'none' && !this._motors[m]) {
          // add new motor if don't already have one
          this._motors[m] = new EV3Motor(this, m, type);
        }

        if (type === 'none' && this._motors[m]) {
          // clear old motor
          this._motors[m] = null;
        }
      }

      this._updateDevices = false; // eslint-disable-next-line no-undefined
    } else if (!this._sensorPorts.includes(undefined) && !this._motorPorts.includes(undefined)) {
      // PARSE SENSOR VALUES
      let offset = 5; // start reading sensor values at byte 5

      for (let i = 0; i < 4; i++) {
        // array 2 float
        const buffer = new Uint8Array([data[offset], data[offset + 1], data[offset + 2], data[offset + 3]]).buffer;
        const view = new DataView(buffer);
        const value = view.getFloat32(0, true);

        if (Ev3Label[this._sensorPorts[i]] === 'button') {
          // Read a button value per port
          this._sensors.buttons[i] = value ? value : 0;
        } else if (Ev3Label[this._sensorPorts[i]]) {
          // if valid
          // Read brightness / distance values and set to 0 if null
          this._sensors[Ev3Label[this._sensorPorts[i]]] = value ? value : 0;
        }

        offset += 4;
      } // PARSE MOTOR POSITION VALUES, EVEN IF NO MOTOR PRESENT


      for (let i = 0; i < 4; i++) {
        const positionArray = [data[offset], data[offset + 1], data[offset + 2], data[offset + 3]];

        if (this._motors[i]) {
          this._motors[i].position = positionArray;
        }

        offset += 4;
      }
    }
  }

}
/**
 * Enum for motor port names.
 * Note: if changed, will break compatibility with previously saved projects.
 * @readonly
 * @enum {string}
 */


const Ev3MotorMenu = ['A', 'B', 'C', 'D'];
/**
 * Enum for sensor port names.
 * Note: if changed, will break compatibility with previously saved projects.
 * @readonly
 * @enum {string}
 */

const Ev3SensorMenu = ['1', '2', '3', '4'];

class Scratch3Ev3Blocks {
  /**
   * The ID of the extension.
   * @return {string} the id
   */
  static get EXTENSION_ID() {
    return 'ev3';
  }
  /**
   * Creates a new instance of the EV3 extension.
   * @param  {object} runtime VM runtime
   * @constructor
   */


  constructor(runtime) {
    /**
     * The Scratch 3.0 runtime.
     * @type {Runtime}
     */
    this.runtime = runtime; // Create a new EV3 peripheral instance

    this._peripheral = new EV3(this.runtime, Scratch3Ev3Blocks.EXTENSION_ID);
    this._playNoteForPicker = this._playNoteForPicker.bind(this);
    this.runtime.on('PLAY_NOTE', this._playNoteForPicker);
  }
  /**
   * Define the EV3 extension.
   * @return {object} Extension description.
   */


  getInfo() {
    return {
      id: Scratch3Ev3Blocks.EXTENSION_ID,
      name: 'LEGO EV3',
      blockIconURI: blockIconURI,
      showStatusButton: true,
      blocks: [{
        opcode: 'motorTurnClockwise',
        text: formatMessage({
          id: 'ev3.motorTurnClockwise',
          default: 'motor [PORT] turn this way for [TIME] seconds',
          description: 'turn a motor clockwise for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'motorPorts',
            defaultValue: 0
          },
          TIME: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        }
      }, {
        opcode: 'motorTurnCounterClockwise',
        text: formatMessage({
          id: 'ev3.motorTurnCounterClockwise',
          default: 'motor [PORT] turn that way for [TIME] seconds',
          description: 'turn a motor counter-clockwise for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'motorPorts',
            defaultValue: 0
          },
          TIME: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        }
      }, {
        opcode: 'motorSetPower',
        text: formatMessage({
          id: 'ev3.motorSetPower',
          default: 'motor [PORT] set power [POWER] %',
          description: 'set a motor\'s power to some value'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'motorPorts',
            defaultValue: 0
          },
          POWER: {
            type: ArgumentType.NUMBER,
            defaultValue: 100
          }
        }
      }, {
        opcode: 'getMotorPosition',
        text: formatMessage({
          id: 'ev3.getMotorPosition',
          default: 'motor [PORT] position',
          description: 'get the measured degrees a motor has turned'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'motorPorts',
            defaultValue: 0
          }
        }
      }, {
        opcode: 'whenButtonPressed',
        text: formatMessage({
          id: 'ev3.whenButtonPressed',
          default: 'when button [PORT] pressed',
          description: 'when a button connected to a port is pressed'
        }),
        blockType: BlockType.HAT,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'sensorPorts',
            defaultValue: 0
          }
        }
      }, {
        opcode: 'whenDistanceLessThan',
        text: formatMessage({
          id: 'ev3.whenDistanceLessThan',
          default: 'when distance < [DISTANCE]',
          description: 'when the value measured by the distance sensor is less than some value'
        }),
        blockType: BlockType.HAT,
        arguments: {
          DISTANCE: {
            type: ArgumentType.NUMBER,
            defaultValue: 5
          }
        }
      }, {
        opcode: 'whenBrightnessLessThan',
        text: formatMessage({
          id: 'ev3.whenBrightnessLessThan',
          default: 'when brightness < [DISTANCE]',
          description: 'when value measured by brightness sensor is less than some value'
        }),
        blockType: BlockType.HAT,
        arguments: {
          DISTANCE: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        }
      }, {
        opcode: 'buttonPressed',
        text: formatMessage({
          id: 'ev3.buttonPressed',
          default: 'button [PORT] pressed?',
          description: 'is a button on some port pressed?'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          PORT: {
            type: ArgumentType.STRING,
            menu: 'sensorPorts',
            defaultValue: 0
          }
        }
      }, {
        opcode: 'getDistance',
        text: formatMessage({
          id: 'ev3.getDistance',
          default: 'distance',
          description: 'gets measured distance'
        }),
        blockType: BlockType.REPORTER
      }, {
        opcode: 'getBrightness',
        text: formatMessage({
          id: 'ev3.getBrightness',
          default: 'brightness',
          description: 'gets measured brightness'
        }),
        blockType: BlockType.REPORTER
      }, {
        opcode: 'beep',
        text: formatMessage({
          id: 'ev3.beepNote',
          default: 'beep note [NOTE] for [TIME] secs',
          description: 'play some note on EV3 for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          NOTE: {
            type: ArgumentType.NOTE,
            defaultValue: 60
          },
          TIME: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.5
          }
        }
      }],
      menus: {
        motorPorts: {
          acceptReporters: true,
          items: this._formatMenu(Ev3MotorMenu)
        },
        sensorPorts: {
          acceptReporters: true,
          items: this._formatMenu(Ev3SensorMenu)
        }
      }
    };
  }

  motorTurnClockwise(args) {
    const port = Cast.toNumber(args.PORT);
    let time = Cast.toNumber(args.TIME) * 1000;
    time = MathUtil.clamp(time, 0, 15000);
    return new Promise(resolve => {
      this._forEachMotor(port, motorIndex => {
        const motor = this._peripheral.motor(motorIndex);

        if (motor) {
          motor.direction = 1;
          motor.turnOnFor(time);
        }
      }); // Run for some time even when no motor is connected


      setTimeout(resolve, time);
    });
  }

  motorTurnCounterClockwise(args) {
    const port = Cast.toNumber(args.PORT);
    let time = Cast.toNumber(args.TIME) * 1000;
    time = MathUtil.clamp(time, 0, 15000);
    return new Promise(resolve => {
      this._forEachMotor(port, motorIndex => {
        const motor = this._peripheral.motor(motorIndex);

        if (motor) {
          motor.direction = -1;
          motor.turnOnFor(time);
        }
      }); // Run for some time even when no motor is connected


      setTimeout(resolve, time);
    });
  }

  motorSetPower(args) {
    const port = Cast.toNumber(args.PORT);
    const power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);

    this._forEachMotor(port, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        motor.power = power;
      }
    });
  }

  getMotorPosition(args) {
    const port = Cast.toNumber(args.PORT);

    if (![0, 1, 2, 3].includes(port)) {
      return;
    }

    const motor = this._peripheral.motor(port);

    let position = 0;

    if (motor) {
      position = MathUtil.wrapClamp(motor.position, 0, 360);
    }

    return position;
  }

  whenButtonPressed(args) {
    const port = Cast.toNumber(args.PORT);

    if (![0, 1, 2, 3].includes(port)) {
      return;
    }

    return this._peripheral.isButtonPressed(port);
  }

  whenDistanceLessThan(args) {
    const distance = MathUtil.clamp(Cast.toNumber(args.DISTANCE), 0, 100);
    return this._peripheral.distance < distance;
  }

  whenBrightnessLessThan(args) {
    const brightness = MathUtil.clamp(Cast.toNumber(args.DISTANCE), 0, 100);
    return this._peripheral.brightness < brightness;
  }

  buttonPressed(args) {
    const port = Cast.toNumber(args.PORT);

    if (![0, 1, 2, 3].includes(port)) {
      return;
    }

    return this._peripheral.isButtonPressed(port);
  }

  getDistance() {
    return this._peripheral.distance;
  }

  getBrightness() {
    return this._peripheral.brightness;
  }

  _playNoteForPicker(note, category) {
    if (category !== this.getInfo().name) return;
    this.beep({
      NOTE: note,
      TIME: 0.25
    });
  }

  beep(args) {
    const note = MathUtil.clamp(Cast.toNumber(args.NOTE), 47, 99); // valid EV3 sounds

    let time = Cast.toNumber(args.TIME) * 1000;
    time = MathUtil.clamp(time, 0, 3000);

    if (time === 0) {
      return; // don't send a beep time of 0
    }

    return new Promise(resolve => {
      // https://en.wikipedia.org/wiki/MIDI_tuning_standard#Frequency_values
      const freq = Math.pow(2, (note - 69 + 12) / 12) * 440;

      this._peripheral.beep(freq, time); // Run for some time even when no piezo is connected.


      setTimeout(resolve, time);
    });
  }
  /**
   * Call a callback for each motor indexed by the provided motor ID.
   *
   * Note: This way of looping through motors is currently unnecessary, but could be
   * useful if an 'all motors' option is added in the future (see WeDo2 extension).
   *
   * @param {MotorID} motorID - the ID specifier.
   * @param {Function} callback - the function to call with the numeric motor index for each motor.
   * @private
   */


  _forEachMotor(motorID, callback) {
    let motors;

    switch (motorID) {
      case 0:
        motors = [0];
        break;

      case 1:
        motors = [1];
        break;

      case 2:
        motors = [2];
        break;

      case 3:
        motors = [3];
        break;

      default:
        log.warn("Invalid motor ID: ".concat(motorID));
        motors = [];
        break;
    }

    for (const index of motors) {
      callback(index);
    }
  }
  /**
   * Formats menus into a format suitable for block menus, and loading previously
   * saved projects:
   * [
   *   {
   *    text: label,
   *    value: index
   *   },
   *   {
   *    text: label,
   *    value: index
   *   },
   *   etc...
   * ]
   *
   * @param {array} menu - a menu to format.
   * @return {object} - a formatted menu as an object.
   * @private
   */


  _formatMenu(menu) {
    const m = [];

    for (let i = 0; i < menu.length; i++) {
      const obj = {};
      obj.text = menu[i];
      obj.value = i.toString();
      m.push(obj);
    }

    return m;
  }

}

module.exports = Scratch3Ev3Blocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/index.js":
/*!**************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/index.js ***!
  \**************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const BLE = __webpack_require__(/*! ../../io/ble */ "./node_modules/scratch-vm/src/io/ble.js");

const godirect = __webpack_require__(/*! @vernier/godirect/dist/godirect.min.umd.js */ "./node_modules/@vernier/godirect/dist/godirect.min.umd.js");

const ScratchLinkDeviceAdapter = __webpack_require__(/*! ./scratch-link-device-adapter */ "./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js");
/**
 * Icon png to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAABGdBTUEAALGPC/xhBQAACCNJREFUeAHtnGtsFFUUgM+dfXbbbbcWaKHSFgrlkWgkJCb6A4kmJfiHIBYBpcFfRg1GEkmEVAvhFYw/TExMxGoICAECiZEIIUQCiiT4gh+KILRQCi2ENIV2t/ue6zl3u2Upu4XuzO4csCe587iPmXO/OWfunTszV4ABWfflQU+0p+9bTcLzEmS5gUPlvagAcVMXcMpnK1u+evW8QLYKaNkWpHKxnt6dQsqFjxo80p10Jt1vx7t30n62Ys+2IJUTUpDlqUNomgYutwsjhZFD5r6slBAOhUHX9YTe6D1GTmrIAhFeBZ2c4JFCpBiggmwlBR7pTGLUewxZYBIUWV7yqgb7g8lotuukt5ihqyELHCSEbusk931ExMxbjSkWSNxEyr3vysxZLFHWnDuT0CtFV6OKmmOBRrV4hMubZoGmMZA6lHTfgsLeHnBEIiCxUY86XRDw+sBfOgZ0m820U5lxIFYAncF+GNvVDo5QaLBu1ClyYTyF4tvd8lZltQgXFA6mW73BxoVt0ShUXG2VCp4QQdDEFqez4Bm7p7gaO0of422r3x4Ji/KrbdIexu4SE2FjgWO6OkCLx6gt6gxOiNV92tiY+ni1Ye1nu7dpQfk35ikru9EBN6unsEDIwgLJPQv8dwCfT3WPt+iFIfAUqM3vL7vpjmuz0KX1gkAfOMN33dxKkjwA9vsTDIS8uubdBZcyAWlqWtohQbRSuru/L1O2vMazAGiLxRKVFqDgDEdAaHCN0kU8Ply2vKWxABhzJZ5ipC6qHlRzfJxVz99S49GdYQEw7PYkuAmokZJ6fumlQUqiNpVSQ56i9JnyHMsCYMRdADGHk0ZyHM1b976XicH0rXtWYR57FPNSGQ7CAiCBCJQ8oXhI0FdmBiPfVnl9ZZmz5DmFDcA+HwIUOEYMcjL2+e57PbBp04HxONI4ifIEKC8TYQMwhs+7IU+hwBFOYQvB5qF8grbwJnRfQXnIhbkIG4AExF+ScE00w0X3AZLwisrDyH1JH1YAA8UlIG029FRZsu6TPfVJiIltWYIjMTLgLUlGs1izeRYmGtS383t9wnu7G2J6fH/Tln2LNUdExGLxvZSOQ1qCS/+P9CFhBZAUuj12PHgCvRJHZ7w4EnhYjya6hXGHQ2Jaxj4ilbVC2AFEUNBVXSdKb3WC29+rmISKiqFn7ARBadyEHUACFHM64VZlDTdWafVh1Yik1ZB5JEsLJGaVtosw37ld4TscWQHX4+oRWO1zWrAEWCR6oMnTCEXijmI1234MVvsPgV+WcmKndGHpwlNtZwbhkZYEkuI4CkuAXfpk0HGAPym0TXEchaUL39Br4JvQeljk+lwxOxBeCRQ3UrFHI+AMBsEV6gcnhlwIS4BU0RORV1V42EqnwnLgSyo3AsM3eA9bPOt8bAEOV6NUWGRZ9FYvHSx6R0pfYgkMmk2DCH1+Z7KwB5gKazjLGgpLgUOAuRZWALnDSncxLAOYCmskbqjhe02h5d6y0sFKF5cXgI8LrLwB9PTeGew6POwNnptlpYOVLi4nFjjuWts957rnBk8tomoZ+bjhPcqOcCcnAG34EaTqOjxmsNKxzQnAkX5wronsOry6zIn66ThljLNcg+W1a2Gi55+MCg6XcKl3NuxrbxouS87TLAcY1V0QV5+8jLyuEekeeSGTS1gOcM/lZpOrlN/DsRzOyi8CY2fLuwUum/wR1BT+ZUzrDKUv9D4LB9rXZEjNTfRjZYFS5r86ebfA3W0bcmMKFh01/5fMoorm6rSjAA2SNc2F8dvmQVWCgdy8fxg8gcEN0pWez80QUyyQFAqn/N9mhmK5PAYN7adecCPnMsUCCZ7U8ari4IGb87wJeKFDA/MlmHXBDVkgTR1CV4/gaThKzBoeKYpuSzqSrqSzEiFuJDayWxqyQJp3RUhYSKfWUSEz5iDIrhrZl8I5b37JvrTBT3wdpd43cOqT/WiJhq6ikQpkW5a8BxuS/X219uXZHoPKmdMUGdEgpWzTll3Kr95Z8VJK7N3NL7b/qHY2rnmdjd6G7oF3q/b/3RoFaPDajwIcBWiQgMHioxZoEKChfqDBc2csnmxtM2ZglMDKArFvduhBbLDv9sOD8oymA0xBCHVtl6+c7ey6Ibdt+3ox7WOoxMCmD4i68PrZkBQaEDUe1tnVqSyyfl79+vr6evz1C2jKogkYWEEc0JnViiZRqKuoqJiZtEJcn0GIsykewzhW2jJVZjzBamxsfK79ase/5MoXL106TnEDwfq36qgIF6HGjKyqFsNkDGMwUNxEDEmIHQTxyNGjH1AchvumBcC4vAuXVpiA+TDYMFDXiiZFoN+SrmMI7tixo/v3337diNtQUzNpPq1RChIra5ccAFKDUEwYLra2fnXu3PmtA0gojqbaVUNl23ft+pPiPW73U7RGYdGH5QCQYCg93C73075S34I5c+ZQa0s/B1Njou51tVVVatJAXcrED3Q4EI5plgsHgAQiSiRCoRD9ECeam9fPo32UJzFQYwJLlix9mdZ9fb1naY2iyiQ2rVtyAEi199Pi5M8/tdB62vRpzceOH3+toaHBh61w2clTp96sqq5ehUnxw0eO7KA8KKpMYtO6JZcOKTUeNRhsp0+ffmtilYI1VLf4+Qvn1784d+5ezEfW144hMR05blglpDgHSbqxt6Wl5Y8ZM6afKq8oL7LZHd54PH7H7w+cOPj9dx8uXbLk+ICynbhm4cJDr7LVMKmhoP5dphaWoFGrHMTAQrgBJCjkFdQHpPntqCUmiWCge14PBsvdFnUYlP8AMAKfKIKmYukAAAAASUVORK5CYII=';
/**
 * Icon png to be displayed in the blocks category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len

const menuIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABGdBTUEAALGPC/xhBQAAA9dJREFUWAnNmE2IFEcUgF/9dE/v7LoaM9kkK4JBRA0EFBIPRm85hBAvEXHXwyo5eFE87GFcReMkObgJiQnkkJzEg9n8HIJixKNe1IMKihgiCbviwV11V3d0d3pmuqsqr5ppcEnb3TNVggVFVVe9eu+r97qqq4tASqp8/fsboQgmU0TMugi571K29bPy9ovPU8Sf16HbpQj3EkYFBcJcr5Am2nZfs94AIWVfqMQeHNwhICUBZ4ypUIA/X2sbIm2AW8AJK0lkEP6TJpfqwXgg4QxmF/fB7Gtvxk1G5ZKHU1CqTgPJoSUXYJYeohSUJu+qrqdVUGh2/pVX4VFffx77WaqBZkrkEFj271+qWH0sXcU3FBzyQe/Mg7B//LbKMTRTxNiDbsMHHjTJlyM7HEJIBHXs2KXFj+oTNSdoQOCYLS5jD9IwBMm5H8NplwwPb/QV4yEIcycaAza9IuA76B38fuz1OF5RXUkmHCdu6rg0BpSMgV/sAe7DdzGFrvvdi0D3mSZjQA0wt7REQsY+iWF0XbfFzyal8SLRxuteD+Du4h4Z/flbqaBHibAQtZmQtcZaAZSMwtTylaR/4vaw1ju5YhWG10pwwAqghmp2FeHO2+t11WqyM80W0m7vAOhsM1kD7CGz8L57Jsq6bitZC/GcWgLf1H6KuHT92cTDAFy/BgXMXm0OCpgV50Bo9kK3BqiBboabQMMU/WoL5im4jToeq/AIgXsiRx5KKCjcwPEsiAv/BQMu9EwyDHXd/3kqCOSzDk6t5/YglQKKeJwq+PNRmJI8kwSTaj1HZy5AhSHqnXkIvU9mMUwEw4Q5wTM57LUtkg8QPw/cdcBJ+PhvKJ0Gj80nGq6JXrg6/XFiX97GXIBpyqTieKpKViOl+WEhWXMaUavvvdIZ8Giy5+Lh3bwKm/t+Be3JazMfxc1tldY26rastiHcsQevTG9pw0znovkAcRWHzSDKnZtaOJLSfMFLB5RqtRBS4LbCurqLCy0YPkU3C0IIPEimMqR2ei7ZX2+KQdRi/WahNT/GmfOD4Vyzhx/66pcjp85dUvcmp6J8+txldXh07PPskdkS+V6EbD0vTOKlB0x9B/O6BS8ULly9PgE6x4kDPR/XX5pyYKj8xcCucsUmkNUQE0JvKKm2VioVK5HRE7UKOHbi6B94RzP+93jtpC0vWgXUF0hr3ipuw8uadwd3jXxoA9IK4Pah8t6BneV9GgjD28Svw1mlxFobgFbeFTz13cKbth93fDryp2CEq0a4hTA+aAPQ/ESJFDdvXLzzzrqNjlTqOP6uDeFf0uhvJ0ZP2QD8D6ZzU6u8YIbBAAAAAElFTkSuQmCC';
/**
 * Enum for Vernier godirect protocol.
 * @readonly
 * @enum {string}
 */

const BLEUUID = {
  service: 'd91714ef-28b9-4f91-ba16-f0d9a604f112',
  commandChar: 'f4bf14a6-c7d5-4b6d-8aa8-df1a7c83adcb',
  responseChar: 'b41e6675-a329-40e0-aa01-44d2f444babe'
};
/**
 * A time interval to wait (in milliseconds) before reporting to the BLE socket
 * that data has stopped coming from the peripheral.
 */

const BLETimeout = 4500;
/**
 * A string to report to the BLE socket when the GdxFor has stopped receiving data.
 * @type {string}
 */

const BLEDataStoppedError = 'Force and Acceleration extension stopped receiving data';
/**
 * Sensor ID numbers for the GDX-FOR.
 */

const GDXFOR_SENSOR = {
  FORCE: 1,
  ACCELERATION_X: 2,
  ACCELERATION_Y: 3,
  ACCELERATION_Z: 4,
  SPIN_SPEED_X: 5,
  SPIN_SPEED_Y: 6,
  SPIN_SPEED_Z: 7
};
/**
 * The update rate, in milliseconds, for sensor data input from the peripheral.
 */

const GDXFOR_UPDATE_RATE = 80;
/**
 * Threshold for pushing and pulling force, for the whenForcePushedOrPulled hat block.
 * @type {number}
 */

const FORCE_THRESHOLD = 5;
/**
 * Threshold for acceleration magnitude, for the "shaken" gesture.
 * @type {number}
 */

const SHAKEN_THRESHOLD = 30;
/**
 * Threshold for acceleration magnitude, to check if we are facing up.
 * @type {number}
 */

const FACING_THRESHOLD = 9;
/**
 * An offset for the facing threshold, used to check that we are no longer facing up.
 * @type {number}
 */

const FACING_THRESHOLD_OFFSET = 5;
/**
 * Threshold for acceleration magnitude, below which we are in freefall.
 * @type {number}
 */

const FREEFALL_THRESHOLD = 0.5;
/**
 * Factor used to account for influence of rotation during freefall.
 * @type {number}
 */

const FREEFALL_ROTATION_FACTOR = 0.3;
/**
 * Threshold in degrees for reporting that the sensor is tilted.
 * @type {number}
 */

const TILT_THRESHOLD = 15;
/**
 * Acceleration due to gravity, in m/s^2.
 * @type {number}
 */

const GRAVITY = 9.8;
/**
 * Manage communication with a GDX-FOR peripheral over a Scratch Link client socket.
 */

class GdxFor {
  /**
   * Construct a GDX-FOR communication object.
   * @param {Runtime} runtime - the Scratch 3.0 runtime
   * @param {string} extensionId - the id of the extension
   */
  constructor(runtime, extensionId) {
    /**
     * The Scratch 3.0 runtime used to trigger the green flag button.
     * @type {Runtime}
     * @private
     */
    this._runtime = runtime;
    /**
     * The BluetoothLowEnergy connection socket for reading/writing peripheral data.
     * @type {BLE}
     * @private
     */

    this._ble = null;
    /**
     * An @vernier/godirect Device
     * @type {Device}
     * @private
     */

    this._device = null;

    this._runtime.registerPeripheralExtension(extensionId, this);
    /**
     * The id of the extension this peripheral belongs to.
     */


    this._extensionId = extensionId;
    /**
     * The most recently received value for each sensor.
     * @type {Object.<string, number>}
     * @private
     */

    this._sensors = {
      force: 0,
      accelerationX: 0,
      accelerationY: 0,
      accelerationZ: 0,
      spinSpeedX: 0,
      spinSpeedY: 0,
      spinSpeedZ: 0
    };
    /**
     * Interval ID for data reading timeout.
     * @type {number}
     * @private
     */

    this._timeoutID = null;
    this.reset = this.reset.bind(this);
    this._onConnect = this._onConnect.bind(this);
  }
  /**
   * Called by the runtime when user wants to scan for a peripheral.
   */


  scan() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this._ble = new BLE(this._runtime, this._extensionId, {
      filters: [{
        namePrefix: 'GDX-FOR'
      }],
      optionalServices: [BLEUUID.service]
    }, this._onConnect, this.reset);
  }
  /**
   * Called by the runtime when user wants to connect to a certain peripheral.
   * @param {number} id - the id of the peripheral to connect to.
   */


  connect(id) {
    if (this._ble) {
      this._ble.connectPeripheral(id);
    }
  }
  /**
   * Called by the runtime when a user exits the connection popup.
   * Disconnect from the GDX FOR.
   */


  disconnect() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this.reset();
  }
  /**
   * Reset all the state and timeout/interval ids.
   */


  reset() {
    this._sensors = {
      force: 0,
      accelerationX: 0,
      accelerationY: 0,
      accelerationZ: 0,
      spinSpeedX: 0,
      spinSpeedY: 0,
      spinSpeedZ: 0
    };

    if (this._timeoutID) {
      window.clearInterval(this._timeoutID);
      this._timeoutID = null;
    }
  }
  /**
   * Return true if connected to the goforce device.
   * @return {boolean} - whether the goforce is connected.
   */


  isConnected() {
    let connected = false;

    if (this._ble) {
      connected = this._ble.isConnected();
    }

    return connected;
  }
  /**
   * Starts reading data from peripheral after BLE has connected to it.
   * @private
   */


  _onConnect() {
    const adapter = new ScratchLinkDeviceAdapter(this._ble, BLEUUID);
    godirect.createDevice(adapter, {
      open: true,
      startMeasurements: false
    }).then(device => {
      // Setup device
      this._device = device;
      this._device.keepValues = false; // todo: possibly remove after updating Vernier godirect module
      // Enable sensors

      this._device.sensors.forEach(sensor => {
        sensor.setEnabled(true);
      }); // Set sensor value-update behavior


      this._device.on('measurements-started', () => {
        const enabledSensors = this._device.sensors.filter(s => s.enabled);

        enabledSensors.forEach(sensor => {
          sensor.on('value-changed', s => {
            this._onSensorValueChanged(s);
          });
        });
        this._timeoutID = window.setInterval(() => this._ble.handleDisconnectError(BLEDataStoppedError), BLETimeout);
      }); // Start device


      this._device.start(GDXFOR_UPDATE_RATE);
    });
  }
  /**
   * Handler for sensor value changes from the goforce device.
   * @param {object} sensor - goforce device sensor whose value has changed
   * @private
   */


  _onSensorValueChanged(sensor) {
    switch (sensor.number) {
      case GDXFOR_SENSOR.FORCE:
        // Normalize the force, which can be measured between -50 and 50 N,
        // to be a value between -100 and 100.
        this._sensors.force = MathUtil.clamp(sensor.value * 2, -100, 100);
        break;

      case GDXFOR_SENSOR.ACCELERATION_X:
        this._sensors.accelerationX = sensor.value;
        break;

      case GDXFOR_SENSOR.ACCELERATION_Y:
        this._sensors.accelerationY = sensor.value;
        break;

      case GDXFOR_SENSOR.ACCELERATION_Z:
        this._sensors.accelerationZ = sensor.value;
        break;

      case GDXFOR_SENSOR.SPIN_SPEED_X:
        this._sensors.spinSpeedX = this._spinSpeedFromGyro(sensor.value);
        break;

      case GDXFOR_SENSOR.SPIN_SPEED_Y:
        this._sensors.spinSpeedY = this._spinSpeedFromGyro(sensor.value);
        break;

      case GDXFOR_SENSOR.SPIN_SPEED_Z:
        this._sensors.spinSpeedZ = this._spinSpeedFromGyro(sensor.value);
        break;
    } // cancel disconnect timeout and start a new one


    window.clearInterval(this._timeoutID);
    this._timeoutID = window.setInterval(() => this._ble.handleDisconnectError(BLEDataStoppedError), BLETimeout);
  }

  _spinSpeedFromGyro(val) {
    const framesPerSec = 1000 / this._runtime.currentStepTime;
    val = MathUtil.radToDeg(val);
    val = val / framesPerSec; // convert to from degrees per sec to degrees per frame

    val = val * -1;
    return val;
  }

  getForce() {
    return this._sensors.force;
  }

  getTiltFrontBack() {
    let back = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    const x = this.getAccelerationX();
    const y = this.getAccelerationY();
    const z = this.getAccelerationZ(); // Compute the yz unit vector

    const y2 = y * y;
    const z2 = z * z;
    let value = y2 + z2;
    value = Math.sqrt(value); // For sufficiently small zy vector values we are essentially at 90 degrees.
    // The following snaps to 90 and avoids divide-by-zero errors.
    // The snap factor was derived through observation -- just enough to
    // still allow single degree steps up to 90 (..., 87, 88, 89, 90).

    if (value < 0.35) {
      value = x < 0 ? 90 : -90;
    } else {
      value = x / value;
      value = Math.atan(value);
      value = MathUtil.radToDeg(value) * -1;
    } // Back is the inverse of front


    if (back) value *= -1;
    return value;
  }

  getTiltLeftRight() {
    let right = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
    const x = this.getAccelerationX();
    const y = this.getAccelerationY();
    const z = this.getAccelerationZ(); // Compute the yz unit vector

    const x2 = x * x;
    const z2 = z * z;
    let value = x2 + z2;
    value = Math.sqrt(value); // For sufficiently small zy vector values we are essentially at 90 degrees.
    // The following snaps to 90 and avoids divide-by-zero errors.
    // The snap factor was derived through observation -- just enough to
    // still allow single degree steps up to 90 (..., 87, 88, 89, 90).

    if (value < 0.35) {
      value = y < 0 ? 90 : -90;
    } else {
      value = y / value;
      value = Math.atan(value);
      value = MathUtil.radToDeg(value) * -1;
    } // Right is the inverse of left


    if (right) value *= -1;
    return value;
  }

  getAccelerationX() {
    return this._sensors.accelerationX;
  }

  getAccelerationY() {
    return this._sensors.accelerationY;
  }

  getAccelerationZ() {
    return this._sensors.accelerationZ;
  }

  getSpinSpeedX() {
    return this._sensors.spinSpeedX;
  }

  getSpinSpeedY() {
    return this._sensors.spinSpeedY;
  }

  getSpinSpeedZ() {
    return this._sensors.spinSpeedZ;
  }

}
/**
 * Enum for pushed and pulled menu options.
 * @readonly
 * @enum {string}
 */


const PushPullValues = {
  PUSHED: 'pushed',
  PULLED: 'pulled'
};
/**
 * Enum for motion gesture menu options.
 * @readonly
 * @enum {string}
 */

const GestureValues = {
  SHAKEN: 'shaken',
  STARTED_FALLING: 'started falling',
  TURNED_FACE_UP: 'turned face up',
  TURNED_FACE_DOWN: 'turned face down'
};
/**
 * Enum for tilt axis menu options.
 * @readonly
 * @enum {string}
 */

const TiltAxisValues = {
  FRONT: 'front',
  BACK: 'back',
  LEFT: 'left',
  RIGHT: 'right',
  ANY: 'any'
};
/**
 * Enum for axis menu options.
 * @readonly
 * @enum {string}
 */

const AxisValues = {
  X: 'x',
  Y: 'y',
  Z: 'z'
};
/**
 * Scratch 3.0 blocks to interact with a GDX-FOR peripheral.
 */

class Scratch3GdxForBlocks {
  /**
   * @return {string} - the name of this extension.
   */
  static get EXTENSION_NAME() {
    return 'Force and Acceleration';
  }
  /**
   * @return {string} - the ID of this extension.
   */


  static get EXTENSION_ID() {
    return 'gdxfor';
  }

  get AXIS_MENU() {
    return [{
      text: 'x',
      value: AxisValues.X
    }, {
      text: 'y',
      value: AxisValues.Y
    }, {
      text: 'z',
      value: AxisValues.Z
    }];
  }

  get TILT_MENU() {
    return [{
      text: formatMessage({
        id: 'gdxfor.tiltDirectionMenu.front',
        default: 'front',
        description: 'label for front element in tilt direction picker for gdxfor extension'
      }),
      value: TiltAxisValues.FRONT
    }, {
      text: formatMessage({
        id: 'gdxfor.tiltDirectionMenu.back',
        default: 'back',
        description: 'label for back element in tilt direction picker for gdxfor extension'
      }),
      value: TiltAxisValues.BACK
    }, {
      text: formatMessage({
        id: 'gdxfor.tiltDirectionMenu.left',
        default: 'left',
        description: 'label for left element in tilt direction picker for gdxfor extension'
      }),
      value: TiltAxisValues.LEFT
    }, {
      text: formatMessage({
        id: 'gdxfor.tiltDirectionMenu.right',
        default: 'right',
        description: 'label for right element in tilt direction picker for gdxfor extension'
      }),
      value: TiltAxisValues.RIGHT
    }];
  }

  get TILT_MENU_ANY() {
    return [...this.TILT_MENU, {
      text: formatMessage({
        id: 'gdxfor.tiltDirectionMenu.any',
        default: 'any',
        description: 'label for any direction element in tilt direction picker for gdxfor extension'
      }),
      value: TiltAxisValues.ANY
    }];
  }

  get PUSH_PULL_MENU() {
    return [{
      text: formatMessage({
        id: 'gdxfor.pushed',
        default: 'pushed',
        description: 'the force sensor was pushed inward'
      }),
      value: PushPullValues.PUSHED
    }, {
      text: formatMessage({
        id: 'gdxfor.pulled',
        default: 'pulled',
        description: 'the force sensor was pulled outward'
      }),
      value: PushPullValues.PULLED
    }];
  }

  get GESTURE_MENU() {
    return [{
      text: formatMessage({
        id: 'gdxfor.shaken',
        default: 'shaken',
        description: 'the sensor was shaken'
      }),
      value: GestureValues.SHAKEN
    }, {
      text: formatMessage({
        id: 'gdxfor.startedFalling',
        default: 'started falling',
        description: 'the sensor started free falling'
      }),
      value: GestureValues.STARTED_FALLING
    }, {
      text: formatMessage({
        id: 'gdxfor.turnedFaceUp',
        default: 'turned face up',
        description: 'the sensor was turned to face up'
      }),
      value: GestureValues.TURNED_FACE_UP
    }, {
      text: formatMessage({
        id: 'gdxfor.turnedFaceDown',
        default: 'turned face down',
        description: 'the sensor was turned to face down'
      }),
      value: GestureValues.TURNED_FACE_DOWN
    }];
  }
  /**
   * Construct a set of GDX-FOR blocks.
   * @param {Runtime} runtime - the Scratch 3.0 runtime.
   */


  constructor(runtime) {
    /**
     * The Scratch 3.0 runtime.
     * @type {Runtime}
     */
    this.runtime = runtime; // Create a new GdxFor peripheral instance

    this._peripheral = new GdxFor(this.runtime, Scratch3GdxForBlocks.EXTENSION_ID);
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: Scratch3GdxForBlocks.EXTENSION_ID,
      name: Scratch3GdxForBlocks.EXTENSION_NAME,
      blockIconURI: blockIconURI,
      menuIconURI: menuIconURI,
      showStatusButton: true,
      blocks: [{
        opcode: 'whenGesture',
        text: formatMessage({
          id: 'gdxfor.whenGesture',
          default: 'when [GESTURE]',
          description: 'when the sensor detects a gesture'
        }),
        blockType: BlockType.HAT,
        arguments: {
          GESTURE: {
            type: ArgumentType.STRING,
            menu: 'gestureOptions',
            defaultValue: GestureValues.SHAKEN
          }
        }
      }, {
        opcode: 'whenForcePushedOrPulled',
        text: formatMessage({
          id: 'gdxfor.whenForcePushedOrPulled',
          default: 'when force sensor [PUSH_PULL]',
          description: 'when the force sensor is pushed or pulled'
        }),
        blockType: BlockType.HAT,
        arguments: {
          PUSH_PULL: {
            type: ArgumentType.STRING,
            menu: 'pushPullOptions',
            defaultValue: PushPullValues.PUSHED
          }
        }
      }, {
        opcode: 'getForce',
        text: formatMessage({
          id: 'gdxfor.getForce',
          default: 'force',
          description: 'gets force'
        }),
        blockType: BlockType.REPORTER
      }, '---', {
        opcode: 'whenTilted',
        text: formatMessage({
          id: 'gdxfor.whenTilted',
          default: 'when tilted [TILT]',
          description: 'when the sensor detects tilt'
        }),
        blockType: BlockType.HAT,
        arguments: {
          TILT: {
            type: ArgumentType.STRING,
            menu: 'tiltAnyOptions',
            defaultValue: TiltAxisValues.ANY
          }
        }
      }, {
        opcode: 'isTilted',
        text: formatMessage({
          id: 'gdxfor.isTilted',
          default: 'tilted [TILT]?',
          description: 'is the device tilted?'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          TILT: {
            type: ArgumentType.STRING,
            menu: 'tiltAnyOptions',
            defaultValue: TiltAxisValues.ANY
          }
        }
      }, {
        opcode: 'getTilt',
        text: formatMessage({
          id: 'gdxfor.getTilt',
          default: 'tilt angle [TILT]',
          description: 'gets tilt'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          TILT: {
            type: ArgumentType.STRING,
            menu: 'tiltOptions',
            defaultValue: TiltAxisValues.FRONT
          }
        }
      }, '---', {
        opcode: 'isFreeFalling',
        text: formatMessage({
          id: 'gdxfor.isFreeFalling',
          default: 'falling?',
          description: 'is the device in free fall?'
        }),
        blockType: BlockType.BOOLEAN
      }, {
        opcode: 'getSpinSpeed',
        text: formatMessage({
          id: 'gdxfor.getSpin',
          default: 'spin speed [DIRECTION]',
          description: 'gets spin speed'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'axisOptions',
            defaultValue: AxisValues.Z
          }
        }
      }, {
        opcode: 'getAcceleration',
        text: formatMessage({
          id: 'gdxfor.getAcceleration',
          default: 'acceleration [DIRECTION]',
          description: 'gets acceleration'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'axisOptions',
            defaultValue: AxisValues.X
          }
        }
      }],
      menus: {
        pushPullOptions: {
          acceptReporters: true,
          items: this.PUSH_PULL_MENU
        },
        gestureOptions: {
          acceptReporters: true,
          items: this.GESTURE_MENU
        },
        axisOptions: {
          acceptReporters: true,
          items: this.AXIS_MENU
        },
        tiltOptions: {
          acceptReporters: true,
          items: this.TILT_MENU
        },
        tiltAnyOptions: {
          acceptReporters: true,
          items: this.TILT_MENU_ANY
        }
      }
    };
  }

  whenForcePushedOrPulled(args) {
    switch (args.PUSH_PULL) {
      case PushPullValues.PUSHED:
        return this._peripheral.getForce() < FORCE_THRESHOLD * -1;

      case PushPullValues.PULLED:
        return this._peripheral.getForce() > FORCE_THRESHOLD;

      default:
        log.warn("unknown push/pull value in whenForcePushedOrPulled: ".concat(args.PUSH_PULL));
        return false;
    }
  }

  getForce() {
    return Math.round(this._peripheral.getForce());
  }

  whenGesture(args) {
    switch (args.GESTURE) {
      case GestureValues.SHAKEN:
        return this.gestureMagnitude() > SHAKEN_THRESHOLD;

      case GestureValues.STARTED_FALLING:
        return this.isFreeFalling();

      case GestureValues.TURNED_FACE_UP:
        return this._isFacing(GestureValues.TURNED_FACE_UP);

      case GestureValues.TURNED_FACE_DOWN:
        return this._isFacing(GestureValues.TURNED_FACE_DOWN);

      default:
        log.warn("unknown gesture value in whenGesture: ".concat(args.GESTURE));
        return false;
    }
  }

  _isFacing(direction) {
    if (typeof this._facingUp === 'undefined') {
      this._facingUp = false;
    }

    if (typeof this._facingDown === 'undefined') {
      this._facingDown = false;
    } // If the sensor is already facing up or down, reduce the threshold.
    // This prevents small fluctations in acceleration while it is being
    // turned from causing the hat block to trigger multiple times.


    let threshold = FACING_THRESHOLD;

    if (this._facingUp || this._facingDown) {
      threshold -= FACING_THRESHOLD_OFFSET;
    }

    this._facingUp = this._peripheral.getAccelerationZ() > threshold;
    this._facingDown = this._peripheral.getAccelerationZ() < threshold * -1;

    switch (direction) {
      case GestureValues.TURNED_FACE_UP:
        return this._facingUp;

      case GestureValues.TURNED_FACE_DOWN:
        return this._facingDown;

      default:
        return false;
    }
  }

  whenTilted(args) {
    return this._isTilted(args.TILT);
  }

  isTilted(args) {
    return this._isTilted(args.TILT);
  }

  getTilt(args) {
    return this._getTiltAngle(args.TILT);
  }

  _isTilted(direction) {
    switch (direction) {
      case TiltAxisValues.ANY:
        return this._getTiltAngle(TiltAxisValues.FRONT) > TILT_THRESHOLD || this._getTiltAngle(TiltAxisValues.BACK) > TILT_THRESHOLD || this._getTiltAngle(TiltAxisValues.LEFT) > TILT_THRESHOLD || this._getTiltAngle(TiltAxisValues.RIGHT) > TILT_THRESHOLD;

      default:
        return this._getTiltAngle(direction) > TILT_THRESHOLD;
    }
  }

  _getTiltAngle(direction) {
    // Tilt values are calculated using acceleration due to gravity,
    // so we need to return 0 when the peripheral is not connected.
    if (!this._peripheral.isConnected()) {
      return 0;
    }

    switch (direction) {
      case TiltAxisValues.FRONT:
        return Math.round(this._peripheral.getTiltFrontBack(true));

      case TiltAxisValues.BACK:
        return Math.round(this._peripheral.getTiltFrontBack(false));

      case TiltAxisValues.LEFT:
        return Math.round(this._peripheral.getTiltLeftRight(true));

      case TiltAxisValues.RIGHT:
        return Math.round(this._peripheral.getTiltLeftRight(false));

      default:
        log.warn("Unknown direction in getTilt: ".concat(direction));
    }
  }

  getSpinSpeed(args) {
    switch (args.DIRECTION) {
      case AxisValues.X:
        return Math.round(this._peripheral.getSpinSpeedX());

      case AxisValues.Y:
        return Math.round(this._peripheral.getSpinSpeedY());

      case AxisValues.Z:
        return Math.round(this._peripheral.getSpinSpeedZ());

      default:
        log.warn("Unknown direction in getSpinSpeed: ".concat(args.DIRECTION));
    }
  }

  getAcceleration(args) {
    switch (args.DIRECTION) {
      case AxisValues.X:
        return Math.round(this._peripheral.getAccelerationX());

      case AxisValues.Y:
        return Math.round(this._peripheral.getAccelerationY());

      case AxisValues.Z:
        return Math.round(this._peripheral.getAccelerationZ());

      default:
        log.warn("Unknown direction in getAcceleration: ".concat(args.DIRECTION));
    }
  }
  /**
   * @param {number} x - x axis vector
   * @param {number} y - y axis vector
   * @param {number} z - z axis vector
   * @return {number} - the magnitude of a three dimension vector.
   */


  magnitude(x, y, z) {
    return Math.sqrt(x * x + y * y + z * z);
  }

  accelMagnitude() {
    return this.magnitude(this._peripheral.getAccelerationX(), this._peripheral.getAccelerationY(), this._peripheral.getAccelerationZ());
  }

  gestureMagnitude() {
    return this.accelMagnitude() - GRAVITY;
  }

  spinMagnitude() {
    return this.magnitude(this._peripheral.getSpinSpeedX(), this._peripheral.getSpinSpeedY(), this._peripheral.getSpinSpeedZ());
  }

  isFreeFalling() {
    // When the peripheral is not connected, the acceleration magnitude
    // is 0 instead of ~9.8, which ends up calculating as a positive
    // free fall; so we need to return 'false' here to prevent returning 'true'.
    if (!this._peripheral.isConnected()) {
      return false;
    }

    const accelMag = this.accelMagnitude();
    const spinMag = this.spinMagnitude(); // We want to account for rotation during freefall,
    // so we tack on a an estimated "rotational effect"
    // The FREEFALL_ROTATION_FACTOR const is used to both scale the
    // gyro measurements and convert them to radians/second.
    // So, we compare our accel magnitude against:
    // FREEFALL_THRESHOLD + (some_scaled_magnitude_of_rotation).

    const ffThresh = FREEFALL_THRESHOLD + FREEFALL_ROTATION_FACTOR * spinMag;
    return accelMag < ffThresh;
  }

}

module.exports = Scratch3GdxForBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js":
/*!************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_gdx_for/scratch-link-device-adapter.js ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Base64Util = __webpack_require__(/*! ../../util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");
/**
 * Adapter class
 */


class ScratchLinkDeviceAdapter {
  constructor(socket, _ref) {
    let {
      service,
      commandChar,
      responseChar
    } = _ref;
    this.socket = socket;
    this._service = service;
    this._commandChar = commandChar;
    this._responseChar = responseChar;
    this._onResponse = this._onResponse.bind(this);
    this._deviceOnResponse = null;
  }

  get godirectAdapter() {
    return true;
  }

  writeCommand(commandBuffer) {
    const data = Base64Util.uint8ArrayToBase64(commandBuffer);
    return this.socket.write(this._service, this._commandChar, data, 'base64');
  }

  setup(_ref2) {
    let {
      onResponse
    } = _ref2;
    this._deviceOnResponse = onResponse;
    return this.socket.startNotifications(this._service, this._responseChar, this._onResponse); // TODO:
    // How do we find out from scratch link if communication closes?
  }

  _onResponse(base64) {
    const array = Base64Util.base64ToUint8Array(base64);
    const response = new DataView(array.buffer);
    return this._deviceOnResponse(response);
  }

}

module.exports = ScratchLinkDeviceAdapter;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_makeymakey/index.js":
/*!*****************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_makeymakey/index.js ***!
  \*****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHN0eWxlPi5zdDJ7ZmlsbDpyZWR9LnN0M3tmaWxsOiNlMGUwZTB9LnN0NHtmaWxsOm5vbmU7c3Ryb2tlOiM2NjY7c3Ryb2tlLXdpZHRoOi41O3N0cm9rZS1taXRlcmxpbWl0OjEwfTwvc3R5bGU+PHBhdGggZD0iTTM1IDI4SDVhMSAxIDAgMCAxLTEtMVYxMmMwLS42LjQtMSAxLTFoMzBjLjUgMCAxIC40IDEgMXYxNWMwIC41LS41IDEtMSAxeiIgZmlsbD0iI2ZmZiIgaWQ9IkxheWVyXzYiLz48ZyBpZD0iTGF5ZXJfNCI+PHBhdGggY2xhc3M9InN0MiIgZD0iTTQgMjVoMzJ2Mi43SDR6TTEzIDI0aC0yLjJhMSAxIDAgMCAxLTEtMXYtOS43YzAtLjYuNC0xIDEtMUgxM2MuNiAwIDEgLjQgMSAxVjIzYzAgLjYtLjUgMS0xIDF6Ii8+PHBhdGggY2xhc3M9InN0MiIgZD0iTTYuMSAxOS4zdi0yLjJjMC0uNS40LTEgMS0xaDkuN2MuNSAwIDEgLjUgMSAxdjIuMmMwIC41LS41IDEtMSAxSDcuMWExIDEgMCAwIDEtMS0xeiIvPjxjaXJjbGUgY2xhc3M9InN0MiIgY3g9IjIyLjgiIGN5PSIxOC4yIiByPSIzLjQiLz48Y2lyY2xlIGNsYXNzPSJzdDIiIGN4PSIzMC42IiBjeT0iMTguMiIgcj0iMy40Ii8+PHBhdGggY2xhc3M9InN0MiIgZD0iTTQuMiAyN2gzMS45di43SDQuMnoiLz48L2c+PGcgaWQ9IkxheWVyXzUiPjxjaXJjbGUgY2xhc3M9InN0MyIgY3g9IjIyLjgiIGN5PSIxOC4yIiByPSIyLjMiLz48Y2lyY2xlIGNsYXNzPSJzdDMiIGN4PSIzMC42IiBjeT0iMTguMiIgcj0iMi4zIi8+PHBhdGggY2xhc3M9InN0MyIgZD0iTTEyLjUgMjIuOWgtMS4yYy0uMyAwLS41LS4yLS41LS41VjE0YzAtLjMuMi0uNS41LS41aDEuMmMuMyAwIC41LjIuNS41djguNGMwIC4zLS4yLjUtLjUuNXoiLz48cGF0aCBjbGFzcz0ic3QzIiBkPSJNNy4yIDE4Ljd2LTEuMmMwLS4zLjItLjUuNS0uNWg4LjRjLjMgMCAuNS4yLjUuNXYxLjJjMCAuMy0uMi41LS41LjVINy43Yy0uMyAwLS41LS4yLS41LS41ek00IDI2aDMydjJINHoiLz48L2c+PGcgaWQ9IkxheWVyXzMiPjxwYXRoIGNsYXNzPSJzdDQiIGQ9Ik0zNS4yIDI3LjlINC44YTEgMSAwIDAgMS0xLTFWMTIuMWMwLS42LjUtMSAxLTFoMzAuNWMuNSAwIDEgLjQgMSAxVjI3YTEgMSAwIDAgMS0xLjEuOXoiLz48cGF0aCBjbGFzcz0ic3Q0IiBkPSJNMzUuMiAyNy45SDQuOGExIDEgMCAwIDEtMS0xVjEyLjFjMC0uNi41LTEgMS0xaDMwLjVjLjUgMCAxIC40IDEgMVYyN2ExIDEgMCAwIDEtMS4xLjl6Ii8+PC9nPjwvc3ZnPg==';
/**
 * Length of the buffer to store key presses for the "when keys pressed in order" hat
 * @type {number}
 */

const KEY_BUFFER_LENGTH = 100;
/**
 * Timeout in milliseconds to reset the completed flag for a sequence.
 * @type {number}
 */

const SEQUENCE_HAT_TIMEOUT = 100;
/**
 * An id for the space key on a keyboard.
 */

const KEY_ID_SPACE = 'SPACE';
/**
 * An id for the left arrow key on a keyboard.
 */

const KEY_ID_LEFT = 'LEFT';
/**
 * An id for the right arrow key on a keyboard.
 */

const KEY_ID_RIGHT = 'RIGHT';
/**
 * An id for the up arrow key on a keyboard.
 */

const KEY_ID_UP = 'UP';
/**
 * An id for the down arrow key on a keyboard.
 */

const KEY_ID_DOWN = 'DOWN';
/**
 * Names used by keyboard io for keys used in scratch.
 * @enum {string}
 */

const SCRATCH_KEY_NAME = {
  [KEY_ID_SPACE]: 'space',
  [KEY_ID_LEFT]: 'left arrow',
  [KEY_ID_UP]: 'up arrow',
  [KEY_ID_RIGHT]: 'right arrow',
  [KEY_ID_DOWN]: 'down arrow'
};
/**
 * Class for the makey makey blocks in Scratch 3.0
 * @constructor
 */

class Scratch3MakeyMakeyBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * A toggle that alternates true and false each frame, so that an
     * edge-triggered hat can trigger on every other frame.
     * @type {boolean}
     */

    this.frameToggle = false; // Set an interval that toggles the frameToggle every frame.

    setInterval(() => {
      this.frameToggle = !this.frameToggle;
    }, this.runtime.currentStepTime);
    this.keyPressed = this.keyPressed.bind(this);
    this.runtime.on('KEY_PRESSED', this.keyPressed);
    this._clearkeyPressBuffer = this._clearkeyPressBuffer.bind(this);
    this.runtime.on('PROJECT_STOP_ALL', this._clearkeyPressBuffer);
    /*
     * An object containing a set of sequence objects.
     * These are the key sequences currently being detected by the "when
     * keys pressed in order" hat block. Each sequence is keyed by its
     * string representation (the sequence's value in the menu, which is a
     * string of KEY_IDs separated by spaces). Each sequence object
     * has an array property (an array of KEY_IDs) and a boolean
     * completed property that is true when the sequence has just been
     * pressed.
     * @type {object}
     */

    this.sequences = {};
    /*
     * An array of the key codes of recently pressed keys.
     * @type {array}
     */

    this.keyPressBuffer = [];
  }
  /*
  * Localized short-form names of the space bar and arrow keys, for use in the
  * displayed menu items of the "when keys pressed in order" block.
  * @type {object}
  */


  get KEY_TEXT_SHORT() {
    return {
      [KEY_ID_SPACE]: formatMessage({
        id: 'makeymakey.spaceKey',
        default: 'space',
        description: 'The space key on a computer keyboard.'
      }),
      [KEY_ID_LEFT]: formatMessage({
        id: 'makeymakey.leftArrowShort',
        default: 'left',
        description: 'Short name for the left arrow key on a computer keyboard.'
      }),
      [KEY_ID_UP]: formatMessage({
        id: 'makeymakey.upArrowShort',
        default: 'up',
        description: 'Short name for the up arrow key on a computer keyboard.'
      }),
      [KEY_ID_RIGHT]: formatMessage({
        id: 'makeymakey.rightArrowShort',
        default: 'right',
        description: 'Short name for the right arrow key on a computer keyboard.'
      }),
      [KEY_ID_DOWN]: formatMessage({
        id: 'makeymakey.downArrowShort',
        default: 'down',
        description: 'Short name for the down arrow key on a computer keyboard.'
      })
    };
  }
  /*
   * An array of strings of KEY_IDs representing the default set of
   * key sequences for use by the "when keys pressed in order" block.
   * @type {array}
   */


  get DEFAULT_SEQUENCES() {
    return ["".concat(KEY_ID_LEFT, " ").concat(KEY_ID_UP, " ").concat(KEY_ID_RIGHT), "".concat(KEY_ID_RIGHT, " ").concat(KEY_ID_UP, " ").concat(KEY_ID_LEFT), "".concat(KEY_ID_LEFT, " ").concat(KEY_ID_RIGHT), "".concat(KEY_ID_RIGHT, " ").concat(KEY_ID_LEFT), "".concat(KEY_ID_UP, " ").concat(KEY_ID_DOWN), "".concat(KEY_ID_DOWN, " ").concat(KEY_ID_UP), "".concat(KEY_ID_UP, " ").concat(KEY_ID_RIGHT, " ").concat(KEY_ID_DOWN, " ").concat(KEY_ID_LEFT), "".concat(KEY_ID_UP, " ").concat(KEY_ID_LEFT, " ").concat(KEY_ID_DOWN, " ").concat(KEY_ID_RIGHT), "".concat(KEY_ID_UP, " ").concat(KEY_ID_UP, " ").concat(KEY_ID_DOWN, " ").concat(KEY_ID_DOWN, " ") + "".concat(KEY_ID_LEFT, " ").concat(KEY_ID_RIGHT, " ").concat(KEY_ID_LEFT, " ").concat(KEY_ID_RIGHT)];
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: 'makeymakey',
      name: 'Makey Makey',
      blockIconURI: blockIconURI,
      blocks: [{
        opcode: 'whenMakeyKeyPressed',
        text: formatMessage({
          id: 'makeymakey.whenKeyPressed',
          default: 'when [KEY] key pressed',
          description: 'when a keyboard key is pressed'
        }),
        blockType: BlockType.HAT,
        arguments: {
          KEY: {
            type: ArgumentType.STRING,
            menu: 'KEY',
            defaultValue: KEY_ID_SPACE
          }
        }
      }, {
        opcode: 'whenCodePressed',
        text: formatMessage({
          id: 'makeymakey.whenKeysPressedInOrder',
          default: 'when [SEQUENCE] pressed in order',
          description: 'when a sequence of keyboard keys is pressed in a specific order'
        }),
        blockType: BlockType.HAT,
        arguments: {
          SEQUENCE: {
            type: ArgumentType.STRING,
            menu: 'SEQUENCE',
            defaultValue: this.DEFAULT_SEQUENCES[0]
          }
        }
      }],
      menus: {
        KEY: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'makeymakey.spaceKey',
              default: 'space',
              description: 'The space key on a computer keyboard.'
            }),
            value: KEY_ID_SPACE
          }, {
            text: formatMessage({
              id: 'makeymakey.upArrow',
              default: 'up arrow',
              description: 'The up arrow key on a computer keyboard.'
            }),
            value: KEY_ID_UP
          }, {
            text: formatMessage({
              id: 'makeymakey.downArrow',
              default: 'down arrow',
              description: 'The down arrow key on a computer keyboard.'
            }),
            value: KEY_ID_DOWN
          }, {
            text: formatMessage({
              id: 'makeymakey.rightArrow',
              default: 'right arrow',
              description: 'The right arrow key on a computer keyboard.'
            }),
            value: KEY_ID_RIGHT
          }, {
            text: formatMessage({
              id: 'makeymakey.leftArrow',
              default: 'left arrow',
              description: 'The left arrow key on a computer keyboard.'
            }),
            value: KEY_ID_LEFT
          }, {
            text: 'w',
            value: 'w'
          }, {
            text: 'a',
            value: 'a'
          }, {
            text: 's',
            value: 's'
          }, {
            text: 'd',
            value: 'd'
          }, {
            text: 'f',
            value: 'f'
          }, {
            text: 'g',
            value: 'g'
          }]
        },
        SEQUENCE: {
          acceptReporters: true,
          items: this.buildSequenceMenu(this.DEFAULT_SEQUENCES)
        }
      }
    };
  }
  /*
   * Build the menu of key sequences.
   * @param {array} sequencesArray an array of strings of KEY_IDs.
   * @returns {array} an array of objects with text and value properties.
   */


  buildSequenceMenu(sequencesArray) {
    return sequencesArray.map(str => this.getMenuItemForSequenceString(str));
  }
  /*
   * Create a menu item for a sequence string.
   * @param {string} sequenceString a string of KEY_IDs.
   * @return {object} an object with text and value properties.
   */


  getMenuItemForSequenceString(sequenceString) {
    let sequenceArray = sequenceString.split(' ');
    sequenceArray = sequenceArray.map(str => this.KEY_TEXT_SHORT[str]);
    return {
      text: sequenceArray.join(' '),
      value: sequenceString
    };
  }
  /*
   * Check whether a keyboard key is currently pressed.
   * Also, toggle the results of the test on alternate frames, so that the
   * hat block fires repeatedly.
   * @param {object} args - the block arguments.
   * @property {number} KEY - a key code.
   * @param {object} util - utility object provided by the runtime.
   */


  whenMakeyKeyPressed(args, util) {
    let key = args.KEY; // Convert the key arg, if it is a KEY_ID, to the key name used by
    // the Keyboard io module.

    if (SCRATCH_KEY_NAME[args.KEY]) {
      key = SCRATCH_KEY_NAME[args.KEY];
    }

    const isDown = util.ioQuery('keyboard', 'getKeyIsDown', [key]);
    return isDown && this.frameToggle;
  }
  /*
   * A function called on the KEY_PRESSED event, to update the key press
   * buffer and check if any of the key sequences have been completed.
   * @param {string} key A scratch key name.
   */


  keyPressed(key) {
    // Store only the first word of the Scratch key name, so that e.g. when
    // "left arrow" is pressed, we store "LEFT", which matches KEY_ID_LEFT
    key = key.split(' ')[0];
    key = key.toUpperCase();
    this.keyPressBuffer.push(key); // Keep the buffer under the length limit

    if (this.keyPressBuffer.length > KEY_BUFFER_LENGTH) {
      this.keyPressBuffer.shift();
    } // Check the buffer for each sequence in use


    for (const str in this.sequences) {
      const arr = this.sequences[str].array; // Bail out if we don't have enough presses for this sequence

      if (this.keyPressBuffer.length < arr.length) {
        continue;
      }

      let missFlag = false; // Slice the buffer to the length of the sequence we're checking

      const bufferSegment = this.keyPressBuffer.slice(-1 * arr.length);

      for (let i = 0; i < arr.length; i++) {
        if (arr[i] !== bufferSegment[i]) {
          missFlag = true;
        }
      } // If the miss flag is false, the sequence matched the buffer


      if (!missFlag) {
        this.sequences[str].completed = true; // Clear the completed flag after a timeout. This is necessary because
        // the hat is edge-triggered (not event triggered). Multiple hats
        // may be checking the same sequence, so this timeout gives them enough
        // time to all trigger before resetting the flag.

        setTimeout(() => {
          this.sequences[str].completed = false;
        }, SEQUENCE_HAT_TIMEOUT);
      }
    }
  }
  /**
   * Clear the key press buffer.
   */


  _clearkeyPressBuffer() {
    this.keyPressBuffer = [];
  }
  /*
   * Add a key sequence to the set currently being checked on each key press.
   * @param {string} sequenceString a string of space-separated KEY_IDs.
   * @param {array} sequenceArray an array of KEY_IDs.
   */


  addSequence(sequenceString, sequenceArray) {
    // If we already have this sequence string, return.
    if (this.sequences.hasOwnProperty(sequenceString)) {
      return;
    }

    this.sequences[sequenceString] = {
      array: sequenceArray,
      completed: false
    };
  }
  /*
   * Check whether a key sequence was recently completed.
   * @param {object} args The block arguments.
   * @property {number} SEQUENCE A string of KEY_IDs.
   */


  whenCodePressed(args) {
    const sequenceString = Cast.toString(args.SEQUENCE).toUpperCase();
    const sequenceArray = sequenceString.split(' ');

    if (sequenceArray.length < 2) {
      return;
    }

    this.addSequence(sequenceString, sequenceArray);
    return this.sequences[sequenceString].completed;
  }

}

module.exports = Scratch3MakeyMakeyBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_microbit/index.js":
/*!***************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_microbit/index.js ***!
  \***************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const BLE = __webpack_require__(/*! ../../io/ble */ "./node_modules/scratch-vm/src/io/ble.js");

const Base64Util = __webpack_require__(/*! ../../util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");
/**
 * Icon png to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAKcElEQVR42u2cfXAU9RnHv7u3L3d7l9yR5PIGXO7MkQKaYiCUWqJhFGvRMk4JZXSc8aXVaSmiYlthVHQEW99FxiIdrVY6teiMdoa+ICqhIqgQAsjwMgYDOQKXl7uY17u9293b3f5x5JKYe8+FJGSfvzbP/n77e/azz+95nt9v90KoqgpN0hdSQ6AB1ABqADWAmmgANYAaQA2gJhpADeBEE2q8GPLaWzu/CslyiY4k9dOn5uijtXGd7+jWkaReVpT3Hrhv6d0awEFC07rgD+ZeYYnXprhwigUAvjj0zbjxQCLebozT7iDzK1ZUWCru2K7L//6MVC8ue45Blz8n6rlQ815QtuohOlXiEdy/AUqPa6y59Mkh6Q1345GNja6m7pHEQKNl3t0704EXat4L6fSOmOeEI1vHKzwAyNJR9MPFpRUPOu0ONm2A0xatWaTLm5WfDrzvAppA8AbiG03fC8CQNkDKZK2YrPAuRrhpifJERsuYywveJc7CqcIDMAyeLm82dEXzw39I/qjXkpr3QuW9lxfAdOABGAKPslWDnbsy7Jl8BxTeM3SqmO0gaA5U6c3jymup0YSn9JyLee67wpTfBQAQjmyF3HFqiJcRtDECjy5dAmbmcgQPvjjxl3Lx4IVjnD/5cE1zkWtyP34VBGcdKLJnLgc9cznk1kMXFdzEn8KJ4KUqqsSHvcxWDf7j1UM8UPr6/YgHhhX8xAaYaXgAIB7fBnbuSrBzV8aNgarEQ/z6/YkLcDTg9V9XlXjQtuqoU1TpcUHlvZDOfDiuyh5qPMCLrJ1bDw3EuUtx81N/BH3pjQBJQ2HMF5V6iKfeRchVm9kkMtrwxmSdobeA9daBde8GwVlBcFYofS1Jw0vaAy9HeJHQwBUPzIBvGxDc92Rmp/BowJs10wkAONfsBs8HAAAltqngOAO8HZ3o6OiMqcvLy4E1Lwc8H8C5ZndMXdLJa/qNacNLCDBw/O8nFUNWxp/64+tWAwBefe1tHKg7CgC4/9d3ori4EHv3HcDrb26PqVt2602ovvaHaGlpw+8ffSamLqXYmya8jG8mpFy6iGLkWLh4HAwG4+r6j4VBfaPpLgU8IMGO9MLqW2pYQ9aQokuR5dgXIwCC1CUcNMj3hpdvLAdSF54EYpCHooRA0Swomo2pC0kCQpIAkqTA6LmYupgxL0X7m78+aG10NXVkpIwxsAwWXncDCESHLkohfPbpbiT6ZFPPZQ9fC0e58Wi6wTDj6UbT/rQAyiERS2pW4Kc3LQDLRO8miCEAKj7d83FcTxyLJJJJ+9MCqKoq9HomMrgkSThxsgEcZ8AMpwMkSYJlKDA0DVUFiHGWRDJp/4jXwqIo4uFHnkZXdw8AYGbZFXhs3WqQJDkhkkim7E8KoMlkxKbnn8DBunrwUli3e8/+yOAA0HjmHDq7upGXm5PUoDUr7hmWRB5Zt3FYwoime+vtd/H6G9uGJIxouniSyP6H7v8FystnY80jGzIA0MihsMAKu20aTp3JzFb6WCWRuDUvHwByw8cOhw2FBVaYjNzIAba1e3Hfb9aiq7MTNStuBwAsvr4KO3d9GnmKztIS5EyxTJiVSDT7p04tipx/9MnnYc7ORlu7NzMxsK3di5AkDHgGw2DTC+uHBeGJshJJZL/fxyMQEDKbRAiCQDAoQhBDYBkKNE2j4uqrhpUBoiSBIMZfEhkN+1NeiWSqEB2rlUg69md0JRIQRHy86z8jXsqNVRLJlP0jqgNJXXgAgjbCcONmCHUvQ+44NWG2s/rtH5Mt/ciToo0wLH4JBGO6LLazRiJk2vBYy4gHHw/bWSN+LZBKEhkMjzn/CaSiKgQOvJDyFB7L7axUJWNJZDA8IhQA1boPin7KZbMSGfUYyFx9b3hXg/cCsoBA2Z0AoYOaxlcC4+mdyCUDKBzanLFBJ3USyaRMuiSSKZmUSSSTMimTCABUlblRU9kAZ0E39p+eii21c+EL0jHbOwu6sfaWgyjND//U4oP6MmzZnfi79XT7mfQSNi7bh0JzOLG19XBY/89r49pYVebGqhuOosDsh1+gsWV3BXYdd2Q+BlaVuXFv9bHgkSbzk+vfcVRyjHhi47J9cftsXLYf7T36Ix8cLHlo6ydlv6qpPI2qssRZcuOy/Wjp4k5s+2zG+offKqtcUt6kJtNv7S0H0RtkvEufXTB/6bML5je2Wy7UVDbEbF9o9mPDsv2oP5v75vbPS26rP5u3fdXiozDppcwDrKlswOlWy9E//DX09Mt/azh8zzNM1RybF86C7pheVGD240CDeX3NWtfml94Rt+0+Mf3Lm8qbEnpfgdmPs+3G9+564vTT//pM/GrHYduWRP0AYOEMN/5S61xT92Vtfd2XtfWb/vu91fHALyxzw9tnkB/cTD5w+2Ou9375HHtfa7exM5mxRpKFaafdQQKgAcDERs98/foLHrXdaXfoABi8vczhWO2/28/TRR5z2h00gKymNl1ton79oigq6bQ7dE67Q+ew9mb1h4FYYwVESgLAXLSRa+3mWpIdK+UYuPiq89f8+XfT/+ftZQ4vLm9ZmUyfdcsv1M2fWfRaUCK8i8vdK1u6ktuAWPWTsztm24o/cnnYHUsrWzd1+fVJ9XtqxbG3XzFdNcPTawjcueibpxK1t+X26f/9R8a953jub4typOvm2b1XnvUmv8JKWMZcaZffX3XDERRP8cGaFRjWxtPLoZvXY4oxgPBNEsgxBhCUKEzL6Ru+JydS8Ak0giKFgESDJFQoKmCgQzAwIfQEWETzmoBIwd2VNaStu8uEHGO4Buz06zHHFv0dRkefAZ1+PQx0KNK2eIoPLCUj2zDc275qzgcBFWv+cf3IyxgTK2KOzQufEM5kfpGF12eGPSf8DXN+No/87HDWiwYYALw+M6ym8AscAxO++X7xCTRM7EDQzht0Da8v/NWo1dQDAxNCocUXs+303IGHdaptOmYXnh/SLlZbV+fwnwJm6UXEm/ojqgM/PFmJQ81OPHfrtqT7bN23BE8seTflYLvz5DwYGQHLKz5Puo/XZ8aLtT+D1dSDuxbsGQIymmz48DbwIguOESJOcce8XaO3oVpZ8k3Em5KVVAAMFnuOB9as1MbimCBunn04vBmR40ls29Wfgxf1KMn1gBdY+MXUCvK4ANvPndpLzrLzALjBN2VPwrDBksgLYkn1jBMp90nVY2++8vAw3RlPeLNYVZSPAEgjKWP6ZCn4lF+gMdnE08spQb73RQB9aXtgo6tJcNodf8rWz3L//Br340UW3sExEkXrFFKSSUVHqkRfkJZ8QSZk5gS6hw9H+GyDQAclSs41BVmSUIn+toAKIUTJskKoQUknCxKlkISKb/sM0NMyyVAhXW+AlYosfgOgQlUJVadTSUWBKoQoudvPioPbenq5oIUTaRUqenhWKi3oyVIUqKpKREoLggDhF6hQb4CV9LRM9rctMPN6glChp2SdTqeSskwoAECSKnG61fzFR/XsGu+FhmONriYl7TImsjoYKJyZSeB8CoBQo6spqU8TCO1fgE7gDVUNoCYaQA2gBlADqAHURAOoAdQAagA10QCOgfwfNp/hXbfBMCAAAAAASUVORK5CYII=';
/**
 * Enum for micro:bit BLE command protocol.
 * https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md
 * @readonly
 * @enum {number}
 */

const BLECommand = {
  CMD_PIN_CONFIG: 0x80,
  CMD_DISPLAY_TEXT: 0x81,
  CMD_DISPLAY_LED: 0x82
};
/**
 * A time interval to wait (in milliseconds) before reporting to the BLE socket
 * that data has stopped coming from the peripheral.
 */

const BLETimeout = 4500;
/**
 * A time interval to wait (in milliseconds) while a block that sends a BLE message is running.
 * @type {number}
 */

const BLESendInterval = 100;
/**
 * A string to report to the BLE socket when the micro:bit has stopped receiving data.
 * @type {string}
 */

const BLEDataStoppedError = 'micro:bit extension stopped receiving data';
/**
 * Enum for micro:bit protocol.
 * https://github.com/LLK/scratch-microbit-firmware/blob/master/protocol.md
 * @readonly
 * @enum {string}
 */

const BLEUUID = {
  service: 0xf005,
  rxChar: '5261da01-fa7e-42ab-850b-7c80220097cc',
  txChar: '5261da02-fa7e-42ab-850b-7c80220097cc'
};
/**
 * Manage communication with a MicroBit peripheral over a Scrath Link client socket.
 */

class MicroBit {
  /**
   * Construct a MicroBit communication object.
   * @param {Runtime} runtime - the Scratch 3.0 runtime
   * @param {string} extensionId - the id of the extension
   */
  constructor(runtime, extensionId) {
    /**
     * The Scratch 3.0 runtime used to trigger the green flag button.
     * @type {Runtime}
     * @private
     */
    this._runtime = runtime;
    /**
     * The BluetoothLowEnergy connection socket for reading/writing peripheral data.
     * @type {BLE}
     * @private
     */

    this._ble = null;

    this._runtime.registerPeripheralExtension(extensionId, this);
    /**
     * The id of the extension this peripheral belongs to.
     */


    this._extensionId = extensionId;
    /**
     * The most recently received value for each sensor.
     * @type {Object.<string, number>}
     * @private
     */

    this._sensors = {
      tiltX: 0,
      tiltY: 0,
      buttonA: 0,
      buttonB: 0,
      touchPins: [0, 0, 0],
      gestureState: 0,
      ledMatrixState: new Uint8Array(5)
    };
    /**
     * The most recently received value for each gesture.
     * @type {Object.<string, Object>}
     * @private
     */

    this._gestures = {
      moving: false,
      move: {
        active: false,
        timeout: false
      },
      shake: {
        active: false,
        timeout: false
      },
      jump: {
        active: false,
        timeout: false
      }
    };
    /**
     * Interval ID for data reading timeout.
     * @type {number}
     * @private
     */

    this._timeoutID = null;
    /**
     * A flag that is true while we are busy sending data to the BLE socket.
     * @type {boolean}
     * @private
     */

    this._busy = false;
    /**
     * ID for a timeout which is used to clear the busy flag if it has been
     * true for a long time.
     */

    this._busyTimeoutID = null;
    this.reset = this.reset.bind(this);
    this._onConnect = this._onConnect.bind(this);
    this._onMessage = this._onMessage.bind(this);
  }
  /**
   * @param {string} text - the text to display.
   * @return {Promise} - a Promise that resolves when writing to peripheral.
   */


  displayText(text) {
    const output = new Uint8Array(text.length);

    for (let i = 0; i < text.length; i++) {
      output[i] = text.charCodeAt(i);
    }

    return this.send(BLECommand.CMD_DISPLAY_TEXT, output);
  }
  /**
   * @param {Uint8Array} matrix - the matrix to display.
   * @return {Promise} - a Promise that resolves when writing to peripheral.
   */


  displayMatrix(matrix) {
    return this.send(BLECommand.CMD_DISPLAY_LED, matrix);
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.
   */


  get tiltX() {
    return this._sensors.tiltX;
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.
   */


  get tiltY() {
    return this._sensors.tiltY;
  }
  /**
   * @return {boolean} - the latest value received for the A button.
   */


  get buttonA() {
    return this._sensors.buttonA;
  }
  /**
   * @return {boolean} - the latest value received for the B button.
   */


  get buttonB() {
    return this._sensors.buttonB;
  }
  /**
   * @return {number} - the latest value received for the motion gesture states.
   */


  get gestureState() {
    return this._sensors.gestureState;
  }
  /**
   * @return {Uint8Array} - the current state of the 5x5 LED matrix.
   */


  get ledMatrixState() {
    return this._sensors.ledMatrixState;
  }
  /**
   * Called by the runtime when user wants to scan for a peripheral.
   */


  scan() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this._ble = new BLE(this._runtime, this._extensionId, {
      filters: [{
        services: [BLEUUID.service]
      }]
    }, this._onConnect, this.reset);
  }
  /**
   * Called by the runtime when user wants to connect to a certain peripheral.
   * @param {number} id - the id of the peripheral to connect to.
   */


  connect(id) {
    if (this._ble) {
      this._ble.connectPeripheral(id);
    }
  }
  /**
   * Disconnect from the micro:bit.
   */


  disconnect() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this.reset();
  }
  /**
   * Reset all the state and timeout/interval ids.
   */


  reset() {
    if (this._timeoutID) {
      window.clearTimeout(this._timeoutID);
      this._timeoutID = null;
    }
  }
  /**
   * Return true if connected to the micro:bit.
   * @return {boolean} - whether the micro:bit is connected.
   */


  isConnected() {
    let connected = false;

    if (this._ble) {
      connected = this._ble.isConnected();
    }

    return connected;
  }
  /**
   * Send a message to the peripheral BLE socket.
   * @param {number} command - the BLE command hex.
   * @param {Uint8Array} message - the message to write
   */


  send(command, message) {
    if (!this.isConnected()) return;
    if (this._busy) return; // Set a busy flag so that while we are sending a message and waiting for
    // the response, additional messages are ignored.

    this._busy = true; // Set a timeout after which to reset the busy flag. This is used in case
    // a BLE message was sent for which we never received a response, because
    // e.g. the peripheral was turned off after the message was sent. We reset
    // the busy flag after a while so that it is possible to try again later.

    this._busyTimeoutID = window.setTimeout(() => {
      this._busy = false;
    }, 5000);
    const output = new Uint8Array(message.length + 1);
    output[0] = command; // attach command to beginning of message

    for (let i = 0; i < message.length; i++) {
      output[i + 1] = message[i];
    }

    const data = Base64Util.uint8ArrayToBase64(output);

    this._ble.write(BLEUUID.service, BLEUUID.txChar, data, 'base64', true).then(() => {
      this._busy = false;
      window.clearTimeout(this._busyTimeoutID);
    });
  }
  /**
   * Starts reading data from peripheral after BLE has connected to it.
   * @private
   */


  _onConnect() {
    this._ble.read(BLEUUID.service, BLEUUID.rxChar, true, this._onMessage);

    this._timeoutID = window.setTimeout(() => this._ble.handleDisconnectError(BLEDataStoppedError), BLETimeout);
  }
  /**
   * Process the sensor data from the incoming BLE characteristic.
   * @param {object} base64 - the incoming BLE data.
   * @private
   */


  _onMessage(base64) {
    // parse data
    const data = Base64Util.base64ToUint8Array(base64);
    this._sensors.tiltX = data[1] | data[0] << 8;
    if (this._sensors.tiltX > 1 << 15) this._sensors.tiltX -= 1 << 16;
    this._sensors.tiltY = data[3] | data[2] << 8;
    if (this._sensors.tiltY > 1 << 15) this._sensors.tiltY -= 1 << 16;
    this._sensors.buttonA = data[4];
    this._sensors.buttonB = data[5];
    this._sensors.touchPins[0] = data[6];
    this._sensors.touchPins[1] = data[7];
    this._sensors.touchPins[2] = data[8];
    this._sensors.gestureState = data[9]; // cancel disconnect timeout and start a new one

    window.clearTimeout(this._timeoutID);
    this._timeoutID = window.setTimeout(() => this._ble.handleDisconnectError(BLEDataStoppedError), BLETimeout);
  }
  /**
   * @param {number} pin - the pin to check touch state.
   * @return {number} - the latest value received for the touch pin states.
   * @private
   */


  _checkPinState(pin) {
    return this._sensors.touchPins[pin];
  }

}
/**
 * Enum for tilt sensor direction.
 * @readonly
 * @enum {string}
 */


const MicroBitTiltDirection = {
  FRONT: 'front',
  BACK: 'back',
  LEFT: 'left',
  RIGHT: 'right',
  ANY: 'any'
};
/**
 * Enum for micro:bit gestures.
 * @readonly
 * @enum {string}
 */

const MicroBitGestures = {
  MOVED: 'moved',
  SHAKEN: 'shaken',
  JUMPED: 'jumped'
};
/**
 * Enum for micro:bit buttons.
 * @readonly
 * @enum {string}
 */

const MicroBitButtons = {
  A: 'A',
  B: 'B',
  ANY: 'any'
};
/**
 * Enum for micro:bit pin states.
 * @readonly
 * @enum {string}
 */

const MicroBitPinState = {
  ON: 'on',
  OFF: 'off'
};
/**
 * Scratch 3.0 blocks to interact with a MicroBit peripheral.
 */

class Scratch3MicroBitBlocks {
  /**
   * @return {string} - the name of this extension.
   */
  static get EXTENSION_NAME() {
    return 'micro:bit';
  }
  /**
   * @return {string} - the ID of this extension.
   */


  static get EXTENSION_ID() {
    return 'microbit';
  }
  /**
   * @return {number} - the tilt sensor counts as "tilted" if its tilt angle meets or exceeds this threshold.
   */


  static get TILT_THRESHOLD() {
    return 15;
  }
  /**
   * @return {array} - text and values for each buttons menu element
   */


  get BUTTONS_MENU() {
    return [{
      text: 'A',
      value: MicroBitButtons.A
    }, {
      text: 'B',
      value: MicroBitButtons.B
    }, {
      text: formatMessage({
        id: 'microbit.buttonsMenu.any',
        default: 'any',
        description: 'label for "any" element in button picker for micro:bit extension'
      }),
      value: MicroBitButtons.ANY
    }];
  }
  /**
   * @return {array} - text and values for each gestures menu element
   */


  get GESTURES_MENU() {
    return [{
      text: formatMessage({
        id: 'microbit.gesturesMenu.moved',
        default: 'moved',
        description: 'label for moved gesture in gesture picker for micro:bit extension'
      }),
      value: MicroBitGestures.MOVED
    }, {
      text: formatMessage({
        id: 'microbit.gesturesMenu.shaken',
        default: 'shaken',
        description: 'label for shaken gesture in gesture picker for micro:bit extension'
      }),
      value: MicroBitGestures.SHAKEN
    }, {
      text: formatMessage({
        id: 'microbit.gesturesMenu.jumped',
        default: 'jumped',
        description: 'label for jumped gesture in gesture picker for micro:bit extension'
      }),
      value: MicroBitGestures.JUMPED
    }];
  }
  /**
   * @return {array} - text and values for each pin state menu element
   */


  get PIN_STATE_MENU() {
    return [{
      text: formatMessage({
        id: 'microbit.pinStateMenu.on',
        default: 'on',
        description: 'label for on element in pin state picker for micro:bit extension'
      }),
      value: MicroBitPinState.ON
    }, {
      text: formatMessage({
        id: 'microbit.pinStateMenu.off',
        default: 'off',
        description: 'label for off element in pin state picker for micro:bit extension'
      }),
      value: MicroBitPinState.OFF
    }];
  }
  /**
   * @return {array} - text and values for each tilt direction menu element
   */


  get TILT_DIRECTION_MENU() {
    return [{
      text: formatMessage({
        id: 'microbit.tiltDirectionMenu.front',
        default: 'front',
        description: 'label for front element in tilt direction picker for micro:bit extension'
      }),
      value: MicroBitTiltDirection.FRONT
    }, {
      text: formatMessage({
        id: 'microbit.tiltDirectionMenu.back',
        default: 'back',
        description: 'label for back element in tilt direction picker for micro:bit extension'
      }),
      value: MicroBitTiltDirection.BACK
    }, {
      text: formatMessage({
        id: 'microbit.tiltDirectionMenu.left',
        default: 'left',
        description: 'label for left element in tilt direction picker for micro:bit extension'
      }),
      value: MicroBitTiltDirection.LEFT
    }, {
      text: formatMessage({
        id: 'microbit.tiltDirectionMenu.right',
        default: 'right',
        description: 'label for right element in tilt direction picker for micro:bit extension'
      }),
      value: MicroBitTiltDirection.RIGHT
    }];
  }
  /**
   * @return {array} - text and values for each tilt direction (plus "any") menu element
   */


  get TILT_DIRECTION_ANY_MENU() {
    return [...this.TILT_DIRECTION_MENU, {
      text: formatMessage({
        id: 'microbit.tiltDirectionMenu.any',
        default: 'any',
        description: 'label for any direction element in tilt direction picker for micro:bit extension'
      }),
      value: MicroBitTiltDirection.ANY
    }];
  }
  /**
   * Construct a set of MicroBit blocks.
   * @param {Runtime} runtime - the Scratch 3.0 runtime.
   */


  constructor(runtime) {
    /**
     * The Scratch 3.0 runtime.
     * @type {Runtime}
     */
    this.runtime = runtime; // Create a new MicroBit peripheral instance

    this._peripheral = new MicroBit(this.runtime, Scratch3MicroBitBlocks.EXTENSION_ID);
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: Scratch3MicroBitBlocks.EXTENSION_ID,
      name: Scratch3MicroBitBlocks.EXTENSION_NAME,
      blockIconURI: blockIconURI,
      showStatusButton: true,
      blocks: [{
        opcode: 'whenButtonPressed',
        text: formatMessage({
          id: 'microbit.whenButtonPressed',
          default: 'when [BTN] button pressed',
          description: 'when the selected button on the micro:bit is pressed'
        }),
        blockType: BlockType.HAT,
        arguments: {
          BTN: {
            type: ArgumentType.STRING,
            menu: 'buttons',
            defaultValue: MicroBitButtons.A
          }
        }
      }, {
        opcode: 'isButtonPressed',
        text: formatMessage({
          id: 'microbit.isButtonPressed',
          default: '[BTN] button pressed?',
          description: 'is the selected button on the micro:bit pressed?'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          BTN: {
            type: ArgumentType.STRING,
            menu: 'buttons',
            defaultValue: MicroBitButtons.A
          }
        }
      }, '---', {
        opcode: 'whenGesture',
        text: formatMessage({
          id: 'microbit.whenGesture',
          default: 'when [GESTURE]',
          description: 'when the selected gesture is detected by the micro:bit'
        }),
        blockType: BlockType.HAT,
        arguments: {
          GESTURE: {
            type: ArgumentType.STRING,
            menu: 'gestures',
            defaultValue: MicroBitGestures.MOVED
          }
        }
      }, '---', {
        opcode: 'displaySymbol',
        text: formatMessage({
          id: 'microbit.displaySymbol',
          default: 'display [MATRIX]',
          description: 'display a pattern on the micro:bit display'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MATRIX: {
            type: ArgumentType.MATRIX,
            defaultValue: '0101010101100010101000100'
          }
        }
      }, {
        opcode: 'displayText',
        text: formatMessage({
          id: 'microbit.displayText',
          default: 'display text [TEXT]',
          description: 'display text on the micro:bit display'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          TEXT: {
            type: ArgumentType.STRING,
            defaultValue: formatMessage({
              id: 'microbit.defaultTextToDisplay',
              default: 'Hello!',
              description: "default text to display.\n                                IMPORTANT - the micro:bit only supports letters a-z, A-Z.\n                                Please substitute a default word in your language\n                                that can be written with those characters,\n                                substitute non-accented characters or leave it as \"Hello!\".\n                                Check the micro:bit site documentation for details"
            })
          }
        }
      }, {
        opcode: 'displayClear',
        text: formatMessage({
          id: 'microbit.clearDisplay',
          default: 'clear display',
          description: 'display nothing on the micro:bit display'
        }),
        blockType: BlockType.COMMAND
      }, '---', {
        opcode: 'whenTilted',
        text: formatMessage({
          id: 'microbit.whenTilted',
          default: 'when tilted [DIRECTION]',
          description: 'when the micro:bit is tilted in a direction'
        }),
        blockType: BlockType.HAT,
        arguments: {
          DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'tiltDirectionAny',
            defaultValue: MicroBitTiltDirection.ANY
          }
        }
      }, {
        opcode: 'isTilted',
        text: formatMessage({
          id: 'microbit.isTilted',
          default: 'tilted [DIRECTION]?',
          description: 'is the micro:bit is tilted in a direction?'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'tiltDirectionAny',
            defaultValue: MicroBitTiltDirection.ANY
          }
        }
      }, {
        opcode: 'getTiltAngle',
        text: formatMessage({
          id: 'microbit.tiltAngle',
          default: 'tilt angle [DIRECTION]',
          description: 'how much the micro:bit is tilted in a direction'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'tiltDirection',
            defaultValue: MicroBitTiltDirection.FRONT
          }
        }
      }, '---', {
        opcode: 'whenPinConnected',
        text: formatMessage({
          id: 'microbit.whenPinConnected',
          default: 'when pin [PIN] connected',
          description: 'when the pin detects a connection to Earth/Ground'
        }),
        blockType: BlockType.HAT,
        arguments: {
          PIN: {
            type: ArgumentType.STRING,
            menu: 'touchPins',
            defaultValue: '0'
          }
        }
      }],
      menus: {
        buttons: {
          acceptReporters: true,
          items: this.BUTTONS_MENU
        },
        gestures: {
          acceptReporters: true,
          items: this.GESTURES_MENU
        },
        pinState: {
          acceptReporters: true,
          items: this.PIN_STATE_MENU
        },
        tiltDirection: {
          acceptReporters: true,
          items: this.TILT_DIRECTION_MENU
        },
        tiltDirectionAny: {
          acceptReporters: true,
          items: this.TILT_DIRECTION_ANY_MENU
        },
        touchPins: {
          acceptReporters: true,
          items: ['0', '1', '2']
        }
      }
    };
  }
  /**
   * Test whether the A or B button is pressed
   * @param {object} args - the block's arguments.
   * @return {boolean} - true if the button is pressed.
   */


  whenButtonPressed(args) {
    if (args.BTN === 'any') {
      return this._peripheral.buttonA | this._peripheral.buttonB;
    } else if (args.BTN === 'A') {
      return this._peripheral.buttonA;
    } else if (args.BTN === 'B') {
      return this._peripheral.buttonB;
    }

    return false;
  }
  /**
   * Test whether the A or B button is pressed
   * @param {object} args - the block's arguments.
   * @return {boolean} - true if the button is pressed.
   */


  isButtonPressed(args) {
    if (args.BTN === 'any') {
      return (this._peripheral.buttonA | this._peripheral.buttonB) !== 0;
    } else if (args.BTN === 'A') {
      return this._peripheral.buttonA !== 0;
    } else if (args.BTN === 'B') {
      return this._peripheral.buttonB !== 0;
    }

    return false;
  }
  /**
   * Test whether the micro:bit is moving
   * @param {object} args - the block's arguments.
   * @return {boolean} - true if the micro:bit is moving.
   */


  whenGesture(args) {
    const gesture = cast.toString(args.GESTURE);

    if (gesture === 'moved') {
      return this._peripheral.gestureState >> 2 & 1;
    } else if (gesture === 'shaken') {
      return this._peripheral.gestureState & 1;
    } else if (gesture === 'jumped') {
      return this._peripheral.gestureState >> 1 & 1;
    }

    return false;
  }
  /**
   * Display a predefined symbol on the 5x5 LED matrix.
   * @param {object} args - the block's arguments.
   * @return {Promise} - a Promise that resolves after a tick.
   */


  displaySymbol(args) {
    const symbol = cast.toString(args.MATRIX).replace(/\s/g, '');

    const reducer = (accumulator, c, index) => {
      const value = c === '0' ? accumulator : accumulator + Math.pow(2, index);
      return value;
    };

    const hex = symbol.split('').reduce(reducer, 0);

    if (hex !== null) {
      this._peripheral.ledMatrixState[0] = hex & 0x1F;
      this._peripheral.ledMatrixState[1] = hex >> 5 & 0x1F;
      this._peripheral.ledMatrixState[2] = hex >> 10 & 0x1F;
      this._peripheral.ledMatrixState[3] = hex >> 15 & 0x1F;
      this._peripheral.ledMatrixState[4] = hex >> 20 & 0x1F;

      this._peripheral.displayMatrix(this._peripheral.ledMatrixState);
    }

    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Display text on the 5x5 LED matrix.
   * @param {object} args - the block's arguments.
   * @return {Promise} - a Promise that resolves after the text is done printing.
   * Note the limit is 19 characters
   * The print time is calculated by multiplying the number of horizontal pixels
   * by the default scroll delay of 120ms.
   * The number of horizontal pixels = 6px for each character in the string,
   * 1px before the string, and 5px after the string.
   */


  displayText(args) {
    const text = String(args.TEXT).substring(0, 19);
    if (text.length > 0) this._peripheral.displayText(text);
    const yieldDelay = 120 * (6 * text.length + 6);
    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, yieldDelay);
    });
  }
  /**
   * Turn all 5x5 matrix LEDs off.
   * @return {Promise} - a Promise that resolves after a tick.
   */


  displayClear() {
    for (let i = 0; i < 5; i++) {
      this._peripheral.ledMatrixState[i] = 0;
    }

    this._peripheral.displayMatrix(this._peripheral.ledMatrixState);

    return new Promise(resolve => {
      setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} DIRECTION - the tilt direction to test (front, back, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  whenTilted(args) {
    return this._isTilted(args.DIRECTION);
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} DIRECTION - the tilt direction to test (front, back, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  isTilted(args) {
    return this._isTilted(args.DIRECTION);
  }
  /**
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} DIRECTION - the direction (front, back, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(front) = -getTiltAngle(back) and getTiltAngle(left) = -getTiltAngle(right).
   */


  getTiltAngle(args) {
    return this._getTiltAngle(args.DIRECTION);
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {TiltDirection} direction - the tilt direction to test (front, back, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   * @private
   */


  _isTilted(direction) {
    switch (direction) {
      case MicroBitTiltDirection.ANY:
        return Math.abs(this._peripheral.tiltX / 10) >= Scratch3MicroBitBlocks.TILT_THRESHOLD || Math.abs(this._peripheral.tiltY / 10) >= Scratch3MicroBitBlocks.TILT_THRESHOLD;

      default:
        return this._getTiltAngle(direction) >= Scratch3MicroBitBlocks.TILT_THRESHOLD;
    }
  }
  /**
   * @param {TiltDirection} direction - the direction (front, back, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(front) = -getTiltAngle(back) and getTiltAngle(left) = -getTiltAngle(right).
   * @private
   */


  _getTiltAngle(direction) {
    switch (direction) {
      case MicroBitTiltDirection.FRONT:
        return Math.round(this._peripheral.tiltY / -10);

      case MicroBitTiltDirection.BACK:
        return Math.round(this._peripheral.tiltY / 10);

      case MicroBitTiltDirection.LEFT:
        return Math.round(this._peripheral.tiltX / -10);

      case MicroBitTiltDirection.RIGHT:
        return Math.round(this._peripheral.tiltX / 10);

      default:
        log.warn("Unknown tilt direction in _getTiltAngle: ".concat(direction));
    }
  }
  /**
   * @param {object} args - the block's arguments.
   * @return {boolean} - the touch pin state.
   * @private
   */


  whenPinConnected(args) {
    const pin = parseInt(args.PIN, 10);
    if (isNaN(pin)) return;
    if (pin < 0 || pin > 2) return false;
    return this._peripheral._checkPinState(pin);
  }

}

module.exports = Scratch3MicroBitBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/1-snare.mp3":
/*!****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/1-snare.mp3 ***!
  \****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3":
/*!**********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3 ***!
  \**********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3":
/*!*******************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3 ***!
  \*******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/12-triangle.mp3":
/*!********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/12-triangle.mp3 ***!
  \********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/13-bongo.mp3":
/*!*****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/13-bongo.mp3 ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/14-conga.mp3":
/*!*****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/14-conga.mp3 ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3":
/*!******************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3 ***!
  \******************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/16-guiro.mp3":
/*!*****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/16-guiro.mp3 ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3":
/*!*********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3 ***!
  \*********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/18-cuica.mp3":
/*!*****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/18-cuica.mp3 ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3":
/*!********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3 ***!
  \********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3":
/*!*********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3 ***!
  \*********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3":
/*!***********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3 ***!
  \***********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3":
/*!**********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3 ***!
  \**********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3":
/*!************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3 ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3":
/*!*********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3 ***!
  \*********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3":
/*!********************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3 ***!
  \********************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/9-claves.mp3":
/*!*****************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/9-claves.mp3 ***!
  \*****************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3":
/*!*****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3 ***!
  \*****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3":
/*!*****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3 ***!
  \*****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3 ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3 ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3 ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3":
/*!*********************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3 ***!
  \*********************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3":
/*!*********************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3 ***!
  \*********************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3 ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3 ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3 ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3 ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3":
/*!**********************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3 ***!
  \**********************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3":
/*!*******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3 ***!
  \*******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3":
/*!******************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3 ***!
  \******************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3":
/*!**************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3 ***!
  \**************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3":
/*!***********************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3 ***!
  \***********************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3":
/*!************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3 ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3":
/*!************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3 ***!
  \************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3":
/*!*****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3 ***!
  \*****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3":
/*!*************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3 ***!
  \*************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3":
/*!****************************************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3 ***!
  \****************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = null;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/index.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/index.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Clone = __webpack_require__(/*! ../../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const Timer = __webpack_require__(/*! ../../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");
/**
 * The instrument and drum sounds, loaded as static assets.
 * @type {object}
 */


let assetData = {};

try {
  assetData = __webpack_require__(/*! ./manifest */ "./node_modules/scratch-vm/src/extensions/scratch3_music/manifest.js");
} catch (e) {// Non-webpack environment, don't worry about assets.
}
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PHRpdGxlPm11c2ljLWJsb2NrLWljb248L3RpdGxlPjxkZWZzPjxwYXRoIGQ9Ik0zMi4xOCAyNS44NzRDMzIuNjM2IDI4LjE1NyAzMC41MTIgMzAgMjcuNDMzIDMwYy0zLjA3IDAtNS45MjMtMS44NDMtNi4zNzItNC4xMjYtLjQ1OC0yLjI4NSAxLjY2NS00LjEzNiA0Ljc0My00LjEzNi42NDcgMCAxLjI4My4wODQgMS44OS4yMzQuMzM4LjA4Ni42MzcuMTguOTM4LjMwMi44Ny0uMDItLjEwNC0yLjI5NC0xLjgzNS0xMi4yMy0yLjEzNC0xMi4zMDIgMy4wNi0xLjg3IDguNzY4LTIuNzUyIDUuNzA4LS44ODUuMDc2IDQuODItMy42NSAzLjg0NC0zLjcyNC0uOTg3LTQuNjUtNy4xNTMuMjYzIDE0LjczOHptLTE2Ljk5OCA1Ljk5QzE1LjYzIDM0LjE0OCAxMy41MDcgMzYgMTAuNDQgMzZjLTMuMDcgMC01LjkyMi0xLjg1Mi02LjM4LTQuMTM2LS40NDgtMi4yODQgMS42NzQtNC4xMzUgNC43NS00LjEzNSAxLjAwMyAwIDEuOTc1LjE5NiAyLjg1NS41NDMuODIyLS4wNTUtLjE1LTIuMzc3LTEuODYyLTEyLjIyOC0yLjEzMy0xMi4zMDMgMy4wNi0xLjg3IDguNzY0LTIuNzUzIDUuNzA2LS44OTQuMDc2IDQuODItMy42NDggMy44MzQtMy43MjQtLjk4Ny00LjY1LTcuMTUyLjI2MiAxNC43Mzh6IiBpZD0iYSIvPjwvZGVmcz48ZyBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPjx1c2UgZmlsbD0iI0ZGRiIgeGxpbms6aHJlZj0iI2EiLz48cGF0aCBzdHJva2Utb3BhY2l0eT0iLjEiIHN0cm9rZT0iIzAwMCIgZD0iTTI4LjQ1NiAyMS42NzVjLS4wMS0uMzEyLS4wODctLjgyNS0uMjU2LTEuNzAyLS4wOTYtLjQ5NS0uNjEyLTMuMDIyLS43NTMtMy43My0uMzk1LTEuOTgtLjc2LTMuOTItMS4xNDItNi4xMTMtLjczMi00LjIyMy0uNjkzLTYuMDUuMzQ0LTYuNTI3LjUtLjIzIDEuMDYtLjA4IDEuODQuMzUuNDE0LjIyNyAyLjE4MiAxLjM2NSAyLjA3IDEuMjk2IDEuOTk0IDEuMjQyIDMuNDY0IDEuNzc0IDQuOTMgMS41NDggMS41MjYtLjIzNyAyLjUwNC0uMDYgMi44NzYuNjE4LjM0OC42MzUuMDE1IDEuNDE2LS43MyAyLjE4LTEuNDcyIDEuNTE2LTMuOTc1IDIuNTE0LTUuODQ4IDIuMDIzLS44MjItLjIyLTEuMjM4LS40NjUtMi4zOC0xLjI2N2wtLjA5NS0uMDY2Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMy4yOTQgMS4zMzYgMi4wOCA5LjE4NyAyLjYzNyAxMS42NzRsLjAwMi4wMTJjLjUyOCAyLjYzNy0xLjg3MyA0LjcyNC01LjIzNiA0LjcyNC0zLjI5IDAtNi4zNjMtMS45ODgtNi44NjItNC41MjgtLjUzLTIuNjQgMS44NzMtNC43MzQgNS4yMzMtNC43MzQuNjcyIDAgMS4zNDcuMDg1IDIuMDE0LjI1LjIyNy4wNTcuNDM2LjExOC42MzYuMTg3em0tMTYuOTk2IDUuOTljLS4wMS0uMzE4LS4wOS0uODM4LS4yNjYtMS43MzctLjA5LS40Ni0uNTk1LTIuOTM3LS43NTMtMy43MjctLjM5LTEuOTYtLjc1LTMuODktMS4xMy02LjA3LS43MzItNC4yMjMtLjY5Mi02LjA1LjM0NC02LjUyNi41MDItLjIzIDEuMDYtLjA4MiAxLjg0LjM1LjQxNS4yMjcgMi4xODIgMS4zNjQgMi4wNyAxLjI5NSAxLjk5MyAxLjI0MiAzLjQ2MiAxLjc3NCA0LjkyNiAxLjU0OCAxLjUyNS0uMjQgMi41MDQtLjA2NCAyLjg3Ni42MTQuMzQ4LjYzNS4wMTUgMS40MTUtLjcyOCAyLjE4LTEuNDc0IDEuNTE3LTMuOTc3IDIuNTEzLTUuODQ3IDIuMDE3LS44Mi0uMjItMS4yMzYtLjQ2NC0yLjM3OC0xLjI2N2wtLjA5NS0uMDY1Yy4wNDcuNTkzLjI2NCAxLjc0LjcxNyAzLjgwMi4yOTQgMS4zMzcgMi4wNzggOS4xOSAyLjYzNiAxMS42NzVsLjAwMy4wMTNjLjUxNyAyLjYzOC0xLjg4NCA0LjczMi01LjIzNCA0LjczMi0zLjI4NyAwLTYuMzYtMS45OTMtNi44Ny00LjU0LS41Mi0yLjY0IDEuODg0LTQuNzMgNS4yNC00LjczLjkwNSAwIDEuODAzLjE1IDIuNjUuNDM2eiIvPjwvZz48L3N2Zz4=';
/**
 * Icon svg to be displayed in the category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len

const menuIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTE2LjA5IDEyLjkzN2MuMjI4IDEuMTQxLS44MzMgMi4wNjMtMi4zNzMgMi4wNjMtMS41MzUgMC0yLjk2Mi0uOTIyLTMuMTg2LTIuMDYzLS4yMy0xLjE0Mi44MzMtMi4wNjggMi4zNzItMi4wNjguMzIzIDAgLjY0MS4wNDIuOTQ1LjExN2EzLjUgMy41IDAgMCAxIC40NjguMTUxYy40MzUtLjAxLS4wNTItMS4xNDctLjkxNy02LjExNC0xLjA2Ny02LjE1MiAxLjUzLS45MzUgNC4zODQtMS4zNzcgMi44NTQtLjQ0Mi4wMzggMi40MS0xLjgyNSAxLjkyMi0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc3LjEzMiA3LjM3ek03LjQ2IDguNTYzYy0xLjg2Mi0uNDkzLTIuMzI1LTMuNTc2LjEzIDcuMzdDNy44MTYgMTcuMDczIDYuNzU0IDE4IDUuMjIgMThjLTEuNTM1IDAtMi45NjEtLjkyNi0zLjE5LTIuMDY4LS4yMjQtMS4xNDIuODM3LTIuMDY3IDIuMzc1LTIuMDY3LjUwMSAwIC45ODcuMDk4IDEuNDI3LjI3Mi40MTItLjAyOC0uMDc0LTEuMTg5LS45My02LjExNEMzLjgzNCAxLjg3IDYuNDMgNy4wODcgOS4yODIgNi42NDZjMi44NTQtLjQ0Ny4wMzggMi40MS0xLjgyMyAxLjkxN3oiIGZpbGw9IiM1NzVFNzUiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvc3ZnPg==';
/**
 * Class for the music-related blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */

class Scratch3MusicBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * The number of drum and instrument sounds currently being played simultaneously.
     * @type {number}
     * @private
     */

    this._concurrencyCounter = 0;
    /**
     * An array of sound players, one for each drum sound.
     * @type {Array}
     * @private
     */

    this._drumPlayers = [];
    /**
     * An array of arrays of sound players. Each instrument has one or more audio players.
     * @type {Array[]}
     * @private
     */

    this._instrumentPlayerArrays = [];
    /**
     * An array of arrays of sound players. Each instrument mya have an audio player for each playable note.
     * @type {Array[]}
     * @private
     */

    this._instrumentPlayerNoteArrays = [];
    /**
     * An array of audio bufferSourceNodes. Each time you play an instrument or drum sound,
     * a bufferSourceNode is created. We keep references to them to make sure their onended
     * events can fire.
     * @type {Array}
     * @private
     */

    this._bufferSources = [];

    this._loadAllSounds();

    this._onTargetCreated = this._onTargetCreated.bind(this);
    this.runtime.on('targetWasCreated', this._onTargetCreated);
    this._playNoteForPicker = this._playNoteForPicker.bind(this);
    this.runtime.on('PLAY_NOTE', this._playNoteForPicker);
  }
  /**
   * Decode the full set of drum and instrument sounds, and store the audio buffers in arrays.
   */


  _loadAllSounds() {
    const loadingPromises = [];
    this.DRUM_INFO.forEach((drumInfo, index) => {
      const filePath = "drums/".concat(drumInfo.fileName);

      const promise = this._storeSound(filePath, index, this._drumPlayers);

      loadingPromises.push(promise);
    });
    this.INSTRUMENT_INFO.forEach((instrumentInfo, instrumentIndex) => {
      this._instrumentPlayerArrays[instrumentIndex] = [];
      this._instrumentPlayerNoteArrays[instrumentIndex] = [];
      instrumentInfo.samples.forEach((sample, noteIndex) => {
        const filePath = "instruments/".concat(instrumentInfo.dirName, "/").concat(sample);

        const promise = this._storeSound(filePath, noteIndex, this._instrumentPlayerArrays[instrumentIndex]);

        loadingPromises.push(promise);
      });
    });
    Promise.all(loadingPromises).then(() => {// @TODO: Update the extension status indicator.
    });
  }
  /**
   * Decode a sound and store the player in an array.
   * @param {string} filePath - the audio file name.
   * @param {number} index - the index at which to store the audio player.
   * @param {array} playerArray - the array of players in which to store it.
   * @return {Promise} - a promise which will resolve once the sound has been stored.
   */


  _storeSound(filePath, index, playerArray) {
    const fullPath = "".concat(filePath, ".mp3");
    if (!assetData[fullPath]) return;
    const soundFile = assetData[fullPath];
    return fetch(soundFile).then(r => r.arrayBuffer()).then(soundBuffer => this._decodeSound(soundBuffer)).then(player => {
      playerArray[index] = player;
    });
  }
  /**
   * Decode a sound and return a promise with the audio buffer.
   * @param  {ArrayBuffer} soundBuffer - a buffer containing the encoded audio.
   * @return {Promise} - a promise which will resolve once the sound has decoded.
   */


  _decodeSound(soundBuffer) {
    const engine = this.runtime.audioEngine;

    if (!engine) {
      return Promise.reject(new Error('No Audio Context Detected'));
    } // Check for newer promise-based API


    return engine.decodeSoundPlayer({
      data: {
        buffer: soundBuffer
      }
    });
  }
  /**
   * Create data for a menu in scratch-blocks format, consisting of an array of objects with text and
   * value properties. The text is a translated string, and the value is one-indexed.
   * @param  {object[]} info - An array of info objects each having a name property.
   * @return {array} - An array of objects with text and value properties.
   * @private
   */


  _buildMenu(info) {
    return info.map((entry, index) => {
      const obj = {};
      obj.text = entry.name;
      obj.value = String(index + 1);
      return obj;
    });
  }
  /**
   * An array of info about each drum.
   * @type {object[]}
   * @param {string} name - the translatable name to display in the drums menu.
   * @param {string} fileName - the name of the audio file containing the drum sound.
   */


  get DRUM_INFO() {
    return [{
      name: formatMessage({
        id: 'music.drumSnare',
        default: '(1) Snare Drum',
        description: 'Sound of snare drum as used in a standard drum kit'
      }),
      fileName: '1-snare'
    }, {
      name: formatMessage({
        id: 'music.drumBass',
        default: '(2) Bass Drum',
        description: 'Sound of bass drum as used in a standard drum kit'
      }),
      fileName: '2-bass-drum'
    }, {
      name: formatMessage({
        id: 'music.drumSideStick',
        default: '(3) Side Stick',
        description: 'Sound of a drum stick hitting the side of a drum (usually the snare)'
      }),
      fileName: '3-side-stick'
    }, {
      name: formatMessage({
        id: 'music.drumCrashCymbal',
        default: '(4) Crash Cymbal',
        description: 'Sound of a drum stick hitting a crash cymbal'
      }),
      fileName: '4-crash-cymbal'
    }, {
      name: formatMessage({
        id: 'music.drumOpenHiHat',
        default: '(5) Open Hi-Hat',
        description: 'Sound of a drum stick hitting a hi-hat while open'
      }),
      fileName: '5-open-hi-hat'
    }, {
      name: formatMessage({
        id: 'music.drumClosedHiHat',
        default: '(6) Closed Hi-Hat',
        description: 'Sound of a drum stick hitting a hi-hat while closed'
      }),
      fileName: '6-closed-hi-hat'
    }, {
      name: formatMessage({
        id: 'music.drumTambourine',
        default: '(7) Tambourine',
        description: 'Sound of a tambourine being struck'
      }),
      fileName: '7-tambourine'
    }, {
      name: formatMessage({
        id: 'music.drumHandClap',
        default: '(8) Hand Clap',
        description: 'Sound of two hands clapping together'
      }),
      fileName: '8-hand-clap'
    }, {
      name: formatMessage({
        id: 'music.drumClaves',
        default: '(9) Claves',
        description: 'Sound of claves being struck together'
      }),
      fileName: '9-claves'
    }, {
      name: formatMessage({
        id: 'music.drumWoodBlock',
        default: '(10) Wood Block',
        description: 'Sound of a wood block being struck'
      }),
      fileName: '10-wood-block'
    }, {
      name: formatMessage({
        id: 'music.drumCowbell',
        default: '(11) Cowbell',
        description: 'Sound of a cowbell being struck'
      }),
      fileName: '11-cowbell'
    }, {
      name: formatMessage({
        id: 'music.drumTriangle',
        default: '(12) Triangle',
        description: 'Sound of a triangle (instrument) being struck'
      }),
      fileName: '12-triangle'
    }, {
      name: formatMessage({
        id: 'music.drumBongo',
        default: '(13) Bongo',
        description: 'Sound of a bongo being struck'
      }),
      fileName: '13-bongo'
    }, {
      name: formatMessage({
        id: 'music.drumConga',
        default: '(14) Conga',
        description: 'Sound of a conga being struck'
      }),
      fileName: '14-conga'
    }, {
      name: formatMessage({
        id: 'music.drumCabasa',
        default: '(15) Cabasa',
        description: 'Sound of a cabasa being shaken'
      }),
      fileName: '15-cabasa'
    }, {
      name: formatMessage({
        id: 'music.drumGuiro',
        default: '(16) Guiro',
        description: 'Sound of a guiro being played'
      }),
      fileName: '16-guiro'
    }, {
      name: formatMessage({
        id: 'music.drumVibraslap',
        default: '(17) Vibraslap',
        description: 'Sound of a Vibraslap being played'
      }),
      fileName: '17-vibraslap'
    }, {
      name: formatMessage({
        id: 'music.drumCuica',
        default: '(18) Cuica',
        description: 'Sound of a cuica being played'
      }),
      fileName: '18-cuica'
    }];
  }
  /**
   * An array of info about each instrument.
   * @type {object[]}
   * @param {string} name - the translatable name to display in the instruments menu.
   * @param {string} dirName - the name of the directory containing audio samples for this instrument.
   * @param {number} [releaseTime] - an optional duration for the release portion of each note.
   * @param {number[]} samples - an array of numbers representing the MIDI note number for each
   *                           sampled sound used to play this instrument.
   */


  get INSTRUMENT_INFO() {
    return [{
      name: formatMessage({
        id: 'music.instrumentPiano',
        default: '(1) Piano',
        description: 'Sound of a piano'
      }),
      dirName: '1-piano',
      releaseTime: 0.5,
      samples: [24, 36, 48, 60, 72, 84, 96, 108]
    }, {
      name: formatMessage({
        id: 'music.instrumentElectricPiano',
        default: '(2) Electric Piano',
        description: 'Sound of an electric piano'
      }),
      dirName: '2-electric-piano',
      releaseTime: 0.5,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentOrgan',
        default: '(3) Organ',
        description: 'Sound of an organ'
      }),
      dirName: '3-organ',
      releaseTime: 0.5,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentGuitar',
        default: '(4) Guitar',
        description: 'Sound of an accoustic guitar'
      }),
      dirName: '4-guitar',
      releaseTime: 0.5,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentElectricGuitar',
        default: '(5) Electric Guitar',
        description: 'Sound of an electric guitar'
      }),
      dirName: '5-electric-guitar',
      releaseTime: 0.5,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentBass',
        default: '(6) Bass',
        description: 'Sound of an accoustic upright bass'
      }),
      dirName: '6-bass',
      releaseTime: 0.25,
      samples: [36, 48]
    }, {
      name: formatMessage({
        id: 'music.instrumentPizzicato',
        default: '(7) Pizzicato',
        description: 'Sound of a string instrument (e.g. violin) being plucked'
      }),
      dirName: '7-pizzicato',
      releaseTime: 0.25,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentCello',
        default: '(8) Cello',
        description: 'Sound of a cello being played with a bow'
      }),
      dirName: '8-cello',
      releaseTime: 0.1,
      samples: [36, 48, 60]
    }, {
      name: formatMessage({
        id: 'music.instrumentTrombone',
        default: '(9) Trombone',
        description: 'Sound of a trombone being played'
      }),
      dirName: '9-trombone',
      samples: [36, 48, 60]
    }, {
      name: formatMessage({
        id: 'music.instrumentClarinet',
        default: '(10) Clarinet',
        description: 'Sound of a clarinet being played'
      }),
      dirName: '10-clarinet',
      samples: [48, 60]
    }, {
      name: formatMessage({
        id: 'music.instrumentSaxophone',
        default: '(11) Saxophone',
        description: 'Sound of a saxophone being played'
      }),
      dirName: '11-saxophone',
      samples: [36, 60, 84]
    }, {
      name: formatMessage({
        id: 'music.instrumentFlute',
        default: '(12) Flute',
        description: 'Sound of a flute being played'
      }),
      dirName: '12-flute',
      samples: [60, 72]
    }, {
      name: formatMessage({
        id: 'music.instrumentWoodenFlute',
        default: '(13) Wooden Flute',
        description: 'Sound of a wooden flute being played'
      }),
      dirName: '13-wooden-flute',
      samples: [60, 72]
    }, {
      name: formatMessage({
        id: 'music.instrumentBassoon',
        default: '(14) Bassoon',
        description: 'Sound of a bassoon being played'
      }),
      dirName: '14-bassoon',
      samples: [36, 48, 60]
    }, {
      name: formatMessage({
        id: 'music.instrumentChoir',
        default: '(15) Choir',
        description: 'Sound of a choir singing'
      }),
      dirName: '15-choir',
      releaseTime: 0.25,
      samples: [48, 60, 72]
    }, {
      name: formatMessage({
        id: 'music.instrumentVibraphone',
        default: '(16) Vibraphone',
        description: 'Sound of a vibraphone being struck'
      }),
      dirName: '16-vibraphone',
      releaseTime: 0.5,
      samples: [60, 72]
    }, {
      name: formatMessage({
        id: 'music.instrumentMusicBox',
        default: '(17) Music Box',
        description: 'Sound of a music box playing'
      }),
      dirName: '17-music-box',
      releaseTime: 0.25,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentSteelDrum',
        default: '(18) Steel Drum',
        description: 'Sound of a steel drum being struck'
      }),
      dirName: '18-steel-drum',
      releaseTime: 0.5,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentMarimba',
        default: '(19) Marimba',
        description: 'Sound of a marimba being struck'
      }),
      dirName: '19-marimba',
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentSynthLead',
        default: '(20) Synth Lead',
        description: 'Sound of a "lead" synthesizer being played'
      }),
      dirName: '20-synth-lead',
      releaseTime: 0.1,
      samples: [60]
    }, {
      name: formatMessage({
        id: 'music.instrumentSynthPad',
        default: '(21) Synth Pad',
        description: 'Sound of a "pad" synthesizer being played'
      }),
      dirName: '21-synth-pad',
      releaseTime: 0.25,
      samples: [60]
    }];
  }
  /**
   * An array that is a mapping from MIDI instrument numbers to Scratch instrument numbers.
   * @type {number[]}
   */


  get MIDI_INSTRUMENTS() {
    return [// Acoustic Grand, Bright Acoustic, Electric Grand, Honky-Tonk
    1, 1, 1, 1, // Electric Piano 1, Electric Piano 2, Harpsichord, Clavinet
    2, 2, 4, 4, // Celesta, Glockenspiel, Music Box, Vibraphone
    17, 17, 17, 16, // Marimba, Xylophone, Tubular Bells, Dulcimer
    19, 16, 17, 17, // Drawbar Organ, Percussive Organ, Rock Organ, Church Organ
    3, 3, 3, 3, // Reed Organ, Accordion, Harmonica, Tango Accordion
    3, 3, 3, 3, // Nylon String Guitar, Steel String Guitar, Electric Jazz Guitar, Electric Clean Guitar
    4, 4, 5, 5, // Electric Muted Guitar, Overdriven Guitar,Distortion Guitar, Guitar Harmonics
    5, 5, 5, 5, // Acoustic Bass, Electric Bass (finger), Electric Bass (pick), Fretless Bass
    6, 6, 6, 6, // Slap Bass 1, Slap Bass 2, Synth Bass 1, Synth Bass 2
    6, 6, 6, 6, // Violin, Viola, Cello, Contrabass
    8, 8, 8, 8, // Tremolo Strings, Pizzicato Strings, Orchestral Strings, Timpani
    8, 7, 8, 19, // String Ensemble 1, String Ensemble 2, SynthStrings 1, SynthStrings 2
    8, 8, 8, 8, // Choir Aahs, Voice Oohs, Synth Voice, Orchestra Hit
    15, 15, 15, 19, // Trumpet, Trombone, Tuba, Muted Trumpet
    9, 9, 9, 9, // French Horn, Brass Section, SynthBrass 1, SynthBrass 2
    9, 9, 9, 9, // Soprano Sax, Alto Sax, Tenor Sax, Baritone Sax
    11, 11, 11, 11, // Oboe, English Horn, Bassoon, Clarinet
    14, 14, 14, 10, // Piccolo, Flute, Recorder, Pan Flute
    12, 12, 13, 13, // Blown Bottle, Shakuhachi, Whistle, Ocarina
    13, 13, 12, 12, // Lead 1 (square), Lead 2 (sawtooth), Lead 3 (calliope), Lead 4 (chiff)
    20, 20, 20, 20, // Lead 5 (charang), Lead 6 (voice), Lead 7 (fifths), Lead 8 (bass+lead)
    20, 20, 20, 20, // Pad 1 (new age), Pad 2 (warm), Pad 3 (polysynth), Pad 4 (choir)
    21, 21, 21, 21, // Pad 5 (bowed), Pad 6 (metallic), Pad 7 (halo), Pad 8 (sweep)
    21, 21, 21, 21, // FX 1 (rain), FX 2 (soundtrack), FX 3 (crystal), FX 4 (atmosphere)
    21, 21, 21, 21, // FX 5 (brightness), FX 6 (goblins), FX 7 (echoes), FX 8 (sci-fi)
    21, 21, 21, 21, // Sitar, Banjo, Shamisen, Koto
    4, 4, 4, 4, // Kalimba, Bagpipe, Fiddle, Shanai
    17, 14, 8, 10, // Tinkle Bell, Agogo, Steel Drums, Woodblock
    17, 17, 18, 19, // Taiko Drum, Melodic Tom, Synth Drum, Reverse Cymbal
    1, 1, 1, 1, // Guitar Fret Noise, Breath Noise, Seashore, Bird Tweet
    21, 21, 21, 21, // Telephone Ring, Helicopter, Applause, Gunshot
    21, 21, 21, 21];
  }
  /**
   * An array that is a mapping from MIDI drum numbers in range (35..81) to Scratch drum numbers.
   * It's in the format [drumNum, pitch, decay].
   * The pitch and decay properties are not currently being used.
   * @type {Array[]}
   */


  get MIDI_DRUMS() {
    return [[1, -4], // "BassDrum" in 2.0, "Bass Drum" in 3.0 (which was "Tom" in 2.0)
    [1, 0], // Same as just above
    [2, 0], [0, 0], [7, 0], [0, 2], [1, -6, 4], [5, 0], [1, -3, 3.2], [5, 0], // "HiHatPedal" in 2.0, "Closed Hi-Hat" in 3.0
    [1, 0, 3], [4, -8], [1, 4, 3], [1, 7, 2.7], [3, -8], [1, 10, 2.7], [4, -2], [3, -11], [4, 2], [6, 0], [3, 0, 3.5], [10, 0], [3, -8, 3.5], [16, -6], [4, 2], [12, 2], [12, 0], [13, 0, 0.2], [13, 0, 2], [13, -5, 2], [12, 12], [12, 5], [10, 19], [10, 12], [14, 0], [14, 0], // "Maracas" in 2.0, "Cabasa" in 3.0 (TODO: pitch up?)
    [17, 12], [17, 5], [15, 0], // "GuiroShort" in 2.0, "Guiro" in 3.0 (which was "GuiroLong" in 2.0) (TODO: decay?)
    [15, 0], [8, 0], [9, 0], [9, -4], [17, -5], [17, 0], [11, -6, 1], [11, -6, 3]];
  }
  /**
   * The key to load & store a target's music-related state.
   * @type {string}
   */


  static get STATE_KEY() {
    return 'Scratch.music';
  }
  /**
   * The default music-related state, to be used when a target has no existing music state.
   * @type {MusicState}
   */


  static get DEFAULT_MUSIC_STATE() {
    return {
      currentInstrument: 0
    };
  }
  /**
   * The minimum and maximum MIDI note numbers, for clamping the input to play note.
   * @type {{min: number, max: number}}
   */


  static get MIDI_NOTE_RANGE() {
    return {
      min: 0,
      max: 130
    };
  }
  /**
   * The minimum and maximum beat values, for clamping the duration of play note, play drum and rest.
   * 100 beats at the default tempo of 60bpm is 100 seconds.
   * @type {{min: number, max: number}}
   */


  static get BEAT_RANGE() {
    return {
      min: 0,
      max: 100
    };
  }
  /** The minimum and maximum tempo values, in bpm.
   * @type {{min: number, max: number}}
   */


  static get TEMPO_RANGE() {
    return {
      min: 20,
      max: 500
    };
  }
  /**
   * The maximum number of sounds to allow to play simultaneously.
   * @type {number}
   */


  static get CONCURRENCY_LIMIT() {
    return 30;
  }
  /**
   * @param {Target} target - collect music state for this target.
   * @returns {MusicState} the mutable music state associated with that target. This will be created if necessary.
   * @private
   */


  _getMusicState(target) {
    let musicState = target.getCustomState(Scratch3MusicBlocks.STATE_KEY);

    if (!musicState) {
      musicState = Clone.simple(Scratch3MusicBlocks.DEFAULT_MUSIC_STATE);
      target.setCustomState(Scratch3MusicBlocks.STATE_KEY, musicState);
    }

    return musicState;
  }
  /**
   * When a music-playing Target is cloned, clone the music state.
   * @param {Target} newTarget - the newly created target.
   * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
   * @listens Runtime#event:targetWasCreated
   * @private
   */


  _onTargetCreated(newTarget, sourceTarget) {
    if (sourceTarget) {
      const musicState = sourceTarget.getCustomState(Scratch3MusicBlocks.STATE_KEY);

      if (musicState) {
        newTarget.setCustomState(Scratch3MusicBlocks.STATE_KEY, Clone.simple(musicState));
      }
    }
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: 'music',
      name: formatMessage({
        id: 'music.categoryName',
        default: 'Music',
        description: 'Label for the Music extension category'
      }),
      menuIconURI: menuIconURI,
      blockIconURI: blockIconURI,
      blocks: [{
        opcode: 'playDrumForBeats',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.playDrumForBeats',
          default: 'play drum [DRUM] for [BEATS] beats',
          description: 'play drum sample for a number of beats'
        }),
        arguments: {
          DRUM: {
            type: ArgumentType.NUMBER,
            menu: 'DRUM',
            defaultValue: 1
          },
          BEATS: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.25
          }
        }
      }, {
        opcode: 'midiPlayDrumForBeats',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.midiPlayDrumForBeats',
          default: 'play drum [DRUM] for [BEATS] beats',
          description: 'play drum sample for a number of beats according to a mapping of MIDI codes'
        }),
        arguments: {
          DRUM: {
            type: ArgumentType.NUMBER,
            menu: 'DRUM',
            defaultValue: 1
          },
          BEATS: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.25
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'restForBeats',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.restForBeats',
          default: 'rest for [BEATS] beats',
          description: 'rest (play no sound) for a number of beats'
        }),
        arguments: {
          BEATS: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.25
          }
        }
      }, {
        opcode: 'playNoteForBeats',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.playNoteForBeats',
          default: 'play note [NOTE] for [BEATS] beats',
          description: 'play a note for a number of beats'
        }),
        arguments: {
          NOTE: {
            type: ArgumentType.NOTE,
            defaultValue: 60
          },
          BEATS: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.25
          }
        }
      }, {
        opcode: 'setInstrument',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.setInstrument',
          default: 'set instrument to [INSTRUMENT]',
          description: 'set the instrument (e.g. piano, guitar, trombone) for notes played'
        }),
        arguments: {
          INSTRUMENT: {
            type: ArgumentType.NUMBER,
            menu: 'INSTRUMENT',
            defaultValue: 1
          }
        }
      }, {
        opcode: 'midiSetInstrument',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.midiSetInstrument',
          default: 'set instrument to [INSTRUMENT]',
          description: 'set the instrument for notes played according to a mapping of MIDI codes'
        }),
        arguments: {
          INSTRUMENT: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'setTempo',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.setTempo',
          default: 'set tempo to [TEMPO]',
          description: 'set tempo (speed) for notes, drums, and rests played'
        }),
        arguments: {
          TEMPO: {
            type: ArgumentType.NUMBER,
            defaultValue: 60
          }
        }
      }, {
        opcode: 'changeTempo',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'music.changeTempo',
          default: 'change tempo by [TEMPO]',
          description: 'change tempo (speed) for notes, drums, and rests played'
        }),
        arguments: {
          TEMPO: {
            type: ArgumentType.NUMBER,
            defaultValue: 20
          }
        }
      }, {
        opcode: 'getTempo',
        text: formatMessage({
          id: 'music.getTempo',
          default: 'tempo',
          description: 'get the current tempo (speed) for notes, drums, and rests played'
        }),
        blockType: BlockType.REPORTER
      }],
      menus: {
        DRUM: {
          acceptReporters: true,
          items: this._buildMenu(this.DRUM_INFO)
        },
        INSTRUMENT: {
          acceptReporters: true,
          items: this._buildMenu(this.INSTRUMENT_INFO)
        }
      }
    };
  }
  /**
   * Play a drum sound for some number of beats.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   * @property {int} DRUM - the number of the drum to play.
   * @property {number} BEATS - the duration in beats of the drum sound.
   */


  playDrumForBeats(args, util) {
    this._playDrumForBeats(args.DRUM, args.BEATS, util);
  }
  /**
   * Play a drum sound for some number of beats according to the range of "MIDI" drum codes supported.
   * This block is implemented for compatibility with old Scratch projects that use the
   * 'drum:duration:elapsed:from:' block.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   */


  midiPlayDrumForBeats(args, util) {
    let drumNum = Cast.toNumber(args.DRUM);
    drumNum = Math.round(drumNum);
    const midiDescription = this.MIDI_DRUMS[drumNum - 35];

    if (midiDescription) {
      drumNum = midiDescription[0];
    } else {
      drumNum = 2; // Default instrument used in Scratch 2.0
    }

    drumNum += 1; // drumNum input to _playDrumForBeats is one-indexed

    this._playDrumForBeats(drumNum, args.BEATS, util);
  }
  /**
   * Internal code to play a drum sound for some number of beats.
   * @param {number} drumNum - the drum number.
   * @param {beats} beats - the duration in beats to pause after playing the sound.
   * @param {object} util - utility object provided by the runtime.
   */


  _playDrumForBeats(drumNum, beats, util) {
    if (this._stackTimerNeedsInit(util)) {
      drumNum = Cast.toNumber(drumNum);
      drumNum = Math.round(drumNum);
      drumNum -= 1; // drums are one-indexed

      drumNum = MathUtil.wrapClamp(drumNum, 0, this.DRUM_INFO.length - 1);
      beats = Cast.toNumber(beats);
      beats = this._clampBeats(beats);

      this._playDrumNum(util, drumNum);

      this._startStackTimer(util, this._beatsToSec(beats));
    } else {
      this._checkStackTimer(util);
    }
  }
  /**
   * Play a drum sound using its 0-indexed number.
   * @param {object} util - utility object provided by the runtime.
   * @param {number} drumNum - the number of the drum to play.
   * @private
   */


  _playDrumNum(util, drumNum) {
    if (util.runtime.audioEngine === null) return;
    if (util.target.sprite.soundBank === null) return; // If we're playing too many sounds, do not play the drum sound.

    if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) {
      return;
    }

    const player = this._drumPlayers[drumNum];
    if (typeof player === 'undefined') return;

    if (player.isPlaying && !player.isStarting) {
      // Take the internal player state and create a new player with it.
      // `.play` does this internally but then instructs the sound to
      // stop.
      player.take();
    }

    const engine = util.runtime.audioEngine;
    const context = engine.audioContext;
    const volumeGain = context.createGain();
    volumeGain.gain.setValueAtTime(util.target.volume / 100, engine.currentTime);
    volumeGain.connect(engine.getInputNode());
    this._concurrencyCounter++;
    player.once('stop', () => {
      this._concurrencyCounter--;
    });
    player.play(); // Connect the player to the gain node.

    player.connect({
      getInputNode() {
        return volumeGain;
      }

    });
  }
  /**
   * Rest for some number of beats.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   * @property {number} BEATS - the duration in beats of the rest.
   */


  restForBeats(args, util) {
    if (this._stackTimerNeedsInit(util)) {
      let beats = Cast.toNumber(args.BEATS);
      beats = this._clampBeats(beats);

      this._startStackTimer(util, this._beatsToSec(beats));
    } else {
      this._checkStackTimer(util);
    }
  }
  /**
   * Play a note using the current musical instrument for some number of beats.
   * This function processes the arguments, and handles the timing of the block's execution.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   * @property {number} NOTE - the pitch of the note to play, interpreted as a MIDI note number.
   * @property {number} BEATS - the duration in beats of the note.
   */


  playNoteForBeats(args, util) {
    if (this._stackTimerNeedsInit(util)) {
      let note = Cast.toNumber(args.NOTE);
      note = MathUtil.clamp(note, Scratch3MusicBlocks.MIDI_NOTE_RANGE.min, Scratch3MusicBlocks.MIDI_NOTE_RANGE.max);
      let beats = Cast.toNumber(args.BEATS);
      beats = this._clampBeats(beats); // If the duration is 0, do not play the note. In Scratch 2.0, "play drum for 0 beats" plays the drum,
      // but "play note for 0 beats" is silent.

      if (beats === 0) return;

      const durationSec = this._beatsToSec(beats);

      this._playNote(util, note, durationSec);

      this._startStackTimer(util, durationSec);
    } else {
      this._checkStackTimer(util);
    }
  }

  _playNoteForPicker(noteNum, category) {
    if (category !== this.getInfo().name) return;
    const util = {
      runtime: this.runtime,
      target: this.runtime.getEditingTarget()
    };

    this._playNote(util, noteNum, 0.25);
  }
  /**
   * Play a note using the current instrument for a duration in seconds.
   * This function actually plays the sound, and handles the timing of the sound, including the
   * "release" portion of the sound, which continues briefly after the block execution has finished.
   * @param {object} util - utility object provided by the runtime.
   * @param {number} note - the pitch of the note to play, interpreted as a MIDI note number.
   * @param {number} durationSec - the duration in seconds to play the note.
   * @private
   */


  _playNote(util, note, durationSec) {
    if (util.runtime.audioEngine === null) return;
    if (util.target.sprite.soundBank === null) return; // If we're playing too many sounds, do not play the note.

    if (this._concurrencyCounter > Scratch3MusicBlocks.CONCURRENCY_LIMIT) {
      return;
    } // Determine which of the audio samples for this instrument to play


    const musicState = this._getMusicState(util.target);

    const inst = musicState.currentInstrument;
    const instrumentInfo = this.INSTRUMENT_INFO[inst];
    const sampleArray = instrumentInfo.samples;

    const sampleIndex = this._selectSampleIndexForNote(note, sampleArray); // If the audio sample has not loaded yet, bail out


    if (typeof this._instrumentPlayerArrays[inst] === 'undefined') return;
    if (typeof this._instrumentPlayerArrays[inst][sampleIndex] === 'undefined') return; // Fetch the sound player to play the note.

    const engine = util.runtime.audioEngine;

    if (!this._instrumentPlayerNoteArrays[inst][note]) {
      this._instrumentPlayerNoteArrays[inst][note] = this._instrumentPlayerArrays[inst][sampleIndex].take();
    }

    const player = this._instrumentPlayerNoteArrays[inst][note];

    if (player.isPlaying && !player.isStarting) {
      // Take the internal player state and create a new player with it.
      // `.play` does this internally but then instructs the sound to
      // stop.
      player.take();
    } // Set its pitch.


    const sampleNote = sampleArray[sampleIndex];

    const notePitchInterval = this._ratioForPitchInterval(note - sampleNote); // Create gain nodes for this note's volume and release, and chain them
    // to the output.


    const context = engine.audioContext;
    const volumeGain = context.createGain();
    volumeGain.gain.setValueAtTime(util.target.volume / 100, engine.currentTime);
    const releaseGain = context.createGain();
    volumeGain.connect(releaseGain);
    releaseGain.connect(engine.getInputNode()); // Schedule the release of the note, ramping its gain down to zero,
    // and then stopping the sound.

    let releaseDuration = this.INSTRUMENT_INFO[inst].releaseTime;

    if (typeof releaseDuration === 'undefined') {
      releaseDuration = 0.01;
    }

    const releaseStart = context.currentTime + durationSec;
    const releaseEnd = releaseStart + releaseDuration;
    releaseGain.gain.setValueAtTime(1, releaseStart);
    releaseGain.gain.linearRampToValueAtTime(0.0001, releaseEnd);
    this._concurrencyCounter++;
    player.once('stop', () => {
      this._concurrencyCounter--;
    }); // Start playing the note

    player.play(); // Connect the player to the gain node.

    player.connect({
      getInputNode() {
        return volumeGain;
      }

    }); // Set playback now after play creates the outputNode.

    player.outputNode.playbackRate.value = notePitchInterval; // Schedule playback to stop.

    player.outputNode.stop(releaseEnd);
  }
  /**
   * The samples array for each instrument is the set of pitches of the available audio samples.
   * This function selects the best one to use to play a given input note, and returns its index
   * in the samples array.
   * @param  {number} note - the input note to select a sample for.
   * @param  {number[]} samples - an array of the pitches of the available samples.
   * @return {index} the index of the selected sample in the samples array.
   * @private
   */


  _selectSampleIndexForNote(note, samples) {
    // Step backwards through the array of samples, i.e. in descending pitch, in order to find
    // the sample that is the closest one below (or matching) the pitch of the input note.
    for (let i = samples.length - 1; i >= 0; i--) {
      if (note >= samples[i]) {
        return i;
      }
    }

    return 0;
  }
  /**
   * Calcuate the frequency ratio for a given musical interval.
   * @param  {number} interval - the pitch interval to convert.
   * @return {number} a ratio corresponding to the input interval.
   * @private
   */


  _ratioForPitchInterval(interval) {
    return Math.pow(2, interval / 12);
  }
  /**
   * Clamp a duration in beats to the allowed min and max duration.
   * @param  {number} beats - a duration in beats.
   * @return {number} - the clamped duration.
   * @private
   */


  _clampBeats(beats) {
    return MathUtil.clamp(beats, Scratch3MusicBlocks.BEAT_RANGE.min, Scratch3MusicBlocks.BEAT_RANGE.max);
  }
  /**
   * Convert a number of beats to a number of seconds, using the current tempo.
   * @param  {number} beats - number of beats to convert to secs.
   * @return {number} seconds - number of seconds `beats` will last.
   * @private
   */


  _beatsToSec(beats) {
    return 60 / this.getTempo() * beats;
  }
  /**
   * Check if the stack timer needs initialization.
   * @param {object} util - utility object provided by the runtime.
   * @return {boolean} - true if the stack timer needs to be initialized.
   * @private
   */


  _stackTimerNeedsInit(util) {
    return !util.stackFrame.timer;
  }
  /**
   * Start the stack timer and the yield the thread if necessary.
   * @param {object} util - utility object provided by the runtime.
   * @param {number} duration - a duration in seconds to set the timer for.
   * @private
   */


  _startStackTimer(util, duration) {
    util.stackFrame.timer = new Timer();
    util.stackFrame.timer.start();
    util.stackFrame.duration = duration;
    util.yield();
  }
  /**
   * Check the stack timer, and if its time is not up yet, yield the thread.
   * @param {object} util - utility object provided by the runtime.
   * @private
   */


  _checkStackTimer(util) {
    const timeElapsed = util.stackFrame.timer.timeElapsed();

    if (timeElapsed < util.stackFrame.duration * 1000) {
      util.yield();
    }
  }
  /**
   * Select an instrument for playing notes.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   * @property {int} INSTRUMENT - the number of the instrument to select.
   */


  setInstrument(args, util) {
    this._setInstrument(args.INSTRUMENT, util, false);
  }
  /**
   * Select an instrument for playing notes according to a mapping of MIDI codes to Scratch instrument numbers.
   * This block is implemented for compatibility with old Scratch projects that use the 'midiInstrument:' block.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   * @property {int} INSTRUMENT - the MIDI number of the instrument to select.
   */


  midiSetInstrument(args, util) {
    this._setInstrument(args.INSTRUMENT, util, true);
  }
  /**
   * Internal code to select an instrument for playing notes. If mapMidi is true, set the instrument according to
   * the MIDI to Scratch instrument mapping.
   * @param {number} instNum - the instrument number.
   * @param {object} util - utility object provided by the runtime.
   * @param {boolean} mapMidi - whether or not instNum is a MIDI instrument number.
   */


  _setInstrument(instNum, util, mapMidi) {
    const musicState = this._getMusicState(util.target);

    instNum = Cast.toNumber(instNum);
    instNum = Math.round(instNum);
    instNum -= 1; // instruments are one-indexed

    if (mapMidi) {
      instNum = (this.MIDI_INSTRUMENTS[instNum] || 0) - 1;
    }

    instNum = MathUtil.wrapClamp(instNum, 0, this.INSTRUMENT_INFO.length - 1);
    musicState.currentInstrument = instNum;
  }
  /**
   * Set the current tempo to a new value.
   * @param {object} args - the block arguments.
   * @property {number} TEMPO - the tempo, in beats per minute.
   */


  setTempo(args) {
    const tempo = Cast.toNumber(args.TEMPO);

    this._updateTempo(tempo);
  }
  /**
   * Change the current tempo by some amount.
   * @param {object} args - the block arguments.
   * @property {number} TEMPO - the amount to change the tempo, in beats per minute.
   */


  changeTempo(args) {
    const change = Cast.toNumber(args.TEMPO);
    const tempo = change + this.getTempo();

    this._updateTempo(tempo);
  }
  /**
   * Update the current tempo, clamping it to the min and max allowable range.
   * @param {number} tempo - the tempo to set, in beats per minute.
   * @private
   */


  _updateTempo(tempo) {
    tempo = MathUtil.clamp(tempo, Scratch3MusicBlocks.TEMPO_RANGE.min, Scratch3MusicBlocks.TEMPO_RANGE.max);
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      stage.tempo = tempo;
    }
  }
  /**
   * Get the current tempo.
   * @return {number} - the current tempo, in beats per minute.
   */


  getTempo() {
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      return stage.tempo;
    }

    return 60;
  }

}

module.exports = Scratch3MusicBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_music/manifest.js":
/*!***************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_music/manifest.js ***!
  \***************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = {
  'drums/1-snare.mp3': __webpack_require__(/*! ./assets/drums/1-snare.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/1-snare.mp3"),
  'drums/2-bass-drum.mp3': __webpack_require__(/*! ./assets/drums/2-bass-drum.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/2-bass-drum.mp3"),
  'drums/3-side-stick.mp3': __webpack_require__(/*! ./assets/drums/3-side-stick.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/3-side-stick.mp3"),
  'drums/4-crash-cymbal.mp3': __webpack_require__(/*! ./assets/drums/4-crash-cymbal.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/4-crash-cymbal.mp3"),
  'drums/5-open-hi-hat.mp3': __webpack_require__(/*! ./assets/drums/5-open-hi-hat.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/5-open-hi-hat.mp3"),
  'drums/6-closed-hi-hat.mp3': __webpack_require__(/*! ./assets/drums/6-closed-hi-hat.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/6-closed-hi-hat.mp3"),
  'drums/7-tambourine.mp3': __webpack_require__(/*! ./assets/drums/7-tambourine.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/7-tambourine.mp3"),
  'drums/8-hand-clap.mp3': __webpack_require__(/*! ./assets/drums/8-hand-clap.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/8-hand-clap.mp3"),
  'drums/9-claves.mp3': __webpack_require__(/*! ./assets/drums/9-claves.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/9-claves.mp3"),
  'drums/10-wood-block.mp3': __webpack_require__(/*! ./assets/drums/10-wood-block.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/10-wood-block.mp3"),
  'drums/11-cowbell.mp3': __webpack_require__(/*! ./assets/drums/11-cowbell.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/11-cowbell.mp3"),
  'drums/12-triangle.mp3': __webpack_require__(/*! ./assets/drums/12-triangle.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/12-triangle.mp3"),
  'drums/13-bongo.mp3': __webpack_require__(/*! ./assets/drums/13-bongo.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/13-bongo.mp3"),
  'drums/14-conga.mp3': __webpack_require__(/*! ./assets/drums/14-conga.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/14-conga.mp3"),
  'drums/15-cabasa.mp3': __webpack_require__(/*! ./assets/drums/15-cabasa.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/15-cabasa.mp3"),
  'drums/16-guiro.mp3': __webpack_require__(/*! ./assets/drums/16-guiro.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/16-guiro.mp3"),
  'drums/17-vibraslap.mp3': __webpack_require__(/*! ./assets/drums/17-vibraslap.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/17-vibraslap.mp3"),
  'drums/18-cuica.mp3': __webpack_require__(/*! ./assets/drums/18-cuica.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/drums/18-cuica.mp3"),
  'instruments/1-piano/24.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/24.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/24.mp3"),
  'instruments/1-piano/36.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/36.mp3"),
  'instruments/1-piano/48.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/48.mp3"),
  'instruments/1-piano/60.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/60.mp3"),
  'instruments/1-piano/72.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/72.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/72.mp3"),
  'instruments/1-piano/84.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/84.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/84.mp3"),
  'instruments/1-piano/96.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/96.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/96.mp3"),
  'instruments/1-piano/108.mp3': __webpack_require__(/*! ./assets/instruments/1-piano/108.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/1-piano/108.mp3"),
  'instruments/2-electric-piano/60.mp3': __webpack_require__(/*! ./assets/instruments/2-electric-piano/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/2-electric-piano/60.mp3"),
  'instruments/3-organ/60.mp3': __webpack_require__(/*! ./assets/instruments/3-organ/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/3-organ/60.mp3"),
  'instruments/4-guitar/60.mp3': __webpack_require__(/*! ./assets/instruments/4-guitar/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/4-guitar/60.mp3"),
  'instruments/5-electric-guitar/60.mp3': __webpack_require__(/*! ./assets/instruments/5-electric-guitar/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/5-electric-guitar/60.mp3"),
  'instruments/6-bass/36.mp3': __webpack_require__(/*! ./assets/instruments/6-bass/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/36.mp3"),
  'instruments/6-bass/48.mp3': __webpack_require__(/*! ./assets/instruments/6-bass/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/6-bass/48.mp3"),
  'instruments/7-pizzicato/60.mp3': __webpack_require__(/*! ./assets/instruments/7-pizzicato/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/7-pizzicato/60.mp3"),
  'instruments/8-cello/36.mp3': __webpack_require__(/*! ./assets/instruments/8-cello/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/36.mp3"),
  'instruments/8-cello/48.mp3': __webpack_require__(/*! ./assets/instruments/8-cello/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/48.mp3"),
  'instruments/8-cello/60.mp3': __webpack_require__(/*! ./assets/instruments/8-cello/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/8-cello/60.mp3"),
  'instruments/9-trombone/36.mp3': __webpack_require__(/*! ./assets/instruments/9-trombone/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/36.mp3"),
  'instruments/9-trombone/48.mp3': __webpack_require__(/*! ./assets/instruments/9-trombone/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/48.mp3"),
  'instruments/9-trombone/60.mp3': __webpack_require__(/*! ./assets/instruments/9-trombone/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/9-trombone/60.mp3"),
  'instruments/10-clarinet/48.mp3': __webpack_require__(/*! ./assets/instruments/10-clarinet/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/48.mp3"),
  'instruments/10-clarinet/60.mp3': __webpack_require__(/*! ./assets/instruments/10-clarinet/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/10-clarinet/60.mp3"),
  'instruments/11-saxophone/36.mp3': __webpack_require__(/*! ./assets/instruments/11-saxophone/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/36.mp3"),
  'instruments/11-saxophone/60.mp3': __webpack_require__(/*! ./assets/instruments/11-saxophone/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/60.mp3"),
  'instruments/11-saxophone/84.mp3': __webpack_require__(/*! ./assets/instruments/11-saxophone/84.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/11-saxophone/84.mp3"),
  'instruments/12-flute/60.mp3': __webpack_require__(/*! ./assets/instruments/12-flute/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/60.mp3"),
  'instruments/12-flute/72.mp3': __webpack_require__(/*! ./assets/instruments/12-flute/72.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/12-flute/72.mp3"),
  'instruments/13-wooden-flute/60.mp3': __webpack_require__(/*! ./assets/instruments/13-wooden-flute/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/60.mp3"),
  'instruments/13-wooden-flute/72.mp3': __webpack_require__(/*! ./assets/instruments/13-wooden-flute/72.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/13-wooden-flute/72.mp3"),
  'instruments/14-bassoon/36.mp3': __webpack_require__(/*! ./assets/instruments/14-bassoon/36.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/36.mp3"),
  'instruments/14-bassoon/48.mp3': __webpack_require__(/*! ./assets/instruments/14-bassoon/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/48.mp3"),
  'instruments/14-bassoon/60.mp3': __webpack_require__(/*! ./assets/instruments/14-bassoon/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/14-bassoon/60.mp3"),
  'instruments/15-choir/48.mp3': __webpack_require__(/*! ./assets/instruments/15-choir/48.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/48.mp3"),
  'instruments/15-choir/60.mp3': __webpack_require__(/*! ./assets/instruments/15-choir/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/60.mp3"),
  'instruments/15-choir/72.mp3': __webpack_require__(/*! ./assets/instruments/15-choir/72.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/15-choir/72.mp3"),
  'instruments/16-vibraphone/60.mp3': __webpack_require__(/*! ./assets/instruments/16-vibraphone/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/60.mp3"),
  'instruments/16-vibraphone/72.mp3': __webpack_require__(/*! ./assets/instruments/16-vibraphone/72.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/16-vibraphone/72.mp3"),
  'instruments/17-music-box/60.mp3': __webpack_require__(/*! ./assets/instruments/17-music-box/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/17-music-box/60.mp3"),
  'instruments/18-steel-drum/60.mp3': __webpack_require__(/*! ./assets/instruments/18-steel-drum/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/18-steel-drum/60.mp3"),
  'instruments/19-marimba/60.mp3': __webpack_require__(/*! ./assets/instruments/19-marimba/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/19-marimba/60.mp3"),
  'instruments/20-synth-lead/60.mp3': __webpack_require__(/*! ./assets/instruments/20-synth-lead/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/20-synth-lead/60.mp3"),
  'instruments/21-synth-pad/60.mp3': __webpack_require__(/*! ./assets/instruments/21-synth-pad/60.mp3 */ "./node_modules/scratch-vm/src/extensions/scratch3_music/assets/instruments/21-synth-pad/60.mp3")
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_pen/index.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_pen/index.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const TargetType = __webpack_require__(/*! ../../extension-support/target-type */ "./node_modules/scratch-vm/src/extension-support/target-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Clone = __webpack_require__(/*! ../../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const Color = __webpack_require__(/*! ../../util/color */ "./node_modules/scratch-vm/src/util/color.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const RenderedTarget = __webpack_require__(/*! ../../sprites/rendered-target */ "./node_modules/scratch-vm/src/sprites/rendered-target.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const StageLayering = __webpack_require__(/*! ../../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const blockIconURI = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHZpZXdCb3g9IjAgMCA0MCA0MCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48dGl0bGU+cGVuLWljb248L3RpdGxlPjxnIHN0cm9rZT0iIzU3NUU3NSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiPjxwYXRoIGQ9Ik04Ljc1MyAzNC42MDJsLTQuMjUgMS43OCAxLjc4My00LjIzN2MxLjIxOC0yLjg5MiAyLjkwNy01LjQyMyA1LjAzLTcuNTM4TDMxLjA2NiA0LjkzYy44NDYtLjg0MiAyLjY1LS40MSA0LjAzMi45NjcgMS4zOCAxLjM3NSAxLjgxNiAzLjE3My45NyA0LjAxNUwxNi4zMTggMjkuNTljLTIuMTIzIDIuMTE2LTQuNjY0IDMuOC03LjU2NSA1LjAxMiIgZmlsbD0iI0ZGRiIvPjxwYXRoIGQ9Ik0yOS40MSA2LjExcy00LjQ1LTIuMzc4LTguMjAyIDUuNzcyYy0xLjczNCAzLjc2Ni00LjM1IDEuNTQ2LTQuMzUgMS41NDYiLz48cGF0aCBkPSJNMzYuNDIgOC44MjVjMCAuNDYzLS4xNC44NzMtLjQzMiAxLjE2NGwtOS4zMzUgOS4zYy4yODItLjI5LjQxLS42NjguNDEtMS4xMiAwLS44NzQtLjUwNy0xLjk2My0xLjQwNi0yLjg2OC0xLjM2Mi0xLjM1OC0zLjE0Ny0xLjgtNC4wMDItLjk5TDMwLjk5IDUuMDFjLjg0NC0uODQgMi42NS0uNDEgNC4wMzUuOTYuODk4LjkwNCAxLjM5NiAxLjk4MiAxLjM5NiAyLjg1NU0xMC41MTUgMzMuNzc0Yy0uNTczLjMwMi0xLjE1Ny41Ny0xLjc2NC44M0w0LjUgMzYuMzgybDEuNzg2LTQuMjM1Yy4yNTgtLjYwNC41My0xLjE4Ni44MzMtMS43NTcuNjkuMTgzIDEuNDQ4LjYyNSAyLjEwOCAxLjI4Mi42Ni42NTggMS4xMDIgMS40MTIgMS4yODcgMi4xMDIiIGZpbGw9IiM0Qzk3RkYiLz48cGF0aCBkPSJNMzYuNDk4IDguNzQ4YzAgLjQ2NC0uMTQuODc0LS40MzMgMS4xNjVsLTE5Ljc0MiAxOS42OGMtMi4xMyAyLjExLTQuNjczIDMuNzkzLTcuNTcyIDUuMDFMNC41IDM2LjM4bC45NzQtMi4zMTYgMS45MjUtLjgwOGMyLjg5OC0xLjIxOCA1LjQ0LTIuOSA3LjU3LTUuMDFsMTkuNzQzLTE5LjY4Yy4yOTItLjI5Mi40MzItLjcwMi40MzItMS4xNjUgMC0uNjQ2LS4yNy0xLjQtLjc4LTIuMTIyLjI1LjE3Mi41LjM3Ny43MzcuNjE0Ljg5OC45MDUgMS4zOTYgMS45ODMgMS4zOTYgMi44NTYiIGZpbGw9IiM1NzVFNzUiIG9wYWNpdHk9Ii4xNSIvPjxwYXRoIGQ9Ik0xOC40NSAxMi44M2MwIC41LS40MDQuOTA1LS45MDQuOTA1cy0uOTA1LS40MDUtLjkwNS0uOTA0YzAtLjUuNDA3LS45MDMuOTA2LS45MDMuNSAwIC45MDQuNDA0LjkwNC45MDR6IiBmaWxsPSIjNTc1RTc1Ii8+PC9nPjwvc3ZnPg==';
/**
 * Enum for pen color parameter values.
 * @readonly
 * @enum {string}
 */

const ColorParam = {
  COLOR: 'color',
  SATURATION: 'saturation',
  BRIGHTNESS: 'brightness',
  TRANSPARENCY: 'transparency'
};
/**
 * @typedef {object} PenState - the pen state associated with a particular target.
 * @property {Boolean} penDown - tracks whether the pen should draw for this target.
 * @property {number} color - the current color (hue) of the pen.
 * @property {PenAttributes} penAttributes - cached pen attributes for the renderer. This is the authoritative value for
 *   diameter but not for pen color.
 */

/**
 * Host for the Pen-related blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */

class Scratch3PenBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * The ID of the renderer Drawable corresponding to the pen layer.
     * @type {int}
     * @private
     */

    this._penDrawableId = -1;
    /**
     * The ID of the renderer Skin corresponding to the pen layer.
     * @type {int}
     * @private
     */

    this._penSkinId = -1;
    this._onTargetCreated = this._onTargetCreated.bind(this);
    this._onTargetMoved = this._onTargetMoved.bind(this);
    runtime.on('targetWasCreated', this._onTargetCreated);
    runtime.on('RUNTIME_DISPOSED', this.clear.bind(this));
  }
  /**
   * The default pen state, to be used when a target has no existing pen state.
   * @type {PenState}
   */


  static get DEFAULT_PEN_STATE() {
    return {
      penDown: false,
      color: 66.66,
      saturation: 100,
      brightness: 100,
      transparency: 0,
      _shade: 50,
      // Used only for legacy `change shade by` blocks
      penAttributes: {
        color4f: [0, 0, 1, 1],
        diameter: 1
      }
    };
  }
  /**
   * The minimum and maximum allowed pen size.
   * The maximum is twice the diagonal of the stage, so that even an
   * off-stage sprite can fill it.
   * @type {{min: number, max: number}}
   */


  static get PEN_SIZE_RANGE() {
    return {
      min: 1,
      max: 1200
    };
  }
  /**
   * The key to load & store a target's pen-related state.
   * @type {string}
   */


  static get STATE_KEY() {
    // tw: We've hardcoded this value in various places for slight performance gains
    // Make sure to update those if this changes.
    return 'Scratch.pen';
  }
  /**
   * Clamp a pen size value to the range allowed by the pen.
   * @param {number} requestedSize - the requested pen size.
   * @returns {number} the clamped size.
   * @private
   */


  _clampPenSize(requestedSize) {
    if (this.runtime.renderer && this.runtime.renderer.useHighQualityRender || !this.runtime.runtimeOptions.miscLimits) {
      return Math.max(0, requestedSize);
    }

    return MathUtil.clamp(requestedSize, Scratch3PenBlocks.PEN_SIZE_RANGE.min, Scratch3PenBlocks.PEN_SIZE_RANGE.max);
  }
  /**
   * Retrieve the ID of the renderer "Skin" corresponding to the pen layer. If
   * the pen Skin doesn't yet exist, create it.
   * @returns {int} the Skin ID of the pen layer, or -1 on failure.
   * @private
   */


  _getPenLayerID() {
    if (this._penSkinId < 0 && this.runtime.renderer) {
      this._penSkinId = this.runtime.renderer.createPenSkin();
      this._penDrawableId = this.runtime.renderer.createDrawable(StageLayering.PEN_LAYER);
      this.runtime.renderer.updateDrawableSkinId(this._penDrawableId, this._penSkinId);
    }

    return this._penSkinId;
  }
  /**
   * @param {Target} target - collect pen state for this target. Probably, but not necessarily, a RenderedTarget.
   * @returns {PenState} the mutable pen state associated with that target. This will be created if necessary.
   * @private
   */


  _getPenState(target) {
    let penState = target._customState['Scratch.pen'];

    if (!penState) {
      penState = Clone.simple(Scratch3PenBlocks.DEFAULT_PEN_STATE);
      target.setCustomState(Scratch3PenBlocks.STATE_KEY, penState);
    }

    return penState;
  }
  /**
   * When a pen-using Target is cloned, clone the pen state.
   * @param {Target} newTarget - the newly created target.
   * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
   * @listens Runtime#event:targetWasCreated
   * @private
   */


  _onTargetCreated(newTarget, sourceTarget) {
    if (sourceTarget) {
      const penState = sourceTarget.getCustomState(Scratch3PenBlocks.STATE_KEY);

      if (penState) {
        newTarget.setCustomState(Scratch3PenBlocks.STATE_KEY, Clone.simple(penState));

        if (penState.penDown) {
          newTarget.onTargetMoved = this._onTargetMoved;
        }
      }
    }
  }
  /**
   * Handle a target which has moved. This only fires when the pen is down.
   * @param {RenderedTarget} target - the target which has moved.
   * @param {number} oldX - the previous X position.
   * @param {number} oldY - the previous Y position.
   * @param {boolean} isForce - whether the movement was forced.
   * @private
   */


  _onTargetMoved(target, oldX, oldY, isForce) {
    // Only move the pen if the movement isn't forced (ie. dragged).
    if (!isForce) {
      const penSkinId = this._getPenLayerID();

      if (penSkinId >= 0) {
        const penState = this._getPenState(target);

        this.runtime.renderer.penLine(penSkinId, penState.penAttributes, oldX, oldY, target.x, target.y);
        this.runtime.requestRedraw();
      }
    }
  }
  /**
   * Wrap a color input into the range (0,100).
   * @param {number} value - the value to be wrapped.
   * @returns {number} the wrapped value.
   * @private
   */


  _wrapColor(value) {
    return MathUtil.wrapClamp(value, 0, 100);
  }
  /**
   * Initialize color parameters menu with localized strings
   * @returns {array} of the localized text and values for each menu element
   * @private
   */


  _initColorParam() {
    return [{
      text: formatMessage({
        id: 'pen.colorMenu.color',
        default: 'color',
        description: 'label for color element in color picker for pen extension'
      }),
      value: ColorParam.COLOR
    }, {
      text: formatMessage({
        id: 'pen.colorMenu.saturation',
        default: 'saturation',
        description: 'label for saturation element in color picker for pen extension'
      }),
      value: ColorParam.SATURATION
    }, {
      text: formatMessage({
        id: 'pen.colorMenu.brightness',
        default: 'brightness',
        description: 'label for brightness element in color picker for pen extension'
      }),
      value: ColorParam.BRIGHTNESS
    }, {
      text: formatMessage({
        id: 'pen.colorMenu.transparency',
        default: 'transparency',
        description: 'label for transparency element in color picker for pen extension'
      }),
      value: ColorParam.TRANSPARENCY
    }];
  }
  /**
   * Clamp a pen color parameter to the range (0,100).
   * @param {number} value - the value to be clamped.
   * @returns {number} the clamped value.
   * @private
   */


  _clampColorParam(value) {
    return MathUtil.clamp(value, 0, 100);
  }
  /**
   * Convert an alpha value to a pen transparency value.
   * Alpha ranges from 0 to 1, where 0 is transparent and 1 is opaque.
   * Transparency ranges from 0 to 100, where 0 is opaque and 100 is transparent.
   * @param {number} alpha - the input alpha value.
   * @returns {number} the transparency value.
   * @private
   */


  _alphaToTransparency(alpha) {
    return (1.0 - alpha) * 100.0;
  }
  /**
   * Convert a pen transparency value to an alpha value.
   * Alpha ranges from 0 to 1, where 0 is transparent and 1 is opaque.
   * Transparency ranges from 0 to 100, where 0 is opaque and 100 is transparent.
   * @param {number} transparency - the input transparency value.
   * @returns {number} the alpha value.
   * @private
   */


  _transparencyToAlpha(transparency) {
    return 1.0 - transparency / 100.0;
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: 'pen',
      name: formatMessage({
        id: 'pen.categoryName',
        default: 'Pen',
        description: 'Label for the pen extension category'
      }),
      blockIconURI: blockIconURI,
      blocks: [{
        opcode: 'clear',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.clear',
          default: 'erase all',
          description: 'erase all pen trails and stamps'
        })
      }, {
        opcode: 'stamp',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.stamp',
          default: 'stamp',
          description: 'render current costume on the background'
        }),
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'penDown',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.penDown',
          default: 'pen down',
          description: 'start leaving a trail when the sprite moves'
        }),
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'penUp',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.penUp',
          default: 'pen up',
          description: 'stop leaving a trail behind the sprite'
        }),
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'setPenColorToColor',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.setColor',
          default: 'set pen color to [COLOR]',
          description: 'set the pen color to a particular (RGB) value'
        }),
        arguments: {
          COLOR: {
            type: ArgumentType.COLOR
          }
        },
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'changePenColorParamBy',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.changeColorParam',
          default: 'change pen [COLOR_PARAM] by [VALUE]',
          description: 'change the state of a pen color parameter'
        }),
        arguments: {
          COLOR_PARAM: {
            type: ArgumentType.STRING,
            menu: 'colorParam',
            defaultValue: ColorParam.COLOR
          },
          VALUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 10
          }
        },
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'setPenColorParamTo',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.setColorParam',
          default: 'set pen [COLOR_PARAM] to [VALUE]',
          description: 'set the state for a pen color parameter e.g. saturation'
        }),
        arguments: {
          COLOR_PARAM: {
            type: ArgumentType.STRING,
            menu: 'colorParam',
            defaultValue: ColorParam.COLOR
          },
          VALUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        },
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'changePenSizeBy',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.changeSize',
          default: 'change pen size by [SIZE]',
          description: 'change the diameter of the trail left by a sprite'
        }),
        arguments: {
          SIZE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        filter: [TargetType.SPRITE]
      }, {
        opcode: 'setPenSizeTo',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.setSize',
          default: 'set pen size to [SIZE]',
          description: 'set the diameter of a trail left by a sprite'
        }),
        arguments: {
          SIZE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        filter: [TargetType.SPRITE]
      },
      /* Legacy blocks, should not be shown in flyout */
      {
        opcode: 'setPenShadeToNumber',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.setShade',
          default: 'set pen shade to [SHADE]',
          description: 'legacy pen blocks - set pen shade'
        }),
        arguments: {
          SHADE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'changePenShadeBy',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.changeShade',
          default: 'change pen shade by [SHADE]',
          description: 'legacy pen blocks - change pen shade'
        }),
        arguments: {
          SHADE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'setPenHueToNumber',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.setHue',
          default: 'set pen color to [HUE]',
          description: 'legacy pen blocks - set pen color to number'
        }),
        arguments: {
          HUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'changePenHueBy',
        blockType: BlockType.COMMAND,
        text: formatMessage({
          id: 'pen.changeHue',
          default: 'change pen color by [HUE]',
          description: 'legacy pen blocks - change pen color'
        }),
        arguments: {
          HUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        },
        hideFromPalette: true
      }],
      menus: {
        colorParam: {
          acceptReporters: true,
          items: this._initColorParam()
        }
      }
    };
  }
  /**
   * The pen "clear" block clears the pen layer's contents.
   */


  clear() {
    // used by compiler
    const penSkinId = this._getPenLayerID();

    if (penSkinId >= 0) {
      this.runtime.renderer.penClear(penSkinId);
      this.runtime.requestRedraw();
    }
  }
  /**
   * The pen "stamp" block stamps the current drawable's image onto the pen layer.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   */


  stamp(args, util) {
    this._stamp(util.target);
  }

  _stamp(target) {
    // used by compiler
    const penSkinId = this._getPenLayerID();

    if (penSkinId >= 0) {
      this.runtime.renderer.penStamp(penSkinId, target.drawableID);
      this.runtime.requestRedraw();
    }
  }
  /**
   * The pen "pen down" block causes the target to leave pen trails on future motion.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   */


  penDown(args, util) {
    this._penDown(util.target);
  }

  _penDown(target) {
    // used by compiler
    const penState = this._getPenState(target);

    if (!penState.penDown) {
      penState.penDown = true;
      target.onTargetMoved = this._onTargetMoved;
    }

    const penSkinId = this._getPenLayerID();

    if (penSkinId >= 0) {
      this.runtime.renderer.penPoint(penSkinId, penState.penAttributes, target.x, target.y);
      this.runtime.requestRedraw();
    }
  }
  /**
   * The pen "pen up" block stops the target from leaving pen trails.
   * @param {object} args - the block arguments.
   * @param {object} util - utility object provided by the runtime.
   */


  penUp(args, util) {
    this._penUp(util.target);
  }

  _penUp(target) {
    // used by compiler
    const penState = this._getPenState(target);

    if (penState.penDown) {
      penState.penDown = false;
      target.onTargetMoved = null;
    }
  }
  /**
   * The pen "set pen color to {color}" block sets the pen to a particular RGB color.
   * The transparency is reset to 0.
   * @param {object} args - the block arguments.
   *  @property {int} COLOR - the color to set, expressed as a 24-bit RGB value (0xRRGGBB).
   * @param {object} util - utility object provided by the runtime.
   */


  setPenColorToColor(args, util) {
    this._setPenColorToColor(args.COLOR, util.target);
  }

  _setPenColorToColor(color, target) {
    // used by compiler
    const penState = this._getPenState(target);

    const rgb = Cast.toRgbColorObject(color);
    const hsv = Color.rgbToHsv(rgb);
    penState.color = hsv.h / 360 * 100;
    penState.saturation = hsv.s * 100;
    penState.brightness = hsv.v * 100;

    if (rgb.hasOwnProperty('a')) {
      penState.transparency = 100 * (1 - rgb.a / 255.0);
    } else {
      penState.transparency = 0;
    } // Set the legacy "shade" value the same way scratch 2 did.


    penState._shade = penState.brightness / 2;

    this._updatePenColor(penState);
  }
  /**
   * Update the cached color from the color, saturation, brightness and transparency values
   * in the provided PenState object.
   * @param {PenState} penState - the pen state to update.
   * @private
   */


  _updatePenColor(penState) {
    const rgb = Color.hsvToRgb({
      h: penState.color * 360 / 100,
      s: penState.saturation / 100,
      v: penState.brightness / 100
    });
    penState.penAttributes.color4f[0] = rgb.r / 255.0;
    penState.penAttributes.color4f[1] = rgb.g / 255.0;
    penState.penAttributes.color4f[2] = rgb.b / 255.0;
    penState.penAttributes.color4f[3] = this._transparencyToAlpha(penState.transparency);
  }
  /**
   * Set or change a single color parameter on the pen state, and update the pen color.
   * @param {ColorParam} param - the name of the color parameter to set or change.
   * @param {number} value - the value to set or change the param by.
   * @param {PenState} penState - the pen state to update.
   * @param {boolean} change - if true change param by value, if false set param to value.
   * @private
   */


  _setOrChangeColorParam(param, value, penState, change) {
    // used by compiler
    switch (param) {
      case ColorParam.COLOR:
        penState.color = this._wrapColor(value + (change ? penState.color : 0));
        break;

      case ColorParam.SATURATION:
        penState.saturation = this._clampColorParam(value + (change ? penState.saturation : 0));
        break;

      case ColorParam.BRIGHTNESS:
        penState.brightness = this._clampColorParam(value + (change ? penState.brightness : 0));
        break;

      case ColorParam.TRANSPARENCY:
        penState.transparency = this._clampColorParam(value + (change ? penState.transparency : 0));
        break;

      default:
        log.warn("Tried to set or change unknown color parameter: ".concat(param));
    }

    this._updatePenColor(penState);
  }
  /**
   * The "change pen {ColorParam} by {number}" block changes one of the pen's color parameters
   * by a given amound.
   * @param {object} args - the block arguments.
   *  @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.
   *  @property {number} VALUE - the amount to change the selected parameter by.
   * @param {object} util - utility object provided by the runtime.
   */


  changePenColorParamBy(args, util) {
    const penState = this._getPenState(util.target);

    this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, true);
  }
  /**
   * The "set pen {ColorParam} to {number}" block sets one of the pen's color parameters
   * to a given amound.
   * @param {object} args - the block arguments.
   *  @property {ColorParam} COLOR_PARAM - the name of the selected color parameter.
   *  @property {number} VALUE - the amount to set the selected parameter to.
   * @param {object} util - utility object provided by the runtime.
   */


  setPenColorParamTo(args, util) {
    const penState = this._getPenState(util.target);

    this._setOrChangeColorParam(args.COLOR_PARAM, Cast.toNumber(args.VALUE), penState, false);
  }
  /**
   * The pen "change pen size by {number}" block changes the pen size by the given amount.
   * @param {object} args - the block arguments.
   *  @property {number} SIZE - the amount of desired size change.
   * @param {object} util - utility object provided by the runtime.
   */


  changePenSizeBy(args, util) {
    this._changePenSizeBy(Cast.toNumber(args.SIZE), util.target);
  }

  _changePenSizeBy(size, target) {
    // used by compiler
    const penAttributes = this._getPenState(target).penAttributes;

    penAttributes.diameter = this._clampPenSize(penAttributes.diameter + size);
  }
  /**
   * The pen "set pen size to {number}" block sets the pen size to the given amount.
   * @param {object} args - the block arguments.
   *  @property {number} SIZE - the amount of desired size change.
   * @param {object} util - utility object provided by the runtime.
   */


  setPenSizeTo(args, util) {
    this._setPenSizeTo(Cast.toNumber(args.SIZE), util.target);
  }

  _setPenSizeTo(size, target) {
    // used by compiler
    const penAttributes = this._getPenState(target).penAttributes;

    penAttributes.diameter = this._clampPenSize(size);
  }
  /* LEGACY OPCODES */

  /**
   * Scratch 2 "hue" param is equivelant to twice the new "color" param.
   * @param {object} args - the block arguments.
   *  @property {number} HUE - the amount to set the hue to.
   * @param {object} util - utility object provided by the runtime.
   */


  setPenHueToNumber(args, util) {
    this._setPenHueToNumber(Cast.toNumber(args.HUE), util.target);
  }

  _setPenHueToNumber(hueValue, target) {
    const penState = this._getPenState(target);

    const colorValue = hueValue / 2;

    this._setOrChangeColorParam(ColorParam.COLOR, colorValue, penState, false);

    this._setOrChangeColorParam(ColorParam.TRANSPARENCY, 0, penState, false);

    this._legacyUpdatePenColor(penState);
  }
  /**
   * Scratch 2 "hue" param is equivelant to twice the new "color" param.
   * @param {object} args - the block arguments.
   *  @property {number} HUE - the amount of desired hue change.
   * @param {object} util - utility object provided by the runtime.
   */


  changePenHueBy(args, util) {
    this._changePenHueBy(Cast.toNumber(args.HUE), util.target);
  }

  _changePenHueBy(hueChange, target) {
    // used by compiler
    const penState = this._getPenState(target);

    const colorChange = hueChange / 2;

    this._setOrChangeColorParam(ColorParam.COLOR, colorChange, penState, true);

    this._legacyUpdatePenColor(penState);
  }
  /**
   * Use legacy "set shade" code to calculate RGB value for shade,
   * then convert back to HSV and store those components.
   * It is important to also track the given shade in penState._shade
   * because it cannot be accurately backed out of the new HSV later.
   * @param {object} args - the block arguments.
   *  @property {number} SHADE - the amount to set the shade to.
   * @param {object} util - utility object provided by the runtime.
   */


  setPenShadeToNumber(args, util) {
    this._setPenShadeToNumber(Cast.toNumber(args.SHADE), util.target);
  }

  _setPenShadeToNumber(shade, target) {
    const penState = this._getPenState(target);

    let newShade = Cast.toNumber(shade); // Wrap clamp the new shade value the way scratch 2 did.

    newShade = newShade % 200;
    if (newShade < 0) newShade += 200; // And store the shade that was used to compute this new color for later use.

    penState._shade = newShade;

    this._legacyUpdatePenColor(penState);
  }
  /**
   * Because "shade" cannot be backed out of hsv consistently, use the previously
   * stored penState._shade to make the shade change.
   * @param {object} args - the block arguments.
   *  @property {number} SHADE - the amount of desired shade change.
   * @param {object} util - utility object provided by the runtime.
   */


  changePenShadeBy(args, util) {
    this._changePenShadeBy(args.SHADE, util.target);
  }

  _changePenShadeBy(shade, target) {
    const penState = this._getPenState(target);

    const shadeChange = Cast.toNumber(shade);

    this._setPenShadeToNumber(penState._shade + shadeChange, target);
  }
  /**
   * Update the pen state's color from its hue & shade values, Scratch 2.0 style.
   * @param {object} penState - update the HSV & RGB values in this pen state from its hue & shade values.
   * @private
   */


  _legacyUpdatePenColor(penState) {
    // Create the new color in RGB using the scratch 2 "shade" model
    let rgb = Color.hsvToRgb({
      h: penState.color * 360 / 100,
      s: 1,
      v: 1
    });
    const shade = penState._shade > 100 ? 200 - penState._shade : penState._shade;

    if (shade < 50) {
      rgb = Color.mixRgb(Color.RGB_BLACK, rgb, (10 + shade) / 60);
    } else {
      rgb = Color.mixRgb(rgb, Color.RGB_WHITE, (shade - 50) / 60);
    } // Update the pen state according to new color


    const hsv = Color.rgbToHsv(rgb);
    penState.color = 100 * hsv.h / 360;
    penState.saturation = 100 * hsv.s;
    penState.brightness = 100 * hsv.v;

    this._updatePenColor(penState);
  }

}

module.exports = Scratch3PenBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_text2speech/index.js":
/*!******************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_text2speech/index.js ***!
  \******************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const languageNames = __webpack_require__(/*! scratch-translate-extension-languages */ "./src/scaffolding/scratch-translate-extension-languages/languages.json");

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const Clone = __webpack_require__(/*! ../../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const fetchWithTimeout = __webpack_require__(/*! ../../util/fetch-with-timeout */ "./node_modules/scratch-vm/src/util/fetch-with-timeout.js");
/**
 * Icon svg to be displayed in the blocks category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1RleHQtdG8tU3BlZWNoLU1lbnU8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9UZXh0LXRvLVNwZWVjaC1NZW51IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyBpZD0idGV4dDJzcGVlY2giIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIuMDAwMDAwLCAyLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik01Ljc1LDguODM0NjcxNzMgQzUuNzUsOC4zMjY5NjM0NCA1LjAwMzAwNzI3LDguMDQyMjEzNzEgNC41NTYyODAxMiw4LjQ0NDE0OTk5IEwzLjIwNjI4MDEyLDkuNTI1MzU3MDIgQzIuNjk2NzMzNzgsOS45MzM0NDk2OCAyLjAzNzQ4Njc1LDEwLjE2NTg3ODggMS4zNSwxMC4xNjU4Nzg4IEwxLjE1LDEwLjE2NTg3ODggQzAuNjMyNTk2MTY1LDEwLjE2NTg3ODggMC4yNSwxMC41MTA2MDAyIDAuMjUsMTAuOTUyMDM1NSBMMC4yNSwxMy4wNjkzOTkzIEMwLjI1LDEzLjUxMDgzNDYgMC42MzI1OTYxNjUsMTMuODU1NTU2IDEuMTUsMTMuODU1NTU2IEwxLjM1LDEzLjg1NTU1NiBDMi4wNzg3Nzg0MSwxMy44NTU1NTYgMi43MjY4NjE2MSwxNC4wNjY3NjM2IDMuMjU5ODYwNDksMTQuNDk5IEw0LjU1OTIwMTQ3LDE1LjU3OTY2MDggQzUuMDEzMDkyNzYsMTUuOTU0NTM5NiA1Ljc1LDE1LjY3MzYzNDQgNS43NSwxNS4xNDE3MTI4IEw1Ljc1LDguODM0NjcxNzMgWiIgaWQ9InNwZWFrZXIiIHN0cm9rZS1vcGFjaXR5PSIwLjE1IiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS13aWR0aD0iMC41IiBmaWxsPSIjNEQ0RDREIj48L3BhdGg+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0xMC43MDQ4MzEzLDggQzkuNzkwNjc0NjgsOS4xMzExNDg0NyA4LjMwNjYxODQsOS43MTQyODU3MSA3LjgzMzMzMzMzLDkuNzE0Mjg1NzEgQzcuODMzMzMzMzMsOS43MTQyODU3MSA3LjUsOS43MTQyODU3MSA3LjUsOS4zODA5NTIzOCBDNy41LDkuMDg1MjI2ODQgOC4wNjIyMDE2OCw4LjkwMTk0MTY0IDguMTg5MDYwNjcsNy41Njc1NDA1OCBDNi44ODk5Njk5MSw2LjkwNjc5MDA1IDYsNS41NTczMjY4MyA2LDQgQzYsMS43OTA4NjEgNy43OTA4NjEsNC4wNTgxMjI1MWUtMTYgMTAsMCBMMTIsMCBDMTQuMjA5MTM5LC00LjA1ODEyMjUxZS0xNiAxNiwxLjc5MDg2MSAxNiw0IEMxNiw2LjIwOTEzOSAxNC4yMDkxMzksOCAxMiw4IEwxMC43MDQ4MzEzLDggWiIgaWQ9InNwZWVjaCIgZmlsbD0iIzBFQkQ4QyI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len

const blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1RleHQtdG8tU3BlZWNoLUJsb2NrPC90aXRsZT4KICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPgogICAgPGcgaWQ9IkV4dGVuc2lvbnMvU29mdHdhcmUvVGV4dC10by1TcGVlY2gtQmxvY2siIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIHN0cm9rZS1vcGFjaXR5PSIwLjE1Ij4KICAgICAgICA8ZyBpZD0idGV4dDJzcGVlY2giIHRyYW5zZm9ybT0idHJhbnNsYXRlKDQuMDAwMDAwLCA0LjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjUsMTcuNjY5MzQzNSBDMTEuNSwxNi42NTM5MjY5IDEwLjAwNjAxNDUsMTYuMDg0NDI3NCA5LjExMjU2MDI0LDE2Ljg4ODMgTDYuNDEyNTYwMjQsMTkuMDUwNzE0IEM1LjM5MzQ2NzU1LDE5Ljg2Njg5OTQgNC4wNzQ5NzM1MSwyMC4zMzE3NTc1IDIuNywyMC4zMzE3NTc1IEwyLjMsMjAuMzMxNzU3NSBDMS4yNjUxOTIzMywyMC4zMzE3NTc1IDAuNSwyMS4wMjEyMDAzIDAuNSwyMS45MDQwNzEgTDAuNSwyNi4xMzg3OTg2IEMwLjUsMjcuMDIxNjY5MyAxLjI2NTE5MjMzLDI3LjcxMTExMiAyLjMsMjcuNzExMTEyIEwyLjcsMjcuNzExMTEyIEM0LjE1NzU1NjgyLDI3LjcxMTExMiA1LjQ1MzcyMzIyLDI4LjEzMzUyNzEgNi41MTk3MjA5OCwyOC45OTggTDkuMTE4NDAyOTMsMzEuMTU5MzIxNiBDMTAuMDI2MTg1NSwzMS45MDkwNzkzIDExLjUsMzEuMzQ3MjY4OSAxMS41LDMwLjI4MzQyNTUgTDExLjUsMTcuNjY5MzQzNSBaIiBpZD0ic3BlYWtlciIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgICAgICA8cGF0aCBkPSJNMjEuNjQzNjA2NiwxNi41IEMxOS45NzcwMDk5LDE4LjQzNzAyMzQgMTcuMTA1MDI3NSwxOS45Mjg1NzE0IDE1LjY2NjY2NjcsMTkuOTI4NTcxNCBDMTUuNTEyNjM5NywxOS45Mjg1NzE0IDE1LjMxNjYyOTIsMTkuODk1OTAzIDE1LjEwOTcyNjUsMTkuNzkyNDUxNyBDMTQuNzM3NjAzOSwxOS42MDYzOTA0IDE0LjUsMTkuMjQ5OTg0NiAxNC41LDE4Ljc2MTkwNDggQzE0LjUsMTguNjU2ODA0MSAxNC41MTcwNTU1LDE4LjU1NDUwNzYgMTQuNTQ5NDQ2NywxOC40NTQwODQ0IEMxNC42MjU3NTQ1LDE4LjIxNzUwNjMgMTUuMTczNTcyMSwxNy40Njc1MzEgMTUuMjc3MjA3MSwxNy4yODA5ODgxIEMxNS41NDYzNTI2LDE2Ljc5NjUyNjEgMTUuNzM5MDI1LDE2LjIwNjM1NjEgMTUuODQzMjg5MSwxNS40MTYwMDM0IEMxMy4xODk3MDA1LDEzLjkyNjgzNjkgMTEuNSwxMS4xMTM5NjY4IDExLjUsOCBDMTEuNSwzLjMwNTU3OTYzIDE1LjMwNTU3OTYsLTAuNSAyMCwtMC41IEwyNCwtMC41IEMyOC42OTQ0MjA0LC0wLjUgMzIuNSwzLjMwNTU3OTYzIDMyLjUsOCBDMzIuNSwxMi42OTQ0MjA0IDI4LjY5NDQyMDQsMTYuNSAyNCwxNi41IEwyMS42NDM2MDY2LDE2LjUgWiIgaWQ9InNwZWVjaCIgZmlsbD0iI0ZGRkZGRiI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
/**
 * The url of the synthesis server.
 * @type {string}
 */

const SERVER_HOST = 'https://synthesis-service.scratch.mit.edu';
/**
 * How long to wait in ms before timing out requests to synthesis server.
 * @type {int}
 */

const SERVER_TIMEOUT = 10000; // 10 seconds

/**
 * Volume for playback of speech sounds, as a percentage.
 * @type {number}
 */

const SPEECH_VOLUME = 250;
/**
 * An id for one of the voices.
 */

const ALTO_ID = 'ALTO';
/**
 * An id for one of the voices.
 */

const TENOR_ID = 'TENOR';
/**
 * An id for one of the voices.
 */

const SQUEAK_ID = 'SQUEAK';
/**
 * An id for one of the voices.
 */

const GIANT_ID = 'GIANT';
/**
 * An id for one of the voices.
 */

const KITTEN_ID = 'KITTEN';
/**
 * Playback rate for the tenor voice, for cases where we have only a female gender voice.
 */

const FEMALE_TENOR_RATE = 0.89; // -2 semitones

/**
 * Playback rate for the giant voice, for cases where we have only a female gender voice.
 */

const FEMALE_GIANT_RATE = 0.79; // -4 semitones

/**
 * Language ids. The value for each language id is a valid Scratch locale.
 */

const ARABIC_ID = 'ar';
const CHINESE_ID = 'zh-cn';
const DANISH_ID = 'da';
const DUTCH_ID = 'nl';
const ENGLISH_ID = 'en';
const FRENCH_ID = 'fr';
const GERMAN_ID = 'de';
const HINDI_ID = 'hi';
const ICELANDIC_ID = 'is';
const ITALIAN_ID = 'it';
const JAPANESE_ID = 'ja';
const KOREAN_ID = 'ko';
const NORWEGIAN_ID = 'nb';
const POLISH_ID = 'pl';
const PORTUGUESE_BR_ID = 'pt-br';
const PORTUGUESE_ID = 'pt';
const ROMANIAN_ID = 'ro';
const RUSSIAN_ID = 'ru';
const SPANISH_ID = 'es';
const SPANISH_419_ID = 'es-419';
const SWEDISH_ID = 'sv';
const TURKISH_ID = 'tr';
const WELSH_ID = 'cy';
/**
 * Class for the text2speech blocks.
 * @constructor
 */

class Scratch3Text2SpeechBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * Map of soundPlayers by sound id.
     * @type {Map<string, SoundPlayer>}
     */

    this._soundPlayers = new Map();
    this._stopAllSpeech = this._stopAllSpeech.bind(this);

    if (this.runtime) {
      this.runtime.on('PROJECT_STOP_ALL', this._stopAllSpeech);
    }

    this._onTargetCreated = this._onTargetCreated.bind(this);

    if (this.runtime) {
      runtime.on('targetWasCreated', this._onTargetCreated);
    }
    /**
     * A list of all Scratch locales that are supported by the extension.
     * @type {Array}
     */


    this._supportedLocales = this._getSupportedLocales();
  }
  /**
   * An object with info for each voice.
   */


  get VOICE_INFO() {
    return {
      [ALTO_ID]: {
        name: formatMessage({
          id: 'text2speech.alto',
          default: 'alto',
          description: 'Name for a voice with ambiguous gender.'
        }),
        gender: 'female',
        playbackRate: 1
      },
      [TENOR_ID]: {
        name: formatMessage({
          id: 'text2speech.tenor',
          default: 'tenor',
          description: 'Name for a voice with ambiguous gender.'
        }),
        gender: 'male',
        playbackRate: 1
      },
      [SQUEAK_ID]: {
        name: formatMessage({
          id: 'text2speech.squeak',
          default: 'squeak',
          description: 'Name for a funny voice with a high pitch.'
        }),
        gender: 'female',
        playbackRate: 1.19 // +3 semitones

      },
      [GIANT_ID]: {
        name: formatMessage({
          id: 'text2speech.giant',
          default: 'giant',
          description: 'Name for a funny voice with a low pitch.'
        }),
        gender: 'male',
        playbackRate: 0.84 // -3 semitones

      },
      [KITTEN_ID]: {
        name: formatMessage({
          id: 'text2speech.kitten',
          default: 'kitten',
          description: 'A baby cat.'
        }),
        gender: 'female',
        playbackRate: 1.41 // +6 semitones

      }
    };
  }
  /**
   * An object with information for each language.
   *
   * A note on the different sets of locales referred to in this extension:
   *
   * SCRATCH LOCALE
   *      Set by the editor, and used to store the language state in the project.
   *      Listed in l10n: https://github.com/LLK/scratch-l10n/blob/master/src/supported-locales.js
   * SUPPORTED LOCALE
   *      A Scratch locale that has a corresponding extension locale.
   * EXTENSION LOCALE
   *      A locale corresponding to one of the available spoken languages
   *      in the extension. There can be multiple supported locales for a single
   *      extension locale. For example, for both written versions of chinese,
   *      zh-cn and zh-tw, we use a single spoken language (Mandarin). So there
   *      are two supported locales, with a single extension locale.
   * SPEECH SYNTH LOCALE
   *      A different locale code system, used by our speech synthesis service.
   *      Each extension locale has a speech synth locale.
   */


  get LANGUAGE_INFO() {
    return {
      [ARABIC_ID]: {
        name: 'Arabic',
        locales: ['ar'],
        speechSynthLocale: 'arb',
        singleGender: true
      },
      [CHINESE_ID]: {
        name: 'Chinese (Mandarin)',
        locales: ['zh-cn', 'zh-tw'],
        speechSynthLocale: 'cmn-CN',
        singleGender: true
      },
      [DANISH_ID]: {
        name: 'Danish',
        locales: ['da'],
        speechSynthLocale: 'da-DK'
      },
      [DUTCH_ID]: {
        name: 'Dutch',
        locales: ['nl'],
        speechSynthLocale: 'nl-NL'
      },
      [ENGLISH_ID]: {
        name: 'English',
        locales: ['en'],
        speechSynthLocale: 'en-US'
      },
      [FRENCH_ID]: {
        name: 'French',
        locales: ['fr'],
        speechSynthLocale: 'fr-FR'
      },
      [GERMAN_ID]: {
        name: 'German',
        locales: ['de'],
        speechSynthLocale: 'de-DE'
      },
      [HINDI_ID]: {
        name: 'Hindi',
        locales: ['hi'],
        speechSynthLocale: 'hi-IN',
        singleGender: true
      },
      [ICELANDIC_ID]: {
        name: 'Icelandic',
        locales: ['is'],
        speechSynthLocale: 'is-IS'
      },
      [ITALIAN_ID]: {
        name: 'Italian',
        locales: ['it'],
        speechSynthLocale: 'it-IT'
      },
      [JAPANESE_ID]: {
        name: 'Japanese',
        locales: ['ja', 'ja-hira'],
        speechSynthLocale: 'ja-JP'
      },
      [KOREAN_ID]: {
        name: 'Korean',
        locales: ['ko'],
        speechSynthLocale: 'ko-KR',
        singleGender: true
      },
      [NORWEGIAN_ID]: {
        name: 'Norwegian',
        locales: ['nb', 'nn'],
        speechSynthLocale: 'nb-NO',
        singleGender: true
      },
      [POLISH_ID]: {
        name: 'Polish',
        locales: ['pl'],
        speechSynthLocale: 'pl-PL'
      },
      [PORTUGUESE_BR_ID]: {
        name: 'Portuguese (Brazilian)',
        locales: ['pt-br'],
        speechSynthLocale: 'pt-BR'
      },
      [PORTUGUESE_ID]: {
        name: 'Portuguese (European)',
        locales: ['pt'],
        speechSynthLocale: 'pt-PT'
      },
      [ROMANIAN_ID]: {
        name: 'Romanian',
        locales: ['ro'],
        speechSynthLocale: 'ro-RO',
        singleGender: true
      },
      [RUSSIAN_ID]: {
        name: 'Russian',
        locales: ['ru'],
        speechSynthLocale: 'ru-RU'
      },
      [SPANISH_ID]: {
        name: 'Spanish (European)',
        locales: ['es'],
        speechSynthLocale: 'es-ES'
      },
      [SPANISH_419_ID]: {
        name: 'Spanish (Latin American)',
        locales: ['es-419'],
        speechSynthLocale: 'es-US'
      },
      [SWEDISH_ID]: {
        name: 'Swedish',
        locales: ['sv'],
        speechSynthLocale: 'sv-SE',
        singleGender: true
      },
      [TURKISH_ID]: {
        name: 'Turkish',
        locales: ['tr'],
        speechSynthLocale: 'tr-TR',
        singleGender: true
      },
      [WELSH_ID]: {
        name: 'Welsh',
        locales: ['cy'],
        speechSynthLocale: 'cy-GB',
        singleGender: true
      }
    };
  }
  /**
   * The key to load & store a target's text2speech state.
   * @return {string} The key.
   */


  static get STATE_KEY() {
    return 'Scratch.text2speech';
  }
  /**
   * The default state, to be used when a target has no existing state.
   * @type {Text2SpeechState}
   */


  static get DEFAULT_TEXT2SPEECH_STATE() {
    return {
      voiceId: ALTO_ID
    };
  }
  /**
   * A default language to use for speech synthesis.
   * @type {string}
   */


  get DEFAULT_LANGUAGE() {
    return ENGLISH_ID;
  }
  /**
   * @param {Target} target - collect  state for this target.
   * @returns {Text2SpeechState} the mutable state associated with that target. This will be created if necessary.
   * @private
   */


  _getState(target) {
    let state = target.getCustomState(Scratch3Text2SpeechBlocks.STATE_KEY);

    if (!state) {
      state = Clone.simple(Scratch3Text2SpeechBlocks.DEFAULT_TEXT2SPEECH_STATE);
      target.setCustomState(Scratch3Text2SpeechBlocks.STATE_KEY, state);
    }

    return state;
  }
  /**
   * When a Target is cloned, clone the state.
   * @param {Target} newTarget - the newly created target.
   * @param {Target} [sourceTarget] - the target used as a source for the new clone, if any.
   * @listens Runtime#event:targetWasCreated
   * @private
   */


  _onTargetCreated(newTarget, sourceTarget) {
    if (sourceTarget) {
      const state = sourceTarget.getCustomState(Scratch3Text2SpeechBlocks.STATE_KEY);

      if (state) {
        newTarget.setCustomState(Scratch3Text2SpeechBlocks.STATE_KEY, Clone.simple(state));
      }
    }
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    // Only localize the default input to the "speak" block if we are in a
    // supported language.
    let defaultTextToSpeak = 'hello';

    if (this.isSupportedLanguage(this.getEditorLanguage())) {
      defaultTextToSpeak = formatMessage({
        id: 'text2speech.defaultTextToSpeak',
        default: 'hello',
        description: 'hello: the default text to speak'
      });
    }

    return {
      id: 'text2speech',
      name: formatMessage({
        id: 'text2speech.categoryName',
        default: 'Text to Speech',
        description: 'Name of the Text to Speech extension.'
      }),
      blockIconURI: blockIconURI,
      menuIconURI: menuIconURI,
      blocks: [{
        opcode: 'speakAndWait',
        text: formatMessage({
          id: 'text2speech.speakAndWaitBlock',
          default: 'speak [WORDS]',
          description: 'Speak some words.'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          WORDS: {
            type: ArgumentType.STRING,
            defaultValue: defaultTextToSpeak
          }
        }
      }, {
        opcode: 'setVoice',
        text: formatMessage({
          id: 'text2speech.setVoiceBlock',
          default: 'set voice to [VOICE]',
          description: 'Set the voice for speech synthesis.'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          VOICE: {
            type: ArgumentType.STRING,
            menu: 'voices',
            defaultValue: ALTO_ID
          }
        }
      }, {
        opcode: 'setLanguage',
        text: formatMessage({
          id: 'text2speech.setLanguageBlock',
          default: 'set language to [LANGUAGE]',
          description: 'Set the language for speech synthesis.'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          LANGUAGE: {
            type: ArgumentType.STRING,
            menu: 'languages',
            defaultValue: this.getCurrentLanguage()
          }
        }
      }],
      menus: {
        voices: {
          acceptReporters: true,
          items: this.getVoiceMenu()
        },
        languages: {
          acceptReporters: true,
          items: this.getLanguageMenu()
        }
      }
    };
  }
  /**
   * Get the language code currently set in the editor, or fall back to the
   * browser locale.
   * @return {string} a Scratch locale code.
   */


  getEditorLanguage() {
    const locale = formatMessage.setup().locale || navigator.language || navigator.userLanguage || this.DEFAULT_LANGUAGE;
    return locale.toLowerCase();
  }
  /**
   * Get the language code currently set for the extension.
   * @returns {string} a Scratch locale code.
   */


  getCurrentLanguage() {
    const stage = this.runtime.getTargetForStage();
    if (!stage) return this.DEFAULT_LANGUAGE; // If no language has been set, set it to the editor locale (or default).

    if (!stage.textToSpeechLanguage) {
      this.setCurrentLanguage(this.getEditorLanguage());
    }

    return stage.textToSpeechLanguage;
  }
  /**
   * Set the language code for the extension.
   * It is stored in the stage so it can be saved and loaded with the project.
   * @param {string} locale a locale code.
   */


  setCurrentLanguage(locale) {
    const stage = this.runtime.getTargetForStage();
    if (!stage) return;

    if (this.isSupportedLanguage(locale)) {
      stage.textToSpeechLanguage = this._getExtensionLocaleForSupportedLocale(locale);
    } // Support language names dropped onto the menu via reporter block
    // such as a variable containing a language name (in any language),
    // or the translate extension's language reporter.


    const localeForDroppedName = languageNames.nameMap[locale.toLowerCase()];

    if (localeForDroppedName && this.isSupportedLanguage(localeForDroppedName)) {
      stage.textToSpeechLanguage = this._getExtensionLocaleForSupportedLocale(localeForDroppedName);
    } // If the language is null, set it to the default language.
    // This can occur e.g. if the extension was loaded with the editor
    // set to a language that is not in the list.


    if (!stage.textToSpeechLanguage) {
      stage.textToSpeechLanguage = this.DEFAULT_LANGUAGE;
    }
  }
  /**
   * Get the extension locale for a supported locale, or null.
   * @param {string} locale a locale code.
   * @returns {?string} a locale supported by the extension.
   */


  _getExtensionLocaleForSupportedLocale(locale) {
    for (const lang in this.LANGUAGE_INFO) {
      if (this.LANGUAGE_INFO[lang].locales.includes(locale)) {
        return lang;
      }
    }

    log.error("cannot find extension locale for locale ".concat(locale));
  }
  /**
   * Get the locale code used by the speech synthesis server corresponding to
   * the current language code set for the extension.
   * @returns {string} a speech synthesis locale.
   */


  _getSpeechSynthLocale() {
    let speechSynthLocale = this.LANGUAGE_INFO[this.DEFAULT_LANGUAGE].speechSynthLocale;

    if (this.LANGUAGE_INFO[this.getCurrentLanguage()]) {
      speechSynthLocale = this.LANGUAGE_INFO[this.getCurrentLanguage()].speechSynthLocale;
    }

    return speechSynthLocale;
  }
  /**
   * Get an array of the locales supported by this extension.
   * @returns {Array} An array of locale strings.
   */


  _getSupportedLocales() {
    return Object.keys(this.LANGUAGE_INFO).reduce((acc, lang) => acc.concat(this.LANGUAGE_INFO[lang].locales), []);
  }
  /**
   * Check if a Scratch language code is in the list of supported languages for the
   * speech synthesis service.
   * @param {string} languageCode the language code to check.
   * @returns {boolean} true if the language code is supported.
   */


  isSupportedLanguage(languageCode) {
    return this._supportedLocales.includes(languageCode);
  }
  /**
   * Get the menu of voices for the "set voice" block.
   * @return {array} the text and value for each menu item.
   */


  getVoiceMenu() {
    return Object.keys(this.VOICE_INFO).map(voiceId => ({
      text: this.VOICE_INFO[voiceId].name,
      value: voiceId
    }));
  }
  /**
   * Get the localized menu of languages for the "set language" block.
   * For each language:
   *   if there is a custom translated spoken language name, use that;
   *   otherwise use the translation in the languageNames menuMap;
   *   otherwise fall back to the untranslated name in LANGUAGE_INFO.
   * @return {array} the text and value for each menu item.
   */


  getLanguageMenu() {
    const editorLanguage = this.getEditorLanguage(); // Get the array of localized language names

    const localizedNameMap = {};
    let nameArray = languageNames.menuMap[editorLanguage];

    if (nameArray) {
      // Also get any localized names of spoken languages
      let spokenNameArray = [];

      if (languageNames.spokenLanguages) {
        spokenNameArray = languageNames.spokenLanguages[editorLanguage];
        nameArray = nameArray.concat(spokenNameArray);
      } // Create a map of language code to localized name
      // The localized spoken language names have been concatenated onto
      // the end of the name array, so the result of the forEach below is
      // when there is both a written language name (e.g. 'Chinese
      // (simplified)') and a spoken language name (e.g. 'Chinese
      // (Mandarin)', we always use the spoken version.


      nameArray.forEach(lang => {
        localizedNameMap[lang.code] = lang.name;
      });
    }

    return Object.keys(this.LANGUAGE_INFO).map(key => {
      let name = this.LANGUAGE_INFO[key].name;
      const localizedName = localizedNameMap[key];

      if (localizedName) {
        name = localizedName;
      } // Uppercase the first character of the name


      name = name.charAt(0).toUpperCase() + name.slice(1);
      return {
        text: name,
        value: key
      };
    });
  }
  /**
   * Set the voice for speech synthesis for this sprite.
   * @param  {object} args Block arguments
   * @param {object} util Utility object provided by the runtime.
   */


  setVoice(args, util) {
    const state = this._getState(util.target);

    let voice = args.VOICE; // If the arg is a dropped number, treat it as a voice index

    let voiceNum = parseInt(voice, 10);

    if (!isNaN(voiceNum)) {
      voiceNum -= 1; // Treat dropped args as one-indexed

      voiceNum = MathUtil.wrapClamp(voiceNum, 0, Object.keys(this.VOICE_INFO).length - 1);
      voice = Object.keys(this.VOICE_INFO)[voiceNum];
    } // Only set the voice if the arg is a valid voice id.


    if (Object.keys(this.VOICE_INFO).includes(voice)) {
      state.voiceId = voice;
    }
  }
  /**
   * Set the language for speech synthesis.
   * @param  {object} args Block arguments
   */


  setLanguage(args) {
    this.setCurrentLanguage(args.LANGUAGE);
  }
  /**
   * Stop all currently playing speech sounds.
   */


  _stopAllSpeech() {
    this._soundPlayers.forEach(player => {
      player.stop();
    });
  }
  /**
   * Convert the provided text into a sound file and then play the file.
   * @param  {object} args Block arguments
   * @param {object} util Utility object provided by the runtime.
   * @return {Promise} A promise that resolves after playing the sound
   */


  speakAndWait(args, util) {
    // Cast input to string
    let words = Cast.toString(args.WORDS);

    let locale = this._getSpeechSynthLocale();

    const state = this._getState(util.target);

    let gender = this.VOICE_INFO[state.voiceId].gender;
    let playbackRate = this.VOICE_INFO[state.voiceId].playbackRate; // Special case for voices where the synthesis service only provides a
    // single gender voice. In that case, always request the female voice,
    // and set special playback rates for the tenor and giant voices.

    if (this.LANGUAGE_INFO[this.getCurrentLanguage()].singleGender) {
      gender = 'female';

      if (state.voiceId === TENOR_ID) {
        playbackRate = FEMALE_TENOR_RATE;
      }

      if (state.voiceId === GIANT_ID) {
        playbackRate = FEMALE_GIANT_RATE;
      }
    }

    if (state.voiceId === KITTEN_ID) {
      words = words.replace(/\S+/g, 'meow');
      locale = this.LANGUAGE_INFO[this.DEFAULT_LANGUAGE].speechSynthLocale;
    } // Build up URL


    let path = "".concat(SERVER_HOST, "/synth");
    path += "?locale=".concat(locale);
    path += "&gender=".concat(gender);
    path += "&text=".concat(encodeURIComponent(words.substring(0, 128))); // Perform HTTP request to get audio file

    return fetchWithTimeout(path, {}, SERVER_TIMEOUT).then(res => {
      if (res.status !== 200) {
        throw new Error("HTTP ".concat(res.status, " error reaching translation service"));
      }

      return res.arrayBuffer();
    }).then(buffer => {
      // Play the sound
      const sound = {
        data: {
          buffer
        }
      };
      return this.runtime.audioEngine.decodeSoundPlayer(sound);
    }).then(soundPlayer => {
      this._soundPlayers.set(soundPlayer.id, soundPlayer);

      soundPlayer.setPlaybackRate(playbackRate); // Increase the volume

      const engine = this.runtime.audioEngine;
      const chain = engine.createEffectChain();
      chain.set('volume', SPEECH_VOLUME);
      soundPlayer.connect(chain);
      soundPlayer.play();
      return new Promise(resolve => {
        soundPlayer.on('stop', () => {
          this._soundPlayers.delete(soundPlayer.id);

          resolve();
        });
      });
    }).catch(err => {
      log.warn(err);
    });
  }

}

module.exports = Scratch3Text2SpeechBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_translate/index.js":
/*!****************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_translate/index.js ***!
  \****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const fetchWithTimeout = __webpack_require__(/*! ../../util/fetch-with-timeout */ "./node_modules/scratch-vm/src/util/fetch-with-timeout.js");

const languageNames = __webpack_require__(/*! scratch-translate-extension-languages */ "./src/scaffolding/scratch-translate-extension-languages/languages.json");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");
/**
 * Icon svg to be displayed in the blocks category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const menuIconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAGAklEQVRYhe1YbUxTVxh+rh02o0KtkOEgKA4U4yeRWCdgxDoxCnH6h22iqSz76aasZlnijzkTBlvS4TJ/LGaJsmiyESe4hAVJvMJGxwQhLKECcRWkpWNZERs6Ctb2Lm97C/fe3n6Jyfzhk5y09z3nPPe57znnPe85DMdxeJ6x6LlW90LgM8BLchR1dXUZeXl5b3Ect+ppXsEwzHBfX98PVVVVY0GbmjW2AdgpaFYP4JxTZ+iLyCVdJFeuXNmdn59fn56enrFkyRIsWhSfk30+H1wuF+x2+1hPT4++oqLiJi/wEoA8AJslXSqdOsOlmARWV1dnlpeXd2ZnZ2fEK0xOqMViGWtoaNh++vRpa9CuZo1ZAJokQlc5dYYROR6RCq1WW56WlhZV3H0H8O9sZIHEQVzEKbTzQooBPBCYz4TlET4oFIosGtZoOHUN+Ph61GYgLuIU2tSscSmAYwAeCcx6NWs8o2aNxVKOkEUi9R55qv428Ng7b3viA/6eAs7dmrctVgD6bYBKGZ6LB4mrk7F/whcmokApfh8BWu6G2mc8ADsktuWmAbtzozGiLUJdu9QQVSC98JUkYNgBfPsboH4Z+GhPoK62FZiaAU7sCrTZmB5VHM3BPjVrrARwUVL1B4CD0vYxLVV68YFNQIICcLrn7SROtTjwEbGIE4iksFIpEVfs1BkeSdvGFUsObAz8Gm8CNTcC/49q42EIEbkLwKfhxCGWIRZC/zrQ/ifgcAWMK5YB+zc8nUBeZFuUORmfQIp/PsHGM/04YMta5oPT6cTs7Cw8Ho+oj9vtzmloaCgPZQtApVI96ejo6K2trR3lOM4nrRftJCzLfq3T6Y7LCfvuNtDL7wepfKgkTz6ZdeHdzePYlq30xz2lUintHhH0UbQ12my2+oKCguMcx7mE7aOHmWHgsxvzzzQP3ysMxMfzt2bxKmNHyZblSE5OjktYEImJidBoNFCr1frOzs5khmHe4Thubp8SCVQoFBwNUUJCwpyNwsfyZGBDOvB2fuCZQAH56KYJKJUpTy1OCOJYvXr1ocbGxjIAPwarRKvYZrNdn5iYEHV8LRW4cBj4oHheXBDT09PPRFwQxKXRaIQpmVjgkSNHfrFardcmJydjIqSMRehtOfzjmMTZmm/8hf5HAnF5vV7RVicSyHGcR6vVHh4YGPjKYrFMkTelq5JAH0B1MzMzUT+iu6cfdwfv+wv9jxchgZomaEFBwcmcnJxVY2NjXQqFQlQ/Pj6O/v7+s2az+U2Hw9Ec7X3tHXfm/v/c2hG3wLCruLm5+VBGRoY2mJGQJ0nc4ODgqZKSkjqKWSzL7olEPjJqx4PRv5CaqvE/OxyTflvWitj3xbBbnUql2kRxjYTRcA4MDHR1d3frguJiIW//NeC9/SVF2LplvcgWK8J6sKWl5UuVSrXO4/HYHj58+FNZWVkLx3HT8Rz0u3vN/t8Ho3aRaH3FgYULrKmpodT8jeBzvDcQ3T1m/5D6RXX0zNmn3TP+uq356xcmkE/NTwLoc+oMTXGpA3CnN7Bi99Hw5s8PL4mlulgFys5BXlwbn4I3qlnjsXgFBr22f+8OrFub7S/79u4Q1cWCEA8KxAmPhRfVrBFy51cK1nJnj+/rvwix0eqVswu5pJDzoPTMKhSZJzQolUoLZSLPCsRFnEI6OYE7I7xPdGYoKiq6YLVaByllWiiIg7iIM5rAYBouBB2yq5w6g+iATWGnqampZGhoqItiJSUP4YrcR9CQUh31JQ7iIk5hm7AXmPxdip5/dNIUCnduYBgm8fLly9tzc3NLwzlSqVTuW7NmzVphQkubwL179+xdXV3HKisrTVJxiJKwnuGHVM2XNjVrPCh3h8IT3+SLLKqrq+tKS0uvrly5UksJKvjsJSkpKd3r9TrkxCGSBxHwIoWXc7zAIOiIOOLUGULOsNHAMIzSZDJ9npmZeSIlJcWfTdPQm0ym8zqd7n257hGPnXxYyePv8py8mVb40ji1+UGZUmFh4Yetra1bzGbzteHh4SlKQNxu961wff7XS3Sau/w0c4VLQF7c8i8IAP4DcHKth/4Ur7MAAAAASUVORK5CYII=';
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len

const blockIconURI = 'data:image/png;base64, ';
/**
 * The url of the translate server.
 * @type {string}
 */

const serverURL = 'https://trampoline.turbowarp.org/translate/';
/**
 * How long to wait in ms before timing out requests to translate server.
 * @type {int}
 */

const serverTimeoutMs = 10000; // 10 seconds (chosen arbitrarily).

/**
 * Class for the translate block in Scratch 3.0.
 * @constructor
 */

class Scratch3TranslateBlocks {
  constructor() {
    /**
     * Language code of the viewer, based on their locale.
     * @type {string}
     * @private
     */
    this._viewerLanguageCode = this.getViewerLanguageCode();
    /**
     * List of supported language name and language code pairs, for use in the block menu.
     * Filled in by getInfo so it is updated when the interface language changes.
     * @type {Array.<object.<string, string>>}
     * @private
     */

    this._supportedLanguages = [];
    /**
     * A randomly selected language code, for use as the default value in the language menu.
     * Properly filled in getInfo so it is updated when the interface languages changes.
     * @type {string}
     * @private
     */

    this._randomLanguageCode = 'en';
    /**
     * The result from the most recent translation.
     * @type {string}
     * @private
     */

    this._translateResult = '';
    /**
     * The language of the text most recently translated.
     * @type {string}
     * @private
     */

    this._lastLangTranslated = '';
    /**
     * The text most recently translated.
     * @type {string}
     * @private
     */

    this._lastTextTranslated = '';
  }
  /**
   * The key to load & store a target's translate state.
   * @return {string} The key.
   */


  static get STATE_KEY() {
    return 'Scratch.translate';
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    this._supportedLanguages = this._getSupportedLanguages(this.getViewerLanguageCode());
    this._randomLanguageCode = this._supportedLanguages[Math.floor(Math.random() * this._supportedLanguages.length)].value;
    return {
      id: 'translate',
      name: formatMessage({
        id: 'translate.categoryName',
        default: 'Translate',
        description: 'Name of extension that adds translate blocks'
      }),
      blockIconURI: blockIconURI,
      menuIconURI: menuIconURI,
      blocks: [{
        opcode: 'getTranslate',
        text: formatMessage({
          id: 'translate.translateBlock',
          default: 'translate [WORDS] to [LANGUAGE]',
          description: 'translate some text to a different language'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          WORDS: {
            type: ArgumentType.STRING,
            defaultValue: formatMessage({
              id: 'translate.defaultTextToTranslate',
              default: 'hello',
              description: 'hello: the default text to translate'
            })
          },
          LANGUAGE: {
            type: ArgumentType.STRING,
            menu: 'languages',
            defaultValue: this._randomLanguageCode
          }
        }
      }, {
        opcode: 'getViewerLanguage',
        text: formatMessage({
          id: 'translate.viewerLanguage',
          default: 'language',
          description: 'the languge of the project viewer'
        }),
        blockType: BlockType.REPORTER,
        arguments: {}
      }],
      menus: {
        languages: {
          acceptReporters: true,
          items: this._supportedLanguages
        }
      }
    };
  }
  /**
   * Computes a list of language code and name pairs for the given language.
   * @param {string} code The language code to get the list of language pairs
   * @return {Array.<object.<string, string>>} An array of languge name and
   *   language code pairs.
   * @private
   */


  _getSupportedLanguages(code) {
    return languageNames.menuMap[code].map(entry => {
      const obj = {
        text: entry.name,
        value: entry.code
      };
      return obj;
    });
  }
  /**
   * Get the human readable language value for the reporter block.
   * @return {string} the language name of the project viewer.
   */


  getViewerLanguage() {
    this._viewerLanguageCode = this.getViewerLanguageCode();
    const names = languageNames.menuMap[this._viewerLanguageCode];
    let langNameObj = names.find(obj => obj.code === this._viewerLanguageCode); // If we don't have a name entry yet, try looking it up via the Google langauge
    // code instead of Scratch's (e.g. for es-419 we look up es to get espanol)

    if (!langNameObj && languageNames.scratchToGoogleMap[this._viewerLanguageCode]) {
      const lookupCode = languageNames.scratchToGoogleMap[this._viewerLanguageCode];
      langNameObj = names.find(obj => obj.code === lookupCode);
    }

    let langName = this._viewerLanguageCode;

    if (langNameObj) {
      langName = langNameObj.name;
    }

    return langName;
  }
  /**
   * Get the viewer's language code.
   * @return {string} the language code.
   */


  getViewerLanguageCode() {
    const locale = formatMessage.setup().locale;
    const viewerLanguages = [locale].concat(navigator.languages);
    const languageKeys = Object.keys(languageNames.menuMap); // Return the first entry in viewerLanguages that matches
    // one of the available language keys.

    const languageCode = viewerLanguages.reduce((acc, lang) => {
      if (acc) {
        return acc;
      }

      if (languageKeys.indexOf(lang.toLowerCase()) > -1) {
        return lang;
      }

      return acc;
    }, '') || 'en';
    return languageCode.toLowerCase();
  }
  /**
   * Get a language code from a block argument. The arg can be a language code
   * or a language name, written in any language.
   * @param  {object} arg A block argument.
   * @return {string} A language code.
   */


  getLanguageCodeFromArg(arg) {
    const languageArg = Cast.toString(arg).toLowerCase(); // Check if the arg matches a language code in the menu.

    if (languageNames.menuMap.hasOwnProperty(languageArg)) {
      return languageArg;
    } // Check for a dropped-in language name, and convert to a language code.


    if (languageNames.nameMap.hasOwnProperty(languageArg)) {
      return languageNames.nameMap[languageArg];
    } // There are some languages we launched in the language menu that Scratch did not
    // end up launching in. In order to keep projects that may have had that menu item
    // working, check for those language codes and let them through.
    // Examples: 'ab', 'hi'.


    if (languageNames.previouslySupported.indexOf(languageArg) !== -1) {
      return languageArg;
    } // Default to English.


    return 'en';
  }
  /**
   * Translates the text in the translate block to the language specified in the menu.
   * @param {object} args - the block arguments.
   * @return {Promise} - a promise that resolves after the response from the translate server.
   */


  getTranslate(args) {
    // If the text contains only digits 0-9 and nothing else, return it without
    // making a request.
    if (/^\d+$/.test(args.WORDS)) return Promise.resolve(args.WORDS); // Don't remake the request if we already have the value.

    if (this._lastTextTranslated === args.WORDS && this._lastLangTranslated === args.LANGUAGE) {
      return this._translateResult;
    }

    const lang = this.getLanguageCodeFromArg(args.LANGUAGE);
    let urlBase = "".concat(serverURL, "translate?language=");
    urlBase += lang;
    urlBase += '&text=';
    urlBase += encodeURIComponent(args.WORDS);
    const tempThis = this;
    const translatePromise = fetchWithTimeout(urlBase, {}, serverTimeoutMs).then(response => response.text()).then(responseText => {
      const translated = JSON.parse(responseText).result;
      tempThis._translateResult = translated; // Cache what we just translated so we don't keep making the
      // same call over and over.

      tempThis._lastTextTranslated = args.WORDS;
      tempThis._lastLangTranslated = args.LANGUAGE;
      return translated;
    }).catch(err => {
      log.warn("error fetching translate result! ".concat(err));
      return '';
    });
    return translatePromise;
  }

}

module.exports = Scratch3TranslateBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/index.js":
/*!********************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/index.js ***!
  \********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Runtime = __webpack_require__(/*! ../../engine/runtime */ "./node_modules/scratch-vm/src/engine/runtime.js");

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Clone = __webpack_require__(/*! ../../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const Video = __webpack_require__(/*! ../../io/video */ "./node_modules/scratch-vm/src/io/video.js");

const VideoMotion = __webpack_require__(/*! ./library */ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/library.js");
/**
 * Icon svg to be displayed in the blocks category menu, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const menuIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjBweCIgaGVpZ2h0PSIyMHB4IiB2aWV3Qm94PSIwIDAgMjAgMjAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJFeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctTWVudSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDUuMDAwMDAwKSIgZmlsbC1ydWxlPSJub256ZXJvIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjI1IiBjeD0iMTYiIGN5PSI4IiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjMEVCRDhDIiBvcGFjaXR5PSIwLjUiIGN4PSIxNiIgY3k9IjYiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiMwRUJEOEMiIG9wYWNpdHk9IjAuNzUiIGN4PSIxNiIgY3k9IjQiIHI9IjIiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsIiBmaWxsPSIjMEVCRDhDIiBjeD0iMTYiIGN5PSIyIiByPSIyIj48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTExLjMzNTk3MzksMi4yMDk3ODgyNSBMOC4yNSw0LjIwOTk1NjQ5IEw4LjI1LDMuMDUgQzguMjUsMi4wNDQ4ODIyNyA3LjQ2ODU5MDMxLDEuMjUgNi41LDEuMjUgTDIuMDUsMS4yNSBDMS4wMzgwNzExOSwxLjI1IDAuMjUsMi4wMzgwNzExOSAwLjI1LDMuMDUgTDAuMjUsNyBDMC4yNSw3Ljk2MzY5OTM3IDEuMDQyMjQ5MTksOC43NTU5NDg1NiAyLjA1LDguOCBMNi41LDguOCBDNy40NTA4MzAwOSw4LjggOC4yNSw3Ljk3MzI3MjUgOC4yNSw3IEw4LjI1LDUuODU4NDUyNDEgTDguNjI4NjIzOTQsNi4wODU2MjY3NyBMMTEuNDI2Nzc2Nyw3Ljc3MzIyMzMgQzExLjQzNjg5NDMsNy43ODMzNDA5MSAxMS40NzU3NjU1LDcuOCAxMS41LDcuOCBDMTEuNjMzNDkzMiw3LjggMTEuNzUsNy42OTEyNjAzNCAxMS43NSw3LjU1IEwxMS43NSwyLjQgQzExLjc1LDIuNDE4MzgyNjkgMTEuNzIxOTAyOSwyLjM1MjgyMjgyIDExLjY4NTYyNjgsMi4yNzg2MjM5NCBDMTEuNjEyOTUyOCwyLjE1NzUwMDY5IDExLjQ3MDc5NjgsMi4xMjkwNjk1IDExLjMzNTk3MzksMi4yMDk3ODgyNSBaIiBpZD0idmlkZW9fMzdfIiBzdHJva2Utb3BhY2l0eT0iMC4xNSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2Utd2lkdGg9IjAuNSIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len

const blockIconURI = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNDBweCIgaGVpZ2h0PSI0MHB4IiB2aWV3Qm94PSIwIDAgNDAgNDAiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjIgKDY3MTQ1KSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5FeHRlbnNpb25zL1NvZnR3YXJlL1ZpZGVvLVNlbnNpbmctQmxvY2s8L3RpdGxlPgogICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+CiAgICA8ZyBpZD0iRXh0ZW5zaW9ucy9Tb2Z0d2FyZS9WaWRlby1TZW5zaW5nLUJsb2NrIiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2Utb3BhY2l0eT0iMC4xNSI+CiAgICAgICAgPGcgaWQ9InZpZGVvLW1vdGlvbiIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMC4wMDAwMDAsIDEwLjAwMDAwMCkiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSIjMDAwMDAwIj4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjI1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjE2IiByPSI0LjUiPjwvY2lyY2xlPgogICAgICAgICAgICA8Y2lyY2xlIGlkPSJPdmFsLUNvcHkiIGZpbGw9IiNGRkZGRkYiIG9wYWNpdHk9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjeD0iMzIiIGN5PSIxMiIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPGNpcmNsZSBpZD0iT3ZhbC1Db3B5IiBmaWxsPSIjRkZGRkZGIiBvcGFjaXR5PSIwLjc1IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGN4PSIzMiIgY3k9IjgiIHI9IjQuNSI+PC9jaXJjbGU+CiAgICAgICAgICAgIDxjaXJjbGUgaWQ9Ik92YWwiIGZpbGw9IiNGRkZGRkYiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgY3g9IjMyIiBjeT0iNCIgcj0iNC41Ij48L2NpcmNsZT4KICAgICAgICAgICAgPHBhdGggZD0iTTIyLjY3MTk0NzcsNC40MTk1NzY0OSBMMTYuNSw4LjQxOTkxMjk4IEwxNi41LDYuMSBDMTYuNSw0LjA4OTc2NDU0IDE0LjkzNzE4MDYsMi41IDEzLDIuNSBMNC4xLDIuNSBDMi4wNzYxNDIzNywyLjUgMC41LDQuMDc2MTQyMzcgMC41LDYuMSBMMC41LDE0IEMwLjUsMTUuOTI3Mzk4NyAyLjA4NDQ5ODM5LDE3LjUxMTg5NzEgNC4xLDE3LjYgTDEzLDE3LjYgQzE0LjkwMTY2MDIsMTcuNiAxNi41LDE1Ljk0NjU0NSAxNi41LDE0IEwxNi41LDExLjcxNjkwNDggTDIyLjc1NzI0NzksMTUuNDcxMjUzNSBMMjIuODUzNTUzNCwxNS41NDY0NDY2IEMyMi44NzM3ODg2LDE1LjU2NjY4MTggMjIuOTUxNTMxLDE1LjYgMjMsMTUuNiBDMjMuMjY2OTg2NSwxNS42IDIzLjUsMTUuMzgyNTIwNyAyMy41LDE1LjEgTDIzLjUsNC44IEMyMy41LDQuODM2NzY1MzggMjMuNDQzODA1OCw0LjcwNTY0NTYzIDIzLjM3MTI1MzUsNC41NTcyNDc4OCBDMjMuMjI1OTA1Niw0LjMxNTAwMTM5IDIyLjk0MTU5MzcsNC4yNTgxMzg5OSAyMi42NzE5NDc3LDQuNDE5NTc2NDkgWiIgaWQ9InZpZGVvXzM3XyIgZmlsbD0iIzRENEQ0RCI+PC9wYXRoPgogICAgICAgIDwvZz4KICAgIDwvZz4KPC9zdmc+';
/**
 * Sensor attribute video sensor block should report.
 * @readonly
 * @enum {string}
 */

const SensingAttribute = {
  /** The amount of motion. */
  MOTION: 'motion',

  /** The direction of the motion. */
  DIRECTION: 'direction'
};
/**
 * Subject video sensor block should report for.
 * @readonly
 * @enum {string}
 */

const SensingSubject = {
  /** The sensor traits of the whole stage. */
  STAGE: 'Stage',

  /** The senosr traits of the area overlapped by this sprite. */
  SPRITE: 'this sprite'
};
/**
 * States the video sensing activity can be set to.
 * @readonly
 * @enum {string}
 */

const VideoState = {
  /** Video turned off. */
  OFF: 'off',

  /** Video turned on with default y axis mirroring. */
  ON: 'on',

  /** Video turned on without default y axis mirroring. */
  ON_FLIPPED: 'on-flipped'
};
/**
 * Class for the motion-related blocks in Scratch 3.0
 * @param {Runtime} runtime - the runtime instantiating this block package.
 * @constructor
 */

class Scratch3VideoSensingBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
    /**
     * The motion detection algoritm used to power the motion amount and
     * direction values.
     * @type {VideoMotion}
     */

    this.detect = new VideoMotion();
    /**
     * The last millisecond epoch timestamp that the video stream was
     * analyzed.
     * @type {number}
     */

    this._lastUpdate = null;
    /**
     * A flag to determine if this extension has been installed in a project.
     * It is set to false the first time getInfo is run.
     * @type {boolean}
     */

    this.firstInstall = true;

    if (this.runtime.ioDevices) {
      // Configure the video device with values from globally stored locations.
      this.runtime.on(Runtime.PROJECT_LOADED, this.updateVideoDisplay.bind(this)); // Clear target motion state values when the project starts.

      this.runtime.on(Runtime.PROJECT_RUN_START, this.reset.bind(this)); // Kick off looping the analysis logic.

      this._loop();
    }
  }
  /**
   * After analyzing a frame the amount of milliseconds until another frame
   * is analyzed.
   * @type {number}
   */


  static get INTERVAL() {
    return 33;
  }
  /**
   * Dimensions the video stream is analyzed at after its rendered to the
   * sample canvas.
   * @type {Array.<number>}
   */


  static get DIMENSIONS() {
    return [480, 360];
  }
  /**
   * The key to load & store a target's motion-related state.
   * @type {string}
   */


  static get STATE_KEY() {
    return 'Scratch.videoSensing';
  }
  /**
   * The default motion-related state, to be used when a target has no existing motion state.
   * @type {MotionState}
   */


  static get DEFAULT_MOTION_STATE() {
    return {
      motionFrameNumber: 0,
      motionAmount: 0,
      motionDirection: 0
    };
  }
  /**
   * The transparency setting of the video preview stored in a value
   * accessible by any object connected to the virtual machine.
   * @type {number}
   */


  get globalVideoTransparency() {
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      return stage.videoTransparency;
    }

    return 50;
  }

  set globalVideoTransparency(transparency) {
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      stage.videoTransparency = transparency;
    }

    return transparency;
  }
  /**
   * The video state of the video preview stored in a value accessible by any
   * object connected to the virtual machine.
   * @type {number}
   */


  get globalVideoState() {
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      return stage.videoState;
    } // Though the default value for the stage is normally 'on', we need to default
    // to 'off' here to prevent the video device from briefly activating
    // while waiting for stage targets to be installed that say it should be off


    return VideoState.OFF;
  }

  set globalVideoState(state) {
    const stage = this.runtime.getTargetForStage();

    if (stage) {
      stage.videoState = state;
    }

    return state;
  }
  /**
   * Get the latest values for video transparency and state,
   * and set the video device to use them.
   */


  updateVideoDisplay() {
    this.setVideoTransparency({
      TRANSPARENCY: this.globalVideoTransparency
    });
    this.videoToggle({
      VIDEO_STATE: this.globalVideoState
    });
  }
  /**
   * Reset the extension's data motion detection data. This will clear out
   * for example old frames, so the first analyzed frame will not be compared
   * against a frame from before reset was called.
   */


  reset() {
    this.detect.reset();
    const targets = this.runtime.targets;

    for (let i = 0; i < targets.length; i++) {
      const state = targets[i].getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);

      if (state) {
        state.motionAmount = 0;
        state.motionDirection = 0;
      }
    }
  }
  /**
   * Occasionally step a loop to sample the video, stamp it to the preview
   * skin, and add a TypedArray copy of the canvas's pixel data.
   * @private
   */


  _loop() {
    setTimeout(this._loop.bind(this), Math.max(this.runtime.currentStepTime, Scratch3VideoSensingBlocks.INTERVAL)); // Add frame to detector

    const time = Date.now();

    if (this._lastUpdate === null) {
      this._lastUpdate = time;
    }

    const offset = time - this._lastUpdate;

    if (offset > Scratch3VideoSensingBlocks.INTERVAL) {
      const frame = this.runtime.ioDevices.video.getFrame({
        format: Video.FORMAT_IMAGE_DATA,
        dimensions: Scratch3VideoSensingBlocks.DIMENSIONS
      });

      if (frame) {
        this._lastUpdate = time;
        this.detect.addFrame(frame.data);
      }
    }
  }
  /**
   * Create data for a menu in scratch-blocks format, consisting of an array
   * of objects with text and value properties. The text is a translated
   * string, and the value is one-indexed.
   * @param {object[]} info - An array of info objects each having a name
   *   property.
   * @return {array} - An array of objects with text and value properties.
   * @private
   */


  _buildMenu(info) {
    return info.map((entry, index) => {
      const obj = {};
      obj.text = entry.name;
      obj.value = entry.value || String(index + 1);
      return obj;
    });
  }
  /**
   * @param {Target} target - collect motion state for this target.
   * @returns {MotionState} the mutable motion state associated with that
   *   target. This will be created if necessary.
   * @private
   */


  _getMotionState(target) {
    let motionState = target.getCustomState(Scratch3VideoSensingBlocks.STATE_KEY);

    if (!motionState) {
      motionState = Clone.simple(Scratch3VideoSensingBlocks.DEFAULT_MOTION_STATE);
      target.setCustomState(Scratch3VideoSensingBlocks.STATE_KEY, motionState);
    }

    return motionState;
  }

  static get SensingAttribute() {
    return SensingAttribute;
  }
  /**
   * An array of choices of whether a reporter should return the frame's
   * motion amount or direction.
   * @type {object[]}
   * @param {string} name - the translatable name to display in sensor
   *   attribute menu
   * @param {string} value - the serializable value of the attribute
   */


  get ATTRIBUTE_INFO() {
    return [{
      name: formatMessage({
        id: 'videoSensing.motion',
        default: 'motion',
        description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
      }),
      value: SensingAttribute.MOTION
    }, {
      name: formatMessage({
        id: 'videoSensing.direction',
        default: 'direction',
        description: 'Attribute for the "video [ATTRIBUTE] on [SUBJECT]" block'
      }),
      value: SensingAttribute.DIRECTION
    }];
  }

  static get SensingSubject() {
    return SensingSubject;
  }
  /**
   * An array of info about the subject choices.
   * @type {object[]}
   * @param {string} name - the translatable name to display in the subject menu
   * @param {string} value - the serializable value of the subject
   */


  get SUBJECT_INFO() {
    return [{
      name: formatMessage({
        id: 'videoSensing.sprite',
        default: 'sprite',
        description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
      }),
      value: SensingSubject.SPRITE
    }, {
      name: formatMessage({
        id: 'videoSensing.stage',
        default: 'stage',
        description: 'Subject for the "video [ATTRIBUTE] on [SUBJECT]" block'
      }),
      value: SensingSubject.STAGE
    }];
  }
  /**
   * States the video sensing activity can be set to.
   * @readonly
   * @enum {string}
   */


  static get VideoState() {
    return VideoState;
  }
  /**
   * An array of info on video state options for the "turn video [STATE]" block.
   * @type {object[]}
   * @param {string} name - the translatable name to display in the video state menu
   * @param {string} value - the serializable value stored in the block
   */


  get VIDEO_STATE_INFO() {
    return [{
      name: formatMessage({
        id: 'videoSensing.off',
        default: 'off',
        description: 'Option for the "turn video [STATE]" block'
      }),
      value: VideoState.OFF
    }, {
      name: formatMessage({
        id: 'videoSensing.on',
        default: 'on',
        description: 'Option for the "turn video [STATE]" block'
      }),
      value: VideoState.ON
    }, {
      name: formatMessage({
        id: 'videoSensing.onFlipped',
        default: 'on flipped',
        description: 'Option for the "turn video [STATE]" block that causes the video to be flipped' + ' horizontally (reversed as in a mirror)'
      }),
      value: VideoState.ON_FLIPPED
    }];
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    // Set the video display properties to defaults the first time
    // getInfo is run. This turns on the video device when it is
    // first added to a project, and is overwritten by a PROJECT_LOADED
    // event listener that later calls updateVideoDisplay
    if (this.firstInstall) {
      this.globalVideoState = VideoState.ON;
      this.globalVideoTransparency = 50;
      this.updateVideoDisplay();
      this.firstInstall = false;
    } // Return extension definition


    return {
      id: 'videoSensing',
      name: formatMessage({
        id: 'videoSensing.categoryName',
        default: 'Video Sensing',
        description: 'Label for the video sensing extension category'
      }),
      blockIconURI: blockIconURI,
      menuIconURI: menuIconURI,
      blocks: [{
        // @todo this hat needs to be set itself to restart existing
        // threads like Scratch 2's behaviour.
        opcode: 'whenMotionGreaterThan',
        text: formatMessage({
          id: 'videoSensing.whenMotionGreaterThan',
          default: 'when video motion > [REFERENCE]',
          description: 'Event that triggers when the amount of motion is greater than [REFERENCE]'
        }),
        blockType: BlockType.HAT,
        arguments: {
          REFERENCE: {
            type: ArgumentType.NUMBER,
            defaultValue: 10
          }
        }
      }, {
        opcode: 'videoOn',
        blockType: BlockType.REPORTER,
        text: formatMessage({
          id: 'videoSensing.videoOn',
          default: 'video [ATTRIBUTE] on [SUBJECT]',
          description: 'Reporter that returns the amount of [ATTRIBUTE] for the selected [SUBJECT]'
        }),
        arguments: {
          ATTRIBUTE: {
            type: ArgumentType.NUMBER,
            menu: 'ATTRIBUTE',
            defaultValue: SensingAttribute.MOTION
          },
          SUBJECT: {
            type: ArgumentType.NUMBER,
            menu: 'SUBJECT',
            defaultValue: SensingSubject.SPRITE
          }
        }
      }, {
        opcode: 'videoToggle',
        text: formatMessage({
          id: 'videoSensing.videoToggle',
          default: 'turn video [VIDEO_STATE]',
          description: 'Controls display of the video preview layer'
        }),
        arguments: {
          VIDEO_STATE: {
            type: ArgumentType.NUMBER,
            menu: 'VIDEO_STATE',
            defaultValue: VideoState.ON
          }
        }
      }, {
        opcode: 'setVideoTransparency',
        text: formatMessage({
          id: 'videoSensing.setVideoTransparency',
          default: 'set video transparency to [TRANSPARENCY]',
          description: 'Controls transparency of the video preview layer'
        }),
        arguments: {
          TRANSPARENCY: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        }
      }],
      menus: {
        ATTRIBUTE: {
          acceptReporters: true,
          items: this._buildMenu(this.ATTRIBUTE_INFO)
        },
        SUBJECT: {
          acceptReporters: true,
          items: this._buildMenu(this.SUBJECT_INFO)
        },
        VIDEO_STATE: {
          acceptReporters: true,
          items: this._buildMenu(this.VIDEO_STATE_INFO)
        }
      }
    };
  }
  /**
   * Analyze a part of the frame that a target overlaps.
   * @param {Target} target - a target to determine where to analyze
   * @returns {MotionState} the motion state for the given target
   */


  _analyzeLocalMotion(target) {
    const drawable = this.runtime.renderer._allDrawables[target.drawableID];

    const state = this._getMotionState(target);

    this.detect.getLocalMotion(drawable, state);
    return state;
  }
  /**
   * A scratch reporter block handle that analyzes the last two frames and
   * depending on the arguments, returns the motion or direction for the
   * whole stage or just the target sprite.
   * @param {object} args - the block arguments
   * @param {BlockUtility} util - the block utility
   * @returns {number} the motion amount or direction of the stage or sprite
   */


  videoOn(args, util) {
    this.detect.analyzeFrame();
    let state = this.detect;

    if (args.SUBJECT === SensingSubject.SPRITE) {
      state = this._analyzeLocalMotion(util.target);
    }

    if (args.ATTRIBUTE === SensingAttribute.MOTION) {
      return state.motionAmount;
    }

    return state.motionDirection;
  }
  /**
   * A scratch hat block edge handle that analyzes the last two frames where
   * the target sprite overlaps and if it has more motion than the given
   * reference value.
   * @param {object} args - the block arguments
   * @param {BlockUtility} util - the block utility
   * @returns {boolean} true if the sprite overlaps more motion than the
   *   reference
   */


  whenMotionGreaterThan(args, util) {
    this.detect.analyzeFrame();

    const state = this._analyzeLocalMotion(util.target);

    return state.motionAmount > Number(args.REFERENCE);
  }
  /**
   * A scratch command block handle that configures the video state from
   * passed arguments.
   * @param {object} args - the block arguments
   * @param {VideoState} args.VIDEO_STATE - the video state to set the device to
   */


  videoToggle(args) {
    const state = args.VIDEO_STATE;
    this.globalVideoState = state;

    if (state === VideoState.OFF) {
      this.runtime.ioDevices.video.disableVideo();
    } else {
      this.runtime.ioDevices.video.enableVideo(); // Mirror if state is ON. Do not mirror if state is ON_FLIPPED.

      this.runtime.ioDevices.video.mirror = state === VideoState.ON;
    }
  }
  /**
   * A scratch command block handle that configures the video preview's
   * transparency from passed arguments.
   * @param {object} args - the block arguments
   * @param {number} args.TRANSPARENCY - the transparency to set the video
   *   preview to
   */


  setVideoTransparency(args) {
    const transparency = Cast.toNumber(args.TRANSPARENCY);
    this.globalVideoTransparency = transparency;
    this.runtime.ioDevices.video.setPreviewGhost(transparency);
  }

}

module.exports = Scratch3VideoSensingBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/library.js":
/*!**********************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/library.js ***!
  \**********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @file library.js
 *
 * Tony Hwang and John Maloney, January 2011
 * Michael "Z" Goddard, March 2018
 *
 * Video motion sensing primitives.
 */
const {
  motionVector,
  scratchAtan2
} = __webpack_require__(/*! ./math */ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/math.js");
/**
 * The width of the intended resolution to analyze for motion.
 * @type {number}
 */


const WIDTH = 480;
/**
 * The height of the intended resolution to analyze for motion.
 * @type {number}
 */

const HEIGHT = 360;
/**
 * A constant value to scale the magnitude of the x and y components called u
 * and v. This creates the motionAmount value.
 *
 * Old note: chosen empirically to give a range of roughly 0-100
 *
 * @type {number}
 */

const AMOUNT_SCALE = 100;
/**
 * A constant value to scale the magnitude of the x and y components called u
 * and v in the local motion derivative. This creates the motionAmount value on
 * a target's motion state.
 *
 * Old note: note 2e-4 * activePixelNum is an experimentally tuned threshold
 * for my logitech Pro 9000 webcam - TTH
 *
 * @type {number}
 */

const LOCAL_AMOUNT_SCALE = AMOUNT_SCALE * 2e-4;
/**
 * The motion amount must be higher than the THRESHOLD to calculate a new
 * direction value.
 * @type {number}
 */

const THRESHOLD = 10;
/**
 * The size of the radius of the window of summarized values when considering
 * the motion inside the full resolution of the sample.
 * @type {number}
 */

const WINSIZE = 8;
/**
 * A ceiling for the motionAmount stored to a local target's motion state. The
 * motionAmount is not allowed to be larger than LOCAL_MAX_AMOUNT.
 * @type {number}
 */

const LOCAL_MAX_AMOUNT = 100;
/**
 * The motion amount for a target's local motion must be higher than the
 * LOCAL_THRESHOLD to calculate a new direction value.
 * @type {number}
 */

const LOCAL_THRESHOLD = THRESHOLD / 3;
/**
 * Store the necessary image pixel data to compares frames of a video and
 * detect an amount and direction of motion in the full sample or in a
 * specified area.
 * @constructor
 */

class VideoMotion {
  constructor() {
    /**
     * The number of frames that have been added from a source.
     * @type {number}
     */
    this.frameNumber = 0;
    /**
     * The frameNumber last analyzed.
     * @type {number}
     */

    this.lastAnalyzedFrame = 0;
    /**
     * The amount of motion detected in the current frame.
     * @type {number}
     */

    this.motionAmount = 0;
    /**
     * The direction the motion detected in the frame is general moving in.
     * @type {number}
     */

    this.motionDirection = 0;
    /**
     * A copy of the current frame's pixel values. A index of the array is
     * represented in RGBA. The lowest byte is red. The next is green. The
     * next is blue. And the last is the alpha value of that pixel.
     * @type {Uint32Array}
     */

    this.curr = null;
    /**
     * A copy of the last frame's pixel values.
     * @type {Uint32Array}
     */

    this.prev = null;
    /**
     * A buffer for holding one component of a pixel's full value twice.
     * One for the current value. And one for the last value.
     * @type {number}
     */

    this._arrays = new ArrayBuffer(WIDTH * HEIGHT * 2 * 1);
    /**
     * A clamped uint8 view of _arrays. One component of each index of the
     * curr member is copied into this array.
     * @type {number}
     */

    this._curr = new Uint8ClampedArray(this._arrays, WIDTH * HEIGHT * 0 * 1, WIDTH * HEIGHT);
    /**
     * A clamped uint8 view of _arrays. One component of each index of the
     * prev member is copied into this array.
     * @type {number}
     */

    this._prev = new Uint8ClampedArray(this._arrays, WIDTH * HEIGHT * 1 * 1, WIDTH * HEIGHT);
  }
  /**
   * Reset internal state so future frame analysis does not consider values
   * from before this method was called.
   */


  reset() {
    this.frameNumber = 0;
    this.lastAnalyzedFrame = 0;
    this.motionAmount = this.motionDirection = 0;
    this.prev = this.curr = null;
  }
  /**
   * Add a frame to be next analyzed. The passed array represent a pixel with
   * each index in the RGBA format.
   * @param {Uint32Array} source - a source frame of pixels to copy
   */


  addFrame(source) {
    this.frameNumber++; // Swap curr to prev.

    this.prev = this.curr; // Create a clone of the array so any modifications made to the source
    // array do not affect the work done in here.

    this.curr = new Uint32Array(source.buffer.slice(0)); // Swap _prev and _curr. Copy one of the color components of the new
    // array into _curr overwriting what was the old _prev data.

    const _tmp = this._prev;
    this._prev = this._curr;
    this._curr = _tmp;

    for (let i = 0; i < this.curr.length; i++) {
      this._curr[i] = this.curr[i] & 0xff;
    }
  }
  /**
   * Analyze the current frame against the previous frame determining the
   * amount of motion and direction of the motion.
   */


  analyzeFrame() {
    if (!this.curr || !this.prev) {
      this.motionAmount = this.motionDirection = -1; // Don't have two frames to analyze yet

      return;
    } // Return early if new data has not been received.


    if (this.lastAnalyzedFrame === this.frameNumber) {
      return;
    }

    this.lastAnalyzedFrame = this.frameNumber;
    const {
      _curr: curr,
      _prev: prev
    } = this;
    const winStep = WINSIZE * 2 + 1;
    const wmax = WIDTH - WINSIZE - 1;
    const hmax = HEIGHT - WINSIZE - 1; // Accumulate 2d motion vectors from groups of pixels and average it
    // later.

    let uu = 0;
    let vv = 0;
    let n = 0; // Iterate over groups of cells building up the components to determine
    // a motion vector for each cell instead of the whole frame to avoid
    // integer overflows.

    for (let i = WINSIZE + 1; i < hmax; i += winStep) {
      for (let j = WINSIZE + 1; j < wmax; j += winStep) {
        let A2 = 0;
        let A1B2 = 0;
        let B1 = 0;
        let C1 = 0;
        let C2 = 0; // This is a performance critical math region.

        let address = (i - WINSIZE) * WIDTH + j - WINSIZE;
        let nextAddress = address + winStep;
        const maxAddress = (i + WINSIZE) * WIDTH + j + WINSIZE;

        for (; address <= maxAddress; address += WIDTH - winStep, nextAddress += WIDTH) {
          for (; address <= nextAddress; address += 1) {
            // The difference in color between the last frame and
            // the current frame.
            const gradT = prev[address] - curr[address]; // The difference between the pixel to the left and the
            // pixel to the right.

            const gradX = curr[address - 1] - curr[address + 1]; // The difference between the pixel above and the pixel
            // below.

            const gradY = curr[address - WIDTH] - curr[address + WIDTH]; // Add the combined values of this pixel to previously
            // considered pixels.

            A2 += gradX * gradX;
            A1B2 += gradX * gradY;
            B1 += gradY * gradY;
            C2 += gradX * gradT;
            C1 += gradY * gradT;
          }
        } // Use the accumalated values from the for loop to determine a
        // motion direction.


        const {
          u,
          v
        } = motionVector(A2, A1B2, B1, C2, C1); // If u and v are within negative winStep to positive winStep,
        // add them to a sum that will later be averaged.

        if (-winStep < u && u < winStep && -winStep < v && v < winStep) {
          uu += u;
          vv += v;
          n++;
        }
      }
    } // Average the summed vector values of all of the motion groups.


    uu /= n;
    vv /= n; // Scale the magnitude of the averaged UV vector.

    this.motionAmount = Math.round(AMOUNT_SCALE * Math.hypot(uu, vv));

    if (this.motionAmount > THRESHOLD) {
      // Scratch direction
      this.motionDirection = scratchAtan2(vv, uu);
    }
  }
  /**
   * Build motion amount and direction values based on stored current and
   * previous frame that overlaps a given drawable.
   * @param {Drawable} drawable - touchable and bounded drawable to build motion for
   * @param {MotionState} state - state to store built values to
   */


  getLocalMotion(drawable, state) {
    if (!this.curr || !this.prev) {
      state.motionAmount = state.motionDirection = -1; // Don't have two frames to analyze yet

      return;
    } // Skip if the current frame has already been considered for this state.


    if (state.motionFrameNumber !== this.frameNumber) {
      const {
        _prev: prev,
        _curr: curr
      } = this; // The public APIs for Renderer#isTouching manage keeping the matrix and
      // silhouette up-to-date, which is needed for drawable#isTouching to work (used below)

      drawable.updateCPURenderAttributes(); // Restrict the region the amount and direction are built from to
      // the area of the current frame overlapped by the given drawable's
      // bounding box.

      const boundingRect = drawable.getFastBounds(); // Transform the bounding box from scratch space to a space from 0,
      // 0 to WIDTH, HEIGHT.

      const xmin = Math.max(Math.floor(boundingRect.left + WIDTH / 2), 1);
      const xmax = Math.min(Math.floor(boundingRect.right + WIDTH / 2), WIDTH - 1);
      const ymin = Math.max(Math.floor(HEIGHT / 2 - boundingRect.top), 1);
      const ymax = Math.min(Math.floor(HEIGHT / 2 - boundingRect.bottom), HEIGHT - 1);
      let A2 = 0;
      let A1B2 = 0;
      let B1 = 0;
      let C1 = 0;
      let C2 = 0;
      let scaleFactor = 0;
      const position = [0, 0, 0]; // This is a performance critical math region.

      for (let i = ymin; i < ymax; i++) {
        for (let j = xmin; j < xmax; j++) {
          // i and j are in a coordinate planning ranging from 0 to
          // HEIGHT and 0 to WIDTH. Transform that into Scratch's
          // range of HEIGHT / 2 to -HEIGHT / 2 and -WIDTH / 2 to
          // WIDTH / 2;
          position[0] = j - WIDTH / 2;
          position[1] = HEIGHT / 2 - i; // Consider only pixels in the drawable that can touch the
          // edge or other drawables. Empty space in the current skin
          // is skipped.

          if (drawable.isTouching(position)) {
            const address = i * WIDTH + j; // The difference in color between the last frame and
            // the current frame.

            const gradT = prev[address] - curr[address]; // The difference between the pixel to the left and the
            // pixel to the right.

            const gradX = curr[address - 1] - curr[address + 1]; // The difference between the pixel above and the pixel
            // below.

            const gradY = curr[address - WIDTH] - curr[address + WIDTH]; // Add the combined values of this pixel to previously
            // considered pixels.

            A2 += gradX * gradX;
            A1B2 += gradX * gradY;
            B1 += gradY * gradY;
            C2 += gradX * gradT;
            C1 += gradY * gradT;
            scaleFactor++;
          }
        }
      } // Use the accumalated values from the for loop to determine a
      // motion direction.


      let {
        u,
        v
      } = motionVector(A2, A1B2, B1, C2, C1);
      let activePixelNum = 0;

      if (scaleFactor) {
        // Store the area of the sprite in pixels
        activePixelNum = scaleFactor;
        scaleFactor /= 2 * WINSIZE * 2 * WINSIZE;
        u = u / scaleFactor;
        v = v / scaleFactor;
      } // Scale the magnitude of the averaged UV vector and the number of
      // overlapping drawable pixels.


      state.motionAmount = Math.round(LOCAL_AMOUNT_SCALE * activePixelNum * Math.hypot(u, v));

      if (state.motionAmount > LOCAL_MAX_AMOUNT) {
        // Clip all magnitudes greater than 100.
        state.motionAmount = LOCAL_MAX_AMOUNT;
      }

      if (state.motionAmount > LOCAL_THRESHOLD) {
        // Scratch direction.
        state.motionDirection = scratchAtan2(v, u);
      } // Skip future calls on this state until a new frame is added.


      state.motionFrameNumber = this.frameNumber;
    }
  }

}

module.exports = VideoMotion;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/math.js":
/*!*******************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_video_sensing/math.js ***!
  \*******************************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * A constant value helping to transform a value in radians to degrees.
 * @type {number}
 */
const TO_DEGREE = 180 / Math.PI;
/**
 * A object reused to save on memory allocation returning u and v vector from
 * motionVector.
 * @type {UV}
 */

const _motionVectorOut = {
  u: 0,
  v: 0
};
/**
 * Determine a motion vector combinations of the color component difference on
 * the x axis, y axis, and temporal axis.
 * @param {number} A2 - a sum of x axis squared
 * @param {number} A1B2 - a sum of x axis times y axis
 * @param {number} B1 - a sum of y axis squared
 * @param {number} C2 - a sum of x axis times temporal axis
 * @param {number} C1 - a sum of y axis times temporal axis
 * @param {UV} out - optional object to store return UV info in
 * @returns {UV} a uv vector representing the motion for the given input
 */

const motionVector = function motionVector(A2, A1B2, B1, C2, C1) {
  let out = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : _motionVectorOut;
  // Compare sums of X * Y and sums of X squared and Y squared.
  const delta = A1B2 * A1B2 - A2 * B1;

  if (delta) {
    // System is not singular - solving by Kramer method.
    const deltaX = -(C1 * A1B2 - C2 * B1);
    const deltaY = -(A1B2 * C2 - A2 * C1);
    const Idelta = 8 / delta;
    out.u = deltaX * Idelta;
    out.v = deltaY * Idelta;
  } else {
    // Singular system - find optical flow in gradient direction.
    const Norm = (A1B2 + A2) * (A1B2 + A2) + (B1 + A1B2) * (B1 + A1B2);

    if (Norm) {
      const IGradNorm = 8 / Norm;
      const temp = -(C1 + C2) * IGradNorm;
      out.u = (A1B2 + A2) * temp;
      out.v = (B1 + A1B2) * temp;
    } else {
      out.u = 0;
      out.v = 0;
    }
  }

  return out;
};
/**
 * Translate an angle in degrees with the range -180 to 180 rotated to
 * Scratch's reference angle.
 * @param {number} degrees - angle in range -180 to 180
 * @returns {number} angle from Scratch's reference angle
 */


const scratchDegrees = function scratchDegrees(degrees) {
  return (degrees + 270) % 360 - 180;
};
/**
 * Get the angle of the y and x component of a 2d vector in degrees in
 * Scratch's coordinate plane.
 * @param {number} y - the y component of a 2d vector
 * @param {number} x - the x component of a 2d vector
 * @returns {number} angle in degrees in Scratch's coordinate plane
 */


const scratchAtan2 = function scratchAtan2(y, x) {
  return scratchDegrees(Math.atan2(y, x) * TO_DEGREE);
};

module.exports = {
  motionVector,
  scratchDegrees,
  scratchAtan2
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/scratch3_wedo2/index.js":
/*!************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/scratch3_wedo2/index.js ***!
  \************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const color = __webpack_require__(/*! ../../util/color */ "./node_modules/scratch-vm/src/util/color.js");

const BLE = __webpack_require__(/*! ../../io/ble */ "./node_modules/scratch-vm/src/io/ble.js");

const Base64Util = __webpack_require__(/*! ../../util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");

const MathUtil = __webpack_require__(/*! ../../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const RateLimiter = __webpack_require__(/*! ../../util/rateLimiter.js */ "./node_modules/scratch-vm/src/util/rateLimiter.js");

const log = __webpack_require__(/*! ../../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Icon svg to be displayed at the left edge of each extension block, encoded as a data URI.
 * @type {string}
 */
// eslint-disable-next-line max-len


const iconURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAABQCAYAAACOEfKtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAF8klEQVR4Ae2cbWxTVRjH/7ctbVc2tyEMNpWBk0VIkLcEjSAQgglTE5HEaKqJi1E/mbCP/dJA0kQbvzgTQ0Ki2T7V6AeYGoEPLJmGKPiyzZDwEpYJCHSbQIcbdLvres1zOa13Xbvdu2eTDp9fst329Lnn5XfPPfece7tphmFAmDkuccdDBDIRgUxEIBMRyEQEMhGBTEQgExHIRAQyEYFMRCATEchEBDIRgUxEIBMRyEQEMhGBTEQgExHIxMPNIByNVQBoBUDb7kgo2KTS9wBoUmFNkVCwW6U3A1gP4JJKHwxHY/S+WcW2RkLBVhV7AMAOAIMAGlWstbyOSCh4QMU2Uoy1PBVL+a7IqZu1vOZIKNg20/azBarGvKxebw9HY22RULADwBFLTBcATQnZl4lVEimN4ssteXQrQfstebQpmW1q30xshyqvxRLbofYnYW9ZYgeV8C5LLOWlzbTxM3ouHI7GPgSwWx3Z0syBSBku6IYnlTbM+uQenJQaMnKHDaqAFnDrcCFbl3G1defEjas0a4N/Vz10OybyvapfrSX1sjpo+WIz0ME7QL3djgtHPTAcjb2mepw/b2ZaGh5NL5RnofR8R99dIC5fHusK5JsrCUpm7TSx21XvbcwTNwnbAsPR2GcA3qaG+H0LsHlDPZ7fca/ujZ+cRW9/Em5vCXzlNVhQUjFpf/3OTSRvXkKJz43Xt1bh1S1LUeq/5+njQ9/iVmLIfL1ieRU2b1iFtavztXNu6TrTi8PfnYI67WdPoOp5przV9Y8iuHdb9rOW9uumPI+vDIElddBckztPOqVn5X36Xj1WVQeynx1sOWbK83jc2PviM/dFXIYNax9H55leXLoyYHsfWwI14JCRRx7x5ckBU1oheYQ+1G9u39lVM0Hej7+cR7w/Yb7e9+5LqChfaLvixcK088BwNNZkAOV02ubK6+odwt3RcfOULSSPGEveG48bNj08If3kqXPmdtO6unkpDzYn0u/TLxrzcumJJ80Ut79sygzoFF6/siw75mUYupOEpmnY0/A0pw33FTsCa+hX5oJhZXgkZb5zub2O20CnL7EwkPeCPm+wI7CEBvi5wuOZ36tJW7X3uGXJXAgxk8P4eNpRPEvgskqfuR0Z/BNGejxvDM3/5gs0pboWv+motqybCc+tqUCzz43kaBJ/X+2eMjZ3ClNsjIzo5ioknXZ2b4AlkKYltLJoaY9jOJm/B0KJbtg4c4F/XOmH3+dF9dLKbBo1OD6QQGV56YQ55ODtO0jcHkZ1VSX8/n9nB9S7RkZ1rFy+NG8ZR9s70TeQQKDEh7vJUdt1Y9/OopXFB2/WcbMpyOexE9mlFS21aLlHMmKHfzBl0QT/hV2bzM9oLXv0xG8YGR0zpdLEn6RT2k+/XjDzoLX2G3u3TZBLUyral/Z5qCyAK1f/sl2/or+IWNel1Eji3MWrpjyCZHWqdNrSe6ieSHFERl4mP+q5GehgHGvvRGal5XI5uzU47f3A/R99YTgdF2wXrmkolr9ToZ5NvTjT4yOhoC2T057CJM/r9WDxoqmXa07R9THcuDVcMO8bt4ag6ynULKvkFjWBTLl0ugZKvNlyqLeSQKfYGgOpgXt2b5zVhlzrS+Dr451YvKg0b95txztxvS8xZ+VuXFuLJ5+oNgV+9c3PuHDxGs6cu+w4v//9RJo6x5bN9UgbBo4cPY1U6j+cSD8orFvzGFYuX4KxsRQGbth6FCICc9m5dY05HtN46AQRqPB5PWjY+ZT5RnMwkxGBFh5ZVmle9Z3MrGbjwfqccrC1vajrV7QCaVCfS6qrJj96nQlFK5CujPRT7MgYyEQEMhGBTGwJpAW4kJ9pBbo0zbx70X7y7AOv8HxP3LyB4YTpb2cZBt2iqL3QEwf9zDbX+waLca439QMeC7a+YBmOxugLiM/OTt2yaOoMoO+H6LOcNwf6xusrthsh/7mIh1yFmYhAJiKQiQhkIgKZiEAmIpCJCGQiApmIQCYikIkIZCICmYhAJiKQiQhkIgKZiEAmIpCJCGQiAjkA+AeOwQKMcWZqHgAAAABJRU5ErkJggg==';
/**
 * A list of WeDo 2.0 BLE service UUIDs.
 * @enum
 */

const BLEService = {
  DEVICE_SERVICE: '00001523-1212-efde-1523-785feabcd123',
  IO_SERVICE: '00004f0e-1212-efde-1523-785feabcd123'
};
/**
 * A list of WeDo 2.0 BLE characteristic UUIDs.
 *
 * Characteristics on DEVICE_SERVICE:
 * - ATTACHED_IO
 *
 * Characteristics on IO_SERVICE:
 * - INPUT_VALUES
 * - INPUT_COMMAND
 * - OUTPUT_COMMAND
 *
 * @enum
 */

const BLECharacteristic = {
  ATTACHED_IO: '00001527-1212-efde-1523-785feabcd123',
  LOW_VOLTAGE_ALERT: '00001528-1212-efde-1523-785feabcd123',
  INPUT_VALUES: '00001560-1212-efde-1523-785feabcd123',
  INPUT_COMMAND: '00001563-1212-efde-1523-785feabcd123',
  OUTPUT_COMMAND: '00001565-1212-efde-1523-785feabcd123'
};
/**
 * A time interval to wait (in milliseconds) in between battery check calls.
 * @type {number}
 */

const BLEBatteryCheckInterval = 5000;
/**
 * A time interval to wait (in milliseconds) while a block that sends a BLE message is running.
 * @type {number}
 */

const BLESendInterval = 100;
/**
 * A maximum number of BLE message sends per second, to be enforced by the rate limiter.
 * @type {number}
 */

const BLESendRateMax = 20;
/**
 * Enum for WeDo 2.0 sensor and output types.
 * @readonly
 * @enum {number}
 */

const WeDo2Device = {
  MOTOR: 1,
  PIEZO: 22,
  LED: 23,
  TILT: 34,
  DISTANCE: 35
};
/**
 * Enum for connection/port ids assigned to internal WeDo 2.0 output devices.
 * @readonly
 * @enum {number}
 */
// TODO: Check for these more accurately at startup?

const WeDo2ConnectID = {
  LED: 6,
  PIEZO: 5
};
/**
 * Enum for ids for various output commands on the WeDo 2.0.
 * @readonly
 * @enum {number}
 */

const WeDo2Command = {
  MOTOR_POWER: 1,
  PLAY_TONE: 2,
  STOP_TONE: 3,
  WRITE_RGB: 4,
  SET_VOLUME: 255
};
/**
 * Enum for modes for input sensors on the WeDo 2.0.
 * @enum {number}
 */

const WeDo2Mode = {
  TILT: 0,
  // angle
  DISTANCE: 0,
  // detect
  LED: 1 // RGB

};
/**
 * Enum for units for input sensors on the WeDo 2.0.
 *
 * 0 = raw
 * 1 = percent
 *
 * @enum {number}
 */

const WeDo2Unit = {
  TILT: 0,
  DISTANCE: 1,
  LED: 0
};
/**
 * Manage power, direction, and timers for one WeDo 2.0 motor.
 */

class WeDo2Motor {
  /**
   * Construct a WeDo 2.0 Motor instance.
   * @param {WeDo2} parent - the WeDo 2.0 peripheral which owns this motor.
   * @param {int} index - the zero-based index of this motor on its parent peripheral.
   */
  constructor(parent, index) {
    /**
     * The WeDo 2.0 peripheral which owns this motor.
     * @type {WeDo2}
     * @private
     */
    this._parent = parent;
    /**
     * The zero-based index of this motor on its parent peripheral.
     * @type {int}
     * @private
     */

    this._index = index;
    /**
     * This motor's current direction: 1 for "this way" or -1 for "that way"
     * @type {number}
     * @private
     */

    this._direction = 1;
    /**
     * This motor's current power level, in the range [0,100].
     * @type {number}
     * @private
     */

    this._power = 100;
    /**
     * Is this motor currently moving?
     * @type {boolean}
     * @private
     */

    this._isOn = false;
    /**
     * If the motor has been turned on or is actively braking for a specific duration, this is the timeout ID for
     * the end-of-action handler. Cancel this when changing plans.
     * @type {Object}
     * @private
     */

    this._pendingTimeoutId = null;
    /**
     * The starting time for the pending timeout.
     * @type {Object}
     * @private
     */

    this._pendingTimeoutStartTime = null;
    /**
     * The delay/duration of the pending timeout.
     * @type {Object}
     * @private
     */

    this._pendingTimeoutDelay = null;
    this.startBraking = this.startBraking.bind(this);
    this.turnOff = this.turnOff.bind(this);
  }
  /**
   * @return {number} - the duration of active braking after a call to startBraking(). Afterward, turn the motor off.
   * @constructor
   */


  static get BRAKE_TIME_MS() {
    return 1000;
  }
  /**
   * @return {int} - this motor's current direction: 1 for "this way" or -1 for "that way"
   */


  get direction() {
    return this._direction;
  }
  /**
   * @param {int} value - this motor's new direction: 1 for "this way" or -1 for "that way"
   */


  set direction(value) {
    if (value < 0) {
      this._direction = -1;
    } else {
      this._direction = 1;
    }
  }
  /**
   * @return {int} - this motor's current power level, in the range [0,100].
   */


  get power() {
    return this._power;
  }
  /**
   * @param {int} value - this motor's new power level, in the range [0,100].
   */


  set power(value) {
    const p = Math.max(0, Math.min(value, 100)); // Lego Wedo 2.0 hub only turns motors at power range [30 - 100], so
    // map value from [0 - 100] to [30 - 100].

    if (p === 0) {
      this._power = 0;
    } else {
      const delta = 100 / p;
      this._power = 30 + 70 / delta;
    }
  }
  /**
   * @return {boolean} - true if this motor is currently moving, false if this motor is off or braking.
   */


  get isOn() {
    return this._isOn;
  }
  /**
   * @return {boolean} - time, in milliseconds, of when the pending timeout began.
   */


  get pendingTimeoutStartTime() {
    return this._pendingTimeoutStartTime;
  }
  /**
   * @return {boolean} - delay, in milliseconds, of the pending timeout.
   */


  get pendingTimeoutDelay() {
    return this._pendingTimeoutDelay;
  }
  /**
   * Turn this motor on indefinitely.
   */


  turnOn() {
    if (this._power === 0) return;

    const cmd = this._parent.generateOutputCommand(this._index + 1, WeDo2Command.MOTOR_POWER, [this._power * this._direction] // power in range 0-100
    );

    this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd);

    this._isOn = true;

    this._clearTimeout();
  }
  /**
   * Turn this motor on for a specific duration.
   * @param {number} milliseconds - run the motor for this long.
   */


  turnOnFor(milliseconds) {
    if (this._power === 0) return;
    milliseconds = Math.max(0, milliseconds);
    this.turnOn();

    this._setNewTimeout(this.startBraking, milliseconds);
  }
  /**
   * Start active braking on this motor. After a short time, the motor will turn off.
   */


  startBraking() {
    if (this._power === 0) return;

    const cmd = this._parent.generateOutputCommand(this._index + 1, WeDo2Command.MOTOR_POWER, [127] // 127 = break
    );

    this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd);

    this._isOn = false;

    this._setNewTimeout(this.turnOff, WeDo2Motor.BRAKE_TIME_MS);
  }
  /**
   * Turn this motor off.
   * @param {boolean} [useLimiter=true] - if true, use the rate limiter
   */


  turnOff() {
    let useLimiter = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
    if (this._power === 0) return;

    const cmd = this._parent.generateOutputCommand(this._index + 1, WeDo2Command.MOTOR_POWER, [0] // 0 = stop
    );

    this._parent.send(BLECharacteristic.OUTPUT_COMMAND, cmd, useLimiter);

    this._isOn = false;
  }
  /**
   * Clear the motor action timeout, if any. Safe to call even when there is no pending timeout.
   * @private
   */


  _clearTimeout() {
    if (this._pendingTimeoutId !== null) {
      clearTimeout(this._pendingTimeoutId);
      this._pendingTimeoutId = null;
      this._pendingTimeoutStartTime = null;
      this._pendingTimeoutDelay = null;
    }
  }
  /**
   * Set a new motor action timeout, after clearing an existing one if necessary.
   * @param {Function} callback - to be called at the end of the timeout.
   * @param {int} delay - wait this many milliseconds before calling the callback.
   * @private
   */


  _setNewTimeout(callback, delay) {
    this._clearTimeout();

    const timeoutID = setTimeout(() => {
      if (this._pendingTimeoutId === timeoutID) {
        this._pendingTimeoutId = null;
        this._pendingTimeoutStartTime = null;
        this._pendingTimeoutDelay = null;
      }

      callback();
    }, delay);
    this._pendingTimeoutId = timeoutID;
    this._pendingTimeoutStartTime = Date.now();
    this._pendingTimeoutDelay = delay;
  }

}
/**
 * Manage communication with a WeDo 2.0 peripheral over a Bluetooth Low Energy client socket.
 */


class WeDo2 {
  constructor(runtime, extensionId) {
    /**
     * The Scratch 3.0 runtime used to trigger the green flag button.
     * @type {Runtime}
     * @private
     */
    this._runtime = runtime;

    this._runtime.on('PROJECT_STOP_ALL', this.stopAll.bind(this));
    /**
     * The id of the extension this peripheral belongs to.
     */


    this._extensionId = extensionId;
    /**
     * A list of the ids of the motors or sensors in ports 1 and 2.
     * @type {string[]}
     * @private
     */

    this._ports = ['none', 'none'];
    /**
     * The motors which this WeDo 2.0 could possibly have.
     * @type {WeDo2Motor[]}
     * @private
     */

    this._motors = [null, null];
    /**
     * The most recently received value for each sensor.
     * @type {Object.<string, number>}
     * @private
     */

    this._sensors = {
      tiltX: 0,
      tiltY: 0,
      distance: 0
    };
    /**
     * The Bluetooth connection socket for reading/writing peripheral data.
     * @type {BLE}
     * @private
     */

    this._ble = null;

    this._runtime.registerPeripheralExtension(extensionId, this);
    /**
     * A rate limiter utility, to help limit the rate at which we send BLE messages
     * over the socket to Scratch Link to a maximum number of sends per second.
     * @type {RateLimiter}
     * @private
     */


    this._rateLimiter = new RateLimiter(BLESendRateMax);
    /**
     * An interval id for the battery check interval.
     * @type {number}
     * @private
     */

    this._batteryLevelIntervalId = null;
    this.reset = this.reset.bind(this);
    this._onConnect = this._onConnect.bind(this);
    this._onMessage = this._onMessage.bind(this);
    this._checkBatteryLevel = this._checkBatteryLevel.bind(this);
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the X axis.
   */


  get tiltX() {
    return this._sensors.tiltX;
  }
  /**
   * @return {number} - the latest value received for the tilt sensor's tilt about the Y axis.
   */


  get tiltY() {
    return this._sensors.tiltY;
  }
  /**
   * @return {number} - the latest value received from the distance sensor.
   */


  get distance() {
    return this._sensors.distance;
  }
  /**
   * Access a particular motor on this peripheral.
   * @param {int} index - the zero-based index of the desired motor.
   * @return {WeDo2Motor} - the WeDo2Motor instance, if any, at that index.
   */


  motor(index) {
    return this._motors[index];
  }
  /**
   * Stop all the motors that are currently running.
   */


  stopAllMotors() {
    this._motors.forEach(motor => {
      if (motor) {
        // Send the motor off command without using the rate limiter.
        // This allows the stop button to stop motors even if we are
        // otherwise flooded with commands.
        motor.turnOff(false);
      }
    });
  }
  /**
   * Set the WeDo 2.0 peripheral's LED to a specific color.
   * @param {int} inputRGB - a 24-bit RGB color in 0xRRGGBB format.
   * @return {Promise} - a promise of the completion of the set led send operation.
   */


  setLED(inputRGB) {
    const rgb = [inputRGB >> 16 & 0x000000FF, inputRGB >> 8 & 0x000000FF, inputRGB & 0x000000FF];
    const cmd = this.generateOutputCommand(WeDo2ConnectID.LED, WeDo2Command.WRITE_RGB, rgb);
    return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);
  }
  /**
   * Sets the input mode of the LED to RGB.
   * @return {Promise} - a promise returned by the send operation.
   */


  setLEDMode() {
    const cmd = this.generateInputCommand(WeDo2ConnectID.LED, WeDo2Device.LED, WeDo2Mode.LED, 0, WeDo2Unit.LED, false);
    return this.send(BLECharacteristic.INPUT_COMMAND, cmd);
  }
  /**
   * Switch off the LED on the WeDo 2.0.
   * @return {Promise} - a promise of the completion of the stop led send operation.
   */


  stopLED() {
    const cmd = this.generateOutputCommand(WeDo2ConnectID.LED, WeDo2Command.WRITE_RGB, [0, 0, 0]);
    return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);
  }
  /**
   * Play a tone from the WeDo 2.0 peripheral for a specific amount of time.
   * @param {int} tone - the pitch of the tone, in Hz.
   * @param {int} milliseconds - the duration of the note, in milliseconds.
   * @return {Promise} - a promise of the completion of the play tone send operation.
   */


  playTone(tone, milliseconds) {
    const cmd = this.generateOutputCommand(WeDo2ConnectID.PIEZO, WeDo2Command.PLAY_TONE, [tone, tone >> 8, milliseconds, milliseconds >> 8]);
    return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd);
  }
  /**
   * Stop the tone playing from the WeDo 2.0 peripheral, if any.
   * @return {Promise} - a promise that the command sent.
   */


  stopTone() {
    const cmd = this.generateOutputCommand(WeDo2ConnectID.PIEZO, WeDo2Command.STOP_TONE); // Send this command without using the rate limiter, because it is
    // only triggered by the stop button.

    return this.send(BLECharacteristic.OUTPUT_COMMAND, cmd, false);
  }
  /**
   * Stop the tone playing and motors on the WeDo 2.0 peripheral.
   */


  stopAll() {
    if (!this.isConnected()) return;
    this.stopTone();
    this.stopAllMotors();
  }
  /**
   * Called by the runtime when user wants to scan for a WeDo 2.0 peripheral.
   */


  scan() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this._ble = new BLE(this._runtime, this._extensionId, {
      filters: [{
        services: [BLEService.DEVICE_SERVICE]
      }],
      optionalServices: [BLEService.IO_SERVICE]
    }, this._onConnect, this.reset);
  }
  /**
   * Called by the runtime when user wants to connect to a certain WeDo 2.0 peripheral.
   * @param {number} id - the id of the peripheral to connect to.
   */


  connect(id) {
    if (this._ble) {
      this._ble.connectPeripheral(id);
    }
  }
  /**
   * Disconnects from the current BLE socket.
   */


  disconnect() {
    if (this._ble) {
      this._ble.disconnect();
    }

    this.reset();
  }
  /**
   * Reset all the state and timeout/interval ids.
   */


  reset() {
    this._ports = ['none', 'none'];
    this._motors = [null, null];
    this._sensors = {
      tiltX: 0,
      tiltY: 0,
      distance: 0
    };

    if (this._batteryLevelIntervalId) {
      window.clearInterval(this._batteryLevelIntervalId);
      this._batteryLevelIntervalId = null;
    }
  }
  /**
   * Called by the runtime to detect whether the WeDo 2.0 peripheral is connected.
   * @return {boolean} - the connected state.
   */


  isConnected() {
    let connected = false;

    if (this._ble) {
      connected = this._ble.isConnected();
    }

    return connected;
  }
  /**
   * Write a message to the WeDo 2.0 peripheral BLE socket.
   * @param {number} uuid - the UUID of the characteristic to write to
   * @param {Array} message - the message to write.
   * @param {boolean} [useLimiter=true] - if true, use the rate limiter
   * @return {Promise} - a promise result of the write operation
   */


  send(uuid, message) {
    let useLimiter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
    if (!this.isConnected()) return Promise.resolve();

    if (useLimiter) {
      if (!this._rateLimiter.okayToSend()) return Promise.resolve();
    }

    return this._ble.write(BLEService.IO_SERVICE, uuid, Base64Util.uint8ArrayToBase64(message), 'base64');
  }
  /**
   * Generate a WeDo 2.0 'Output Command' in the byte array format
   * (CONNECT ID, COMMAND ID, NUMBER OF BYTES, VALUES ...).
   *
   * This sends a command to the WeDo 2.0 to actuate the specified outputs.
   *
   * @param  {number} connectID - the port (Connect ID) to send a command to.
   * @param  {number} commandID - the id of the byte command.
   * @param  {array}  values    - the list of values to write to the command.
   * @return {array}            - a generated output command.
   */


  generateOutputCommand(connectID, commandID) {
    let values = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
    let command = [connectID, commandID];

    if (values) {
      command = command.concat(values.length).concat(values);
    }

    return command;
  }
  /**
   * Generate a WeDo 2.0 'Input Command' in the byte array format
   * (COMMAND ID, COMMAND TYPE, CONNECT ID, TYPE ID, MODE, DELTA INTERVAL (4 BYTES),
   * UNIT, NOTIFICATIONS ENABLED).
   *
   * This sends a command to the WeDo 2.0 that sets that input format
   * of the specified inputs and sets value change notifications.
   *
   * @param  {number}  connectID           - the port (Connect ID) to send a command to.
   * @param  {number}  type                - the type of input sensor.
   * @param  {number}  mode                - the mode of the input sensor.
   * @param  {number}  delta               - the delta change needed to trigger notification.
   * @param  {array}   units               - the unit of the input sensor value.
   * @param  {boolean} enableNotifications - whether to enable notifications.
   * @return {array}                       - a generated input command.
   */


  generateInputCommand(connectID, type, mode, delta, units, enableNotifications) {
    const command = [1, // Command ID = 1 = "Sensor Format"
    2, // Command Type = 2 = "Write"
    connectID, type, mode, delta, 0, // Delta Interval Byte 2
    0, // Delta Interval Byte 3
    0, // Delta Interval Byte 4
    units, enableNotifications ? 1 : 0];
    return command;
  }
  /**
   * Sets LED mode and initial color and starts reading data from peripheral after BLE has connected.
   * @private
   */


  _onConnect() {
    this.setLEDMode();
    this.setLED(0x0000FF);

    this._ble.startNotifications(BLEService.DEVICE_SERVICE, BLECharacteristic.ATTACHED_IO, this._onMessage);

    this._batteryLevelIntervalId = window.setInterval(this._checkBatteryLevel, BLEBatteryCheckInterval);
  }
  /**
   * Process the sensor data from the incoming BLE characteristic.
   * @param {object} base64 - the incoming BLE data.
   * @private
   */


  _onMessage(base64) {
    const data = Base64Util.base64ToUint8Array(base64); // log.info(data);

    /**
     * If first byte of data is '1' or '2', then either clear the
     * sensor present in ports 1 or 2 or set their format.
     *
     * If first byte of data is anything else, read incoming sensor value.
     */

    switch (data[0]) {
      case 1:
      case 2:
        {
          const connectID = data[0];

          if (data[1] === 0) {
            // clear sensor or motor
            this._clearPort(connectID);
          } else {
            // register sensor or motor
            this._registerSensorOrMotor(connectID, data[3]);
          }

          break;
        }

      default:
        {
          // read incoming sensor value
          const connectID = data[1];
          const type = this._ports[connectID - 1];

          if (type === WeDo2Device.DISTANCE) {
            this._sensors.distance = data[2];
          }

          if (type === WeDo2Device.TILT) {
            this._sensors.tiltX = data[2];
            this._sensors.tiltY = data[3];
          }

          break;
        }
    }
  }
  /**
   * Check the battery level on the WeDo 2.0. If the WeDo 2.0 has disconnected
   * for some reason, the BLE socket will get an error back and automatically
   * close the socket.
   */


  _checkBatteryLevel() {
    this._ble.read(BLEService.DEVICE_SERVICE, BLECharacteristic.LOW_VOLTAGE_ALERT, false);
  }
  /**
   * Register a new sensor or motor connected at a port.  Store the type of
   * sensor or motor internally, and then register for notifications on input
   * values if it is a sensor.
   * @param {number} connectID - the port to register a sensor or motor on.
   * @param {number} type - the type ID of the sensor or motor
   * @private
   */


  _registerSensorOrMotor(connectID, type) {
    // Record which port is connected to what type of device
    this._ports[connectID - 1] = type; // Record motor port

    if (type === WeDo2Device.MOTOR) {
      this._motors[connectID - 1] = new WeDo2Motor(this, connectID - 1);
    } else {
      // Set input format for tilt or distance sensor
      const typeString = type === WeDo2Device.DISTANCE ? 'DISTANCE' : 'TILT';
      const cmd = this.generateInputCommand(connectID, type, WeDo2Mode[typeString], 1, WeDo2Unit[typeString], true);
      this.send(BLECharacteristic.INPUT_COMMAND, cmd);

      this._ble.startNotifications(BLEService.IO_SERVICE, BLECharacteristic.INPUT_VALUES, this._onMessage);
    }
  }
  /**
   * Clear the sensor or motor present at port 1 or 2.
   * @param {number} connectID - the port to clear.
   * @private
   */


  _clearPort(connectID) {
    const type = this._ports[connectID - 1];

    if (type === WeDo2Device.TILT) {
      this._sensors.tiltX = this._sensors.tiltY = 0;
    }

    if (type === WeDo2Device.DISTANCE) {
      this._sensors.distance = 0;
    }

    this._ports[connectID - 1] = 'none';
    this._motors[connectID - 1] = null;
  }

}
/**
 * Enum for motor specification.
 * @readonly
 * @enum {string}
 */


const WeDo2MotorLabel = {
  DEFAULT: 'motor',
  A: 'motor A',
  B: 'motor B',
  ALL: 'all motors'
};
/**
 * Enum for motor direction specification.
 * @readonly
 * @enum {string}
 */

const WeDo2MotorDirection = {
  FORWARD: 'this way',
  BACKWARD: 'that way',
  REVERSE: 'reverse'
};
/**
 * Enum for tilt sensor direction.
 * @readonly
 * @enum {string}
 */

const WeDo2TiltDirection = {
  UP: 'up',
  DOWN: 'down',
  LEFT: 'left',
  RIGHT: 'right',
  ANY: 'any'
};
/**
 * Scratch 3.0 blocks to interact with a LEGO WeDo 2.0 peripheral.
 */

class Scratch3WeDo2Blocks {
  /**
   * @return {string} - the ID of this extension.
   */
  static get EXTENSION_ID() {
    return 'wedo2';
  }
  /**
   * @return {number} - the tilt sensor counts as "tilted" if its tilt angle meets or exceeds this threshold.
   */


  static get TILT_THRESHOLD() {
    return 15;
  }
  /**
   * Construct a set of WeDo 2.0 blocks.
   * @param {Runtime} runtime - the Scratch 3.0 runtime.
   */


  constructor(runtime) {
    /**
     * The Scratch 3.0 runtime.
     * @type {Runtime}
     */
    this.runtime = runtime; // Create a new WeDo 2.0 peripheral instance

    this._peripheral = new WeDo2(this.runtime, Scratch3WeDo2Blocks.EXTENSION_ID);
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: Scratch3WeDo2Blocks.EXTENSION_ID,
      name: 'WeDo 2.0',
      blockIconURI: iconURI,
      showStatusButton: true,
      blocks: [{
        opcode: 'motorOnFor',
        text: formatMessage({
          id: 'wedo2.motorOnFor',
          default: 'turn [MOTOR_ID] on for [DURATION] seconds',
          description: 'turn a motor on for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: WeDo2MotorLabel.DEFAULT
          },
          DURATION: {
            type: ArgumentType.NUMBER,
            defaultValue: 1
          }
        }
      }, {
        opcode: 'motorOn',
        text: formatMessage({
          id: 'wedo2.motorOn',
          default: 'turn [MOTOR_ID] on',
          description: 'turn a motor on indefinitely'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: WeDo2MotorLabel.DEFAULT
          }
        }
      }, {
        opcode: 'motorOff',
        text: formatMessage({
          id: 'wedo2.motorOff',
          default: 'turn [MOTOR_ID] off',
          description: 'turn a motor off'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: WeDo2MotorLabel.DEFAULT
          }
        }
      }, {
        opcode: 'startMotorPower',
        text: formatMessage({
          id: 'wedo2.startMotorPower',
          default: 'set [MOTOR_ID] power to [POWER]',
          description: 'set the motor\'s power and turn it on'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: WeDo2MotorLabel.DEFAULT
          },
          POWER: {
            type: ArgumentType.NUMBER,
            defaultValue: 100
          }
        }
      }, {
        opcode: 'setMotorDirection',
        text: formatMessage({
          id: 'wedo2.setMotorDirection',
          default: 'set [MOTOR_ID] direction to [MOTOR_DIRECTION]',
          description: 'set the motor\'s turn direction'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          MOTOR_ID: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_ID',
            defaultValue: WeDo2MotorLabel.DEFAULT
          },
          MOTOR_DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'MOTOR_DIRECTION',
            defaultValue: WeDo2MotorDirection.FORWARD
          }
        }
      }, {
        opcode: 'setLightHue',
        text: formatMessage({
          id: 'wedo2.setLightHue',
          default: 'set light color to [HUE]',
          description: 'set the LED color'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          HUE: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        }
      }, {
        opcode: 'playNoteFor',
        text: formatMessage({
          id: 'wedo2.playNoteFor',
          default: 'play note [NOTE] for [DURATION] seconds',
          description: 'play a certain note for some time'
        }),
        blockType: BlockType.COMMAND,
        arguments: {
          NOTE: {
            type: ArgumentType.NUMBER,
            // TODO: ArgumentType.MIDI_NOTE?
            defaultValue: 60
          },
          DURATION: {
            type: ArgumentType.NUMBER,
            defaultValue: 0.5
          }
        },
        hideFromPalette: true
      }, {
        opcode: 'whenDistance',
        text: formatMessage({
          id: 'wedo2.whenDistance',
          default: 'when distance [OP] [REFERENCE]',
          description: 'check for when distance is < or > than reference'
        }),
        blockType: BlockType.HAT,
        arguments: {
          OP: {
            type: ArgumentType.STRING,
            menu: 'OP',
            defaultValue: '<'
          },
          REFERENCE: {
            type: ArgumentType.NUMBER,
            defaultValue: 50
          }
        }
      }, {
        opcode: 'whenTilted',
        text: formatMessage({
          id: 'wedo2.whenTilted',
          default: 'when tilted [TILT_DIRECTION_ANY]',
          description: 'check when tilted in a certain direction'
        }),
        func: 'isTilted',
        blockType: BlockType.HAT,
        arguments: {
          TILT_DIRECTION_ANY: {
            type: ArgumentType.STRING,
            menu: 'TILT_DIRECTION_ANY',
            defaultValue: WeDo2TiltDirection.ANY
          }
        }
      }, {
        opcode: 'getDistance',
        text: formatMessage({
          id: 'wedo2.getDistance',
          default: 'distance',
          description: 'the value returned by the distance sensor'
        }),
        blockType: BlockType.REPORTER
      }, {
        opcode: 'isTilted',
        text: formatMessage({
          id: 'wedo2.isTilted',
          default: 'tilted [TILT_DIRECTION_ANY]?',
          description: 'whether the tilt sensor is tilted'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          TILT_DIRECTION_ANY: {
            type: ArgumentType.STRING,
            menu: 'TILT_DIRECTION_ANY',
            defaultValue: WeDo2TiltDirection.ANY
          }
        }
      }, {
        opcode: 'getTiltAngle',
        text: formatMessage({
          id: 'wedo2.getTiltAngle',
          default: 'tilt angle [TILT_DIRECTION]',
          description: 'the angle returned by the tilt sensor'
        }),
        blockType: BlockType.REPORTER,
        arguments: {
          TILT_DIRECTION: {
            type: ArgumentType.STRING,
            menu: 'TILT_DIRECTION',
            defaultValue: WeDo2TiltDirection.UP
          }
        }
      }],
      menus: {
        MOTOR_ID: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'wedo2.motorId.default',
              default: 'motor',
              description: 'label for motor element in motor menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorLabel.DEFAULT
          }, {
            text: formatMessage({
              id: 'wedo2.motorId.a',
              default: 'motor A',
              description: 'label for motor A element in motor menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorLabel.A
          }, {
            text: formatMessage({
              id: 'wedo2.motorId.b',
              default: 'motor B',
              description: 'label for motor B element in motor menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorLabel.B
          }, {
            text: formatMessage({
              id: 'wedo2.motorId.all',
              default: 'all motors',
              description: 'label for all motors element in motor menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorLabel.ALL
          }]
        },
        MOTOR_DIRECTION: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'wedo2.motorDirection.forward',
              default: 'this way',
              description: 'label for forward element in motor direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorDirection.FORWARD
          }, {
            text: formatMessage({
              id: 'wedo2.motorDirection.backward',
              default: 'that way',
              description: 'label for backward element in motor direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorDirection.BACKWARD
          }, {
            text: formatMessage({
              id: 'wedo2.motorDirection.reverse',
              default: 'reverse',
              description: 'label for reverse element in motor direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2MotorDirection.REVERSE
          }]
        },
        TILT_DIRECTION: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'wedo2.tiltDirection.up',
              default: 'up',
              description: 'label for up element in tilt direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2TiltDirection.UP
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.down',
              default: 'down',
              description: 'label for down element in tilt direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2TiltDirection.DOWN
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.left',
              default: 'left',
              description: 'label for left element in tilt direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2TiltDirection.LEFT
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.right',
              default: 'right',
              description: 'label for right element in tilt direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2TiltDirection.RIGHT
          }]
        },
        TILT_DIRECTION_ANY: {
          acceptReporters: true,
          items: [{
            text: formatMessage({
              id: 'wedo2.tiltDirection.up',
              default: 'up'
            }),
            value: WeDo2TiltDirection.UP
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.down',
              default: 'down'
            }),
            value: WeDo2TiltDirection.DOWN
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.left',
              default: 'left'
            }),
            value: WeDo2TiltDirection.LEFT
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.right',
              default: 'right'
            }),
            value: WeDo2TiltDirection.RIGHT
          }, {
            text: formatMessage({
              id: 'wedo2.tiltDirection.any',
              default: 'any',
              description: 'label for any element in tilt direction menu for LEGO WeDo 2 extension'
            }),
            value: WeDo2TiltDirection.ANY
          }]
        },
        OP: {
          acceptReporters: true,
          items: ['<', '>']
        }
      }
    };
  }
  /**
   * Turn specified motor(s) on for a specified duration.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to activate.
   * @property {int} DURATION - the amount of time to run the motors.
   * @return {Promise} - a promise which will resolve at the end of the duration.
   */


  motorOnFor(args) {
    // TODO: cast args.MOTOR_ID?
    let durationMS = Cast.toNumber(args.DURATION) * 1000;
    durationMS = MathUtil.clamp(durationMS, 0, 15000);
    return new Promise(resolve => {
      this._forEachMotor(args.MOTOR_ID, motorIndex => {
        const motor = this._peripheral.motor(motorIndex);

        if (motor) {
          motor.turnOnFor(durationMS);
        }
      }); // Run for some time even when no motor is connected


      setTimeout(resolve, durationMS);
    });
  }
  /**
   * Turn specified motor(s) on indefinitely.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to activate.
   * @return {Promise} - a Promise that resolves after some delay.
   */


  motorOn(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        motor.turnOn();
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Turn specified motor(s) off.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to deactivate.
   * @return {Promise} - a Promise that resolves after some delay.
   */


  motorOff(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        motor.turnOff();
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Turn specified motor(s) off.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to be affected.
   * @property {int} POWER - the new power level for the motor(s).
   * @return {Promise} - a Promise that resolves after some delay.
   */


  startMotorPower(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        motor.power = MathUtil.clamp(Cast.toNumber(args.POWER), 0, 100);
        motor.turnOn();
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Set the direction of rotation for specified motor(s).
   * If the direction is 'reverse' the motor(s) will be reversed individually.
   * @param {object} args - the block's arguments.
   * @property {MotorID} MOTOR_ID - the motor(s) to be affected.
   * @property {MotorDirection} MOTOR_DIRECTION - the new direction for the motor(s).
   * @return {Promise} - a Promise that resolves after some delay.
   */


  setMotorDirection(args) {
    // TODO: cast args.MOTOR_ID?
    this._forEachMotor(args.MOTOR_ID, motorIndex => {
      const motor = this._peripheral.motor(motorIndex);

      if (motor) {
        switch (args.MOTOR_DIRECTION) {
          case WeDo2MotorDirection.FORWARD:
            motor.direction = 1;
            break;

          case WeDo2MotorDirection.BACKWARD:
            motor.direction = -1;
            break;

          case WeDo2MotorDirection.REVERSE:
            motor.direction = -motor.direction;
            break;

          default:
            log.warn("Unknown motor direction in setMotorDirection: ".concat(args.DIRECTION));
            break;
        } // keep the motor on if it's running, and update the pending timeout if needed


        if (motor.isOn) {
          if (motor.pendingTimeoutDelay) {
            motor.turnOnFor(motor.pendingTimeoutStartTime + motor.pendingTimeoutDelay - Date.now());
          } else {
            motor.turnOn();
          }
        }
      }
    });

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Set the LED's hue.
   * @param {object} args - the block's arguments.
   * @property {number} HUE - the hue to set, in the range [0,100].
   * @return {Promise} - a Promise that resolves after some delay.
   */


  setLightHue(args) {
    // Convert from [0,100] to [0,360]
    let inputHue = Cast.toNumber(args.HUE);
    inputHue = MathUtil.wrapClamp(inputHue, 0, 100);
    const hue = inputHue * 360 / 100;
    const rgbObject = color.hsvToRgb({
      h: hue,
      s: 1,
      v: 1
    });
    const rgbDecimal = color.rgbToDecimal(rgbObject);

    this._peripheral.setLED(rgbDecimal);

    return new Promise(resolve => {
      window.setTimeout(() => {
        resolve();
      }, BLESendInterval);
    });
  }
  /**
   * Make the WeDo 2.0 peripheral play a MIDI note for the specified duration.
   * @param {object} args - the block's arguments.
   * @property {number} NOTE - the MIDI note to play.
   * @property {number} DURATION - the duration of the note, in seconds.
   * @return {Promise} - a promise which will resolve at the end of the duration.
   */


  playNoteFor(args) {
    let durationMS = Cast.toNumber(args.DURATION) * 1000;
    durationMS = MathUtil.clamp(durationMS, 0, 3000);
    const note = MathUtil.clamp(Cast.toNumber(args.NOTE), 25, 125); // valid WeDo 2.0 sounds

    if (durationMS === 0) return; // WeDo 2.0 plays duration '0' forever

    return new Promise(resolve => {
      const tone = this._noteToTone(note);

      this._peripheral.playTone(tone, durationMS); // Run for some time even when no piezo is connected


      setTimeout(resolve, durationMS);
    });
  }
  /**
   * Compare the distance sensor's value to a reference.
   * @param {object} args - the block's arguments.
   * @property {string} OP - the comparison operation: '<' or '>'.
   * @property {number} REFERENCE - the value to compare against.
   * @return {boolean} - the result of the comparison, or false on error.
   */


  whenDistance(args) {
    switch (args.OP) {
      case '<':
        return this._peripheral.distance < Cast.toNumber(args.REFERENCE);

      case '>':
        return this._peripheral.distance > Cast.toNumber(args.REFERENCE);

      default:
        log.warn("Unknown comparison operator in whenDistance: ".concat(args.OP));
        return false;
    }
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  whenTilted(args) {
    return this._isTilted(args.TILT_DIRECTION_ANY);
  }
  /**
   * @return {number} - the distance sensor's value, scaled to the [0,100] range.
   */


  getDistance() {
    return this._peripheral.distance;
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION_ANY - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   */


  isTilted(args) {
    return this._isTilted(args.TILT_DIRECTION_ANY);
  }
  /**
   * @param {object} args - the block's arguments.
   * @property {TiltDirection} TILT_DIRECTION - the direction (up, down, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).
   */


  getTiltAngle(args) {
    return this._getTiltAngle(args.TILT_DIRECTION);
  }
  /**
   * Test whether the tilt sensor is currently tilted.
   * @param {TiltDirection} direction - the tilt direction to test (up, down, left, right, or any).
   * @return {boolean} - true if the tilt sensor is tilted past a threshold in the specified direction.
   * @private
   */


  _isTilted(direction) {
    switch (direction) {
      case WeDo2TiltDirection.ANY:
        return this._getTiltAngle(WeDo2TiltDirection.UP) >= Scratch3WeDo2Blocks.TILT_THRESHOLD || this._getTiltAngle(WeDo2TiltDirection.DOWN) >= Scratch3WeDo2Blocks.TILT_THRESHOLD || this._getTiltAngle(WeDo2TiltDirection.LEFT) >= Scratch3WeDo2Blocks.TILT_THRESHOLD || this._getTiltAngle(WeDo2TiltDirection.RIGHT) >= Scratch3WeDo2Blocks.TILT_THRESHOLD;

      default:
        return this._getTiltAngle(direction) >= Scratch3WeDo2Blocks.TILT_THRESHOLD;
    }
  }
  /**
   * @param {TiltDirection} direction - the direction (up, down, left, right) to check.
   * @return {number} - the tilt sensor's angle in the specified direction.
   * Note that getTiltAngle(up) = -getTiltAngle(down) and getTiltAngle(left) = -getTiltAngle(right).
   * @private
   */


  _getTiltAngle(direction) {
    switch (direction) {
      case WeDo2TiltDirection.UP:
        return this._peripheral.tiltY > 45 ? 256 - this._peripheral.tiltY : -this._peripheral.tiltY;

      case WeDo2TiltDirection.DOWN:
        return this._peripheral.tiltY > 45 ? this._peripheral.tiltY - 256 : this._peripheral.tiltY;

      case WeDo2TiltDirection.LEFT:
        return this._peripheral.tiltX > 45 ? 256 - this._peripheral.tiltX : -this._peripheral.tiltX;

      case WeDo2TiltDirection.RIGHT:
        return this._peripheral.tiltX > 45 ? this._peripheral.tiltX - 256 : this._peripheral.tiltX;

      default:
        log.warn("Unknown tilt direction in _getTiltAngle: ".concat(direction));
    }
  }
  /**
   * Call a callback for each motor indexed by the provided motor ID.
   * @param {MotorID} motorID - the ID specifier.
   * @param {Function} callback - the function to call with the numeric motor index for each motor.
   * @private
   */


  _forEachMotor(motorID, callback) {
    let motors;

    switch (motorID) {
      case WeDo2MotorLabel.A:
        motors = [0];
        break;

      case WeDo2MotorLabel.B:
        motors = [1];
        break;

      case WeDo2MotorLabel.ALL:
      case WeDo2MotorLabel.DEFAULT:
        motors = [0, 1];
        break;

      default:
        log.warn("Invalid motor ID: ".concat(motorID));
        motors = [];
        break;
    }

    for (const index of motors) {
      callback(index);
    }
  }
  /**
   * @param {number} midiNote - the MIDI note value to convert.
   * @return {number} - the frequency, in Hz, corresponding to that MIDI note value.
   * @private
   */


  _noteToTone(midiNote) {
    // Note that MIDI note 69 is A4, 440 Hz
    return 440 * Math.pow(2, (midiNote - 69) / 12);
  }

}

module.exports = Scratch3WeDo2Blocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/extensions/tw/index.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-vm/src/extensions/tw/index.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const BlockType = __webpack_require__(/*! ../../extension-support/block-type */ "./node_modules/scratch-vm/src/extension-support/block-type.js");

const ArgumentType = __webpack_require__(/*! ../../extension-support/argument-type */ "./node_modules/scratch-vm/src/extension-support/argument-type.js");

const Cast = __webpack_require__(/*! ../../util/cast */ "./node_modules/scratch-vm/src/util/cast.js"); // eslint-disable-next-line max-len


const iconURI = "data:image/svg+xml;base64,".concat(btoa('<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path fill="none" stroke="#fff" stroke-width="11.51815371" d="M24.457 7.707a18.41 18.41 0 0 0-.365 2.31c-.02.224 0 .507.06.852.061.405.092.689.092.851 0 .527-.345.79-1.034.79-.446 0-.74-.131-.881-.395-.02-.446-.01-1.054.03-1.824.04-.912.061-1.52.061-1.824-.02 0-.05-.02-.091-.06a98.522 98.522 0 0 0-5.32.364c-.04.264-.04.588 0 .973l.122 1.094c-.081.629-.122 1.56-.122 2.797.061.527.091 2.786.091 6.779v2.219c0 .344.051.587.152.73h1.885c.77-.102 1.155.222 1.155.972 0 .446-.213.76-.638.942-.264.102-.73.122-1.399.061-.405-.04-.881-.05-1.428-.03-.75.101-1.662.182-2.736.243-1.094.06-1.763-.091-2.006-.456-.162-.243-.162-.496 0-.76.283-.446 1.023-.669 2.219-.669.628 0 .942-.172.942-.516 0-.183-.01-.355-.03-.517 0-.507.01-.953.03-1.338.06-1.094.06-2.634 0-4.62-.081-2.878-.05-5.462.091-7.752l-.09-.09c-.63.04-1.805.03-3.527-.031-.081 0-.7.04-1.854.121.283 1.946.446 3.334.486 4.165l-.06.82c-.021.305-.274.457-.76.457-.386 0-.71-.73-.973-2.19-.122-.87-.244-1.752-.365-2.644 0-.142-.071-.385-.213-.73-.122-.364-.39-.97-.39-1.152 0-.641.593-.489 1.363-.61.06 0 .162.01.304.03.142.02.243.03.304.03H17.1a57.098 57.098 0 0 0 5.411-.486c.122-.06.304-.121.547-.182.426-.04.79.06 1.095.304.304.223.405.547.304.972z"/><path fill="none" stroke="#ff4c4c" stroke-width="5.75909785" d="M24.333 7.71q-.244 1.065-.365 2.311-.03.335.06.851.092.608.092.851 0 .79-1.034.79-.669 0-.881-.394-.03-.67.03-1.824.06-1.368.06-1.824-.03 0-.09-.061-2.827.122-5.32.365-.06.395 0 .973l.122 1.094q-.122.942-.122 2.796.091.79.091 6.78v2.218q0 .517.152.73h1.885q1.155-.152 1.155.973 0 .668-.638.942-.396.152-1.399.06-.608-.06-1.428-.03-1.125.152-2.736.243-1.642.092-2.006-.456-.244-.364 0-.76.425-.668 2.219-.668.942 0 .942-.517 0-.274-.03-.517 0-.76.03-1.337.091-1.642 0-4.62-.122-4.317.091-7.752l-.091-.091q-.942.06-3.526-.03-.122 0-1.854.12.425 2.919.486 4.165l-.06.821q-.031.456-.76.456-.578 0-.974-2.189-.182-1.307-.364-2.644 0-.213-.213-.73-.182-.547-.182-.82 0-.76 1.155-.943.09 0 .304.03.212.03.304.03h7.538q2.797-.12 5.411-.485.182-.092.547-.183.639-.06 1.095.304.456.335.304.973z"/><path fill="#fff" d="M24.31 7.714q-.243 1.064-.365 2.31-.03.335.061.852.091.608.091.85 0 .791-1.033.791-.67 0-.882-.395-.03-.669.03-1.824.061-1.368.061-1.824-.03 0-.09-.06-2.828.121-5.32.364-.061.396 0 .973l.121 1.094q-.121.943-.121 2.797.09.79.09 6.779v2.219q0 .517.153.73h1.884q1.156-.153 1.156.972 0 .669-.639.942-.395.152-1.398.061-.608-.06-1.429-.03-1.125.152-2.736.243-1.641.091-2.006-.456-.243-.365 0-.76.426-.669 2.22-.669.941 0 .941-.516 0-.274-.03-.517 0-.76.03-1.338.092-1.641 0-4.62-.121-4.317.092-7.752l-.092-.09q-.942.06-3.526-.031-.121 0-1.854.121.426 2.919.486 4.165l-.06.82q-.03.457-.76.457-.578 0-.973-2.19-.182-1.306-.365-2.644 0-.212-.213-.73-.182-.546-.182-.82 0-.76 1.155-.942.091 0 .304.03t.304.03h7.539q2.796-.121 5.41-.486.183-.091.548-.182.638-.061 1.094.304.456.334.304.972z"/></svg>'));
/**
 * Class for TurboWarp blocks
 * @constructor
 */

class TurboWarpBlocks {
  constructor(runtime) {
    /**
     * The runtime instantiating this block package.
     * @type {Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * @returns {object} metadata for this extension and its blocks.
   */


  getInfo() {
    return {
      id: 'tw',
      name: 'TurboWarp',
      color1: '#ff4c4c',
      color2: '#e64444',
      color3: '#c73a3a',
      docsURI: 'https://docs.turbowarp.org/blocks',
      menuIconURI: iconURI,
      blockIconURI: iconURI,
      blocks: [{
        opcode: 'getLastKeyPressed',
        text: formatMessage({
          id: 'tw.blocks.lastKeyPressed',
          default: 'last key pressed',
          description: 'Block that returns the last key that was pressed'
        }),
        blockType: BlockType.REPORTER
      }, {
        opcode: 'getButtonIsDown',
        text: formatMessage({
          id: 'tw.blocks.buttonIsDown',
          default: '[MOUSE_BUTTON] mouse button down?',
          description: 'Block that returns whether a specific mouse button is down'
        }),
        blockType: BlockType.BOOLEAN,
        arguments: {
          MOUSE_BUTTON: {
            type: ArgumentType.NUMBER,
            menu: 'mouseButton',
            defaultValue: '0'
          }
        }
      }],
      menus: {
        mouseButton: {
          items: [{
            text: formatMessage({
              id: 'tw.blocks.mouseButton.primary',
              default: '(0) primary',
              description: 'Dropdown item to select primary (usually left) mouse button'
            }),
            value: '0'
          }, {
            text: formatMessage({
              id: 'tw.blocks.mouseButton.middle',
              default: '(1) middle',
              description: 'Dropdown item to select middle mouse button'
            }),
            value: '1'
          }, {
            text: formatMessage({
              id: 'tw.blocks.mouseButton.secondary',
              default: '(2) secondary',
              description: 'Dropdown item to select secondary (usually right) mouse button'
            }),
            value: '2'
          }],
          acceptReporters: true
        }
      }
    };
  }

  getLastKeyPressed(args, util) {
    return util.ioQuery('keyboard', 'getLastKeyPressed');
  }

  getButtonIsDown(args, util) {
    const button = Cast.toNumber(args.MOUSE_BUTTON);
    return util.ioQuery('mouse', 'getButtonIsDown', [button]);
  }

}

module.exports = TurboWarpBlocks;

/***/ }),

/***/ "./node_modules/scratch-vm/src/import/load-costume.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-vm/src/import/load-costume.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const AsyncLimiter = __webpack_require__(/*! ../util/async-limiter */ "./node_modules/scratch-vm/src/util/async-limiter.js");

const {
  loadSvgString,
  serializeSvgToString
} = __webpack_require__(/*! scratch-svg-renderer */ "./node_modules/scratch-svg-renderer/src/index.js");

const {
  parseVectorMetadata
} = __webpack_require__(/*! ../serialization/tw-costume-import-export */ "./node_modules/scratch-vm/src/serialization/tw-costume-import-export.js");

const loadVector_ = function loadVector_(costume, runtime, rotationCenter, optVersion) {
  return new Promise(resolve => {
    let svgString = costume.asset.decodeText(); // TW: We allow SVGs to specify their rotation center using a special comment.

    if (typeof rotationCenter === 'undefined') {
      const parsedRotationCenter = parseVectorMetadata(svgString);

      if (parsedRotationCenter) {
        rotationCenter = parsedRotationCenter;
        costume.rotationCenterX = rotationCenter[0];
        costume.rotationCenterY = rotationCenter[1];
      }
    } // SVG Renderer load fixes "quirks" associated with Scratch 2 projects


    if (optVersion && optVersion === 2) {
      // scratch-svg-renderer fixes syntax that causes loading issues,
      // and if optVersion is 2, fixes "quirks" associated with Scratch 2 SVGs,
      const fixedSvgString = serializeSvgToString(loadSvgString(svgString, true
      /* fromVersion2 */
      )); // If the string changed, put back into storage

      if (svgString !== fixedSvgString) {
        svgString = fixedSvgString;
        const storage = runtime.storage;
        costume.asset.encodeTextData(fixedSvgString, storage.DataFormat.SVG, true);
        costume.assetId = costume.asset.assetId;
        costume.md5 = "".concat(costume.assetId, ".").concat(costume.dataFormat);
      }
    } // createSVGSkin does the right thing if rotationCenter isn't provided, so it's okay if it's
    // undefined here


    costume.skinId = runtime.renderer.createSVGSkin(svgString, rotationCenter);
    costume.size = runtime.renderer.getSkinSize(costume.skinId); // Now we should have a rotationCenter even if we didn't before

    if (!rotationCenter) {
      rotationCenter = runtime.renderer.getSkinRotationCenter(costume.skinId);
      costume.rotationCenterX = rotationCenter[0];
      costume.rotationCenterY = rotationCenter[1];
      costume.bitmapResolution = 1;
    }

    if (runtime.isPackaged) {
      costume.asset = null;
    }

    resolve(costume);
  });
};

const canvasPool = function () {
  /**
   * A pool of canvas objects that can be reused to reduce memory
   * allocations. And time spent in those allocations and the later garbage
   * collection.
   */
  class CanvasPool {
    constructor() {
      this.pool = [];
      this.clearSoon = null;
    }
    /**
     * After a short wait period clear the pool to let the VM collect
     * garbage.
     */


    clear() {
      if (!this.clearSoon) {
        this.clearSoon = new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
          this.pool.length = 0;
          this.clearSoon = null;
        });
      }
    }
    /**
     * Return a canvas. Create the canvas if the pool is empty.
     * @returns {HTMLCanvasElement} A canvas element.
     */


    create() {
      return this.pool.pop() || document.createElement('canvas');
    }
    /**
     * Release the canvas to be reused.
     * @param {HTMLCanvasElement} canvas A canvas element.
     */


    release(canvas) {
      this.clear();
      this.pool.push(canvas);
    }

  }

  return new CanvasPool();
}();
/**
 * @param {string} src URL of image
 * @returns {Promise<HTMLImageElement>}
 */


const readAsImageElement = src => new Promise((resolve, reject) => {
  const image = new Image();

  image.onload = function () {
    resolve(image);
    image.onload = null;
    image.onerror = null;
  };

  image.onerror = function () {
    reject(new Error('Costume load failed. Asset could not be read.'));
    image.onload = null;
    image.onerror = null;
  };

  image.src = src;
});
/**
 * @param {Asset} asset scratch-storage asset
 * @returns {Promise<HTMLImageElement|ImageBitmap>}
 */


const _persistentReadImage = async asset => {
  // Sometimes, when a lot of images are loaded at once, especially in Chrome, reading an image
  // can throw an error even on valid images. To mitigate this, we'll retry image reading a few
  // time with delays.
  let firstError;

  for (let i = 0; i < 3; i++) {
    try {
      if (typeof createImageBitmap === 'function') {
        const imageBitmap = await createImageBitmap(new Blob([asset.data.buffer], {
          type: asset.assetType.contentType
        })); // If we do too many createImageBitmap at the same time, some browsers (Chrome) will
        // sometimes resolve with undefined. We limit concurrency so this shouldn't ever
        // happen, but if it somehow does, throw an error so it can be retried or so that it
        // falls back to scratch's broken costume handling.

        if (!imageBitmap) {
          throw new Error("createImageBitmap resolved with ".concat(imageBitmap));
        }

        return imageBitmap;
      }

      return await readAsImageElement(asset.encodeDataURI());
    } catch (e) {
      if (!firstError) {
        firstError = e;
      }

      log.warn(e);
      await new Promise(resolve => setTimeout(resolve, Math.random() * 2000));
    }
  }

  throw firstError;
}; // Browsers break when we do too many createImageBitmap at the same time.


const readImage = new AsyncLimiter(_persistentReadImage, 25);
/**
 * Return a promise to fetch a bitmap from storage and return it as a canvas
 * If the costume has bitmapResolution 1, it will be converted to bitmapResolution 2 here (the standard for Scratch 3)
 * If the costume has a text layer asset, which is a text part from Scratch 1.4, then this function
 * will merge the two image assets. See the issue LLK/scratch-vm#672 for more information.
 * @param {!object} costume - the Scratch costume object.
 * @param {!Runtime} runtime - Scratch runtime, used to access the v2BitmapAdapter
 * @param {?object} rotationCenter - optionally passed in coordinates for the center of rotation for the image. If
 *     none is given, the rotation center of the costume will be set to the middle of the costume later on.
 * @property {number} costume.bitmapResolution - the resolution scale for a bitmap costume.
 * @returns {?Promise} - a promise which will resolve to an object {canvas, rotationCenter, assetMatchesBase},
 *     or reject on error.
 *     assetMatchesBase is true if the asset matches the base layer; false if it required adjustment
 */

const fetchBitmapCanvas_ = function fetchBitmapCanvas_(costume, runtime, rotationCenter) {
  if (!costume || !costume.asset) {
    // TODO: We can probably remove this check...
    return Promise.reject('Costume load failed. Assets were missing.');
  }

  if (!runtime.v2BitmapAdapter) {
    return Promise.reject('No V2 Bitmap adapter present.');
  }

  return Promise.all([costume.asset, costume.textLayerAsset].map(asset => {
    if (!asset) {
      return null;
    }

    return readImage.do(asset);
  })).then(_ref => {
    let [baseImageElement, textImageElement] = _ref;

    if (!baseImageElement) {
      throw new Error('Loading bitmap costume base failed.');
    }

    const scale = costume.bitmapResolution === 1 ? 2 : 1;
    let imageOrCanvas;
    let canvas;

    if (textImageElement) {
      canvas = canvasPool.create();
      canvas.width = baseImageElement.width;
      canvas.height = baseImageElement.height;
      const ctx = canvas.getContext('2d');
      ctx.drawImage(baseImageElement, 0, 0);
      ctx.drawImage(textImageElement, 0, 0);
      imageOrCanvas = canvas;
    } else {
      imageOrCanvas = baseImageElement;
    }

    if (scale !== 1) {
      // resize() returns a new canvas.
      imageOrCanvas = runtime.v2BitmapAdapter.resize(imageOrCanvas, imageOrCanvas.width * scale, imageOrCanvas.height * scale); // Old canvas is no longer used.

      if (canvas) {
        canvasPool.release(canvas);
      }
    } // This informs TurboWarp/scratch-render that this canvas won't be reused by the canvas pool,
    // which helps it optimize memory use.


    imageOrCanvas.reusable = false; // By scaling, we've converted it to bitmap resolution 2

    if (rotationCenter) {
      rotationCenter[0] = rotationCenter[0] * scale;
      rotationCenter[1] = rotationCenter[1] * scale;
      costume.rotationCenterX = rotationCenter[0];
      costume.rotationCenterY = rotationCenter[1];
    }

    costume.bitmapResolution = 2; // Clean up the costume object

    delete costume.textLayerMD5;
    delete costume.textLayerAsset;
    return {
      image: imageOrCanvas,
      rotationCenter,
      // True if the asset matches the base layer; false if it required adjustment
      assetMatchesBase: scale === 1 && !textImageElement
    };
  }).finally(() => {
    // Clean up the text layer properties if it fails to load
    delete costume.textLayerMD5;
    delete costume.textLayerAsset;
  });
};

const toDataURL = imageOrCanvas => {
  if (imageOrCanvas instanceof HTMLCanvasElement) {
    return imageOrCanvas.toDataURL();
  }

  const canvas = canvasPool.create();
  canvas.width = imageOrCanvas.width;
  canvas.height = imageOrCanvas.height;
  const ctx = canvas.getContext('2d');
  ctx.drawImage(imageOrCanvas, 0, 0);
  const url = canvas.toDataURL();
  canvasPool.release(canvas);
  return url;
};

const loadBitmap_ = function loadBitmap_(costume, runtime, _rotationCenter) {
  return fetchBitmapCanvas_(costume, runtime, _rotationCenter).then(fetched => {
    const updateCostumeAsset = function updateCostumeAsset(dataURI) {
      if (!runtime.v2BitmapAdapter) {
        // TODO: This might be a bad practice since the returned
        // promise isn't acted on. If this is something we should be
        // creating a rejected promise for we should also catch it
        // somewhere and act on that error (like logging).
        //
        // Return a rejection to stop executing updateCostumeAsset.
        return Promise.reject('No V2 Bitmap adapter present.');
      }

      const storage = runtime.storage;
      costume.asset = storage.createAsset(storage.AssetType.ImageBitmap, storage.DataFormat.PNG, runtime.v2BitmapAdapter.convertDataURIToBinary(dataURI), null, true // generate md5
      );
      costume.dataFormat = storage.DataFormat.PNG;
      costume.assetId = costume.asset.assetId;
      costume.md5 = "".concat(costume.assetId, ".").concat(costume.dataFormat);
    };

    if (!fetched.assetMatchesBase) {
      updateCostumeAsset(toDataURL(fetched.image));
    }

    return fetched;
  }).then(_ref2 => {
    let {
      image,
      rotationCenter
    } = _ref2;
    // createBitmapSkin does the right thing if costume.rotationCenter is undefined.
    // That will be the case if you upload a bitmap asset or create one by taking a photo.
    let center;

    if (rotationCenter) {
      // fetchBitmapCanvas will ensure that the costume's bitmap resolution is 2 and its rotation center is
      // scaled to match, so it's okay to always divide by 2.
      center = [rotationCenter[0] / 2, rotationCenter[1] / 2];
    } // TODO: costume.bitmapResolution will always be 2 at this point because of fetchBitmapCanvas_, so we don't
    // need to pass it in here.


    costume.skinId = runtime.renderer.createBitmapSkin(image, costume.bitmapResolution, center);
    const renderSize = runtime.renderer.getSkinSize(costume.skinId);
    costume.size = [renderSize[0] * 2, renderSize[1] * 2]; // Actual size, since all bitmaps are resolution 2

    if (!rotationCenter) {
      rotationCenter = runtime.renderer.getSkinRotationCenter(costume.skinId); // Actual rotation center, since all bitmaps are resolution 2

      costume.rotationCenterX = rotationCenter[0] * 2;
      costume.rotationCenterY = rotationCenter[1] * 2;
      costume.bitmapResolution = 2;
    }

    if (runtime.isPackaged) {
      costume.asset = null;
    }

    return costume;
  });
}; // Handle all manner of costume errors with a Gray Question Mark (default costume)
// and preserve as much of the original costume data as possible
// Returns a promise of a costume


const handleCostumeLoadError = function handleCostumeLoadError(costume, runtime) {
  // Keep track of the old asset information until we're done loading the default costume
  const oldAsset = costume.asset; // could be null

  const oldAssetId = costume.assetId;
  const oldRotationX = costume.rotationCenterX;
  const oldRotationY = costume.rotationCenterY;
  const oldBitmapResolution = costume.bitmapResolution;
  const oldDataFormat = costume.dataFormat;
  const AssetType = runtime.storage.AssetType;
  const isVector = costume.dataFormat === AssetType.ImageVector.runtimeFormat; // Use default asset if original fails to load

  costume.assetId = isVector ? runtime.storage.defaultAssetId.ImageVector : runtime.storage.defaultAssetId.ImageBitmap;
  costume.asset = runtime.storage.get(costume.assetId);
  costume.md5 = "".concat(costume.assetId, ".").concat(costume.asset.dataFormat);
  const defaultCostumePromise = isVector ? loadVector_(costume, runtime) : loadBitmap_(costume, runtime);
  return defaultCostumePromise.then(loadedCostume => {
    loadedCostume.broken = {};
    loadedCostume.broken.assetId = oldAssetId;
    loadedCostume.broken.md5 = "".concat(oldAssetId, ".").concat(oldDataFormat); // Should be null if we got here because the costume was missing

    loadedCostume.broken.asset = oldAsset;
    loadedCostume.broken.dataFormat = oldDataFormat;
    loadedCostume.broken.rotationCenterX = oldRotationX;
    loadedCostume.broken.rotationCenterY = oldRotationY;
    loadedCostume.broken.bitmapResolution = oldBitmapResolution;
    return loadedCostume;
  });
};
/**
 * Initialize a costume from an asset asynchronously.
 * Do not call this unless there is a renderer attached.
 * @param {!object} costume - the Scratch costume object.
 * @property {int} skinId - the ID of the costume's render skin, once installed.
 * @property {number} rotationCenterX - the X component of the costume's origin.
 * @property {number} rotationCenterY - the Y component of the costume's origin.
 * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.
 * @property {!Asset} costume.asset - the asset of the costume loaded from storage.
 * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
 * @param {?int} optVersion - Version of Scratch that the costume comes from. If this is set
 *     to 2, scratch 3 will perform an upgrade step to handle quirks in SVGs from Scratch 2.0.
 * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error.
 */


const loadCostumeFromAsset = function loadCostumeFromAsset(costume, runtime, optVersion) {
  costume.assetId = costume.asset.assetId;
  const renderer = runtime.renderer;

  if (!renderer) {
    log.warn('No rendering module present; cannot load costume: ', costume.name);
    return Promise.resolve(costume);
  }

  const AssetType = runtime.storage.AssetType;
  let rotationCenter; // Use provided rotation center and resolution if they are defined. Bitmap resolution
  // should only ever be 1 or 2.

  if (typeof costume.rotationCenterX === 'number' && !isNaN(costume.rotationCenterX) && typeof costume.rotationCenterY === 'number' && !isNaN(costume.rotationCenterY)) {
    rotationCenter = [costume.rotationCenterX, costume.rotationCenterY];
  }

  if (costume.asset.assetType.runtimeFormat === AssetType.ImageVector.runtimeFormat) {
    return loadVector_(costume, runtime, rotationCenter, optVersion).catch(error => {
      log.warn("Error loading vector image: ".concat(error));
      return handleCostumeLoadError(costume, runtime);
    });
  }

  return loadBitmap_(costume, runtime, rotationCenter, optVersion).catch(error => {
    log.warn("Error loading bitmap image: ".concat(error));
    return handleCostumeLoadError(costume, runtime);
  });
};
/**
 * Load a costume's asset into memory asynchronously.
 * Do not call this unless there is a renderer attached.
 * @param {!string} md5ext - the MD5 and extension of the costume to be loaded.
 * @param {!object} costume - the Scratch costume object.
 * @property {int} skinId - the ID of the costume's render skin, once installed.
 * @property {number} rotationCenterX - the X component of the costume's origin.
 * @property {number} rotationCenterY - the Y component of the costume's origin.
 * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.
 * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
 * @param {?int} optVersion - Version of Scratch that the costume comes from. If this is set
 *     to 2, scratch 3 will perform an upgrade step to handle quirks in SVGs from Scratch 2.0.
 * @returns {?Promise} - a promise which will resolve after skinId is set, or null on error.
 */


const loadCostume = function loadCostume(md5ext, costume, runtime, optVersion) {
  const idParts = StringUtil.splitFirst(md5ext, '.');
  const md5 = idParts[0];
  const ext = idParts[1].toLowerCase();
  costume.dataFormat = ext;

  if (costume.asset) {
    // Costume comes with asset. It could be coming from image upload, drag and drop, or file
    return loadCostumeFromAsset(costume, runtime, optVersion);
  } // Need to load the costume from storage. The server should have a reference to this md5.


  if (!runtime.storage) {
    log.warn('No storage module present; cannot load costume asset: ', md5ext);
    return Promise.resolve(costume);
  }

  if (!runtime.storage.defaultAssetId) {
    log.warn("No default assets found");
    return Promise.resolve(costume);
  }

  const AssetType = runtime.storage.AssetType;
  const assetType = ext === 'svg' ? AssetType.ImageVector : AssetType.ImageBitmap;
  const costumePromise = runtime.storage.load(assetType, md5, ext);
  let textLayerPromise;

  if (costume.textLayerMD5) {
    textLayerPromise = runtime.storage.load(AssetType.ImageBitmap, costume.textLayerMD5, 'png');
  } else {
    textLayerPromise = Promise.resolve(null);
  }

  return Promise.all([costumePromise, textLayerPromise]).then(assetArray => {
    if (assetArray[0]) {
      costume.asset = assetArray[0];
    } else {
      return handleCostumeLoadError(costume, runtime);
    }

    if (assetArray[1]) {
      costume.textLayerAsset = assetArray[1];
    }

    return loadCostumeFromAsset(costume, runtime, optVersion);
  }).catch(error => {
    // Handle case where storage.load rejects with errors
    // instead of resolving null
    log.warn('Error loading costume: ', error);
    return handleCostumeLoadError(costume, runtime);
  });
};

module.exports = {
  loadCostume,
  loadCostumeFromAsset
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/import/load-sound.js":
/*!**********************************************************!*\
  !*** ./node_modules/scratch-vm/src/import/load-sound.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Initialize a sound from an asset asynchronously.
 * @param {!object} sound - the Scratch sound object.
 * @property {string} md5 - the MD5 and extension of the sound to be loaded.
 * @property {Buffer} data - sound data will be written here once loaded.
 * @param {!Asset} soundAsset - the asset loaded from storage.
 * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
 * @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to.
 * @returns {!Promise} - a promise which will resolve to the sound when ready.
 */


const loadSoundFromAsset = function loadSoundFromAsset(sound, soundAsset, runtime, soundBank) {
  sound.assetId = soundAsset.assetId;

  if (!runtime.audioEngine) {
    log.warn('No audio engine present; cannot load sound asset: ', sound.md5);
    return Promise.resolve(sound);
  }

  return runtime.audioEngine.decodeSoundPlayer(Object.assign({}, sound, {
    data: soundAsset.data
  })).then(soundPlayer => {
    sound.soundId = soundPlayer.id; // Set the sound sample rate and sample count based on the
    // the audio buffer from the audio engine since the sound
    // gets resampled by the audio engine

    const soundBuffer = soundPlayer.buffer;
    sound.rate = soundBuffer.sampleRate;
    sound.sampleCount = soundBuffer.length;

    if (soundBank !== null) {
      soundBank.addSoundPlayer(soundPlayer);
    }

    if (runtime.isPackaged) {
      sound.asset = null;
    }

    return sound;
  });
}; // Handle sound loading errors by replacing the runtime sound with the
// default sound from storage, but keeping track of the original sound metadata
// in a `broken` field


const handleSoundLoadError = function handleSoundLoadError(sound, runtime, soundBank) {
  // Keep track of the old asset information until we're done loading the default sound
  const oldAsset = sound.asset; // could be null

  const oldAssetId = sound.assetId;
  const oldSample = sound.sampleCount;
  const oldRate = sound.rate;
  const oldFormat = sound.format;
  const oldDataFormat = sound.dataFormat; // Use default asset if original fails to load

  sound.assetId = runtime.storage.defaultAssetId.Sound;
  sound.asset = runtime.storage.get(sound.assetId);
  sound.md5 = "".concat(sound.assetId, ".").concat(sound.asset.dataFormat);
  return loadSoundFromAsset(sound, sound.asset, runtime, soundBank).then(loadedSound => {
    loadedSound.broken = {};
    loadedSound.broken.assetId = oldAssetId;
    loadedSound.broken.md5 = "".concat(oldAssetId, ".").concat(oldDataFormat); // Should be null if we got here because the sound was missing

    loadedSound.broken.asset = oldAsset;
    loadedSound.broken.sampleCount = oldSample;
    loadedSound.broken.rate = oldRate;
    loadedSound.broken.format = oldFormat;
    loadedSound.broken.dataFormat = oldDataFormat;
    return loadedSound;
  });
};
/**
 * Load a sound's asset into memory asynchronously.
 * @param {!object} sound - the Scratch sound object.
 * @property {string} md5 - the MD5 and extension of the sound to be loaded.
 * @property {Buffer} data - sound data will be written here once loaded.
 * @param {!Runtime} runtime - Scratch runtime, used to access the storage module.
 * @param {SoundBank} soundBank - Scratch Audio SoundBank to add sounds to.
 * @returns {!Promise} - a promise which will resolve to the sound when ready.
 */


const loadSound = function loadSound(sound, runtime, soundBank) {
  if (!runtime.storage) {
    log.warn('No storage module present; cannot load sound asset: ', sound.md5);
    return Promise.resolve(sound);
  }

  const idParts = StringUtil.splitFirst(sound.md5, '.');
  const md5 = idParts[0];
  const ext = idParts[1].toLowerCase();
  sound.dataFormat = ext;
  return (sound.asset && Promise.resolve(sound.asset) || runtime.storage.load(runtime.storage.AssetType.Sound, md5, ext)).then(soundAsset => {
    sound.asset = soundAsset;

    if (!soundAsset) {
      log.warn('Failed to find sound data: ', sound.md5);
      return handleSoundLoadError(sound, runtime, soundBank);
    }

    return loadSoundFromAsset(sound, soundAsset, runtime, soundBank);
  }).catch(e => {
    log.warn("Failed to load sound: ".concat(sound.md5, " with error: ").concat(e));
    return handleSoundLoadError(sound, runtime, soundBank);
  });
};

module.exports = {
  loadSound,
  loadSoundFromAsset
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/index.js":
/*!**********************************************!*\
  !*** ./node_modules/scratch-vm/src/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const VirtualMachine = __webpack_require__(/*! ./virtual-machine */ "./node_modules/scratch-vm/src/virtual-machine.js");

module.exports = VirtualMachine;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/ble.js":
/*!***********************************************!*\
  !*** ./node_modules/scratch-vm/src/io/ble.js ***!
  \***********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const JSONRPC = __webpack_require__(/*! ../util/jsonrpc */ "./node_modules/scratch-vm/src/util/jsonrpc.js");

class BLE extends JSONRPC {
  /**
   * A BLE peripheral socket object.  It handles connecting, over web sockets, to
   * BLE peripherals, and reading and writing data to them.
   * @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.
   * @param {string} extensionId - the id of the extension using this socket.
   * @param {object} peripheralOptions - the list of options for peripheral discovery.
   * @param {object} connectCallback - a callback for connection.
   * @param {object} resetCallback - a callback for resetting extension state.
   */
  constructor(runtime, extensionId, peripheralOptions, connectCallback) {
    let resetCallback = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
    super();
    this._socket = runtime.getScratchLinkSocket('BLE');

    this._socket.setOnOpen(this.requestPeripheral.bind(this));

    this._socket.setOnClose(this.handleDisconnectError.bind(this));

    this._socket.setOnError(this._handleRequestError.bind(this));

    this._socket.setHandleMessage(this._handleMessage.bind(this));

    this._sendMessage = this._socket.sendMessage.bind(this._socket);
    this._availablePeripherals = {};
    this._connectCallback = connectCallback;
    this._connected = false;
    this._characteristicDidChangeCallback = null;
    this._resetCallback = resetCallback;
    this._discoverTimeoutID = null;
    this._extensionId = extensionId;
    this._peripheralOptions = peripheralOptions;
    this._runtime = runtime;

    this._socket.open();
  }
  /**
   * Request connection to the peripheral.
   * If the web socket is not yet open, request when the socket promise resolves.
   */


  requestPeripheral() {
    this._availablePeripherals = {};

    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    }

    this._discoverTimeoutID = window.setTimeout(this._handleDiscoverTimeout.bind(this), 15000);
    this.sendRemoteRequest('discover', this._peripheralOptions).catch(e => {
      this._handleRequestError(e);
    });
  }
  /**
   * Try connecting to the input peripheral id, and then call the connect
   * callback if connection is successful.
   * @param {number} id - the id of the peripheral to connect to
   */


  connectPeripheral(id) {
    this.sendRemoteRequest('connect', {
      peripheralId: id
    }).then(() => {
      this._connected = true;

      this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);

      this._connectCallback();
    }).catch(e => {
      this._handleRequestError(e);
    });
  }
  /**
   * Close the websocket.
   */


  disconnect() {
    if (this._connected) {
      this._connected = false;
    }

    if (this._socket.isOpen()) {
      this._socket.close();
    }

    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    } // Sets connection status icon to orange


    this._runtime.emit(this._runtime.constructor.PERIPHERAL_DISCONNECTED);
  }
  /**
   * @return {bool} whether the peripheral is connected.
   */


  isConnected() {
    return this._connected;
  }
  /**
   * Start receiving notifications from the specified ble service.
   * @param {number} serviceId - the ble service to read.
   * @param {number} characteristicId - the ble characteristic to get notifications from.
   * @param {object} onCharacteristicChanged - callback for characteristic change notifications.
   * @return {Promise} - a promise from the remote startNotifications request.
   */


  startNotifications(serviceId, characteristicId) {
    let onCharacteristicChanged = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
    const params = {
      serviceId,
      characteristicId
    };
    this._characteristicDidChangeCallback = onCharacteristicChanged;
    return this.sendRemoteRequest('startNotifications', params).catch(e => {
      this.handleDisconnectError(e);
    });
  }
  /**
   * Read from the specified ble service.
   * @param {number} serviceId - the ble service to read.
   * @param {number} characteristicId - the ble characteristic to read.
   * @param {boolean} optStartNotifications - whether to start receiving characteristic change notifications.
   * @param {object} onCharacteristicChanged - callback for characteristic change notifications.
   * @return {Promise} - a promise from the remote read request.
   */


  read(serviceId, characteristicId) {
    let optStartNotifications = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    let onCharacteristicChanged = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
    const params = {
      serviceId,
      characteristicId
    };

    if (optStartNotifications) {
      params.startNotifications = true;
    }

    if (onCharacteristicChanged) {
      this._characteristicDidChangeCallback = onCharacteristicChanged;
    }

    return this.sendRemoteRequest('read', params).catch(e => {
      this.handleDisconnectError(e);
    });
  }
  /**
   * Write data to the specified ble service.
   * @param {number} serviceId - the ble service to write.
   * @param {number} characteristicId - the ble characteristic to write.
   * @param {string} message - the message to send.
   * @param {string} encoding - the message encoding type.
   * @param {boolean} withResponse - if true, resolve after peripheral's response.
   * @return {Promise} - a promise from the remote send request.
   */


  write(serviceId, characteristicId, message) {
    let encoding = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
    let withResponse = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
    const params = {
      serviceId,
      characteristicId,
      message
    };

    if (encoding) {
      params.encoding = encoding;
    }

    if (withResponse !== null) {
      params.withResponse = withResponse;
    }

    return this.sendRemoteRequest('write', params).catch(e => {
      this.handleDisconnectError(e);
    });
  }
  /**
   * Handle a received call from the socket.
   * @param {string} method - a received method label.
   * @param {object} params - a received list of parameters.
   * @return {object} - optional return value.
   */


  didReceiveCall(method, params) {
    switch (method) {
      case 'didDiscoverPeripheral':
        this._availablePeripherals[params.peripheralId] = params;

        this._runtime.emit(this._runtime.constructor.PERIPHERAL_LIST_UPDATE, this._availablePeripherals);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'userDidPickPeripheral':
        this._availablePeripherals[params.peripheralId] = params;

        this._runtime.emit(this._runtime.constructor.USER_PICKED_PERIPHERAL, this._availablePeripherals);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'userDidNotPickPeripheral':
        this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'characteristicDidChange':
        if (this._characteristicDidChangeCallback) {
          this._characteristicDidChangeCallback(params.message);
        }

        break;

      case 'ping':
        return 42;
    }
  }
  /**
   * Handle an error resulting from losing connection to a peripheral.
   *
   * This could be due to:
   * - battery depletion
   * - going out of bluetooth range
   * - being powered down
   *
   * Disconnect the socket, and if the extension using this socket has a
   * reset callback, call it. Finally, emit an error to the runtime.
   */


  handleDisconnectError() {
    // log.error(`BLE error: ${JSON.stringify(e)}`);
    if (!this._connected) return;
    this.disconnect();

    if (this._resetCallback) {
      this._resetCallback();
    }

    this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTION_LOST_ERROR, {
      message: "Scratch lost connection to",
      extensionId: this._extensionId
    });
  }

  _handleRequestError() {
    // log.error(`BLE error: ${JSON.stringify(e)}`);
    this._runtime.emit(this._runtime.constructor.PERIPHERAL_REQUEST_ERROR, {
      message: "Scratch lost connection to",
      extensionId: this._extensionId
    });
  }

  _handleDiscoverTimeout() {
    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    }

    this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);
  }

}

module.exports = BLE;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/bt.js":
/*!**********************************************!*\
  !*** ./node_modules/scratch-vm/src/io/bt.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const JSONRPC = __webpack_require__(/*! ../util/jsonrpc */ "./node_modules/scratch-vm/src/util/jsonrpc.js");

class BT extends JSONRPC {
  /**
   * A BT peripheral socket object.  It handles connecting, over web sockets, to
   * BT peripherals, and reading and writing data to them.
   * @param {Runtime} runtime - the Runtime for sending/receiving GUI update events.
   * @param {string} extensionId - the id of the extension using this socket.
   * @param {object} peripheralOptions - the list of options for peripheral discovery.
   * @param {object} connectCallback - a callback for connection.
   * @param {object} resetCallback - a callback for resetting extension state.
   * @param {object} messageCallback - a callback for message sending.
   */
  constructor(runtime, extensionId, peripheralOptions, connectCallback) {
    let resetCallback = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : null;
    let messageCallback = arguments.length > 5 ? arguments[5] : undefined;
    super();
    this._socket = runtime.getScratchLinkSocket('BT');

    this._socket.setOnOpen(this.requestPeripheral.bind(this));

    this._socket.setOnError(this._handleRequestError.bind(this));

    this._socket.setOnClose(this.handleDisconnectError.bind(this));

    this._socket.setHandleMessage(this._handleMessage.bind(this));

    this._sendMessage = this._socket.sendMessage.bind(this._socket);
    this._availablePeripherals = {};
    this._connectCallback = connectCallback;
    this._connected = false;
    this._characteristicDidChangeCallback = null;
    this._resetCallback = resetCallback;
    this._discoverTimeoutID = null;
    this._extensionId = extensionId;
    this._peripheralOptions = peripheralOptions;
    this._messageCallback = messageCallback;
    this._runtime = runtime;

    this._socket.open();
  }
  /**
   * Request connection to the peripheral.
   * If the web socket is not yet open, request when the socket promise resolves.
   */


  requestPeripheral() {
    this._availablePeripherals = {};

    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    }

    this._discoverTimeoutID = window.setTimeout(this._handleDiscoverTimeout.bind(this), 15000);
    this.sendRemoteRequest('discover', this._peripheralOptions).catch(e => this._handleRequestError(e));
  }
  /**
   * Try connecting to the input peripheral id, and then call the connect
   * callback if connection is successful.
   * @param {number} id - the id of the peripheral to connect to
   * @param {string} pin - an optional pin for pairing
   */


  connectPeripheral(id) {
    let pin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
    const params = {
      peripheralId: id
    };

    if (pin) {
      params.pin = pin;
    }

    this.sendRemoteRequest('connect', params).then(() => {
      this._connected = true;

      this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTED);

      this._connectCallback();
    }).catch(e => {
      this._handleRequestError(e);
    });
  }
  /**
   * Close the websocket.
   */


  disconnect() {
    if (this._connected) {
      this._connected = false;
    }

    if (this._socket.isOpen()) {
      this._socket.close();
    }

    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    } // Sets connection status icon to orange


    this._runtime.emit(this._runtime.constructor.PERIPHERAL_DISCONNECTED);
  }
  /**
   * @return {bool} whether the peripheral is connected.
   */


  isConnected() {
    return this._connected;
  }

  sendMessage(options) {
    return this.sendRemoteRequest('send', options).catch(e => {
      this.handleDisconnectError(e);
    });
  }
  /**
   * Handle a received call from the socket.
   * @param {string} method - a received method label.
   * @param {object} params - a received list of parameters.
   * @return {object} - optional return value.
   */


  didReceiveCall(method, params) {
    // TODO: Add peripheral 'undiscover' handling
    switch (method) {
      case 'didDiscoverPeripheral':
        this._availablePeripherals[params.peripheralId] = params;

        this._runtime.emit(this._runtime.constructor.PERIPHERAL_LIST_UPDATE, this._availablePeripherals);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'userDidPickPeripheral':
        this._availablePeripherals[params.peripheralId] = params;

        this._runtime.emit(this._runtime.constructor.USER_PICKED_PERIPHERAL, this._availablePeripherals);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'userDidNotPickPeripheral':
        this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);

        if (this._discoverTimeoutID) {
          window.clearTimeout(this._discoverTimeoutID);
        }

        break;

      case 'didReceiveMessage':
        this._messageCallback(params); // TODO: refine?


        break;

      default:
        return 'nah';
    }
  }
  /**
   * Handle an error resulting from losing connection to a peripheral.
   *
   * This could be due to:
   * - battery depletion
   * - going out of bluetooth range
   * - being powered down
   *
   * Disconnect the socket, and if the extension using this socket has a
   * reset callback, call it. Finally, emit an error to the runtime.
   */


  handleDisconnectError() {
    // log.error(`BT error: ${JSON.stringify(e)}`);
    if (!this._connected) return;
    this.disconnect();

    if (this._resetCallback) {
      this._resetCallback();
    }

    this._runtime.emit(this._runtime.constructor.PERIPHERAL_CONNECTION_LOST_ERROR, {
      message: "Scratch lost connection to",
      extensionId: this._extensionId
    });
  }

  _handleRequestError() {
    // log.error(`BT error: ${JSON.stringify(e)}`);
    this._runtime.emit(this._runtime.constructor.PERIPHERAL_REQUEST_ERROR, {
      message: "Scratch lost connection to",
      extensionId: this._extensionId
    });
  }

  _handleDiscoverTimeout() {
    if (this._discoverTimeoutID) {
      window.clearTimeout(this._discoverTimeoutID);
    }

    this._runtime.emit(this._runtime.constructor.PERIPHERAL_SCAN_TIMEOUT);
  }

}

module.exports = BT;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/clock.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/clock.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");

class Clock {
  constructor(runtime) {
    this._projectTimer = new Timer({
      now: () => runtime.currentMSecs
    });

    this._projectTimer.start();

    this._pausedTime = null;
    this._paused = false;
    /**
     * Reference to the owning Runtime.
     * @type{!Runtime}
     */

    this.runtime = runtime;
  }

  projectTimer() {
    if (this._paused) {
      return this._pausedTime / 1000;
    }

    return this._projectTimer.timeElapsed() / 1000;
  }

  pause() {
    this._paused = true;
    this._pausedTime = this._projectTimer.timeElapsed();
  }

  resume() {
    this._paused = false;

    const dt = this._projectTimer.timeElapsed() - this._pausedTime;

    this._projectTimer.startTime += dt;
  }

  resetProjectTimer() {
    this._projectTimer.start();
  }

}

module.exports = Clock;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/cloud.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/cloud.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

class Cloud {
  /**
   * @typedef updateVariable
   * @param {string} name The name of the cloud variable to update on the server
   * @param {(string | number)} value The value to update the cloud variable with.
   */

  /**
   * A cloud data provider, responsible for managing the connection to the
   * cloud data server and for posting data about cloud data activity to
   * this IO device.
   * @typedef {object} CloudProvider
   * @property {updateVariable} updateVariable A function which sends a cloud variable
   * update to the cloud data server.
   * @property {Function} requestCloseConnection A function which closes
   * the connection to the cloud data server.
   */

  /**
   * Part of a cloud io data post indicating a cloud variable update.
   * @typedef {object} VarUpdateData
   * @property {string} name The name of the variable to update
   * @property {(number | string)} value The scalar value to update the variable with
   */

  /**
   * A cloud io data post message.
   * @typedef {object} CloudIOData
   * @property {VarUpdateData} varUpdate A {@link VarUpdateData} message indicating
   * a cloud variable update
   */

  /**
   * Cloud IO Device responsible for sending and receiving messages from
   * cloud provider (mananging the cloud server connection) and interacting
   * with cloud variables in the current project.
   * @param {Runtime} runtime The runtime context for this cloud io device.
   */
  constructor(runtime) {
    /**
     * Reference to the cloud data provider, responsible for mananging
     * the web socket connection to the cloud data server.
     * @type {?CloudProvider}
     */
    this.provider = null;
    /**
     * Reference to the runtime that owns this cloud io device.
     * @type {!Runtime}
     */

    this.runtime = runtime;
    /**
     * Reference to the stage target which owns the cloud variables
     * in the project.
     * @type {?Target}
     */

    this.stage = null;
  }
  /**
   * Set a reference to the cloud data provider.
   * @param {CloudProvider} provider The cloud data provider
   */


  setProvider(provider) {
    this.provider = provider;
  }
  /**
   * Set a reference to the stage target which owns the
   * cloud variables in the project.
   * @param {Target} stage The stage target
   */


  setStage(stage) {
    this.stage = stage;
  }
  /**
   * Handle incoming data to this io device.
   * @param {CloudIOData} data The {@link CloudIOData} object to process
   */


  postData(data) {
    if (data.varUpdate) {
      this.updateCloudVariable(data.varUpdate);
    }
  }

  requestCreateVariable(variable) {
    if (this.runtime.canAddCloudVariable()) {
      if (this.provider) {
        this.provider.createVariable(variable.name, variable.value); // We'll set the cloud flag and update the
        // cloud variable limit when we actually
        // get a confirmation from the cloud data server
      }
    } // TODO else track creation for later

  }
  /**
   * Request the cloud data provider to update the given variable with
   * the given value. Does nothing if this io device does not have a provider set.
   * @param {string} name The name of the variable to update
   * @param {string | number} value The value to update the variable with
   */


  requestUpdateVariable(name, value) {
    if (this.provider) {
      this.provider.updateVariable(name, value);
    }
  }
  /**
   * Request the cloud data provider to rename the variable with the given name
   * to the given new name. Does nothing if this io device does not have a provider set.
   * @param {string} oldName The name of the variable to rename
   * @param {string | number} newName The new name for the variable
   */


  requestRenameVariable(oldName, newName) {
    if (this.provider) {
      this.provider.renameVariable(oldName, newName);
    }
  }
  /**
   * Request the cloud data provider to delete the variable with the given name
   * Does nothing if this io device does not have a provider set.
   * @param {string} name The name of the variable to delete
   */


  requestDeleteVariable(name) {
    if (this.provider) {
      this.provider.deleteVariable(name);
    }
  }
  /**
   * Update a cloud variable in the runtime based on the message received
   * from the cloud provider.
   * @param {VarData} varUpdate A {@link VarData} object describing
   * a cloud variable update received from the cloud data provider.
   */


  updateCloudVariable(varUpdate) {
    const varName = varUpdate.name;
    const variable = this.stage.lookupVariableByNameAndType(varName, Variable.SCALAR_TYPE);

    if (!variable || !variable.isCloud) {
      log.warn("Received an update for a cloud variable that does not exist: ".concat(varName));
      return;
    }

    variable.value = varUpdate.value;
  }
  /**
   * Request the cloud data provider to close the web socket connection and
   * clear this io device of references to the cloud data provider and the
   * stage.
   */


  clear() {
    if (!this.provider) return;
    this.provider.requestCloseConnection();
    this.provider = null;
    this.stage = null;
  }

}

module.exports = Cloud;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/keyboard.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/keyboard.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");
/**
 * Names used internally for keys used in scratch, also known as "scratch keys".
 * @enum {string}
 */


const KEY_NAME = {
  SPACE: 'space',
  LEFT: 'left arrow',
  UP: 'up arrow',
  RIGHT: 'right arrow',
  DOWN: 'down arrow',
  ENTER: 'enter',
  // tw: extra keys
  BACKSPACE: 'backspace',
  DELETE: 'delete',
  SHIFT: 'shift',
  CAPS_LOCK: 'caps lock',
  SCROLL_LOCK: 'scroll lock',
  CONTROL: 'control',
  ESCAPE: 'escape',
  INSERT: 'insert',
  HOME: 'home',
  END: 'end',
  PAGE_UP: 'page up',
  PAGE_DOWN: 'page down'
};
/**
 * An array of the names of scratch keys.
 * @type {Array<string>}
 */

const KEY_NAME_LIST = Object.keys(KEY_NAME).map(name => KEY_NAME[name]);

class Keyboard {
  constructor(runtime) {
    /**
     * List of currently pressed scratch keys.
     * A scratch key is:
     * A key you can press on a keyboard, excluding modifier keys.
     * An uppercase string of length one;
     *     except for special key names for arrow keys and space (e.g. 'left arrow').
     * Can be a non-english unicode letter like: æ ø ש נ 手 廿.
     * @type{Array.<string>}
     */
    this._keysPressed = [];
    /**
     * Reference to the owning Runtime.
     * Can be used, for example, to activate hats.
     * @type{!Runtime}
     */

    this.runtime = runtime; // tw: track last pressed key

    this.lastKeyPressed = '';
    this._numeralKeyCodesToStringKey = new Map();
  }
  /**
   * Convert from a keyboard event key name to a Scratch key name.
   * @param  {string} keyString the input key string.
   * @return {string} the corresponding Scratch key, or an empty string.
   */


  _keyStringToScratchKey(keyString) {
    keyString = Cast.toString(keyString); // Convert space and arrow keys to their Scratch key names.

    switch (keyString) {
      case ' ':
        return KEY_NAME.SPACE;

      case 'ArrowLeft':
      case 'Left':
        return KEY_NAME.LEFT;

      case 'ArrowUp':
      case 'Up':
        return KEY_NAME.UP;

      case 'Right':
      case 'ArrowRight':
        return KEY_NAME.RIGHT;

      case 'Down':
      case 'ArrowDown':
        return KEY_NAME.DOWN;

      case 'Enter':
        return KEY_NAME.ENTER;
      // tw: extra keys

      case 'Backspace':
        return KEY_NAME.BACKSPACE;

      case 'Delete':
        return KEY_NAME.DELETE;

      case 'Shift':
        return KEY_NAME.SHIFT;

      case 'CapsLock':
        return KEY_NAME.CAPS_LOCK;

      case 'ScrollLock':
        return KEY_NAME.SCROLL_LOCK;

      case 'Control':
        return KEY_NAME.CONTROL;

      case 'Escape':
        return KEY_NAME.ESCAPE;

      case 'Insert':
        return KEY_NAME.INSERT;

      case 'Home':
        return KEY_NAME.HOME;

      case 'End':
        return KEY_NAME.END;

      case 'PageUp':
        return KEY_NAME.PAGE_UP;

      case 'PageDown':
        return KEY_NAME.PAGE_DOWN;
    } // Ignore modifier keys


    if (keyString.length > 1) {
      return '';
    } // tw: toUpperCase() happens later. We need to track key case.


    return keyString;
  }
  /**
   * Convert from a block argument to a Scratch key name.
   * @param  {string} keyArg the input arg.
   * @return {string} the corresponding Scratch key.
   */


  _keyArgToScratchKey(keyArg) {
    // If a number was dropped in, try to convert from ASCII to Scratch key.
    if (typeof keyArg === 'number') {
      // Check for the ASCII range containing numbers, some punctuation,
      // and uppercase letters.
      if (keyArg >= 48 && keyArg <= 90) {
        return String.fromCharCode(keyArg);
      }

      switch (keyArg) {
        case 32:
          return KEY_NAME.SPACE;

        case 37:
          return KEY_NAME.LEFT;

        case 38:
          return KEY_NAME.UP;

        case 39:
          return KEY_NAME.RIGHT;

        case 40:
          return KEY_NAME.DOWN;
      }
    }

    keyArg = Cast.toString(keyArg); // If the arg matches a special key name, return it.

    if (KEY_NAME_LIST.includes(keyArg)) {
      return keyArg;
    } // Use only the first character.


    if (keyArg.length > 1) {
      keyArg = keyArg[0];
    } // Check for the space character.


    if (keyArg === ' ') {
      return KEY_NAME.SPACE;
    } // tw: support Scratch 2 hacked blocks
    // There are more hacked blocks but most of them get mangled by Scratch 2 -> Scratch 3 conversion


    if (keyArg === '\r') {
      // this probably belongs upstream
      return KEY_NAME.ENTER;
    }

    if (keyArg === '\u001b') {
      return KEY_NAME.ESCAPE;
    }

    return keyArg.toUpperCase();
  }
  /**
   * Keyboard DOM event handler.
   * @param  {object} data Data from DOM event.
   */


  postData(data) {
    if (!data.key) return; // tw: convert single letter keys to uppercase because of changes in _keyStringToScratchKey

    const scratchKeyCased = this._keyStringToScratchKey(data.key);

    const scratchKey = scratchKeyCased.length === 1 ? scratchKeyCased.toUpperCase() : scratchKeyCased;
    if (scratchKey === '') return;

    const index = this._keysPressed.indexOf(scratchKey);

    if (data.isDown) {
      // tw: track last pressed key
      this.lastKeyPressed = scratchKeyCased;
      this.runtime.emit('KEY_PRESSED', scratchKey); // If not already present, add to the list.

      if (index < 0) {
        this._keysPressed.push(scratchKey);
      }
    } else if (index > -1) {
      // If already present, remove from the list.
      this._keysPressed.splice(index, 1);
    } // Fix for https://github.com/LLK/scratch-vm/issues/2271


    if (data.hasOwnProperty('keyCode')) {
      const keyCode = data.keyCode;

      if (this._numeralKeyCodesToStringKey.has(keyCode)) {
        const lastKeyOfSameCode = this._numeralKeyCodesToStringKey.get(keyCode);

        if (lastKeyOfSameCode !== scratchKey) {
          const indexToUnpress = this._keysPressed.indexOf(lastKeyOfSameCode);

          if (indexToUnpress !== -1) {
            this._keysPressed.splice(indexToUnpress, 1);
          }
        }
      }

      this._numeralKeyCodesToStringKey.set(keyCode, scratchKey);
    }
  }
  /**
   * Get key down state for a specified key.
   * @param  {Any} keyArg key argument.
   * @return {boolean} Is the specified key down?
   */


  getKeyIsDown(keyArg) {
    if (keyArg === 'any') {
      return this._keysPressed.length > 0;
    }

    const scratchKey = this._keyArgToScratchKey(keyArg);

    return this._keysPressed.indexOf(scratchKey) > -1;
  } // tw: expose last pressed key


  getLastKeyPressed() {
    return this.lastKeyPressed;
  }

}

module.exports = Keyboard;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/mouse.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/mouse.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const roundToThreeDecimals = number => Math.round(number * 1000) / 1000;

class Mouse {
  constructor(runtime) {
    this._clientX = 0;
    this._clientY = 0;
    this._scratchX = 0;
    this._scratchY = 0;
    this._buttons = new Set();
    this.usesRightClickDown = false;
    this._isDown = false;
    /**
     * Reference to the owning Runtime.
     * Can be used, for example, to activate hats.
     * @type{!Runtime}
     */

    this.runtime = runtime;
  }
  /**
   * Activate "event_whenthisspriteclicked" hats.
   * @param  {Target} target to trigger hats on.
   * @private
   */


  _activateClickHats(target) {
    // Activate both "this sprite clicked" and "stage clicked"
    // They were separated into two opcodes for labeling,
    // but should act the same way.
    // Intentionally not checking isStage to make it work when sharing blocks.
    // @todo the blocks should be converted from one to another when shared
    this.runtime.startHats('event_whenthisspriteclicked', null, target);
    this.runtime.startHats('event_whenstageclicked', null, target);
  }
  /**
   * Find a target by XY location
   * @param  {number} x X position to be sent to the renderer.
   * @param  {number} y Y position to be sent to the renderer.
   * @return {Target} the target at that location
   * @private
   */


  _pickTarget(x, y) {
    if (this.runtime.renderer) {
      const drawableID = this.runtime.renderer.pick(x, y);

      for (let i = 0; i < this.runtime.targets.length; i++) {
        const target = this.runtime.targets[i];

        if (target.hasOwnProperty('drawableID') && target.drawableID === drawableID) {
          return target;
        }
      }
    } // Return the stage if no target was found


    return this.runtime.getTargetForStage();
  }
  /**
   * Mouse DOM event handler.
   * @param  {object} data Data from DOM event.
   */


  postData(data) {
    if (typeof data.x === 'number') {
      this._clientX = data.x;
      this._scratchX = MathUtil.clamp(this.runtime.stageWidth * (data.x / data.canvasWidth - 0.5), -(this.runtime.stageWidth / 2), this.runtime.stageWidth / 2);
    }

    if (typeof data.y === 'number') {
      this._clientY = data.y;
      this._scratchY = MathUtil.clamp(-this.runtime.stageHeight * (data.y / data.canvasHeight - 0.5), -(this.runtime.stageHeight / 2), this.runtime.stageHeight / 2);
    }

    if (typeof data.isDown !== 'undefined') {
      // If no button specified, default to left button for compatibility
      const button = typeof data.button === 'undefined' ? 0 : data.button;

      if (data.isDown) {
        this._buttons.add(button);
      } else {
        this._buttons.delete(button);
      }

      const previousDownState = this._isDown;
      this._isDown = data.isDown; // Do not trigger if down state has not changed

      if (previousDownState === this._isDown) return; // Never trigger click hats at the end of a drag

      if (data.wasDragged) return; // Do not activate click hats for clicks outside canvas bounds

      if (!(data.x > 0 && data.x < data.canvasWidth && data.y > 0 && data.y < data.canvasHeight)) return;

      const target = this._pickTarget(data.x, data.y);

      const isNewMouseDown = !previousDownState && this._isDown;
      const isNewMouseUp = previousDownState && !this._isDown; // Draggable targets start click hats on mouse up.
      // Non-draggable targets start click hats on mouse down.

      if (target.draggable && isNewMouseUp) {
        this._activateClickHats(target);
      } else if (!target.draggable && isNewMouseDown) {
        this._activateClickHats(target);
      }
    }
  }
  /**
   * Get the X position of the mouse in client coordinates.
   * @return {number} Non-clamped X position of the mouse cursor.
   */


  getClientX() {
    return this._clientX;
  }
  /**
   * Get the Y position of the mouse in client coordinates.
   * @return {number} Non-clamped Y position of the mouse cursor.
   */


  getClientY() {
    return this._clientY;
  }
  /**
   * Get the X position of the mouse in scratch coordinates.
   * @return {number} Clamped and integer rounded X position of the mouse cursor.
   */


  getScratchX() {
    if (this.runtime.runtimeOptions.miscLimits) {
      return Math.round(this._scratchX);
    }

    return roundToThreeDecimals(this._scratchX);
  }
  /**
   * Get the Y position of the mouse in scratch coordinates.
   * @return {number} Clamped and integer rounded Y position of the mouse cursor.
   */


  getScratchY() {
    if (this.runtime.runtimeOptions.miscLimits) {
      return Math.round(this._scratchY);
    }

    return roundToThreeDecimals(this._scratchY);
  }
  /**
   * Get the down state of the mouse.
   * @return {boolean} Is the mouse down?
   */


  getIsDown() {
    return this._isDown;
  }
  /**
   * tw: Get the down state of a specific button of the mouse.
   * @param {number} button The ID of the button. 0 = left, 1 = middle, 2 = right
   * @return {boolean} Is the mouse button down?
   */


  getButtonIsDown(button) {
    if (button === 2) {
      this.usesRightClickDown = true;
    }

    return this._buttons.has(button);
  }

}

module.exports = Mouse;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/mouseWheel.js":
/*!******************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/mouseWheel.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class MouseWheel {
  constructor(runtime) {
    /**
     * Reference to the owning Runtime.
     * @type{!Runtime}
     */
    this.runtime = runtime;
  }
  /**
   * Mouse wheel DOM event handler.
   * @param  {object} data Data from DOM event.
   */


  postData(data) {
    const matchFields = {};

    if (data.deltaY < 0) {
      matchFields.KEY_OPTION = 'up arrow';
    } else if (data.deltaY > 0) {
      matchFields.KEY_OPTION = 'down arrow';
    } else {
      return;
    }

    this.runtime.startHats('event_whenkeypressed', matchFields);
  }

}

module.exports = MouseWheel;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/userData.js":
/*!****************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/userData.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class UserData {
  constructor() {
    this._username = '';
  }
  /**
   * Handler for updating the username
   * @param {object} data Data posted to this ioDevice.
   * @property {!string} username The new username.
   */


  postData(data) {
    this._username = data.username;
  }
  /**
   * Getter for username. Initially empty string, until set via postData.
   * @returns {!string} The current username
   */


  getUsername() {
    return this._username;
  }

}

module.exports = UserData;

/***/ }),

/***/ "./node_modules/scratch-vm/src/io/video.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/io/video.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

class Video {
  constructor(runtime) {
    this.runtime = runtime;
    /**
     * @typedef VideoProvider
     * @property {Function} enableVideo - Requests camera access from the user, and upon success,
     * enables the video feed
     * @property {Function} disableVideo - Turns off the video feed
     * @property {Function} getFrame - Return frame data from the video feed in
     * specified dimensions, format, and mirroring.
     */

    this.provider = null;
    /**
     * Id representing a Scratch Renderer skin the video is rendered to for
     * previewing.
     * @type {number}
     */

    this._skinId = -1;
    /**
     * Id for a drawable using the video's skin that will render as a video
     * preview.
     * @type {Drawable}
     */

    this._drawable = -1;
    /**
     * Store the last state of the video transparency ghost effect
     * @type {number}
     */

    this._ghost = 0;
    /**
     * Store a flag that allows the preview to be forced transparent.
     * @type {number}
     */

    this._forceTransparentPreview = false;
  }

  static get FORMAT_IMAGE_DATA() {
    return 'image-data';
  }

  static get FORMAT_CANVAS() {
    return 'canvas';
  }
  /**
   * Dimensions the video stream is analyzed at after its rendered to the
   * sample canvas.
   * @type {Array.<number>}
   */


  static get DIMENSIONS() {
    return [480, 360];
  }
  /**
   * Order preview drawable is inserted at in the renderer.
   * @type {number}
   */


  static get ORDER() {
    return 1;
  }
  /**
   * Set a video provider for this device. A default implementation of
   * a video provider can be found in scratch-gui/src/lib/video/video-provider
   * @param {VideoProvider} provider - Video provider to use
   */


  setProvider(provider) {
    this.provider = provider;
  }
  /**
   * Request video be enabled.  Sets up video, creates video skin and enables preview.
   *
   * ioDevices.video.requestVideo()
   *
   * @return {Promise.<Video>} resolves a promise to this IO device when video is ready.
   */


  enableVideo() {
    if (!this.provider) return null;
    return this.provider.enableVideo().then(() => this._setupPreview());
  }
  /**
   * Disable video stream (turn video off)
   * @return {void}
   */


  disableVideo() {
    this._disablePreview();

    if (!this.provider) return null;
    this.provider.disableVideo();
  }
  /**
   * Return frame data from the video feed in a specified dimensions, format, and mirroring.
   *
   * @param {object} frameInfo A descriptor of the frame you would like to receive.
   * @param {Array.<number>} frameInfo.dimensions [width, height] array of numbers.  Defaults to [480,360]
   * @param {boolean} frameInfo.mirror If you specificly want a mirror/non-mirror frame, defaults to the global
   *                                   mirror state (ioDevices.video.mirror)
   * @param {string} frameInfo.format Requested video format, available formats are 'image-data' and 'canvas'.
   * @param {number} frameInfo.cacheTimeout Will reuse previous image data if the time since capture is less than
   *                                        the cacheTimeout.  Defaults to 16ms.
   *
   * @return {ArrayBuffer|Canvas|string|null} Frame data in requested format, null when errors.
   */


  getFrame(_ref) {
    let {
      dimensions = Video.DIMENSIONS,
      mirror = this.mirror,
      format = Video.FORMAT_IMAGE_DATA,
      cacheTimeout = this._frameCacheTimeout
    } = _ref;
    if (this.provider) return this.provider.getFrame({
      dimensions,
      mirror,
      format,
      cacheTimeout
    });
    return null;
  }
  /**
   * Set the preview ghost effect
   * @param {number} ghost from 0 (visible) to 100 (invisible) - ghost effect
   */


  setPreviewGhost(ghost) {
    this._ghost = ghost; // Confirm that the default value has been changed to a valid id for the drawable

    if (this._drawable !== -1) {
      this.runtime.renderer.updateDrawableEffect(this._drawable, 'ghost', this._forceTransparentPreview ? 100 : ghost);
    }
  }

  _disablePreview() {
    if (this._skinId !== -1) {
      this.runtime.renderer.updateBitmapSkin(this._skinId, new ImageData(...Video.DIMENSIONS), 1);
      this.runtime.renderer.updateDrawableVisible(this._drawable, false);
    }

    this._renderPreviewFrame = null;
  }

  _setupPreview() {
    const {
      renderer
    } = this.runtime;
    if (!renderer) return;

    if (this._skinId === -1 && this._drawable === -1) {
      this._skinId = renderer.createBitmapSkin(new ImageData(...Video.DIMENSIONS), 1);
      this._drawable = renderer.createDrawable(StageLayering.VIDEO_LAYER);
      renderer.updateDrawableSkinId(this._drawable, this._skinId); // TW: Video probably contains the user's face. This is private information.
      // This API won't exist if we're using a vanilla scratch-render

      if (renderer.markSkinAsPrivate) {
        renderer.markSkinAsPrivate(this._skinId);
      }
    } // if we haven't already created and started a preview frame render loop, do so


    if (!this._renderPreviewFrame) {
      renderer.updateDrawableEffect(this._drawable, 'ghost', this._forceTransparentPreview ? 100 : this._ghost);
      renderer.updateDrawableVisible(this._drawable, true);

      this._renderPreviewFrame = () => {
        clearTimeout(this._renderPreviewTimeout);

        if (!this._renderPreviewFrame) {
          return;
        }

        this._renderPreviewTimeout = setTimeout(this._renderPreviewFrame, this.runtime.currentStepTime);
        const imageData = this.getFrame({
          format: Video.FORMAT_IMAGE_DATA,
          cacheTimeout: this.runtime.currentStepTime
        });

        if (!imageData) {
          renderer.updateBitmapSkin(this._skinId, new ImageData(...Video.DIMENSIONS), 1);
          return;
        }

        renderer.updateBitmapSkin(this._skinId, imageData, 1);
        this.runtime.requestRedraw();
      };

      this._renderPreviewFrame();
    }
  }

  get videoReady() {
    if (this.provider) return this.provider.videoReady;
    return false;
  }
  /**
   * Method implemented by all IO devices to allow external changes.
   * The only change available externally is hiding the preview, used e.g. to
   * prevent drawing the preview into project thumbnails.
   * @param {object} - data passed to this IO device.
   * @property {boolean} forceTransparentPreview - whether the preview should be forced transparent.
   */


  postData(_ref2) {
    let {
      forceTransparentPreview
    } = _ref2;
    this._forceTransparentPreview = forceTransparentPreview; // Setting the ghost to the current value will pick up the forceTransparentPreview
    // flag and override the current ghost. The complexity is to prevent blocks
    // from overriding forceTransparentPreview

    this.setPreviewGhost(this._ghost);
  }

}

module.exports = Video;

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/deserialize-assets.js":
/*!*************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/deserialize-assets.js ***!
  \*************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const JSZip = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Deserializes sound from file into storage cache so that it can
 * be loaded into the runtime.
 * @param {object} sound Descriptor for sound from sb3 file
 * @param {Runtime} runtime The runtime containing the storage to cache the sounds in
 * @param {JSZip} zip The zip containing the sound file being described by `sound`
 * @param {string} assetFileName Optional file name for the given asset
 * (sb2 files have filenames of the form [int].[ext],
 * sb3 files have filenames of the form [md5].[ext])
 * @return {Promise} Promise that resolves after the described sound has been stored
 * into the runtime storage cache, the sound was already stored, or an error has
 * occurred.
 */


const deserializeSound = function deserializeSound(sound, runtime, zip, assetFileName) {
  const fileName = assetFileName ? assetFileName : sound.md5;
  const storage = runtime.storage;

  if (!storage) {
    log.warn('No storage module present; cannot load sound asset: ', fileName);
    return Promise.resolve(null);
  }

  if (!zip) {
    // Zip will not be provided if loading project json from server
    return Promise.resolve(null);
  }

  let soundFile = zip.file(fileName);

  if (!soundFile) {
    // look for assetfile in a flat list of files, or in a folder
    const fileMatch = new RegExp("^([^/]*/)?".concat(fileName, "$"));
    soundFile = zip.file(fileMatch)[0]; // use first matching file
  }

  if (!soundFile) {
    log.error("Could not find sound file associated with the ".concat(sound.name, " sound."));
    return Promise.resolve(null);
  }

  if (!JSZip.support.uint8array) {
    log.error('JSZip uint8array is not supported in this browser.');
    return Promise.resolve(null);
  }

  const dataFormat = sound.dataFormat.toLowerCase() === 'mp3' ? storage.DataFormat.MP3 : storage.DataFormat.WAV;
  return soundFile.async('uint8array').then(data => storage.createAsset(storage.AssetType.Sound, dataFormat, data, null, true)).then(asset => {
    sound.asset = asset;
    sound.assetId = asset.assetId;
    sound.md5 = "".concat(asset.assetId, ".").concat(asset.dataFormat);
  });
};
/**
 * Deserializes costume from file into storage cache so that it can
 * be loaded into the runtime.
 * @param {object} costume Descriptor for costume from sb3 file
 * @param {Runtime} runtime The runtime containing the storage to cache the costumes in
 * @param {JSZip} zip The zip containing the costume file being described by `costume`
 * @param {string} assetFileName Optional file name for the given asset
 * (sb2 files have filenames of the form [int].[ext],
 * sb3 files have filenames of the form [md5].[ext])
 * @param {string} textLayerFileName Optional file name for the given asset's text layer
 * (sb2 only; files have filenames of the form [int].png)
 * @return {Promise} Promise that resolves after the described costume has been stored
 * into the runtime storage cache, the costume was already stored, or an error has
 * occurred.
 */


const deserializeCostume = function deserializeCostume(costume, runtime, zip, assetFileName, textLayerFileName) {
  const storage = runtime.storage;
  const assetId = costume.assetId;
  const fileName = assetFileName ? assetFileName : "".concat(assetId, ".").concat(costume.dataFormat);

  if (!storage) {
    log.warn('No storage module present; cannot load costume asset: ', fileName);
    return Promise.resolve(null);
  }

  if (costume.asset) {
    // When uploading a sprite from an image file, the asset data will be provided
    // @todo Cache the asset data somewhere and pull it out here
    return Promise.resolve(storage.createAsset(costume.asset.assetType, costume.asset.dataFormat, new Uint8Array(Object.keys(costume.asset.data).map(key => costume.asset.data[key])), null, true)).then(asset => {
      costume.asset = asset;
      costume.assetId = asset.assetId;
      costume.md5 = "".concat(asset.assetId, ".").concat(asset.dataFormat);
    });
  }

  if (!zip) {
    // Zip will not be provided if loading project json from server
    return Promise.resolve(null);
  }

  let costumeFile = zip.file(fileName);

  if (!costumeFile) {
    // look for assetfile in a flat list of files, or in a folder
    const fileMatch = new RegExp("^([^/]*/)?".concat(fileName, "$"));
    costumeFile = zip.file(fileMatch)[0]; // use the first matched file
  }

  if (!costumeFile) {
    log.error("Could not find costume file associated with the ".concat(costume.name, " costume."));
    return Promise.resolve(null);
  }

  let assetType = null;
  const costumeFormat = costume.dataFormat.toLowerCase();

  if (costumeFormat === 'svg') {
    assetType = storage.AssetType.ImageVector;
  } else if (['png', 'bmp', 'jpeg', 'jpg', 'gif'].indexOf(costumeFormat) >= 0) {
    assetType = storage.AssetType.ImageBitmap;
  } else {
    log.error("Unexpected file format for costume: ".concat(costumeFormat));
  }

  if (!JSZip.support.uint8array) {
    log.error('JSZip uint8array is not supported in this browser.');
    return Promise.resolve(null);
  } // textLayerMD5 exists if there is a text layer, which is a png of text from Scratch 1.4
  // that was opened in Scratch 2.0. In this case, set costume.textLayerAsset.


  let textLayerFilePromise;

  if (costume.textLayerMD5) {
    const textLayerFile = zip.file(textLayerFileName);

    if (!textLayerFile) {
      log.error("Could not find text layer file associated with the ".concat(costume.name, " costume."));
      return Promise.resolve(null);
    }

    textLayerFilePromise = textLayerFile.async('uint8array').then(data => storage.createAsset(storage.AssetType.ImageBitmap, 'png', data, costume.textLayerMD5)).then(asset => {
      costume.textLayerAsset = asset;
    });
  } else {
    textLayerFilePromise = Promise.resolve(null);
  }

  return Promise.all([textLayerFilePromise, costumeFile.async('uint8array').then(data => storage.createAsset(assetType, // TODO eventually we want to map non-png's to their actual file types?
  costumeFormat, data, null, true)).then(asset => {
    costume.asset = asset;
    costume.assetId = asset.assetId;
    costume.md5 = "".concat(asset.assetId, ".").concat(asset.dataFormat);
  })]);
};

module.exports = {
  deserializeSound,
  deserializeCostume
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/sb2.js":
/*!**********************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/sb2.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * Partial implementation of an SB2 JSON importer.
 * Parses provided JSON and then generates all needed
 * scratch-vm runtime structures.
 */
const Blocks = __webpack_require__(/*! ../engine/blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

const RenderedTarget = __webpack_require__(/*! ../sprites/rendered-target */ "./node_modules/scratch-vm/src/sprites/rendered-target.js");

const Sprite = __webpack_require__(/*! ../sprites/sprite */ "./node_modules/scratch-vm/src/sprites/sprite.js");

const Color = __webpack_require__(/*! ../util/color */ "./node_modules/scratch-vm/src/util/color.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const specMap = __webpack_require__(/*! ./sb2_specmap */ "./node_modules/scratch-vm/src/serialization/sb2_specmap.js");

const Comment = __webpack_require__(/*! ../engine/comment */ "./node_modules/scratch-vm/src/engine/comment.js");

const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const MonitorRecord = __webpack_require__(/*! ../engine/monitor-record */ "./node_modules/scratch-vm/src/engine/monitor-record.js");

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

const ScratchXUtilities = __webpack_require__(/*! ../extension-support/tw-scratchx-utilities */ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js");

const {
  loadCostume
} = __webpack_require__(/*! ../import/load-costume.js */ "./node_modules/scratch-vm/src/import/load-costume.js");

const {
  loadSound
} = __webpack_require__(/*! ../import/load-sound.js */ "./node_modules/scratch-vm/src/import/load-sound.js");

const {
  deserializeCostume,
  deserializeSound
} = __webpack_require__(/*! ./deserialize-assets.js */ "./node_modules/scratch-vm/src/serialization/deserialize-assets.js"); // Constants used during deserialization of an SB2 file


const CORE_EXTENSIONS = ['argument', 'control', 'data', 'event', 'looks', 'math', 'motion', 'operator', 'procedures', 'sensing', 'sound']; // Adjust script coordinates to account for
// larger block size in scratch-blocks.
// @todo: Determine more precisely the right formulas here.

const WORKSPACE_X_SCALE = 1.5;
const WORKSPACE_Y_SCALE = 2.2; // By examining ScratchX projects, we've found that ScratchX can use either "\u001f" or "."
// to separate the extension name from the extension method opcode eg. "Text To Speech.say"
// eslint-disable-next-line no-control-regex

const SCRATCHX_OPCODE_SEPARATOR = /\u001f|\./;
/**
 * @param {string} opcode
 * @returns {boolean}
 */

const isPossiblyScratchXBlock = opcode => SCRATCHX_OPCODE_SEPARATOR.test(opcode);
/**
 * @param {string} opcode
 * @returns {string}
 */


const mapScratchXOpcode = opcode => {
  const [extensionName, extensionMethod] = opcode.split(SCRATCHX_OPCODE_SEPARATOR);
  const newOpcodeBase = ScratchXUtilities.generateExtensionId(extensionName);
  return "".concat(newOpcodeBase, "_").concat(extensionMethod);
};
/**
 * @param {object} block
 * @returns {object}
 */


const mapScratchXBlock = block => {
  const opcode = block[0];
  const argumentCount = block.length - 1;
  const args = [];

  for (let i = 0; i < argumentCount; i++) {
    args.push({
      type: 'input',
      inputOp: 'text',
      inputName: ScratchXUtilities.argumentIndexToId(i)
    });
  }

  return {
    opcode: mapScratchXOpcode(opcode),
    argMap: args
  };
};
/**
 * Convert a Scratch 2.0 procedure string (e.g., "my_procedure %s %b %n")
 * into an argument map. This allows us to provide the expected inputs
 * to a mutated procedure call.
 * @param {string} procCode Scratch 2.0 procedure string.
 * @return {object} Argument map compatible with those in sb2specmap.
 */


const parseProcedureArgMap = function parseProcedureArgMap(procCode) {
  const argMap = [{} // First item in list is op string.
  ];
  const INPUT_PREFIX = 'input';
  let inputCount = 0; // Split by %n, %b, %s.

  const parts = procCode.split(/(?=[^\\]%[nbs])/);

  for (let i = 0; i < parts.length; i++) {
    const part = parts[i].trim();

    if (part.substring(0, 1) === '%') {
      const argType = part.substring(1, 2);
      const arg = {
        type: 'input',
        inputName: INPUT_PREFIX + inputCount++
      };

      if (argType === 'n') {
        arg.inputOp = 'math_number';
      } else if (argType === 's') {
        arg.inputOp = 'text';
      } else if (argType === 'b') {
        arg.inputOp = 'boolean';
      }

      argMap.push(arg);
    }
  }

  return argMap;
};
/**
 * Generate a list of "argument IDs" for procdefs and caller mutations.
 * IDs just end up being `input0`, `input1`, ... which is good enough.
 * @param {string} procCode Scratch 2.0 procedure string.
 * @return {Array.<string>} Array of argument id strings.
 */


const parseProcedureArgIds = function parseProcedureArgIds(procCode) {
  return parseProcedureArgMap(procCode).map(arg => arg.inputName).filter(name => name); // Filter out unnamed inputs which are labels
};
/**
 * Flatten a block tree into a block list.
 * Children are temporarily stored on the `block.children` property.
 * @param {Array.<object>} blocks list generated by `parseBlockList`.
 * @return {Array.<object>} Flattened list to be passed to `blocks.createBlock`.
 */


const flatten = function flatten(blocks) {
  let finalBlocks = [];

  for (let i = 0; i < blocks.length; i++) {
    const block = blocks[i];
    finalBlocks.push(block);

    if (block.children) {
      finalBlocks = finalBlocks.concat(flatten(block.children));
    }

    delete block.children;
  }

  return finalBlocks;
};
/**
 * Parse any list of blocks from SB2 JSON into a list of VM-format blocks.
 * Could be used to parse a top-level script,
 * a list of blocks in a branch (e.g., in forever),
 * or a list of blocks in an argument (e.g., move [pick random...]).
 * @param {Array.<object>} blockList SB2 JSON-format block list.
 * @param {Function} addBroadcastMsg function to update broadcast message name map
 * @param {Function} getVariableId function to retreive a variable's ID based on name
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 * @param {ParseState} parseState - info on the state of parsing beyond the current block.
 * @param {object<int, Comment>} comments - Comments from sb2 project that need to be attached to blocks.
 * They are indexed in this object by the sb2 flattened block list index indicating
 * which block they should attach to.
 * @param {int} commentIndex The current index of the top block in this list if it were in a flattened
 * list of all blocks for the target
 * @return {Array<Array.<object>|int>} Tuple where first item is the Scratch VM-format block list, and
 * second item is the updated comment index
 */


const parseBlockList = function parseBlockList(blockList, addBroadcastMsg, getVariableId, extensions, parseState, comments, commentIndex) {
  const resultingList = [];
  let previousBlock = null; // For setting next.

  for (let i = 0; i < blockList.length; i++) {
    const block = blockList[i]; // eslint-disable-next-line no-use-before-define

    const parsedBlockAndComments = parseBlock(block, addBroadcastMsg, getVariableId, extensions, parseState, comments, commentIndex);
    const parsedBlock = parsedBlockAndComments[0]; // Update commentIndex

    commentIndex = parsedBlockAndComments[1];
    if (!parsedBlock) continue;

    if (previousBlock) {
      parsedBlock.parent = previousBlock.id;
      previousBlock.next = parsedBlock.id;
    }

    previousBlock = parsedBlock;
    resultingList.push(parsedBlock);
  }

  return [resultingList, commentIndex];
};
/**
 * Parse a Scratch object's scripts into VM blocks.
 * This should only handle top-level scripts that include X, Y coordinates.
 * @param {!object} scripts Scripts object from SB2 JSON.
 * @param {!Blocks} blocks Blocks object to load parsed blocks into.
 * @param {Function} addBroadcastMsg function to update broadcast message name map
 * @param {Function} getVariableId function to retreive a variable's ID based on name
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 * @param {object} comments Comments that need to be attached to the blocks that need to be parsed
 */


const parseScripts = function parseScripts(scripts, blocks, addBroadcastMsg, getVariableId, extensions, comments) {
  // Keep track of the index of the current script being
  // parsed in order to attach block comments correctly
  let scriptIndexForComment = 0;

  for (let i = 0; i < scripts.length; i++) {
    const script = scripts[i];
    const scriptX = script[0];
    const scriptY = script[1];
    const blockList = script[2];
    const parseState = {};
    const [parsedBlockList, newCommentIndex] = parseBlockList(blockList, addBroadcastMsg, getVariableId, extensions, parseState, comments, scriptIndexForComment);
    scriptIndexForComment = newCommentIndex;

    if (parsedBlockList[0]) {
      parsedBlockList[0].x = scriptX * WORKSPACE_X_SCALE;
      parsedBlockList[0].y = scriptY * WORKSPACE_Y_SCALE;
      parsedBlockList[0].topLevel = true;
      parsedBlockList[0].parent = null;
    } // Flatten children and create add the blocks.


    const convertedBlocks = flatten(parsedBlockList);

    for (let j = 0; j < convertedBlocks.length; j++) {
      blocks.createBlock(convertedBlocks[j]);
    }
  }
};
/**
 * Create a callback for assigning fixed IDs to imported variables
 * Generator stores the global variable mapping in a closure
 * @param {!string} targetId the id of the target to scope the variable to
 * @return {string} variable ID
 */


const generateVariableIdGetter = function () {
  let globalVariableNameMap = {};

  const namer = (targetId, name, type) => "".concat(targetId, "-").concat(StringUtil.replaceUnsafeChars(name), "-").concat(type);

  return function (targetId, topLevel) {
    // Reset the global variable map if topLevel
    if (topLevel) globalVariableNameMap = {};
    return function (name, type) {
      if (topLevel) {
        // Store the name/id pair in the globalVariableNameMap
        globalVariableNameMap["".concat(name, "-").concat(type)] = namer(targetId, name, type);
        return globalVariableNameMap["".concat(name, "-").concat(type)];
      } // Not top-level, so first check the global name map


      if (globalVariableNameMap["".concat(name, "-").concat(type)]) return globalVariableNameMap["".concat(name, "-").concat(type)];
      return namer(targetId, name, type);
    };
  };
}();

const globalBroadcastMsgStateGenerator = function () {
  let broadcastMsgNameMap = {};
  const allBroadcastFields = [];
  const emptyStringName = uid();
  return function (topLevel) {
    if (topLevel) broadcastMsgNameMap = {};
    return {
      broadcastMsgMapUpdater: function broadcastMsgMapUpdater(name, field) {
        name = name.toLowerCase();

        if (name === '') {
          name = emptyStringName;
        }

        broadcastMsgNameMap[name] = "broadcastMsgId-".concat(StringUtil.replaceUnsafeChars(name));
        allBroadcastFields.push(field);
        return broadcastMsgNameMap[name];
      },
      globalBroadcastMsgs: broadcastMsgNameMap,
      allBroadcastFields: allBroadcastFields,
      emptyMsgName: emptyStringName
    };
  };
}();
/**
 * Parse a single monitor object and create all its in-memory VM objects.
 *
 * It is important that monitors are parsed last,
 * - after all sprite targets have finished parsing, and
 * - after the rest of the stage has finished parsing.
 *
 * It is specifically important that all the scripts in the project
 * have been parsed and all the relevant targets exist, have uids,
 * and have their variables initialized.
 * Calling this function before these things are true, will result in
 * undefined behavior.
 * @param {!object} object - From-JSON "Monitor object"
 * @param {!Runtime} runtime - (in/out) Runtime object to load monitor info into.
 * @param {!Array.<Target>} targets - Targets have already been parsed.
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 */


const parseMonitorObject = (object, runtime, targets, extensions) => {
  // If we can't find the block in the spec map, ignore it.
  // This happens for things like Lego Wedo 1.0 monitors.
  const mapped = specMap[object.cmd];

  if (!mapped) {
    log.warn("Could not find monitor block with opcode: ".concat(object.cmd));
    return;
  } // In scratch 2.0, there are two monitors that now correspond to extension
  // blocks (tempo and video motion/direction). In the case of the
  // video motion/direction block, this reporter is not monitorable in Scratch 3.0.
  // In the case of the tempo block, we should import it and load the music extension
  // only when the monitor is actually visible.


  const opcode = specMap[object.cmd].opcode;
  const extIndex = opcode.indexOf('_');
  const extID = opcode.substring(0, extIndex);

  if (extID === 'videoSensing') {
    return;
  } else if (CORE_EXTENSIONS.indexOf(extID) === -1 && extID !== '' && !extensions.extensionIDs.has(extID) && !object.visible) {
    // Don't import this monitor if it refers to a non-core extension that
    // doesn't exist anywhere else in the project and it isn't visible.
    // This should only apply to the tempo block at this point since
    // there are no other sb2 blocks that are now extension monitors.
    return;
  }

  let target = null; // List blocks don't come in with their target name set.
  // Find the target by searching for a target with matching variable name/type.

  if (!object.hasOwnProperty('target')) {
    for (let i = 0; i < targets.length; i++) {
      const currTarget = targets[i];
      const listVariables = Object.keys(currTarget.variables).filter(key => {
        const variable = currTarget.variables[key];
        return variable.type === Variable.LIST_TYPE && variable.name === object.listName;
      });

      if (listVariables.length > 0) {
        target = currTarget; // Keep this target for later use

        object.target = currTarget.getName(); // Set target name to normalize with other monitors
      }
    }
  } // Get the target for this monitor, if not gotten above.


  target = target || targets.filter(t => t.getName() === object.target)[0];
  if (!target) throw new Error('Cannot create monitor for target that cannot be found by name'); // Create var id getter to make block naming/parsing easier, variables already created.

  const getVariableId = generateVariableIdGetter(target.id, false); // eslint-disable-next-line no-use-before-define

  const [block, _] = parseBlock([object.cmd, object.param], // Scratch 2 monitor blocks only have one param.
  null, // `addBroadcastMsg`, not needed for monitor blocks.
  getVariableId, extensions, {}, null, // `comments`, not needed for monitor blocks
  null // `commentIndex`, not needed for monitor blocks
  ); // Monitor blocks have special IDs to match the toolbox obtained from the getId
  // function in the runtime.monitorBlocksInfo. Variable monitors, however,
  // get their IDs from the variable id they reference.

  if (object.cmd === 'getVar:') {
    block.id = getVariableId(object.param, Variable.SCALAR_TYPE);
  } else if (object.cmd === 'contentsOfList:') {
    block.id = getVariableId(object.param, Variable.LIST_TYPE);
  } else if (runtime.monitorBlockInfo.hasOwnProperty(block.opcode)) {
    block.id = runtime.monitorBlockInfo[block.opcode].getId(target.id, block.fields);
  } else {
    // If the opcode can't be found in the runtime monitorBlockInfo,
    // then default to using the block opcode as the id instead.
    // This is for extension monitors, and assumes that extension monitors
    // cannot be sprite specific.
    block.id = block.opcode;
  } // Block needs a targetId if it is targetting something other than the stage


  block.targetId = target.isStage ? null : target.id; // Property required for running monitored blocks.

  block.isMonitored = object.visible;
  const existingMonitorBlock = runtime.monitorBlocks._blocks[block.id];

  if (existingMonitorBlock) {
    // A monitor block already exists if the toolbox has been loaded and
    // the monitor block is not target specific (because the block gets recycled).
    // Update the existing block with the relevant monitor information.
    existingMonitorBlock.isMonitored = object.visible;
    existingMonitorBlock.targetId = block.targetId;
  } else {
    // Blocks can be created with children, flatten and add to monitorBlocks.
    const newBlocks = flatten([block]);

    for (let i = 0; i < newBlocks.length; i++) {
      runtime.monitorBlocks.createBlock(newBlocks[i]);
    }
  } // Convert numbered mode into strings for better understandability.


  switch (object.mode) {
    case 1:
      object.mode = 'default';
      break;

    case 2:
      object.mode = 'large';
      break;

    case 3:
      object.mode = 'slider';
      break;
  } // Create a monitor record for the runtime's monitorState


  runtime.requestAddMonitor(MonitorRecord({
    id: block.id,
    targetId: block.targetId,
    spriteName: block.targetId ? object.target : null,
    opcode: block.opcode,
    params: runtime.monitorBlocks._getBlockParams(block),
    value: '',
    mode: object.mode,
    sliderMin: object.sliderMin,
    sliderMax: object.sliderMax,
    isDiscrete: object.isDiscrete,
    x: object.x,
    y: object.y,
    width: object.width,
    height: object.height,
    visible: object.visible
  }));
};
/**
 * Parse the assets of a single "Scratch object" and load them. This
 * preprocesses objects to support loading the data for those assets over a
 * network while the objects are further processed into Blocks, Sprites, and a
 * list of needed Extensions.
 * @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime - Runtime object to load all structures into.
 * @param {boolean} topLevel - Whether this is the top-level object (stage).
 * @param {?object} zip - Optional zipped assets for local file import
 * @return {?{costumePromises:Array.<Promise>,soundPromises:Array.<Promise>,soundBank:SoundBank,children:object}}
 *   Object of arrays of promises and child objects for asset objects used in
 *   Sprites. As well as a SoundBank for the sound assets. null for unsupported
 *   objects.
 */


const parseScratchAssets = function parseScratchAssets(object, runtime, topLevel, zip) {
  if (!object.hasOwnProperty('objName')) {
    // Skip parsing monitors. Or any other objects missing objName.
    return null;
  }

  const assets = {
    costumePromises: [],
    soundPromises: [],
    soundBank: runtime.audioEngine && runtime.audioEngine.createBank(),
    children: []
  }; // Costumes from JSON.

  const costumePromises = assets.costumePromises;

  if (object.hasOwnProperty('costumes')) {
    for (let i = 0; i < object.costumes.length; i++) {
      const costumeSource = object.costumes[i];
      const bitmapResolution = costumeSource.bitmapResolution || 1;
      const costume = {
        name: costumeSource.costumeName,
        bitmapResolution: bitmapResolution,
        rotationCenterX: topLevel ? 240 * bitmapResolution : costumeSource.rotationCenterX,
        rotationCenterY: topLevel ? 180 * bitmapResolution : costumeSource.rotationCenterY,
        // TODO we eventually want this next property to be called
        // md5ext to reflect what it actually contains, however this
        // will be a very extensive change across many repositories
        // and should be done carefully and altogether
        md5: costumeSource.baseLayerMD5,
        skinId: null
      };
      const md5ext = costumeSource.baseLayerMD5;
      const idParts = StringUtil.splitFirst(md5ext, '.');
      const md5 = idParts[0];
      let ext;

      if (idParts.length === 2 && idParts[1]) {
        ext = idParts[1];
      } else {
        // Default to 'png' if baseLayerMD5 is not formatted correctly
        ext = 'png'; // Fix costume md5 for later

        costume.md5 = "".concat(costume.md5, ".").concat(ext);
      }

      costume.dataFormat = ext;
      costume.assetId = md5;

      if (costumeSource.textLayerMD5) {
        costume.textLayerMD5 = StringUtil.splitFirst(costumeSource.textLayerMD5, '.')[0];
      } // If there is no internet connection, or if the asset is not in storage
      // for some reason, and we are doing a local .sb2 import, (e.g. zip is provided)
      // the file name of the costume should be the baseLayerID followed by the file ext


      const assetFileName = "".concat(costumeSource.baseLayerID, ".").concat(ext);
      const textLayerFileName = costumeSource.textLayerID ? "".concat(costumeSource.textLayerID, ".png") : null;
      costumePromises.push(deserializeCostume(costume, runtime, zip, assetFileName, textLayerFileName).then(() => loadCostume(costume.md5, costume, runtime, 2
      /* optVersion */
      )));
    }
  } // Sounds from JSON


  const {
    soundBank,
    soundPromises
  } = assets;

  if (object.hasOwnProperty('sounds')) {
    for (let s = 0; s < object.sounds.length; s++) {
      const soundSource = object.sounds[s];
      const sound = {
        name: soundSource.soundName,
        format: soundSource.format,
        rate: soundSource.rate,
        sampleCount: soundSource.sampleCount,
        // TODO we eventually want this next property to be called
        // md5ext to reflect what it actually contains, however this
        // will be a very extensive change across many repositories
        // and should be done carefully and altogether
        // (for example, the audio engine currently relies on this
        // property to be named 'md5')
        md5: soundSource.md5,
        data: null
      };
      const md5ext = soundSource.md5;
      const idParts = StringUtil.splitFirst(md5ext, '.');
      const md5 = idParts[0];
      const ext = idParts[1].toLowerCase();
      sound.dataFormat = ext;
      sound.assetId = md5; // If there is no internet connection, or if the asset is not in storage
      // for some reason, and we are doing a local .sb2 import, (e.g. zip is provided)
      // the file name of the sound should be the soundID (provided from the project.json)
      // followed by the file ext

      const assetFileName = "".concat(soundSource.soundID, ".").concat(ext);
      soundPromises.push(deserializeSound(sound, runtime, zip, assetFileName).then(() => loadSound(sound, runtime, soundBank)));
    }
  } // The stage will have child objects; recursively process them.


  const childrenAssets = assets.children;

  if (object.children) {
    for (let m = 0; m < object.children.length; m++) {
      childrenAssets.push(parseScratchAssets(object.children[m], runtime, false, zip));
    }
  }

  return assets;
};
/**
 * Parse a single "Scratch object" and create all its in-memory VM objects.
 * TODO: parse the "info" section, especially "savedExtensions"
 * @param {!object} object - From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime - Runtime object to load all structures into.
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 * @param {boolean} topLevel - Whether this is the top-level object (stage).
 * @param {?object} zip - Optional zipped assets for local file import
 * @param {object} assets - Promises for assets of this scratch object grouped
 *   into costumes and sounds
 * @return {!Promise.<Array.<Target>>} Promise for the loaded targets when ready, or null for unsupported objects.
 */


const parseScratchObject = function parseScratchObject(object, runtime, extensions, topLevel, zip, assets) {
  if (!object.hasOwnProperty('objName')) {
    if (object.hasOwnProperty('listName')) {
      // Shim these objects so they can be processed as monitors
      object.cmd = 'contentsOfList:';
      object.param = object.listName;
      object.mode = 'list';
    } // Defer parsing monitors until targets are all parsed


    object.deferredMonitor = true;
    return Promise.resolve(object);
  } // Blocks container for this object.


  const blocks = new Blocks(runtime); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.

  const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON.

  if (object.hasOwnProperty('objName')) {
    if (topLevel && object.objName !== 'Stage') {
      for (const child of object.children) {
        if (!child.hasOwnProperty('objName') && child.target === object.objName) {
          child.target = 'Stage';
        }
      }

      object.objName = 'Stage';
    }

    sprite.name = object.objName;
  } // Costumes from JSON.


  const costumePromises = assets.costumePromises; // Sounds from JSON

  const {
    soundBank,
    soundPromises
  } = assets; // Create the first clone, and load its run-state from JSON.

  const target = sprite.createClone(topLevel ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);
  const getVariableId = generateVariableIdGetter(target.id, topLevel);
  const globalBroadcastMsgObj = globalBroadcastMsgStateGenerator(topLevel);
  const addBroadcastMsg = globalBroadcastMsgObj.broadcastMsgMapUpdater; // Load target properties from JSON.

  if (object.hasOwnProperty('variables')) {
    for (let j = 0; j < object.variables.length; j++) {
      const variable = object.variables[j]; // A variable is a cloud variable if:
      // - the project says it's a cloud variable, and
      // - it's a stage variable, and
      // - the runtime can support another cloud variable

      const isCloud = variable.isPersistent && topLevel && runtime.canAddCloudVariable();
      const newVariable = new Variable(getVariableId(variable.name, Variable.SCALAR_TYPE), variable.name, Variable.SCALAR_TYPE, isCloud);
      if (isCloud) runtime.addCloudVariable();
      newVariable.value = variable.value;
      target.variables[newVariable.id] = newVariable;
    }
  } // If included, parse any and all comments on the object (this includes top-level
  // workspace comments as well as comments attached to specific blocks)


  const blockComments = {};

  if (object.hasOwnProperty('scriptComments')) {
    const comments = object.scriptComments.map(commentDesc => {
      const [commentX, commentY, commentWidth, commentHeight, commentFullSize, flattenedBlockIndex, commentText] = commentDesc;
      const isBlockComment = commentDesc[5] >= 0;
      const newComment = new Comment(null, // generate a new id for this comment
      commentText, // text content of sb2 comment
      // Only serialize x & y position of comment if it's a workspace comment
      // If it's a block comment, we'll let scratch-blocks handle positioning
      isBlockComment ? null : commentX * WORKSPACE_X_SCALE, isBlockComment ? null : commentY * WORKSPACE_Y_SCALE, commentWidth * WORKSPACE_X_SCALE, commentHeight * WORKSPACE_Y_SCALE, !commentFullSize);

      if (isBlockComment) {
        // commentDesc[5] refers to the index of the block that this
        // comment is attached to --  in a flattened version of the
        // scripts array.
        // If commentDesc[5] is -1, this is a workspace comment (we don't need to do anything
        // extra at this point), otherwise temporarily save the flattened script array
        // index as the blockId property of the new comment. We will
        // change this to refer to the actual block id of the corresponding
        // block when that block gets created
        newComment.blockId = flattenedBlockIndex; // Add this comment to the block comments object with its script index
        // as the key

        if (blockComments.hasOwnProperty(flattenedBlockIndex)) {
          blockComments[flattenedBlockIndex].push(newComment);
        } else {
          blockComments[flattenedBlockIndex] = [newComment];
        }
      }

      return newComment;
    }); // Add all the comments that were just created to the target.comments,
    // referenced by id

    comments.forEach(comment => {
      target.comments[comment.id] = comment;
    });
  } // If included, parse any and all scripts/blocks on the object.


  if (object.hasOwnProperty('scripts')) {
    parseScripts(object.scripts, blocks, addBroadcastMsg, getVariableId, extensions, blockComments);
  } // If there are any comments referring to a numerical block ID, make them
  // workspace comments. These are comments that were originally created as
  // block comments, detached from the block, and then had the associated
  // block deleted.
  // These comments should be imported as workspace comments
  // by making their blockIDs (which currently refer to non-existing blocks)
  // null (See #1452).


  for (const commentIndex in blockComments) {
    const currBlockComments = blockComments[commentIndex];
    currBlockComments.forEach(c => {
      if (typeof c.blockId === 'number') {
        c.blockId = null;
      }
    });
  } // Update stage specific blocks (e.g. sprite clicked <=> stage clicked)


  blocks.updateTargetSpecificBlocks(topLevel); // topLevel = isStage

  if (object.hasOwnProperty('lists')) {
    for (let k = 0; k < object.lists.length; k++) {
      const list = object.lists[k];
      const newVariable = new Variable(getVariableId(list.listName, Variable.LIST_TYPE), list.listName, Variable.LIST_TYPE, false);
      newVariable.value = list.contents;
      target.variables[newVariable.id] = newVariable;
    }
  }

  if (object.hasOwnProperty('scratchX')) {
    target.x = object.scratchX;
  }

  if (object.hasOwnProperty('scratchY')) {
    target.y = object.scratchY;
  }

  if (object.hasOwnProperty('direction')) {
    target.direction = object.direction;
  }

  if (object.hasOwnProperty('isDraggable')) {
    target.draggable = object.isDraggable;
  }

  if (object.hasOwnProperty('scale')) {
    // SB2 stores as 1.0 = 100%; we use % in the VM.
    target.size = object.scale * 100;
  }

  if (object.hasOwnProperty('visible')) {
    target.visible = object.visible;
  }

  if (object.hasOwnProperty('currentCostumeIndex')) {
    // Current costume index can sometimes be a floating
    // point number, use Math.floor to come up with an appropriate index
    // and clamp it to the actual number of costumes the object has for good measure.
    target.currentCostume = MathUtil.clamp(Math.floor(object.currentCostumeIndex), 0, object.costumes.length - 1);
  }

  if (object.hasOwnProperty('rotationStyle')) {
    if (object.rotationStyle === 'none') {
      target.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE;
    } else if (object.rotationStyle === 'leftRight') {
      target.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT;
    } else if (object.rotationStyle === 'normal') {
      target.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
    }
  }

  if (object.hasOwnProperty('tempoBPM')) {
    target.tempo = object.tempoBPM;
  }

  if (object.hasOwnProperty('videoAlpha')) {
    // SB2 stores alpha as opacity, where 1.0 is opaque.
    // We convert to a percentage, and invert it so 100% is full transparency.
    target.videoTransparency = 100 - 100 * object.videoAlpha;
  }

  if (object.hasOwnProperty('info')) {
    if (object.info.hasOwnProperty('videoOn')) {
      if (object.info.videoOn) {
        target.videoState = RenderedTarget.VIDEO_STATE.ON;
      } else {
        target.videoState = RenderedTarget.VIDEO_STATE.OFF;
      }
    }
  }

  if (object.hasOwnProperty('indexInLibrary')) {
    // Temporarily store the 'indexInLibrary' property from the sb2 file
    // so that we can correctly order sprites in the target pane.
    // This will be deleted after we are done parsing and ordering the targets list.
    target.targetPaneOrder = object.indexInLibrary;
  }

  target.isStage = topLevel;
  Promise.all(costumePromises).then(costumes => {
    sprite.costumes = costumes;
  });
  Promise.all(soundPromises).then(sounds => {
    sprite.sounds = sounds; // Make sure if soundBank is undefined, sprite.soundBank is then null.

    sprite.soundBank = soundBank || null;
  }); // The stage will have child objects; recursively process them.

  const childrenPromises = [];

  if (object.children) {
    for (let m = 0; m < object.children.length; m++) {
      childrenPromises.push(parseScratchObject(object.children[m], runtime, extensions, false, zip, assets.children[m]));
    }
  } // Parse extension list from ScratchX projects.


  if (topLevel) {
    const savedExtensions = object.info && object.info.savedExtensions;

    if (Array.isArray(savedExtensions)) {
      for (const extension of savedExtensions) {
        const id = ScratchXUtilities.generateExtensionId(extension.extensionName);
        const url = extension.javascriptURL;
        extensions.extensionURLs.set(id, url);
      }
    }
  }

  return Promise.all(costumePromises.concat(soundPromises)).then(() => Promise.all(childrenPromises).then(children => {
    // Need create broadcast msgs as variables after
    // all other targets have finished processing.
    if (target.isStage) {
      const allBroadcastMsgs = globalBroadcastMsgObj.globalBroadcastMsgs;
      const allBroadcastMsgFields = globalBroadcastMsgObj.allBroadcastFields;
      const oldEmptyMsgName = globalBroadcastMsgObj.emptyMsgName;

      if (allBroadcastMsgs[oldEmptyMsgName]) {
        // Find a fresh 'messageN'
        let currIndex = 1;

        while (allBroadcastMsgs["message".concat(currIndex)]) {
          currIndex += 1;
        }

        const newEmptyMsgName = "message".concat(currIndex); // Add the new empty message name to the broadcast message
        // name map, and assign it the old id.
        // Then, delete the old entry in map.

        allBroadcastMsgs[newEmptyMsgName] = allBroadcastMsgs[oldEmptyMsgName];
        delete allBroadcastMsgs[oldEmptyMsgName]; // Now update all the broadcast message fields with
        // the new empty message name.

        for (let i = 0; i < allBroadcastMsgFields.length; i++) {
          if (allBroadcastMsgFields[i].value === '') {
            allBroadcastMsgFields[i].value = newEmptyMsgName;
          }
        }
      } // Traverse the broadcast message name map and create
      // broadcast messages as variables on the stage (which is this
      // target).


      for (const msgName in allBroadcastMsgs) {
        const msgId = allBroadcastMsgs[msgName];
        const newMsg = new Variable(msgId, msgName, Variable.BROADCAST_MESSAGE_TYPE, false);
        target.variables[newMsg.id] = newMsg;
      }
    }

    let targets = [target];
    const deferredMonitors = [];

    for (let n = 0; n < children.length; n++) {
      if (children[n]) {
        if (children[n].deferredMonitor) {
          deferredMonitors.push(children[n]);
        } else {
          targets = targets.concat(children[n]);
        }
      }
    } // It is important that monitors are parsed last
    // - after all sprite targets have finished parsing
    // - and this is the last thing that happens in the stage parsing
    // It is specifically important that all the scripts in the project
    // have been parsed and all the relevant targets exist, have uids,
    // and have their variables initialized.


    for (let n = 0; n < deferredMonitors.length; n++) {
      parseMonitorObject(deferredMonitors[n], runtime, targets, extensions);
    }

    return targets;
  }));
};

const reorderParsedTargets = function reorderParsedTargets(targets) {
  // Reorder parsed targets based on the temporary targetPaneOrder property
  // and then delete it.
  const reordered = targets.map((t, index) => {
    t.layerOrder = index;
    return t;
  }).sort((a, b) => a.targetPaneOrder - b.targetPaneOrder); // Delete the temporary target pane ordering since we shouldn't need it anymore.

  reordered.forEach(t => {
    delete t.targetPaneOrder;
  });
  return reordered;
};
/**
 * Top-level handler. Parse provided JSON,
 * and process the top-level object (the stage object).
 * @param {!object} json SB2-format JSON to load.
 * @param {!Runtime} runtime Runtime object to load all structures into.
 * @param {boolean=} optForceSprite If set, treat as sprite (Sprite2).
 * @param {?object} zip Optional zipped assets for local file import
 * @return {Promise.<ImportedProject>} Promise that resolves to the loaded targets when ready.
 */


const sb2import = function sb2import(json, runtime, optForceSprite, zip) {
  const extensions = {
    extensionIDs: new Set(),
    extensionURLs: new Map()
  };
  return Promise.resolve(parseScratchAssets(json, runtime, !optForceSprite, zip)) // Force this promise to wait for the next loop in the js tick. Let
  // storage have some time to send off asset requests.
  .then(assets => Promise.resolve(assets)).then(assets => parseScratchObject(json, runtime, extensions, !optForceSprite, zip, assets)).then(reorderParsedTargets).then(targets => ({
    targets,
    extensions
  }));
};
/**
 * Given the sb2 block, inspect the specmap for a translation method or object.
 * @param {!object} block a sb2 formatted block
 * @return {object} specmap block to parse this opcode
 */


const specMapBlock = function specMapBlock(block) {
  const opcode = block[0];
  const mapped = opcode && specMap[opcode];

  if (!mapped) {
    if (opcode && isPossiblyScratchXBlock(opcode)) {
      return mapScratchXBlock(block);
    }

    log.warn("Couldn't find SB2 block: ".concat(opcode));
    return null;
  }

  if (typeof mapped === 'function') {
    return mapped(block);
  }

  return mapped;
};
/**
 * Parse a single SB2 JSON-formatted block and its children.
 * @param {!object} sb2block SB2 JSON-formatted block.
 * @param {Function} addBroadcastMsg function to update broadcast message name map
 * @param {Function} getVariableId function to retrieve a variable's ID based on name
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 * @param {ParseState} parseState - info on the state of parsing beyond the current block.
 * @param {object<int, Comment>} comments - Comments from sb2 project that need to be attached to blocks.
 * They are indexed in this object by the sb2 flattened block list index indicating
 * which block they should attach to.
 * @param {int} commentIndex The comment index for the block to be parsed if it were in a flattened
 * list of all blocks for the target
 * @return {Array.<object|int>} Tuple where first item is the Scratch VM-format block (or null if unsupported object),
 * and second item is the updated comment index (after this block and its children are parsed)
 */


const parseBlock = function parseBlock(sb2block, addBroadcastMsg, getVariableId, extensions, parseState, comments, commentIndex) {
  const commentsForParsedBlock = comments && typeof commentIndex === 'number' && !isNaN(commentIndex) ? comments[commentIndex] : null;
  const blockMetadata = specMapBlock(sb2block);

  if (!blockMetadata) {
    // No block opcode found, exclude this block, increment the commentIndex,
    // make all block comments into workspace comments and send them to zero/zero
    // to prevent serialization issues.
    if (commentsForParsedBlock) {
      commentsForParsedBlock.forEach(comment => {
        comment.blockId = null;
        comment.x = comment.y = 0;
      });
    }

    return [null, commentIndex + 1];
  }

  const oldOpcode = sb2block[0]; // If the block is from an extension, record it.

  const index = blockMetadata.opcode.indexOf('_');
  const prefix = blockMetadata.opcode.substring(0, index);

  if (CORE_EXTENSIONS.indexOf(prefix) === -1) {
    if (prefix !== '') extensions.extensionIDs.add(prefix);
  } // Block skeleton.


  const activeBlock = {
    id: uid(),
    // Generate a new block unique ID.
    opcode: blockMetadata.opcode,
    // Converted, e.g. "motion_movesteps".
    inputs: {},
    // Inputs to this block and the blocks they point to.
    fields: {},
    // Fields on this block and their values.
    next: null,
    // Next block.
    shadow: false,
    // No shadow blocks in an SB2 by default.
    children: [] // Store any generated children, flattened in `flatten`.

  }; // Attach any comments to this block..

  if (commentsForParsedBlock) {
    // Attach only the last comment to the block, make all others workspace comments
    activeBlock.comment = commentsForParsedBlock[commentsForParsedBlock.length - 1].id;
    commentsForParsedBlock.forEach(comment => {
      if (comment.id === activeBlock.comment) {
        comment.blockId = activeBlock.id;
      } else {
        // All other comments don't get a block ID and are sent back to zero.
        // This is important, because if they have `null` x/y, serialization breaks.
        comment.blockId = null;
        comment.x = comment.y = 0;
      }
    });
  }

  commentIndex++;
  const parentExpectedArg = parseState.expectedArg; // For a procedure call, generate argument map from proc string.

  if (oldOpcode === 'call') {
    blockMetadata.argMap = parseProcedureArgMap(sb2block[1]);
  } // Look at the expected arguments in `blockMetadata.argMap.`
  // The basic problem here is to turn positional SB2 arguments into
  // non-positional named Scratch VM arguments.


  for (let i = 0; i < blockMetadata.argMap.length; i++) {
    const expectedArg = blockMetadata.argMap[i];
    const providedArg = sb2block[i + 1]; // (i = 0 is opcode)
    // Whether the input is obscuring a shadow.

    let shadowObscured = false; // Positional argument is an input.

    if (expectedArg.type === 'input') {
      // Create a new block and input metadata.
      const inputUid = uid();
      activeBlock.inputs[expectedArg.inputName] = {
        name: expectedArg.inputName,
        block: null,
        shadow: null
      };

      if (typeof providedArg === 'object' && providedArg) {
        // Block or block list occupies the input.
        let innerBlocks;
        parseState.expectedArg = expectedArg;

        if (typeof providedArg[0] === 'object' && providedArg[0]) {
          // Block list occupies the input.
          [innerBlocks, commentIndex] = parseBlockList(providedArg, addBroadcastMsg, getVariableId, extensions, parseState, comments, commentIndex);
        } else {
          // Single block occupies the input.
          const parsedBlockDesc = parseBlock(providedArg, addBroadcastMsg, getVariableId, extensions, parseState, comments, commentIndex);
          innerBlocks = parsedBlockDesc[0] ? [parsedBlockDesc[0]] : []; // Update commentIndex

          commentIndex = parsedBlockDesc[1];
        }

        parseState.expectedArg = parentExpectedArg; // Check if innerBlocks is not an empty list.
        // An empty list indicates that all the inner blocks from the sb2 have
        // unknown opcodes and have been skipped.

        if (innerBlocks.length > 0) {
          let previousBlock = null;

          for (let j = 0; j < innerBlocks.length; j++) {
            if (j === 0) {
              innerBlocks[j].parent = activeBlock.id;
            } else {
              innerBlocks[j].parent = previousBlock;
            }

            previousBlock = innerBlocks[j].id;
          }

          activeBlock.inputs[expectedArg.inputName].block = innerBlocks[0].id;
          activeBlock.children = activeBlock.children.concat(innerBlocks);
        } // Obscures any shadow.


        shadowObscured = true;
      } // Generate a shadow block to occupy the input.


      if (!expectedArg.inputOp) {
        // Undefined inputOp. inputOp should always be defined for inputs.
        log.warn("Unknown input operation for input ".concat(expectedArg.inputName, " of opcode ").concat(activeBlock.opcode, "."));
        continue;
      }

      if (expectedArg.inputOp === 'boolean' || expectedArg.inputOp === 'substack') {
        // No editable shadow input; e.g., for a boolean.
        continue;
      } // Each shadow has a field generated for it automatically.
      // Value to be filled in the field.


      let fieldValue = providedArg; // Shadows' field names match the input name, except for these:

      let fieldName = expectedArg.inputName;

      if (expectedArg.inputOp === 'math_number' || expectedArg.inputOp === 'math_whole_number' || expectedArg.inputOp === 'math_positive_number' || expectedArg.inputOp === 'math_integer' || expectedArg.inputOp === 'math_angle') {
        fieldName = 'NUM'; // Fields are given Scratch 2.0 default values if obscured.

        if (shadowObscured) {
          fieldValue = 10;
        }
      } else if (expectedArg.inputOp === 'text') {
        fieldName = 'TEXT';

        if (shadowObscured) {
          fieldValue = '';
        }
      } else if (expectedArg.inputOp === 'colour_picker') {
        // Convert SB2 color to hex.
        fieldValue = Color.decimalToHex(providedArg);
        fieldName = 'COLOUR';

        if (shadowObscured) {
          fieldValue = '#990000';
        }
      } else if (expectedArg.inputOp === 'event_broadcast_menu') {
        fieldName = 'BROADCAST_OPTION';

        if (shadowObscured) {
          fieldValue = '';
        }
      } else if (expectedArg.inputOp === 'sensing_of_object_menu') {
        if (shadowObscured) {
          fieldValue = '_stage_';
        } else if (fieldValue === 'Stage') {
          fieldValue = '_stage_';
        }
      } else if (expectedArg.inputOp === 'note') {
        if (shadowObscured) {
          fieldValue = 60;
        }
      } else if (expectedArg.inputOp === 'music.menu.DRUM') {
        if (shadowObscured) {
          fieldValue = 1;
        }
      } else if (expectedArg.inputOp === 'music.menu.INSTRUMENT') {
        if (shadowObscured) {
          fieldValue = 1;
        }
      } else if (expectedArg.inputOp === 'videoSensing.menu.ATTRIBUTE') {
        if (shadowObscured) {
          fieldValue = 'motion';
        }
      } else if (expectedArg.inputOp === 'videoSensing.menu.SUBJECT') {
        if (shadowObscured) {
          fieldValue = 'this sprite';
        }
      } else if (expectedArg.inputOp === 'videoSensing.menu.VIDEO_STATE') {
        if (shadowObscured) {
          fieldValue = 'on';
        }
      } else if (shadowObscured) {
        // Filled drop-down menu.
        fieldValue = '';
      }

      const fields = {};
      fields[fieldName] = {
        name: fieldName,
        value: fieldValue
      }; // event_broadcast_menus have some extra properties to add to the
      // field and a different value than the rest

      if (expectedArg.inputOp === 'event_broadcast_menu') {
        // Need to update the broadcast message name map with
        // the value of this field.
        // Also need to provide the fields[fieldName] object,
        // so that we can later update its value property, e.g.
        // if sb2 message name is empty string, we will later
        // replace this field's value with messageN
        // once we can traverse through all the existing message names
        // and come up with a fresh messageN.
        const broadcastId = addBroadcastMsg(fieldValue, fields[fieldName]);
        fields[fieldName].id = broadcastId;
        fields[fieldName].variableType = expectedArg.variableType;
      }

      activeBlock.children.push({
        id: inputUid,
        opcode: expectedArg.inputOp,
        inputs: {},
        fields: fields,
        next: null,
        topLevel: false,
        parent: activeBlock.id,
        shadow: true
      });
      activeBlock.inputs[expectedArg.inputName].shadow = inputUid; // If no block occupying the input, alias to the shadow.

      if (!activeBlock.inputs[expectedArg.inputName].block) {
        activeBlock.inputs[expectedArg.inputName].block = inputUid;
      }
    } else if (expectedArg.type === 'field') {
      // Add as a field on this block.
      activeBlock.fields[expectedArg.fieldName] = {
        name: expectedArg.fieldName,
        value: providedArg
      };

      if (expectedArg.fieldName === 'CURRENTMENU') {
        // In 3.0, the field value of the `sensing_current` block
        // is in all caps.
        activeBlock.fields[expectedArg.fieldName].value = providedArg.toUpperCase();

        if (providedArg === 'day of week') {
          activeBlock.fields[expectedArg.fieldName].value = 'DAYOFWEEK';
        }
      }

      if (expectedArg.fieldName === 'VARIABLE') {
        // Add `id` property to variable fields
        activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg, Variable.SCALAR_TYPE);
      } else if (expectedArg.fieldName === 'LIST') {
        // Add `id` property to variable fields
        activeBlock.fields[expectedArg.fieldName].id = getVariableId(providedArg, Variable.LIST_TYPE);
      } else if (expectedArg.fieldName === 'BROADCAST_OPTION') {
        // Add the name in this field to the broadcast msg name map.
        // Also need to provide the fields[fieldName] object,
        // so that we can later update its value property, e.g.
        // if sb2 message name is empty string, we will later
        // replace this field's value with messageN
        // once we can traverse through all the existing message names
        // and come up with a fresh messageN.
        const broadcastId = addBroadcastMsg(providedArg, activeBlock.fields[expectedArg.fieldName]);
        activeBlock.fields[expectedArg.fieldName].id = broadcastId;
      }

      const varType = expectedArg.variableType;

      if (typeof varType === 'string') {
        activeBlock.fields[expectedArg.fieldName].variableType = varType;
      }
    }
  } // Updates for blocks that have new menus (e.g. in Looks)


  switch (oldOpcode) {
    case 'comeToFront':
      activeBlock.fields.FRONT_BACK = {
        name: 'FRONT_BACK',
        value: 'front'
      };
      break;

    case 'goBackByLayers:':
      activeBlock.fields.FORWARD_BACKWARD = {
        name: 'FORWARD_BACKWARD',
        value: 'backward'
      };
      break;

    case 'backgroundIndex':
      activeBlock.fields.NUMBER_NAME = {
        name: 'NUMBER_NAME',
        value: 'number'
      };
      break;

    case 'sceneName':
      activeBlock.fields.NUMBER_NAME = {
        name: 'NUMBER_NAME',
        value: 'name'
      };
      break;

    case 'costumeIndex':
      activeBlock.fields.NUMBER_NAME = {
        name: 'NUMBER_NAME',
        value: 'number'
      };
      break;

    case 'costumeName':
      activeBlock.fields.NUMBER_NAME = {
        name: 'NUMBER_NAME',
        value: 'name'
      };
      break;
  } // Special cases to generate mutations.


  if (oldOpcode === 'stopScripts') {
    // Mutation for stop block: if the argument is 'other scripts',
    // the block needs a next connection.
    if (sb2block[1] === 'other scripts in sprite' || sb2block[1] === 'other scripts in stage') {
      activeBlock.mutation = {
        tagName: 'mutation',
        hasnext: 'true',
        children: []
      };
    }
  } else if (oldOpcode === 'procDef') {
    // Mutation for procedure definition:
    // store all 2.0 proc data.
    const procData = sb2block.slice(1); // Create a new block and input metadata.

    const inputUid = uid();
    const inputName = 'custom_block';
    activeBlock.inputs[inputName] = {
      name: inputName,
      block: inputUid,
      shadow: inputUid
    };
    activeBlock.children = [{
      id: inputUid,
      opcode: 'procedures_prototype',
      inputs: {},
      fields: {},
      next: null,
      shadow: true,
      children: [],
      mutation: {
        tagName: 'mutation',
        proccode: procData[0],
        // e.g., "abc %n %b %s"
        argumentnames: JSON.stringify(procData[1]),
        // e.g. ['arg1', 'arg2']
        argumentids: JSON.stringify(parseProcedureArgIds(procData[0])),
        argumentdefaults: JSON.stringify(procData[2]),
        // e.g., [1, 'abc']
        warp: procData[3],
        // Warp mode, e.g., true/false.
        children: []
      }
    }];
  } else if (oldOpcode === 'call') {
    // Mutation for procedure call:
    // string for proc code (e.g., "abc %n %b %s").
    activeBlock.mutation = {
      tagName: 'mutation',
      children: [],
      proccode: sb2block[1],
      argumentids: JSON.stringify(parseProcedureArgIds(sb2block[1]))
    };
  } else if (oldOpcode === 'getParam') {
    let returnCode = sb2block[2]; // Ensure the returnCode is "b" if used in a boolean input.

    if (parentExpectedArg && parentExpectedArg.inputOp === 'boolean' && returnCode !== 'b') {
      returnCode = 'b';
    } // Assign correct opcode based on the block shape.


    switch (returnCode) {
      case 'r':
        activeBlock.opcode = 'argument_reporter_string_number';
        break;

      case 'b':
        activeBlock.opcode = 'argument_reporter_boolean';
        break;
    }
  }

  return [activeBlock, commentIndex];
};

module.exports = {
  deserialize: sb2import
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/sb2_specmap.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/sb2_specmap.js ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * The specMap below handles a few pieces of "translation" work between
 * the SB2 JSON format and the data we need to run a project
 * in the Scratch 3.0 VM.
 * Notably:
 *  - Map 2.0 and 1.4 opcodes (forward:) into 3.0-format (motion_movesteps).
 *  - Map ordered, unnamed args to unordered, named inputs and fields.
 * Keep this up-to-date as 3.0 blocks are renamed, changed, etc.
 * Originally this was generated largely by a hand-guided scripting process.
 * The relevant data lives here:
 * https://github.com/LLK/scratch-flash/blob/master/src/Specs.as
 * (for the old opcode and argument order).
 * and here:
 * https://github.com/LLK/scratch-blocks/tree/develop/blocks_vertical
 * (for the new opcodes and argument names).
 * and here:
 * https://github.com/LLK/scratch-blocks/blob/develop/tests/
 * (for the shadow blocks created for each block).
 * I started with the `commands` array in Specs.as, and discarded irrelevant
 * properties. By hand, I matched the opcode name to the 3.0 opcode.
 * Finally, I filled in the expected arguments as below.
 */
const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");
/**
 * @typedef {object} SB2SpecMap_blockInfo
 * @property {string} opcode - the Scratch 3.0 block opcode. Use 'extensionID.opcode' for extension opcodes.
 * @property {Array.<SB2SpecMap_argInfo>} argMap - metadata for this block's arguments.
 */

/**
 * @typedef {object} SB2SpecMap_argInfo
 * @property {string} type - the type of this arg (such as 'input' or 'field')
 * @property {string} inputOp - the scratch-blocks shadow type for this arg
 * @property {string} inputName - the name this argument will take when provided to the block implementation
 */

/**
 * Mapping of Scratch 2.0 opcode to Scratch 3.0 block metadata.
 * @type {object.<SB2SpecMap_blockInfo>}
 */


const specMap = {
  'forward:': {
    opcode: 'motion_movesteps',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'STEPS'
    }]
  },
  'turnRight:': {
    opcode: 'motion_turnright',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DEGREES'
    }]
  },
  'turnLeft:': {
    opcode: 'motion_turnleft',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DEGREES'
    }]
  },
  'heading:': {
    opcode: 'motion_pointindirection',
    argMap: [{
      type: 'input',
      inputOp: 'math_angle',
      inputName: 'DIRECTION'
    }]
  },
  'pointTowards:': {
    opcode: 'motion_pointtowards',
    argMap: [{
      type: 'input',
      inputOp: 'motion_pointtowards_menu',
      inputName: 'TOWARDS'
    }]
  },
  'gotoX:y:': {
    opcode: 'motion_gotoxy',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'X'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'Y'
    }]
  },
  'gotoSpriteOrMouse:': {
    opcode: 'motion_goto',
    argMap: [{
      type: 'input',
      inputOp: 'motion_goto_menu',
      inputName: 'TO'
    }]
  },
  'glideSecs:toX:y:elapsed:from:': {
    opcode: 'motion_glidesecstoxy',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SECS'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'X'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'Y'
    }]
  },
  'changeXposBy:': {
    opcode: 'motion_changexby',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DX'
    }]
  },
  'xpos:': {
    opcode: 'motion_setx',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'X'
    }]
  },
  'changeYposBy:': {
    opcode: 'motion_changeyby',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DY'
    }]
  },
  'ypos:': {
    opcode: 'motion_sety',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'Y'
    }]
  },
  'bounceOffEdge': {
    opcode: 'motion_ifonedgebounce',
    argMap: []
  },
  'setRotationStyle': {
    opcode: 'motion_setrotationstyle',
    argMap: [{
      type: 'field',
      fieldName: 'STYLE'
    }]
  },
  'xpos': {
    opcode: 'motion_xposition',
    argMap: []
  },
  'ypos': {
    opcode: 'motion_yposition',
    argMap: []
  },
  'heading': {
    opcode: 'motion_direction',
    argMap: []
  },
  'scrollRight': {
    opcode: 'motion_scroll_right',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DISTANCE'
    }]
  },
  'scrollUp': {
    opcode: 'motion_scroll_up',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DISTANCE'
    }]
  },
  'scrollAlign': {
    opcode: 'motion_align_scene',
    argMap: [{
      type: 'field',
      fieldName: 'ALIGNMENT'
    }]
  },
  'xScroll': {
    opcode: 'motion_xscroll',
    argMap: []
  },
  'yScroll': {
    opcode: 'motion_yscroll',
    argMap: []
  },
  'say:duration:elapsed:from:': {
    opcode: 'looks_sayforsecs',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'MESSAGE'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SECS'
    }]
  },
  'say:': {
    opcode: 'looks_say',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'MESSAGE'
    }]
  },
  'think:duration:elapsed:from:': {
    opcode: 'looks_thinkforsecs',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'MESSAGE'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SECS'
    }]
  },
  'think:': {
    opcode: 'looks_think',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'MESSAGE'
    }]
  },
  'show': {
    opcode: 'looks_show',
    argMap: []
  },
  'hide': {
    opcode: 'looks_hide',
    argMap: []
  },
  'hideAll': {
    opcode: 'looks_hideallsprites',
    argMap: []
  },
  'lookLike:': {
    opcode: 'looks_switchcostumeto',
    argMap: [{
      type: 'input',
      inputOp: 'looks_costume',
      inputName: 'COSTUME'
    }]
  },
  'nextCostume': {
    opcode: 'looks_nextcostume',
    argMap: []
  },
  'startScene': {
    opcode: 'looks_switchbackdropto',
    argMap: [{
      type: 'input',
      inputOp: 'looks_backdrops',
      inputName: 'BACKDROP'
    }]
  },
  'changeGraphicEffect:by:': {
    opcode: 'looks_changeeffectby',
    argMap: [{
      type: 'field',
      fieldName: 'EFFECT'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'CHANGE'
    }]
  },
  'setGraphicEffect:to:': {
    opcode: 'looks_seteffectto',
    argMap: [{
      type: 'field',
      fieldName: 'EFFECT'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'VALUE'
    }]
  },
  'filterReset': {
    opcode: 'looks_cleargraphiceffects',
    argMap: []
  },
  'changeSizeBy:': {
    opcode: 'looks_changesizeby',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'CHANGE'
    }]
  },
  'setSizeTo:': {
    opcode: 'looks_setsizeto',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SIZE'
    }]
  },
  'changeStretchBy:': {
    opcode: 'looks_changestretchby',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'CHANGE'
    }]
  },
  'setStretchTo:': {
    opcode: 'looks_setstretchto',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'STRETCH'
    }]
  },
  'comeToFront': {
    opcode: 'looks_gotofrontback',
    argMap: []
  },
  'goBackByLayers:': {
    opcode: 'looks_goforwardbackwardlayers',
    argMap: [{
      type: 'input',
      inputOp: 'math_integer',
      inputName: 'NUM'
    }]
  },
  'costumeIndex': {
    opcode: 'looks_costumenumbername',
    argMap: []
  },
  'costumeName': {
    opcode: 'looks_costumenumbername',
    argMap: []
  },
  'sceneName': {
    opcode: 'looks_backdropnumbername',
    argMap: []
  },
  'scale': {
    opcode: 'looks_size',
    argMap: []
  },
  'startSceneAndWait': {
    opcode: 'looks_switchbackdroptoandwait',
    argMap: [{
      type: 'input',
      inputOp: 'looks_backdrops',
      inputName: 'BACKDROP'
    }]
  },
  'nextScene': {
    opcode: 'looks_nextbackdrop',
    argMap: []
  },
  'backgroundIndex': {
    opcode: 'looks_backdropnumbername',
    argMap: []
  },
  'playSound:': {
    opcode: 'sound_play',
    argMap: [{
      type: 'input',
      inputOp: 'sound_sounds_menu',
      inputName: 'SOUND_MENU'
    }]
  },
  'doPlaySoundAndWait': {
    opcode: 'sound_playuntildone',
    argMap: [{
      type: 'input',
      inputOp: 'sound_sounds_menu',
      inputName: 'SOUND_MENU'
    }]
  },
  'stopAllSounds': {
    opcode: 'sound_stopallsounds',
    argMap: []
  },
  'playDrum': {
    opcode: 'music_playDrumForBeats',
    argMap: [{
      type: 'input',
      inputOp: 'music_menu_DRUM',
      inputName: 'DRUM'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'BEATS'
    }]
  },
  'drum:duration:elapsed:from:': {
    opcode: 'music_midiPlayDrumForBeats',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'DRUM'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'BEATS'
    }]
  },
  'rest:elapsed:from:': {
    opcode: 'music_restForBeats',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'BEATS'
    }]
  },
  'noteOn:duration:elapsed:from:': {
    opcode: 'music_playNoteForBeats',
    argMap: [{
      type: 'input',
      inputOp: 'note',
      inputName: 'NOTE'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'BEATS'
    }]
  },
  'instrument:': {
    opcode: 'music_setInstrument',
    argMap: [{
      type: 'input',
      inputOp: 'music_menu_INSTRUMENT',
      inputName: 'INSTRUMENT'
    }]
  },
  'midiInstrument:': {
    opcode: 'music_midiSetInstrument',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'INSTRUMENT'
    }]
  },
  'changeVolumeBy:': {
    opcode: 'sound_changevolumeby',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'VOLUME'
    }]
  },
  'setVolumeTo:': {
    opcode: 'sound_setvolumeto',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'VOLUME'
    }]
  },
  'volume': {
    opcode: 'sound_volume',
    argMap: []
  },
  'changeTempoBy:': {
    opcode: 'music_changeTempo',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'TEMPO'
    }]
  },
  'setTempoTo:': {
    opcode: 'music_setTempo',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'TEMPO'
    }]
  },
  'tempo': {
    opcode: 'music_getTempo',
    argMap: []
  },
  'clearPenTrails': {
    opcode: 'pen_clear',
    argMap: []
  },
  'stampCostume': {
    opcode: 'pen_stamp',
    argMap: []
  },
  'putPenDown': {
    opcode: 'pen_penDown',
    argMap: []
  },
  'putPenUp': {
    opcode: 'pen_penUp',
    argMap: []
  },
  'penColor:': {
    opcode: 'pen_setPenColorToColor',
    argMap: [{
      type: 'input',
      inputOp: 'colour_picker',
      inputName: 'COLOR'
    }]
  },
  'changePenHueBy:': {
    opcode: 'pen_changePenHueBy',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'HUE'
    }]
  },
  'setPenHueTo:': {
    opcode: 'pen_setPenHueToNumber',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'HUE'
    }]
  },
  'changePenShadeBy:': {
    opcode: 'pen_changePenShadeBy',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SHADE'
    }]
  },
  'setPenShadeTo:': {
    opcode: 'pen_setPenShadeToNumber',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SHADE'
    }]
  },
  'changePenSizeBy:': {
    opcode: 'pen_changePenSizeBy',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SIZE'
    }]
  },
  'penSize:': {
    opcode: 'pen_setPenSizeTo',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'SIZE'
    }]
  },
  'senseVideoMotion': {
    opcode: 'videoSensing_videoOn',
    argMap: [{
      type: 'input',
      inputOp: 'videoSensing_menu_ATTRIBUTE',
      inputName: 'ATTRIBUTE'
    }, {
      type: 'input',
      inputOp: 'videoSensing_menu_SUBJECT',
      inputName: 'SUBJECT'
    }]
  },
  'whenGreenFlag': {
    opcode: 'event_whenflagclicked',
    argMap: []
  },
  'whenKeyPressed': {
    opcode: 'event_whenkeypressed',
    argMap: [{
      type: 'field',
      fieldName: 'KEY_OPTION'
    }]
  },
  'whenClicked': {
    opcode: 'event_whenthisspriteclicked',
    argMap: []
  },
  'whenSceneStarts': {
    opcode: 'event_whenbackdropswitchesto',
    argMap: [{
      type: 'field',
      fieldName: 'BACKDROP'
    }]
  },
  'whenSensorGreaterThan': _ref => {
    let [, sensor] = _ref;

    if (sensor === 'video motion') {
      return {
        opcode: 'videoSensing_whenMotionGreaterThan',
        argMap: [// skip the first arg, since we converted to a video specific sensing block
        {}, {
          type: 'input',
          inputOp: 'math_number',
          inputName: 'REFERENCE'
        }]
      };
    }

    return {
      opcode: 'event_whengreaterthan',
      argMap: [{
        type: 'field',
        fieldName: 'WHENGREATERTHANMENU'
      }, {
        type: 'input',
        inputOp: 'math_number',
        inputName: 'VALUE'
      }]
    };
  },
  'whenIReceive': {
    opcode: 'event_whenbroadcastreceived',
    argMap: [{
      type: 'field',
      fieldName: 'BROADCAST_OPTION',
      variableType: Variable.BROADCAST_MESSAGE_TYPE
    }]
  },
  'broadcast:': {
    opcode: 'event_broadcast',
    argMap: [{
      type: 'input',
      inputOp: 'event_broadcast_menu',
      inputName: 'BROADCAST_INPUT',
      variableType: Variable.BROADCAST_MESSAGE_TYPE
    }]
  },
  'doBroadcastAndWait': {
    opcode: 'event_broadcastandwait',
    argMap: [{
      type: 'input',
      inputOp: 'event_broadcast_menu',
      inputName: 'BROADCAST_INPUT',
      variableType: Variable.BROADCAST_MESSAGE_TYPE
    }]
  },
  'wait:elapsed:from:': {
    opcode: 'control_wait',
    argMap: [{
      type: 'input',
      inputOp: 'math_positive_number',
      inputName: 'DURATION'
    }]
  },
  'doRepeat': {
    opcode: 'control_repeat',
    argMap: [{
      type: 'input',
      inputOp: 'math_whole_number',
      inputName: 'TIMES'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'doForever': {
    opcode: 'control_forever',
    argMap: [{
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'doIf': {
    opcode: 'control_if',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'CONDITION'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'doIfElse': {
    opcode: 'control_if_else',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'CONDITION'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK2'
    }]
  },
  'doWaitUntil': {
    opcode: 'control_wait_until',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'CONDITION'
    }]
  },
  'doUntil': {
    opcode: 'control_repeat_until',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'CONDITION'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'doWhile': {
    opcode: 'control_while',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'CONDITION'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'doForLoop': {
    opcode: 'control_for_each',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'VALUE'
    }, {
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'stopScripts': {
    opcode: 'control_stop',
    argMap: [{
      type: 'field',
      fieldName: 'STOP_OPTION'
    }]
  },
  'whenCloned': {
    opcode: 'control_start_as_clone',
    argMap: []
  },
  'createCloneOf': {
    opcode: 'control_create_clone_of',
    argMap: [{
      type: 'input',
      inputOp: 'control_create_clone_of_menu',
      inputName: 'CLONE_OPTION'
    }]
  },
  'deleteClone': {
    opcode: 'control_delete_this_clone',
    argMap: []
  },
  'COUNT': {
    opcode: 'control_get_counter',
    argMap: []
  },
  'INCR_COUNT': {
    opcode: 'control_incr_counter',
    argMap: []
  },
  'CLR_COUNT': {
    opcode: 'control_clear_counter',
    argMap: []
  },
  'warpSpeed': {
    opcode: 'control_all_at_once',
    argMap: [{
      type: 'input',
      inputOp: 'substack',
      inputName: 'SUBSTACK'
    }]
  },
  'touching:': {
    opcode: 'sensing_touchingobject',
    argMap: [{
      type: 'input',
      inputOp: 'sensing_touchingobjectmenu',
      inputName: 'TOUCHINGOBJECTMENU'
    }]
  },
  'touchingColor:': {
    opcode: 'sensing_touchingcolor',
    argMap: [{
      type: 'input',
      inputOp: 'colour_picker',
      inputName: 'COLOR'
    }]
  },
  'color:sees:': {
    opcode: 'sensing_coloristouchingcolor',
    argMap: [{
      type: 'input',
      inputOp: 'colour_picker',
      inputName: 'COLOR'
    }, {
      type: 'input',
      inputOp: 'colour_picker',
      inputName: 'COLOR2'
    }]
  },
  'distanceTo:': {
    opcode: 'sensing_distanceto',
    argMap: [{
      type: 'input',
      inputOp: 'sensing_distancetomenu',
      inputName: 'DISTANCETOMENU'
    }]
  },
  'doAsk': {
    opcode: 'sensing_askandwait',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'QUESTION'
    }]
  },
  'answer': {
    opcode: 'sensing_answer',
    argMap: []
  },
  'keyPressed:': {
    opcode: 'sensing_keypressed',
    argMap: [{
      type: 'input',
      inputOp: 'sensing_keyoptions',
      inputName: 'KEY_OPTION'
    }]
  },
  'mousePressed': {
    opcode: 'sensing_mousedown',
    argMap: []
  },
  'mouseX': {
    opcode: 'sensing_mousex',
    argMap: []
  },
  'mouseY': {
    opcode: 'sensing_mousey',
    argMap: []
  },
  'soundLevel': {
    opcode: 'sensing_loudness',
    argMap: []
  },
  'isLoud': {
    opcode: 'sensing_loud',
    argMap: []
  },
  // 'senseVideoMotion': {
  //     opcode: 'sensing_videoon',
  //     argMap: [
  //         {
  //             type: 'input',
  //             inputOp: 'sensing_videoonmenuone',
  //             inputName: 'VIDEOONMENU1'
  //         },
  //         {
  //             type: 'input',
  //             inputOp: 'sensing_videoonmenutwo',
  //             inputName: 'VIDEOONMENU2'
  //         }
  //     ]
  // },
  'setVideoState': {
    opcode: 'videoSensing_videoToggle',
    argMap: [{
      type: 'input',
      inputOp: 'videoSensing_menu_VIDEO_STATE',
      inputName: 'VIDEO_STATE'
    }]
  },
  'setVideoTransparency': {
    opcode: 'videoSensing_setVideoTransparency',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'TRANSPARENCY'
    }]
  },
  'timer': {
    opcode: 'sensing_timer',
    argMap: []
  },
  'timerReset': {
    opcode: 'sensing_resettimer',
    argMap: []
  },
  'getAttribute:of:': {
    opcode: 'sensing_of',
    argMap: [{
      type: 'field',
      fieldName: 'PROPERTY'
    }, {
      type: 'input',
      inputOp: 'sensing_of_object_menu',
      inputName: 'OBJECT'
    }]
  },
  'timeAndDate': {
    opcode: 'sensing_current',
    argMap: [{
      type: 'field',
      fieldName: 'CURRENTMENU'
    }]
  },
  'timestamp': {
    opcode: 'sensing_dayssince2000',
    argMap: []
  },
  'getUserName': {
    opcode: 'sensing_username',
    argMap: []
  },
  'getUserId': {
    opcode: 'sensing_userid',
    argMap: []
  },
  '+': {
    opcode: 'operator_add',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM1'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM2'
    }]
  },
  '-': {
    opcode: 'operator_subtract',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM1'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM2'
    }]
  },
  '*': {
    opcode: 'operator_multiply',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM1'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM2'
    }]
  },
  '/': {
    opcode: 'operator_divide',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM1'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM2'
    }]
  },
  'randomFrom:to:': {
    opcode: 'operator_random',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'FROM'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'TO'
    }]
  },
  '<': {
    opcode: 'operator_lt',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND1'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND2'
    }]
  },
  '=': {
    opcode: 'operator_equals',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND1'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND2'
    }]
  },
  '>': {
    opcode: 'operator_gt',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND1'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'OPERAND2'
    }]
  },
  '&': {
    opcode: 'operator_and',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'OPERAND1'
    }, {
      type: 'input',
      inputOp: 'boolean',
      inputName: 'OPERAND2'
    }]
  },
  '|': {
    opcode: 'operator_or',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'OPERAND1'
    }, {
      type: 'input',
      inputOp: 'boolean',
      inputName: 'OPERAND2'
    }]
  },
  'not': {
    opcode: 'operator_not',
    argMap: [{
      type: 'input',
      inputOp: 'boolean',
      inputName: 'OPERAND'
    }]
  },
  'concatenate:with:': {
    opcode: 'operator_join',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'STRING1'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'STRING2'
    }]
  },
  'letter:of:': {
    opcode: 'operator_letter_of',
    argMap: [{
      type: 'input',
      inputOp: 'math_whole_number',
      inputName: 'LETTER'
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'STRING'
    }]
  },
  'stringLength:': {
    opcode: 'operator_length',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'STRING'
    }]
  },
  '%': {
    opcode: 'operator_mod',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM1'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM2'
    }]
  },
  'rounded': {
    opcode: 'operator_round',
    argMap: [{
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM'
    }]
  },
  'computeFunction:of:': {
    opcode: 'operator_mathop',
    argMap: [{
      type: 'field',
      fieldName: 'OPERATOR'
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'NUM'
    }]
  },
  'readVariable': {
    opcode: 'data_variable',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }]
  },
  // Scratch 2 uses this alternative variable getter opcode only in monitors,
  // blocks use the `readVariable` opcode above.
  'getVar:': {
    opcode: 'data_variable',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }]
  },
  'setVar:to:': {
    opcode: 'data_setvariableto',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'VALUE'
    }]
  },
  'changeVar:by:': {
    opcode: 'data_changevariableby',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }, {
      type: 'input',
      inputOp: 'math_number',
      inputName: 'VALUE'
    }]
  },
  'showVariable:': {
    opcode: 'data_showvariable',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }]
  },
  'hideVariable:': {
    opcode: 'data_hidevariable',
    argMap: [{
      type: 'field',
      fieldName: 'VARIABLE',
      variableType: Variable.SCALAR_TYPE
    }]
  },
  'contentsOfList:': {
    opcode: 'data_listcontents',
    argMap: [{
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'append:toList:': {
    opcode: 'data_addtolist',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'ITEM'
    }, {
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'deleteLine:ofList:': {
    opcode: 'data_deleteoflist',
    argMap: [{
      type: 'input',
      inputOp: 'math_integer',
      inputName: 'INDEX'
    }, {
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'insert:at:ofList:': {
    opcode: 'data_insertatlist',
    argMap: [{
      type: 'input',
      inputOp: 'text',
      inputName: 'ITEM'
    }, {
      type: 'input',
      inputOp: 'math_integer',
      inputName: 'INDEX'
    }, {
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'setLine:ofList:to:': {
    opcode: 'data_replaceitemoflist',
    argMap: [{
      type: 'input',
      inputOp: 'math_integer',
      inputName: 'INDEX'
    }, {
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'ITEM'
    }]
  },
  'getLine:ofList:': {
    opcode: 'data_itemoflist',
    argMap: [{
      type: 'input',
      inputOp: 'math_integer',
      inputName: 'INDEX'
    }, {
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'lineCountOfList:': {
    opcode: 'data_lengthoflist',
    argMap: [{
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'list:contains:': {
    opcode: 'data_listcontainsitem',
    argMap: [{
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }, {
      type: 'input',
      inputOp: 'text',
      inputName: 'ITEM'
    }]
  },
  'showList:': {
    opcode: 'data_showlist',
    argMap: [{
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'hideList:': {
    opcode: 'data_hidelist',
    argMap: [{
      type: 'field',
      fieldName: 'LIST',
      variableType: Variable.LIST_TYPE
    }]
  },
  'procDef': {
    opcode: 'procedures_definition',
    argMap: []
  },
  'getParam': {
    // Doesn't map to single opcode. Import step assigns final correct opcode.
    opcode: 'argument_reporter_string_number',
    argMap: [{
      type: 'field',
      fieldName: 'VALUE'
    }]
  },
  'call': {
    opcode: 'procedures_call',
    argMap: []
  }
};
/**
 * Add to the specMap entries for an opcode from a Scratch 2.0 extension. Two entries will be made with the same
 * metadata; this is done to support projects saved by both older and newer versions of the Scratch 2.0 editor.
 * @param {string} sb2Extension - the Scratch 2.0 name of the extension
 * @param {string} sb2Opcode - the Scratch 2.0 opcode
 * @param {SB2SpecMap_blockInfo} blockInfo - the Scratch 3.0 block info
 */

const addExtensionOp = function addExtensionOp(sb2Extension, sb2Opcode, blockInfo) {
  /**
   * This string separates the name of an extension and the name of an opcode in more recent Scratch 2.0 projects.
   * Earlier projects used '.' as a separator, up until we added the 'LEGO WeDo 2.0' extension...
   * @type {string}
   */
  const sep = '\u001F'; // Unicode Unit Separator
  // make one entry for projects saved by recent versions of the Scratch 2.0 editor

  specMap["".concat(sb2Extension).concat(sep).concat(sb2Opcode)] = blockInfo; // make a second for projects saved by older versions of the Scratch 2.0 editor

  specMap["".concat(sb2Extension, ".").concat(sb2Opcode)] = blockInfo;
};

const weDo2 = 'LEGO WeDo 2.0';
addExtensionOp(weDo2, 'motorOnFor', {
  opcode: 'wedo2_motorOnFor',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_ID',
    inputName: 'MOTOR_ID'
  }, {
    type: 'input',
    inputOp: 'math_number',
    inputName: 'DURATION'
  }]
});
addExtensionOp(weDo2, 'motorOn', {
  opcode: 'wedo2_motorOn',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_ID',
    inputName: 'MOTOR_ID'
  }]
});
addExtensionOp(weDo2, 'motorOff', {
  opcode: 'wedo2_motorOff',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_ID',
    inputName: 'MOTOR_ID'
  }]
});
addExtensionOp(weDo2, 'startMotorPower', {
  opcode: 'wedo2_startMotorPower',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_ID',
    inputName: 'MOTOR_ID'
  }, {
    type: 'input',
    inputOp: 'math_number',
    inputName: 'POWER'
  }]
});
addExtensionOp(weDo2, 'setMotorDirection', {
  opcode: 'wedo2_setMotorDirection',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_ID',
    inputName: 'MOTOR_ID'
  }, {
    type: 'input',
    inputOp: 'wedo2_menu_MOTOR_DIRECTION',
    inputName: 'MOTOR_DIRECTION'
  }]
});
addExtensionOp(weDo2, 'setLED', {
  opcode: 'wedo2_setLightHue',
  argMap: [{
    type: 'input',
    inputOp: 'math_number',
    inputName: 'HUE'
  }]
});
addExtensionOp(weDo2, 'playNote', {
  opcode: 'wedo2_playNoteFor',
  argMap: [{
    type: 'input',
    inputOp: 'math_number',
    inputName: 'NOTE'
  }, {
    type: 'input',
    inputOp: 'math_number',
    inputName: 'DURATION'
  }]
});
addExtensionOp(weDo2, 'whenDistance', {
  opcode: 'wedo2_whenDistance',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_OP',
    inputName: 'OP'
  }, {
    type: 'input',
    inputOp: 'math_number',
    inputName: 'REFERENCE'
  }]
});
addExtensionOp(weDo2, 'whenTilted', {
  opcode: 'wedo2_whenTilted',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_TILT_DIRECTION_ANY',
    inputName: 'TILT_DIRECTION_ANY'
  }]
});
addExtensionOp(weDo2, 'getDistance', {
  opcode: 'wedo2_getDistance',
  argMap: []
});
addExtensionOp(weDo2, 'isTilted', {
  opcode: 'wedo2_isTilted',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_TILT_DIRECTION_ANY',
    inputName: 'TILT_DIRECTION_ANY'
  }]
});
addExtensionOp(weDo2, 'getTilt', {
  opcode: 'wedo2_getTiltAngle',
  argMap: [{
    type: 'input',
    inputOp: 'wedo2_menu_TILT_DIRECTION',
    inputName: 'TILT_DIRECTION'
  }]
});
module.exports = specMap;

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/sb3.js":
/*!**********************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/sb3.js ***!
  \**********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/**
 * @fileoverview
 * An SB3 serializer and deserializer. Parses provided
 * JSON and then generates all needed scratch-vm runtime structures.
 */
const Blocks = __webpack_require__(/*! ../engine/blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

const Sprite = __webpack_require__(/*! ../sprites/sprite */ "./node_modules/scratch-vm/src/sprites/sprite.js");

const Variable = __webpack_require__(/*! ../engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const Comment = __webpack_require__(/*! ../engine/comment */ "./node_modules/scratch-vm/src/engine/comment.js");

const MonitorRecord = __webpack_require__(/*! ../engine/monitor-record */ "./node_modules/scratch-vm/src/engine/monitor-record.js");

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js");

const uid = __webpack_require__(/*! ../util/uid */ "./node_modules/scratch-vm/src/util/uid.js");

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const VariableUtil = __webpack_require__(/*! ../util/variable-util */ "./node_modules/scratch-vm/src/util/variable-util.js");

const compress = __webpack_require__(/*! ./tw-compress-sb3 */ "./node_modules/scratch-vm/src/serialization/tw-compress-sb3.js");

const {
  loadCostume
} = __webpack_require__(/*! ../import/load-costume.js */ "./node_modules/scratch-vm/src/import/load-costume.js");

const {
  loadSound
} = __webpack_require__(/*! ../import/load-sound.js */ "./node_modules/scratch-vm/src/import/load-sound.js");

const {
  deserializeCostume,
  deserializeSound
} = __webpack_require__(/*! ./deserialize-assets.js */ "./node_modules/scratch-vm/src/serialization/deserialize-assets.js");

const hasOwnProperty = Object.prototype.hasOwnProperty;
/**
 * @typedef {object} ImportedProject
 * @property {Array.<Target>} targets - the imported Scratch 3.0 target objects.
 * @property {ImportedExtensionsInfo} extensionsInfo - the ID of each extension actually used by this project.
 */

/**
 * @typedef {object} ImportedExtensionsInfo
 * @property {Set.<string>} extensionIDs - the ID of each extension actually in use by blocks in this project.
 * @property {Map.<string, string>} extensionURLs - map of ID => URL from project metadata. May not match extensionIDs.
 */
// Constants used during serialization and deserialization

const INPUT_SAME_BLOCK_SHADOW = 1; // unobscured shadow

const INPUT_BLOCK_NO_SHADOW = 2; // no shadow

const INPUT_DIFF_BLOCK_SHADOW = 3; // obscured shadow
// There shouldn't be a case where block is null, but shadow is present...
// Constants used during deserialization of an SB3 file

const CORE_EXTENSIONS = ['argument', 'colour', 'control', 'data', 'event', 'looks', 'math', 'motion', 'operator', 'procedures', 'sensing', 'sound']; // Constants referring to 'primitive' blocks that are usually shadows,
// or in the case of variables and lists, appear quite often in projects
// math_number

const MATH_NUM_PRIMITIVE = 4; // there's no reason these constants can't collide
// math_positive_number

const POSITIVE_NUM_PRIMITIVE = 5; // with the above, but removing duplication for clarity
// math_whole_number

const WHOLE_NUM_PRIMITIVE = 6; // math_integer

const INTEGER_NUM_PRIMITIVE = 7; // math_angle

const ANGLE_NUM_PRIMITIVE = 8; // colour_picker

const COLOR_PICKER_PRIMITIVE = 9; // text

const TEXT_PRIMITIVE = 10; // event_broadcast_menu

const BROADCAST_PRIMITIVE = 11; // data_variable

const VAR_PRIMITIVE = 12; // data_listcontents

const LIST_PRIMITIVE = 13; // Map block opcodes to the above primitives and the name of the field we can use
// to find the value of the field

const primitiveOpcodeInfoMap = {
  math_number: [MATH_NUM_PRIMITIVE, 'NUM'],
  math_positive_number: [POSITIVE_NUM_PRIMITIVE, 'NUM'],
  math_whole_number: [WHOLE_NUM_PRIMITIVE, 'NUM'],
  math_integer: [INTEGER_NUM_PRIMITIVE, 'NUM'],
  math_angle: [ANGLE_NUM_PRIMITIVE, 'NUM'],
  colour_picker: [COLOR_PICKER_PRIMITIVE, 'COLOUR'],
  text: [TEXT_PRIMITIVE, 'TEXT'],
  event_broadcast_menu: [BROADCAST_PRIMITIVE, 'BROADCAST_OPTION'],
  data_variable: [VAR_PRIMITIVE, 'VARIABLE'],
  data_listcontents: [LIST_PRIMITIVE, 'LIST']
};
/**
 * Serializes primitives described above into a more compact format
 * @param {object} block the block to serialize
 * @return {array} An array representing the information in the block,
 * or null if the given block is not one of the primitives described above.
 */

const serializePrimitiveBlock = function serializePrimitiveBlock(block) {
  // Returns an array represeting a primitive block or null if not one of
  // the primitive types above
  if (hasOwnProperty.call(primitiveOpcodeInfoMap, block.opcode)) {
    const primitiveInfo = primitiveOpcodeInfoMap[block.opcode];
    const primitiveConstant = primitiveInfo[0];
    const fieldName = primitiveInfo[1];
    const field = block.fields[fieldName];
    const primitiveDesc = [primitiveConstant, field.value];

    if (block.opcode === 'event_broadcast_menu') {
      primitiveDesc.push(field.id);
    } else if (block.opcode === 'data_variable' || block.opcode === 'data_listcontents') {
      primitiveDesc.push(field.id);

      if (block.topLevel) {
        primitiveDesc.push(block.x ? Math.round(block.x) : 0);
        primitiveDesc.push(block.y ? Math.round(block.y) : 0);
      }
    }

    return primitiveDesc;
  }

  return null;
};
/**
 * Serializes the inputs field of a block in a compact form using
 * constants described above to represent the relationship between the
 * inputs of this block (e.g. if there is an unobscured shadow, an obscured shadow
 * -- a block plugged into a droppable input -- or, if there is just a block).
 * Based on this relationship, serializes the ids of the block and shadow (if present)
 *
 * @param {object} inputs The inputs to serialize
 * @return {object} An object representing the serialized inputs
 */


const serializeInputs = function serializeInputs(inputs) {
  const obj = Object.create(null);

  for (const inputName in inputs) {
    if (!hasOwnProperty.call(inputs, inputName)) continue; // if block and shadow refer to the same block, only serialize one

    if (inputs[inputName].block === inputs[inputName].shadow) {
      // has block and shadow, and they are the same
      obj[inputName] = [INPUT_SAME_BLOCK_SHADOW, inputs[inputName].block];
    } else if (inputs[inputName].shadow === null) {
      // does not have shadow
      obj[inputName] = [INPUT_BLOCK_NO_SHADOW, inputs[inputName].block];
    } else {
      // block and shadow are both present and are different
      obj[inputName] = [INPUT_DIFF_BLOCK_SHADOW, inputs[inputName].block, inputs[inputName].shadow];
    }
  }

  return obj;
};
/**
 * Serialize the fields of a block in a more compact form.
 * @param {object} fields The fields object to serialize
 * @return {object} An object representing the serialized fields
 */


const serializeFields = function serializeFields(fields) {
  const obj = Object.create(null);

  for (const fieldName in fields) {
    if (!hasOwnProperty.call(fields, fieldName)) continue;
    obj[fieldName] = [fields[fieldName].value];

    if (fields[fieldName].hasOwnProperty('id')) {
      obj[fieldName].push(fields[fieldName].id);
    }
  }

  return obj;
};
/**
 * Serialize the given block in the SB3 format with some compression of inputs,
 * fields, and primitives.
 * @param {object} block The block to serialize
 * @return {object | array} A serialized representation of the block. This is an
 * array if the block is one of the primitive types described above or an object,
 * if not.
 */


const serializeBlock = function serializeBlock(block) {
  const serializedPrimitive = serializePrimitiveBlock(block);
  if (serializedPrimitive) return serializedPrimitive; // If serializedPrimitive is null, proceed with serializing a non-primitive block

  const obj = Object.create(null);
  obj.opcode = block.opcode; // NOTE: this is extremely important to serialize even if null;
  // not serializing `next: null` results in strange behavior with block
  // execution

  obj.next = block.next;
  obj.parent = block.parent;
  obj.inputs = serializeInputs(block.inputs);
  obj.fields = serializeFields(block.fields);
  obj.shadow = block.shadow;

  if (block.topLevel) {
    obj.topLevel = true;
    obj.x = block.x ? Math.round(block.x) : 0;
    obj.y = block.y ? Math.round(block.y) : 0;
  } else {
    obj.topLevel = false;
  }

  if (block.mutation) {
    obj.mutation = block.mutation;
  }

  if (block.comment) {
    obj.comment = block.comment;
  }

  return obj;
};
/**
 * Compresses the serialized inputs replacing block/shadow ids that refer to
 * one of the primitives with the primitive itself. E.g.
 *
 * blocks: {
 *      aUidForMyBlock: {
 *          inputs: {
 *               MYINPUT: [1, 'aUidForAnUnobscuredShadowPrimitive']
 *          }
 *      },
 *      aUidForAnUnobscuredShadowPrimitive: [4, 10]
 *      // the above is a primitive representing a 'math_number' with value 10
 * }
 *
 * becomes:
 *
 * blocks: {
 *      aUidForMyBlock: {
 *          inputs: {
 *               MYINPUT: [1, [4, 10]]
 *          }
 *      }
 * }
 * Note: this function modifies the given blocks object in place
 * @param {object} block The block with inputs to compress
 * @param {objec} blocks The object containing all the blocks currently getting serialized
 * @return {object} The serialized block with compressed inputs
 */


const compressInputTree = function compressInputTree(block, blocks) {
  // This is the second pass on the block
  // so the inputs field should be an object of key - array pairs
  const serializedInputs = block.inputs;

  for (const inputName in serializedInputs) {
    // don't need to check for hasOwnProperty because of how we constructed
    // inputs
    const currInput = serializedInputs[inputName]; // traverse currInput skipping the first element, which describes whether the block
    // and shadow are the same

    for (let i = 1; i < currInput.length; i++) {
      if (!currInput[i]) continue; // need this check b/c block/shadow can be null

      const blockOrShadowID = currInput[i]; // replace element of currInput directly
      // (modifying input block directly)

      const blockOrShadow = blocks[blockOrShadowID];

      if (Array.isArray(blockOrShadow)) {
        currInput[i] = blockOrShadow; // Modifying blocks in place!

        delete blocks[blockOrShadowID];
      }
    }
  }

  return block;
};
/**
 * Get sanitized non-core extension ID for a given sb3 opcode.
 * Note that this should never return a URL. If in the future the SB3 loader supports loading extensions by URL, this
 * ID should be used to (for example) look up the extension's full URL from a table in the SB3's JSON.
 * @param {!string} opcode The opcode to examine for extension.
 * @return {?string} The extension ID, if it exists and is not a core extension.
 */


const getExtensionIdForOpcode = function getExtensionIdForOpcode(opcode) {
  // Allowed ID characters are those matching the regular expression [\w-]: A-Z, a-z, 0-9, and hyphen ("-").
  const index = opcode.indexOf('_');
  const forbiddenSymbols = /[^\w-]/g;
  const prefix = opcode.substring(0, index).replace(forbiddenSymbols, '-');

  if (CORE_EXTENSIONS.indexOf(prefix) === -1) {
    if (prefix !== '') return prefix;
  }
};
/**
 * Serialize the given blocks object (representing all the blocks for the target
 * currently being serialized.)
 * @param {object} blocks The blocks to be serialized
 * @return {Array} An array of the serialized blocks with compressed inputs and
 * compressed primitives and the list of all extension IDs present
 * in the serialized blocks.
 */


const serializeBlocks = function serializeBlocks(blocks) {
  const obj = Object.create(null);
  const extensionIDs = new Set();

  for (const blockID in blocks) {
    if (!blocks.hasOwnProperty(blockID)) continue;
    obj[blockID] = serializeBlock(blocks[blockID], blocks);
    const extensionID = getExtensionIdForOpcode(blocks[blockID].opcode);

    if (extensionID) {
      extensionIDs.add(extensionID);
    }
  } // once we have completed a first pass, do a second pass on block inputs


  for (const blockID in obj) {
    // don't need to do the hasOwnProperty check here since we
    // created an object that doesn't get extra properties/functions
    const serializedBlock = obj[blockID]; // caution, this function deletes parts of this object in place as
    // it's traversing it

    obj[blockID] = compressInputTree(serializedBlock, obj); // second pass on connecting primitives to serialized inputs directly
  } // Do one last pass and remove any top level shadows (these are caused by
  // a bug: LLK/scratch-vm#1011, and this pass should be removed once that is
  // completely fixed)


  for (const blockID in obj) {
    const serializedBlock = obj[blockID]; // If the current block is serialized as a primitive (e.g. it's an array
    // instead of an object), AND it is not one of the top level primitives
    // e.g. variable getter or list getter, then it should be deleted as it's
    // a shadow block, and there are no blocks that reference it, otherwise
    // they would have been compressed in the last pass)

    if (Array.isArray(serializedBlock) && [VAR_PRIMITIVE, LIST_PRIMITIVE].indexOf(serializedBlock[0]) < 0) {
      log.warn("Found an unexpected top level primitive with block ID: ".concat(blockID, "; deleting it from serialized blocks."));
      delete obj[blockID];
    }
  }

  return [obj, Array.from(extensionIDs)];
};
/**
 * Serialize the given costume.
 * @param {object} costume The costume to be serialized.
 * @return {object} A serialized representation of the costume.
 */


const serializeCostume = function serializeCostume(costume) {
  const obj = Object.create(null);
  obj.name = costume.name;
  const costumeToSerialize = costume.broken || costume;
  obj.bitmapResolution = costumeToSerialize.bitmapResolution;
  obj.dataFormat = costumeToSerialize.dataFormat.toLowerCase();
  obj.assetId = costumeToSerialize.assetId; // serialize this property with the name 'md5ext' because that's
  // what it's actually referring to. TODO runtime objects need to be
  // updated to actually refer to this as 'md5ext' instead of 'md5'
  // but that change should be made carefully since it is very
  // pervasive

  obj.md5ext = costumeToSerialize.md5;
  obj.rotationCenterX = costumeToSerialize.rotationCenterX;
  obj.rotationCenterY = costumeToSerialize.rotationCenterY;
  return obj;
};
/**
 * Serialize the given sound.
 * @param {object} sound The sound to be serialized.
 * @return {object} A serialized representation of the sound.
 */


const serializeSound = function serializeSound(sound) {
  const obj = Object.create(null);
  obj.name = sound.name;
  const soundToSerialize = sound.broken || sound;
  obj.assetId = soundToSerialize.assetId;
  obj.dataFormat = soundToSerialize.dataFormat.toLowerCase();
  obj.format = soundToSerialize.format;
  obj.rate = soundToSerialize.rate;
  obj.sampleCount = soundToSerialize.sampleCount; // serialize this property with the name 'md5ext' because that's
  // what it's actually referring to. TODO runtime objects need to be
  // updated to actually refer to this as 'md5ext' instead of 'md5'
  // but that change should be made carefully since it is very
  // pervasive

  obj.md5ext = soundToSerialize.md5;
  return obj;
}; // Using some bugs, it can be possible to get values like undefined, null, or complex objects into
// variables or lists. This will cause make the project unusable after exporting without JSON editing
// as it will fail validation in scratch-parser.
// To avoid this, we'll convert those objects to strings before saving them.


const isVariableValueSafeForJSON = value => typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean';

const makeSafeForJSON = value => {
  if (Array.isArray(value)) {
    let copy = null;

    for (let i = 0; i < value.length; i++) {
      if (!isVariableValueSafeForJSON(value[i])) {
        if (!copy) {
          // Only copy the list when needed
          copy = value.slice();
        }

        copy[i] = "".concat(copy[i]);
      }
    }

    if (copy) {
      return copy;
    }

    return value;
  }

  if (isVariableValueSafeForJSON(value)) {
    return value;
  }

  return "".concat(value);
};
/**
 * Serialize the given variables object.
 * @param {object} variables The variables to be serialized.
 * @return {object} A serialized representation of the variables. They get
 * separated by type to compress the representation of each given variable and
 * reduce duplicate information.
 */


const serializeVariables = function serializeVariables(variables) {
  const obj = Object.create(null); // separate out variables into types at the top level so we don't have
  // keep track of a type for each

  obj.variables = Object.create(null);
  obj.lists = Object.create(null);
  obj.broadcasts = Object.create(null);

  for (const varId in variables) {
    const v = variables[varId];

    if (v.type === Variable.BROADCAST_MESSAGE_TYPE) {
      obj.broadcasts[varId] = v.value; // name and value is the same for broadcast msgs

      continue;
    }

    if (v.type === Variable.LIST_TYPE) {
      obj.lists[varId] = [v.name, makeSafeForJSON(v.value)];
      continue;
    } // otherwise should be a scalar type


    obj.variables[varId] = [v.name, makeSafeForJSON(v.value)]; // only scalar vars have the potential to be cloud vars

    if (v.isCloud) obj.variables[varId].push(true);
  }

  return obj;
};

const serializeComments = function serializeComments(comments) {
  const obj = Object.create(null);

  for (const commentId in comments) {
    if (!comments.hasOwnProperty(commentId)) continue;
    const comment = comments[commentId];
    const serializedComment = Object.create(null);
    serializedComment.blockId = comment.blockId;
    serializedComment.x = comment.x;
    serializedComment.y = comment.y;
    serializedComment.width = comment.width;
    serializedComment.height = comment.height;
    serializedComment.minimized = comment.minimized;
    serializedComment.text = comment.text;
    obj[commentId] = serializedComment;
  }

  return obj;
};
/**
 * Serialize the given target. Only serialize properties that are necessary
 * for saving and loading this target.
 * @param {object} target The target to be serialized.
 * @param {Set} extensions A set of extensions to add extension IDs to
 * @return {object} A serialized representation of the given target.
 */


const serializeTarget = function serializeTarget(target, extensions) {
  const obj = Object.create(null);
  let targetExtensions = [];
  obj.isStage = target.isStage;
  obj.name = obj.isStage ? 'Stage' : target.name;
  const vars = serializeVariables(target.variables);
  obj.variables = vars.variables;
  obj.lists = vars.lists;
  obj.broadcasts = vars.broadcasts;
  [obj.blocks, targetExtensions] = serializeBlocks(target.blocks);
  obj.comments = serializeComments(target.comments); // TODO remove this check/patch when (#1901) is fixed

  if (target.currentCostume < 0 || target.currentCostume >= target.costumes.length) {
    log.warn("currentCostume property for target ".concat(target.name, " is out of range"));
    target.currentCostume = MathUtil.clamp(target.currentCostume, 0, target.costumes.length - 1);
  }

  obj.currentCostume = target.currentCostume;
  obj.costumes = target.costumes.map(serializeCostume);
  obj.sounds = target.sounds.map(serializeSound);
  if (target.hasOwnProperty('volume')) obj.volume = target.volume;
  if (target.hasOwnProperty('layerOrder')) obj.layerOrder = target.layerOrder;

  if (obj.isStage) {
    // Only the stage should have these properties
    if (target.hasOwnProperty('tempo')) obj.tempo = target.tempo;
    if (target.hasOwnProperty('videoTransparency')) obj.videoTransparency = target.videoTransparency;
    if (target.hasOwnProperty('videoState')) obj.videoState = target.videoState;
    if (target.hasOwnProperty('textToSpeechLanguage')) obj.textToSpeechLanguage = target.textToSpeechLanguage;
  } else {
    // The stage does not need the following properties, but sprites should
    obj.visible = target.visible;
    obj.x = target.x;
    obj.y = target.y;
    obj.size = target.size;
    obj.direction = target.direction;
    obj.draggable = target.draggable;
    obj.rotationStyle = target.rotationStyle;
  } // Add found extensions to the extensions object


  targetExtensions.forEach(extensionId => {
    extensions.add(extensionId);
  });
  return obj;
};

const getSimplifiedLayerOrdering = function getSimplifiedLayerOrdering(targets) {
  const layerOrders = targets.map(t => t.getLayerOrder());
  return MathUtil.reducedSortOrdering(layerOrders);
};

const serializeMonitors = function serializeMonitors(monitors, runtime) {
  // Monitors position is always stored as position from top-left corner in 480x360 stage.
  const xOffset = (runtime.stageWidth - 480) / 2;
  const yOffset = (runtime.stageHeight - 360) / 2;
  return monitors.valueSeq() // Don't include hidden monitors from extensions
  // https://github.com/LLK/scratch-vm/issues/2331
  .filter(monitorData => {
    const extensionID = getExtensionIdForOpcode(monitorData.opcode);
    return !extensionID || monitorData.visible;
  }).map(monitorData => {
    const serializedMonitor = {
      id: monitorData.id,
      mode: monitorData.mode,
      opcode: monitorData.opcode,
      params: monitorData.params,
      spriteName: monitorData.spriteName,
      value: Array.isArray(monitorData.value) ? [] : 0,
      width: monitorData.width,
      height: monitorData.height,
      x: monitorData.x - xOffset,
      y: monitorData.y - yOffset,
      visible: monitorData.visible
    };

    if (monitorData.mode !== 'list') {
      serializedMonitor.sliderMin = monitorData.sliderMin;
      serializedMonitor.sliderMax = monitorData.sliderMax;
      serializedMonitor.isDiscrete = monitorData.isDiscrete;
    }

    return serializedMonitor;
  });
};
/**
 * Serializes the specified VM runtime.
 * @param {!Runtime} runtime VM runtime instance to be serialized.
 * @param {string=} targetId Optional target id if serializing only a single target
 * @return {object} Serialized runtime instance.
 */


const serialize = function serialize(runtime, targetId) {
  let {
    allowOptimization = true
  } = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  // Fetch targets
  const obj = Object.create(null); // Create extension set to hold extension ids found while serializing targets

  const extensions = new Set();
  const originalTargetsToSerialize = targetId ? [runtime.getTargetById(targetId)] : runtime.targets.filter(target => target.isOriginal);
  const layerOrdering = getSimplifiedLayerOrdering(originalTargetsToSerialize);
  const flattenedOriginalTargets = originalTargetsToSerialize.map(t => t.toJSON()); // If the renderer is attached, and we're serializing a whole project (not a sprite)
  // add a temporary layerOrder property to each target.

  if (runtime.renderer && !targetId) {
    flattenedOriginalTargets.forEach((t, index) => {
      t.layerOrder = layerOrdering[index];
    });
  }

  const serializedTargets = flattenedOriginalTargets.map(t => serializeTarget(t, extensions));

  if (targetId) {
    return serializedTargets[0];
  }

  obj.targets = serializedTargets;
  obj.monitors = serializeMonitors(runtime.getMonitorState(), runtime); // Assemble extension list

  obj.extensions = Array.from(extensions); // Save list of URLs to load the current extensions
  // Extension manager only exists when runtime is wrapped by VirtualMachine

  if (runtime.extensionManager) {
    // We'll save the extensions in the format:
    // {
    //   "extension_id": "https://...",
    //   "other_id": "https://..."
    // }
    // Which lets the VM know which URLs correspond to which IDs, which is useful when the project
    // is being loaded. For example, if the extension is eventually converted to a builtin extension
    // or if it is already loaded, then it doesn't need to fetch the script again.
    const extensionURLs = runtime.extensionManager.getExtensionURLs();
    const urlsToSave = {};

    for (const extension of extensions) {
      const url = extensionURLs[extension];

      if (typeof url === 'string') {
        urlsToSave[extension] = url;
      }
    } // Only save this object if any URLs would actually be saved.


    if (Object.keys(urlsToSave).length !== 0) {
      obj.extensionURLs = urlsToSave;
    }
  } // Assemble metadata


  const meta = Object.create(null);
  meta.semver = '3.0.0'; // TW: There isn't a good reason to put the full version number in the json, so we don't.

  meta.vm = '0.2.0';

  if (runtime.origin) {
    meta.origin = runtime.origin;
  } // Attach full user agent string to metadata if available


  meta.agent = ''; // TW: Never include full user agent to slightly improve user privacy
  // if (typeof navigator !== 'undefined') meta.agent = navigator.userAgent;
  // Assemble payload and return

  obj.meta = meta;

  if (allowOptimization) {
    compress(obj);
  }

  return obj;
};
/**
 * Deserialize a block input descriptors. This is either a
 * block id or a serialized primitive, e.g. an array
 * (see serializePrimitiveBlock function).
 * @param {string | array} inputDescOrId The block input descriptor to be serialized.
 * @param {string} parentId The id of the parent block for this input block.
 * @param {boolean} isShadow Whether or not this input block is a shadow.
 * @param {object} blocks The entire blocks object currently in the process of getting serialized.
 * @return {object} The deserialized input descriptor.
 */


const deserializeInputDesc = function deserializeInputDesc(inputDescOrId, parentId, isShadow, blocks) {
  if (!Array.isArray(inputDescOrId)) return inputDescOrId;
  const primitiveObj = Object.create(null);
  const newId = uid();
  primitiveObj.id = newId;
  primitiveObj.next = null;
  primitiveObj.parent = parentId;
  primitiveObj.shadow = isShadow;
  primitiveObj.inputs = Object.create(null); // need a reference to parent id

  switch (inputDescOrId[0]) {
    case MATH_NUM_PRIMITIVE:
      {
        primitiveObj.opcode = 'math_number';
        primitiveObj.fields = {
          NUM: {
            name: 'NUM',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case POSITIVE_NUM_PRIMITIVE:
      {
        primitiveObj.opcode = 'math_positive_number';
        primitiveObj.fields = {
          NUM: {
            name: 'NUM',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case WHOLE_NUM_PRIMITIVE:
      {
        primitiveObj.opcode = 'math_whole_number';
        primitiveObj.fields = {
          NUM: {
            name: 'NUM',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case INTEGER_NUM_PRIMITIVE:
      {
        primitiveObj.opcode = 'math_integer';
        primitiveObj.fields = {
          NUM: {
            name: 'NUM',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case ANGLE_NUM_PRIMITIVE:
      {
        primitiveObj.opcode = 'math_angle';
        primitiveObj.fields = {
          NUM: {
            name: 'NUM',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case COLOR_PICKER_PRIMITIVE:
      {
        primitiveObj.opcode = 'colour_picker';
        primitiveObj.fields = {
          COLOUR: {
            name: 'COLOUR',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case TEXT_PRIMITIVE:
      {
        primitiveObj.opcode = 'text';
        primitiveObj.fields = {
          TEXT: {
            name: 'TEXT',
            value: inputDescOrId[1]
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case BROADCAST_PRIMITIVE:
      {
        primitiveObj.opcode = 'event_broadcast_menu';
        primitiveObj.fields = {
          BROADCAST_OPTION: {
            name: 'BROADCAST_OPTION',
            value: inputDescOrId[1],
            id: inputDescOrId[2],
            variableType: Variable.BROADCAST_MESSAGE_TYPE
          }
        };
        primitiveObj.topLevel = false;
        break;
      }

    case VAR_PRIMITIVE:
      {
        primitiveObj.opcode = 'data_variable';
        primitiveObj.fields = {
          VARIABLE: {
            name: 'VARIABLE',
            value: inputDescOrId[1],
            id: inputDescOrId[2],
            variableType: Variable.SCALAR_TYPE
          }
        };

        if (inputDescOrId.length > 3) {
          primitiveObj.topLevel = true;
          primitiveObj.x = inputDescOrId[3];
          primitiveObj.y = inputDescOrId[4];
        }

        break;
      }

    case LIST_PRIMITIVE:
      {
        primitiveObj.opcode = 'data_listcontents';
        primitiveObj.fields = {
          LIST: {
            name: 'LIST',
            value: inputDescOrId[1],
            id: inputDescOrId[2],
            variableType: Variable.LIST_TYPE
          }
        };

        if (inputDescOrId.length > 3) {
          primitiveObj.topLevel = true;
          primitiveObj.x = inputDescOrId[3];
          primitiveObj.y = inputDescOrId[4];
        }

        break;
      }

    default:
      {
        log.error("Found unknown primitive type during deserialization: ".concat(JSON.stringify(inputDescOrId)));
        return null;
      }
  }

  blocks[newId] = primitiveObj;
  return newId;
};
/**
 * Deserialize the given block inputs.
 * @param {object} inputs The inputs to deserialize.
 * @param {string} parentId The block id of the parent block
 * @param {object} blocks The object representing the entire set of blocks currently
 * in the process of getting deserialized.
 * @return {object} The deserialized and uncompressed inputs.
 */


const deserializeInputs = function deserializeInputs(inputs, parentId, blocks) {
  // Explicitly not using Object.create(null) here
  // because we call prototype functions later in the vm
  const obj = {};

  for (const inputName in inputs) {
    if (!hasOwnProperty.call(inputs, inputName)) continue;
    const inputDescArr = inputs[inputName]; // If this block has already been deserialized (it's not an array) skip it

    if (!Array.isArray(inputDescArr)) continue;
    let block = null;
    let shadow = null;
    const blockShadowInfo = inputDescArr[0];

    if (blockShadowInfo === INPUT_SAME_BLOCK_SHADOW) {
      // block and shadow are the same id, and only one is provided
      block = shadow = deserializeInputDesc(inputDescArr[1], parentId, true, blocks);
    } else if (blockShadowInfo === INPUT_BLOCK_NO_SHADOW) {
      block = deserializeInputDesc(inputDescArr[1], parentId, false, blocks);
    } else {
      // assume INPUT_DIFF_BLOCK_SHADOW
      block = deserializeInputDesc(inputDescArr[1], parentId, false, blocks);
      shadow = deserializeInputDesc(inputDescArr[2], parentId, true, blocks);
    }

    obj[inputName] = {
      name: inputName,
      block: block,
      shadow: shadow
    };
  }

  return obj;
};
/**
 * Deserialize the given block fields.
 * @param {object} fields The fields to be deserialized
 * @return {object} The deserialized and uncompressed block fields.
 */


const deserializeFields = function deserializeFields(fields) {
  // Explicitly not using Object.create(null) here
  // because we call prototype functions later in the vm
  const obj = {};

  for (const fieldName in fields) {
    if (!hasOwnProperty.call(fields, fieldName)) continue;
    const fieldDescArr = fields[fieldName]; // If this block has already been deserialized (it's not an array) skip it

    if (!Array.isArray(fieldDescArr)) continue;
    obj[fieldName] = {
      name: fieldName,
      value: fieldDescArr[0]
    };

    if (fieldDescArr.length > 1) {
      obj[fieldName].id = fieldDescArr[1];
    }

    if (fieldName === 'BROADCAST_OPTION') {
      obj[fieldName].variableType = Variable.BROADCAST_MESSAGE_TYPE;
    } else if (fieldName === 'VARIABLE') {
      obj[fieldName].variableType = Variable.SCALAR_TYPE;
    } else if (fieldName === 'LIST') {
      obj[fieldName].variableType = Variable.LIST_TYPE;
    }
  }

  return obj;
};
/**
 * Covnert serialized INPUT and FIELD primitives back to hydrated block templates.
 * Should be able to deserialize a format that has already been deserialized.  The only
 * "east" path to adding new targets/code requires going through deserialize, so it should
 * work with pre-parsed deserialized blocks.
 *
 * @param {object} blocks Serialized SB3 "blocks" property of a target. Will be mutated.
 * @return {object} input is modified and returned
 */


const deserializeBlocks = function deserializeBlocks(blocks) {
  for (const blockId in blocks) {
    if (!Object.prototype.hasOwnProperty.call(blocks, blockId)) {
      continue;
    }

    const block = blocks[blockId];

    if (Array.isArray(block)) {
      // this is one of the primitives
      // delete the old entry in object.blocks and replace it w/the
      // deserialized object
      delete blocks[blockId];
      deserializeInputDesc(block, null, false, blocks);
      continue;
    }

    block.id = blockId; // add id back to block since it wasn't serialized

    block.inputs = deserializeInputs(block.inputs, blockId, blocks);
    block.fields = deserializeFields(block.fields);
  }

  return blocks;
};
/**
 * Parse the assets of a single "Scratch object" and load them. This
 * preprocesses objects to support loading the data for those assets over a
 * network while the objects are further processed into Blocks, Sprites, and a
 * list of needed Extensions.
 * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime Runtime object to load all structures into.
 * @param {JSZip} zip Sb3 file describing this project (to load assets from)
 * @return {?{costumePromises:Array.<Promise>,soundPromises:Array.<Promise>,soundBank:SoundBank}}
 * Object of arrays of promises for asset objects used in Sprites. As well as a
 * SoundBank for the sound assets. null for unsupported objects.
 */


const parseScratchAssets = function parseScratchAssets(object, runtime, zip) {
  if (!object.hasOwnProperty('name')) {
    // Watcher/monitor - skip this object until those are implemented in VM.
    // @todo
    return Promise.resolve(null);
  }

  const assets = {
    costumePromises: null,
    soundPromises: null,
    soundBank: runtime.audioEngine && runtime.audioEngine.createBank()
  }; // Costumes from JSON.

  assets.costumePromises = (object.costumes || []).map(costumeSource => {
    // @todo: Make sure all the relevant metadata is being pulled out.
    const costume = {
      // costumeSource only has an asset if an image is being uploaded as
      // a sprite
      asset: costumeSource.asset,
      assetId: costumeSource.assetId,
      skinId: null,
      name: costumeSource.name,
      bitmapResolution: costumeSource.bitmapResolution,
      rotationCenterX: costumeSource.rotationCenterX,
      rotationCenterY: costumeSource.rotationCenterY
    };
    const dataFormat = costumeSource.dataFormat || costumeSource.assetType && costumeSource.assetType.runtimeFormat || // older format
    'png'; // if all else fails, guess that it might be a PNG

    const costumeMd5Ext = costumeSource.hasOwnProperty('md5ext') ? costumeSource.md5ext : "".concat(costumeSource.assetId, ".").concat(dataFormat);
    costume.md5 = costumeMd5Ext;
    costume.dataFormat = dataFormat; // deserializeCostume should be called on the costume object we're
    // creating above instead of the source costume object, because this way
    // we're always loading the 'sb3' representation of the costume
    // any translation that needs to happen will happen in the process
    // of building up the costume object into an sb3 format

    return deserializeCostume(costume, runtime, zip).then(() => loadCostume(costumeMd5Ext, costume, runtime)); // Only attempt to load the costume after the deserialization
    // process has been completed
  }); // Sounds from JSON

  assets.soundPromises = (object.sounds || []).map(soundSource => {
    const sound = {
      assetId: soundSource.assetId,
      format: soundSource.format,
      rate: soundSource.rate,
      sampleCount: soundSource.sampleCount,
      name: soundSource.name,
      // TODO we eventually want this property to be called md5ext,
      // but there are many things relying on this particular name at the
      // moment, so this translation is very important
      md5: soundSource.md5ext,
      dataFormat: soundSource.dataFormat,
      data: null
    }; // deserializeSound should be called on the sound object we're
    // creating above instead of the source sound object, because this way
    // we're always loading the 'sb3' representation of the costume
    // any translation that needs to happen will happen in the process
    // of building up the costume object into an sb3 format

    return deserializeSound(sound, runtime, zip).then(() => loadSound(sound, runtime, assets.soundBank)); // Only attempt to load the sound after the deserialization
    // process has been completed.
  });
  return assets;
};
/**
 * Parse a single "Scratch object" and create all its in-memory VM objects.
 * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime Runtime object to load all structures into.
 * @param {ImportedExtensionsInfo} extensions - (in/out) parsed extension information will be stored here.
 * @param {JSZip} zip Sb3 file describing this project (to load assets from)
 * @param {object} assets - Promises for assets of this scratch object grouped
 *   into costumes and sounds
 * @return {!Promise.<Target>} Promise for the target created (stage or sprite), or null for unsupported objects.
 */


const parseScratchObject = function parseScratchObject(object, runtime, extensions, zip, assets) {
  if (!object.hasOwnProperty('name')) {
    // Watcher/monitor - skip this object until those are implemented in VM.
    // @todo
    return Promise.resolve(null);
  } // Blocks container for this object.


  const blocks = new Blocks(runtime); // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.

  const sprite = new Sprite(blocks, runtime); // Sprite/stage name from JSON.

  if (object.hasOwnProperty('name')) {
    sprite.name = object.name;
  }

  if (object.hasOwnProperty('blocks')) {
    deserializeBlocks(object.blocks); // Take a second pass to create objects and add extensions

    for (const blockId in object.blocks) {
      if (!object.blocks.hasOwnProperty(blockId)) continue;
      const blockJSON = object.blocks[blockId];
      blocks.createBlock(blockJSON); // If the block is from an extension, record it.

      const extensionID = getExtensionIdForOpcode(blockJSON.opcode);

      if (extensionID) {
        extensions.extensionIDs.add(extensionID);
      }
    }
  } // Costumes from JSON.


  const {
    costumePromises
  } = assets; // Sounds from JSON

  const {
    soundBank,
    soundPromises
  } = assets; // Create the first clone, and load its run-state from JSON.

  const target = sprite.createClone(object.isStage ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER); // Load target properties from JSON.

  if (object.hasOwnProperty('tempo')) {
    target.tempo = object.tempo;
  }

  if (object.hasOwnProperty('volume')) {
    target.volume = object.volume;
  }

  if (object.hasOwnProperty('videoTransparency')) {
    target.videoTransparency = object.videoTransparency;
  }

  if (object.hasOwnProperty('videoState')) {
    target.videoState = object.videoState;
  }

  if (object.hasOwnProperty('textToSpeechLanguage')) {
    target.textToSpeechLanguage = object.textToSpeechLanguage;
  }

  if (object.hasOwnProperty('variables')) {
    for (const varId in object.variables) {
      const variable = object.variables[varId]; // A variable is a cloud variable if:
      // - the project says it's a cloud variable, and
      // - it's a stage variable, and
      // - the runtime can support another cloud variable

      const isCloud = variable.length === 3 && variable[2] && object.isStage && runtime.canAddCloudVariable();
      const newVariable = new Variable(varId, // var id is the index of the variable desc array in the variables obj
      variable[0], // name of the variable
      Variable.SCALAR_TYPE, // type of the variable
      isCloud);
      if (isCloud) runtime.addCloudVariable();
      newVariable.value = variable[1];
      target.variables[newVariable.id] = newVariable;
    }
  }

  if (object.hasOwnProperty('lists')) {
    for (const listId in object.lists) {
      const list = object.lists[listId];
      const newList = new Variable(listId, list[0], Variable.LIST_TYPE, false);
      newList.value = list[1];
      target.variables[newList.id] = newList;
    }
  }

  if (object.hasOwnProperty('broadcasts')) {
    for (const broadcastId in object.broadcasts) {
      const broadcast = object.broadcasts[broadcastId];
      const newBroadcast = new Variable(broadcastId, broadcast, Variable.BROADCAST_MESSAGE_TYPE, false); // no need to explicitly set the value, variable constructor
      // sets the value to the same as the name for broadcast msgs

      target.variables[newBroadcast.id] = newBroadcast;
    }
  }

  if (object.hasOwnProperty('comments')) {
    for (const commentId in object.comments) {
      const comment = object.comments[commentId];
      const newComment = new Comment(commentId, comment.text, comment.x, comment.y, comment.width, comment.height, comment.minimized);

      if (comment.blockId) {
        newComment.blockId = comment.blockId;
      }

      target.comments[newComment.id] = newComment;
    }
  }

  if (object.hasOwnProperty('x')) {
    target.x = object.x;
  }

  if (object.hasOwnProperty('y')) {
    target.y = object.y;
  }

  if (object.hasOwnProperty('direction')) {
    target.direction = object.direction;
  }

  if (object.hasOwnProperty('size')) {
    target.size = object.size;
  }

  if (object.hasOwnProperty('visible')) {
    target.visible = object.visible;
  }

  if (object.hasOwnProperty('currentCostume')) {
    target.currentCostume = MathUtil.clamp(object.currentCostume, 0, object.costumes.length - 1);
  }

  if (object.hasOwnProperty('rotationStyle')) {
    target.rotationStyle = object.rotationStyle;
  }

  if (object.hasOwnProperty('isStage')) {
    target.isStage = object.isStage;
  }

  if (object.hasOwnProperty('targetPaneOrder')) {
    // Temporarily store the 'targetPaneOrder' property
    // so that we can correctly order sprites in the target pane.
    // This will be deleted after we are done parsing and ordering the targets list.
    target.targetPaneOrder = object.targetPaneOrder;
  }

  if (object.hasOwnProperty('draggable')) {
    target.draggable = object.draggable;
  }

  Promise.all(costumePromises).then(costumes => {
    sprite.costumes = costumes;
  });
  Promise.all(soundPromises).then(sounds => {
    sprite.sounds = sounds; // Make sure if soundBank is undefined, sprite.soundBank is then null.

    sprite.soundBank = soundBank || null;
  });
  return Promise.all(costumePromises.concat(soundPromises)).then(() => target);
};

const deserializeMonitor = function deserializeMonitor(monitorData, runtime, targets, extensions) {
  // Monitors position is always stored as position from top-left corner in 480x360 stage.
  const xOffset = (runtime.stageWidth - 480) / 2;
  const yOffset = (runtime.stageHeight - 360) / 2;
  monitorData.x += xOffset;
  monitorData.y += yOffset;
  monitorData.x = MathUtil.clamp(monitorData.x, 0, runtime.stageWidth);
  monitorData.y = MathUtil.clamp(monitorData.y, 0, runtime.stageHeight); // If the serialized monitor has spriteName defined, look up the sprite
  // by name in the given list of targets and update the monitor's targetId
  // to match the sprite's id.

  if (monitorData.spriteName) {
    const filteredTargets = targets.filter(t => t.sprite.name === monitorData.spriteName);

    if (filteredTargets && filteredTargets.length > 0) {
      monitorData.targetId = filteredTargets[0].id;
    } else {
      log.warn("Tried to deserialize sprite specific monitor ".concat(monitorData.opcode, " but could not find sprite ").concat(monitorData.spriteName, "."));
    }
  } // Get information about this monitor, if it exists, given the monitor's opcode.
  // This will be undefined for extension blocks


  const monitorBlockInfo = runtime.monitorBlockInfo[monitorData.opcode]; // Due to a bug (see https://github.com/LLK/scratch-vm/pull/2322), renamed list monitors may have been serialized
  // with an outdated/incorrect LIST parameter. Fix it up to use the current name of the actual corresponding list.

  if (monitorData.opcode === 'data_listcontents') {
    const listTarget = monitorData.targetId ? targets.find(t => t.id === monitorData.targetId) : targets.find(t => t.isStage);

    if (listTarget && Object.prototype.hasOwnProperty.call(listTarget.variables, monitorData.id)) {
      monitorData.params.LIST = listTarget.variables[monitorData.id].name;
    }
  } // Convert the serialized monitorData params into the block fields structure


  const fields = {};

  for (const paramKey in monitorData.params) {
    const field = {
      name: paramKey,
      value: monitorData.params[paramKey]
    };
    fields[paramKey] = field;
  } // Variables, lists, and non-sprite-specific monitors, including any extension
  // monitors should already have the correct monitor ID serialized in the monitorData,
  // find the correct id for all other monitors.


  if (monitorData.opcode !== 'data_variable' && monitorData.opcode !== 'data_listcontents' && monitorBlockInfo && monitorBlockInfo.isSpriteSpecific) {
    monitorData.id = monitorBlockInfo.getId(monitorData.targetId, fields);
  } else {
    // Replace unsafe characters in monitor ID, if there are any.
    // These would have come from projects that were originally 2.0 projects
    // that had unsafe characters in the variable name (and then the name was
    // used as part of the variable ID when importing the project).
    monitorData.id = StringUtil.replaceUnsafeChars(monitorData.id);
  } // If the runtime already has a monitor block for this monitor's id,
  // update the existing block with the relevant monitor information.


  const existingMonitorBlock = runtime.monitorBlocks._blocks[monitorData.id];

  if (existingMonitorBlock) {
    // A monitor block already exists if the toolbox has been loaded and
    // the monitor block is not target specific (because the block gets recycled).
    existingMonitorBlock.isMonitored = monitorData.visible;
    existingMonitorBlock.targetId = monitorData.targetId;
  } else {
    // If a monitor block doesn't already exist for this monitor,
    // construct a monitor block to add to the monitor blocks container
    const monitorBlock = {
      id: monitorData.id,
      opcode: monitorData.opcode,
      inputs: {},
      // Assuming that monitor blocks don't have droppable fields
      fields: fields,
      topLevel: true,
      next: null,
      parent: null,
      shadow: false,
      x: 0,
      y: 0,
      isMonitored: monitorData.visible,
      targetId: monitorData.targetId
    }; // Variables and lists have additional properties
    // stored in their fields, update this info in the
    // monitor block fields

    if (monitorData.opcode === 'data_variable') {
      const field = monitorBlock.fields.VARIABLE;
      field.id = monitorData.id;
      field.variableType = Variable.SCALAR_TYPE;
    } else if (monitorData.opcode === 'data_listcontents') {
      const field = monitorBlock.fields.LIST;
      field.id = monitorData.id;
      field.variableType = Variable.LIST_TYPE;
    }

    runtime.monitorBlocks.createBlock(monitorBlock); // If the block is from an extension, record it.

    const extensionID = getExtensionIdForOpcode(monitorBlock.opcode);

    if (extensionID) {
      extensions.extensionIDs.add(extensionID);
    }
  }

  runtime.requestAddMonitor(MonitorRecord(monitorData));
}; // Replace variable IDs throughout the project with
// xml-safe versions.
// This is to fix up projects imported from 2.0 where xml-unsafe names
// were getting added to the variable ids.


const replaceUnsafeCharsInVariableIds = function replaceUnsafeCharsInVariableIds(targets) {
  const allVarRefs = VariableUtil.getAllVarRefsForTargets(targets, true); // Re-id the variables in the actual targets

  targets.forEach(t => {
    Object.keys(t.variables).forEach(id => {
      const newId = StringUtil.replaceUnsafeChars(id);
      if (newId === id) return;
      t.variables[id].id = newId;
      t.variables[newId] = t.variables[id];
      delete t.variables[id];
    });
  }); // Replace the IDs in the blocks refrencing variables or lists

  for (const id in allVarRefs) {
    const newId = StringUtil.replaceUnsafeChars(id);
    if (id === newId) continue; // ID was already safe, skip
    // We're calling this on the stage target because we need a
    // target to call on but this shouldn't matter because we're passing
    // in all the varRefs we want to operate on

    VariableUtil.updateVariableIdentifiers(allVarRefs[id], newId);
  }

  return targets;
};
/**
 * Deserialize the specified representation of a VM runtime and loads it into the provided runtime instance.
 * @param  {object} json - JSON representation of a VM runtime.
 * @param  {Runtime} runtime - Runtime instance
 * @param {JSZip} zip - Sb3 file describing this project (to load assets from)
 * @param {boolean} isSingleSprite - If true treat as single sprite, else treat as whole project
 * @returns {Promise.<ImportedProject>} Promise that resolves to the list of targets after the project is deserialized
 */


const deserialize = function deserialize(json, runtime, zip, isSingleSprite) {
  const extensions = {
    extensionIDs: new Set(),
    extensionURLs: new Map()
  }; // Store the origin field (e.g. project originated at CSFirst) so that we can save it again.

  if (json.meta && json.meta.origin) {
    runtime.origin = json.meta.origin;
  } else {
    runtime.origin = null;
  } // Extract custom extension IDs, if they exist.


  if (json.extensionURLs) {
    extensions.extensionURLs = new Map(Object.entries(json.extensionURLs));
  } // First keep track of the current target order in the json,
  // then sort by the layer order property before parsing the targets
  // so that their corresponding render drawables can be created in
  // their layer order (e.g. back to front)


  const targetObjects = ((isSingleSprite ? [json] : json.targets) || []).map((t, i) => Object.assign(t, {
    targetPaneOrder: i
  })).sort((a, b) => a.layerOrder - b.layerOrder);
  const monitorObjects = json.monitors || [];
  return Promise.resolve(targetObjects.map(target => parseScratchAssets(target, runtime, zip))) // Force this promise to wait for the next loop in the js tick. Let
  // storage have some time to send off asset requests.
  .then(assets => Promise.resolve(assets)).then(assets => Promise.all(targetObjects.map((target, index) => parseScratchObject(target, runtime, extensions, zip, assets[index])))).then(targets => targets // Re-sort targets back into original sprite-pane ordering
  .map((t, i) => {
    // Add layer order property to deserialized targets.
    // This property is used to initialize executable targets in
    // the correct order and is deleted in VM's installTargets function
    t.layerOrder = i;
    return t;
  }).sort((a, b) => a.targetPaneOrder - b.targetPaneOrder).map(t => {
    // Delete the temporary properties used for
    // sprite pane ordering and stage layer ordering
    delete t.targetPaneOrder;
    return t;
  })).then(targets => replaceUnsafeCharsInVariableIds(targets)).then(targets => {
    monitorObjects.map(monitorDesc => deserializeMonitor(monitorDesc, runtime, targets, extensions));
    return targets;
  }).then(targets => ({
    targets,
    extensions
  }));
};

module.exports = {
  serialize: serialize,
  deserialize: deserialize,
  deserializeBlocks: deserializeBlocks,
  serializeBlocks: serializeBlocks,
  getExtensionIdForOpcode: getExtensionIdForOpcode
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/serialize-assets.js":
/*!***********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/serialize-assets.js ***!
  \***********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Serialize all the assets of the given type ('sounds' or 'costumes')
 * in the provided runtime into an array of file descriptors.
 * A file descriptor is an object containing the name of the file
 * to be written and the contents of the file, the serialized asset.
 * @param {Runtime} runtime The runtime with the assets to be serialized
 * @param {string} assetType The type of assets to be serialized: 'sounds' | 'costumes'
 * @param {string=} optTargetId Optional target id to serialize assets for
 * @returns {Array<object>} An array of file descriptors for each asset
 */
const serializeAssets = function serializeAssets(runtime, assetType, optTargetId) {
  const targets = optTargetId ? [runtime.getTargetById(optTargetId)] : runtime.targets;
  const assetDescs = [];

  for (let i = 0; i < targets.length; i++) {
    const currTarget = targets[i];
    const currAssets = currTarget.sprite[assetType];

    for (let j = 0; j < currAssets.length; j++) {
      const currAsset = currAssets[j];
      const asset = currAsset.broken ? currAsset.broken.asset : currAsset.asset;

      if (asset) {
        // Serialize asset if it exists, otherwise skip
        assetDescs.push({
          fileName: "".concat(asset.assetId, ".").concat(asset.dataFormat),
          fileContent: asset.data
        });
      }
    }
  }

  return assetDescs;
};
/**
 * Serialize all the sounds in the provided runtime or, if a target id is provided,
 * in the specified target into an array of file descriptors.
 * A file descriptor is an object containing the name of the file
 * to be written and the contents of the file, the serialized sound.
 * @param {Runtime} runtime The runtime with the sounds to be serialized
 * @param {string=} optTargetId Optional targetid for serializing sounds of a single target
 * @returns {Array<object>} An array of file descriptors for each sound
 */


const serializeSounds = function serializeSounds(runtime, optTargetId) {
  return serializeAssets(runtime, 'sounds', optTargetId);
};
/**
 * Serialize all the costumes in the provided runtime into an array of file
 * descriptors. A file descriptor is an object containing the name of the file
 * to be written and the contents of the file, the serialized costume.
 * @param {Runtime} runtime The runtime with the costumes to be serialized
 * @param {string} optTargetId Optional targetid for serializing costumes of a single target
 * @returns {Array<object>} An array of file descriptors for each costume
 */


const serializeCostumes = function serializeCostumes(runtime, optTargetId) {
  return serializeAssets(runtime, 'costumes', optTargetId);
};

module.exports = {
  serializeSounds,
  serializeCostumes
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/tw-compress-sb3.js":
/*!**********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/tw-compress-sb3.js ***!
  \**********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

const SOUP = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#%()*+,-./:;=?@[]^_`{|}~';

const generateId = i => {
  let str = '';

  while (i >= 0) {
    str = SOUP[i % SOUP.length] + str;
    i = Math.floor(i / SOUP.length) - 1;
  }

  return str;
};

class Pool {
  constructor() {
    this.generatedIds = new Map();
    this.references = new Map();
    this.skippedIds = new Set(); // IDs in Object.keys(vm.runtime.monitorBlocks._blocks) already have meaning, so make sure to skip those
    // We don't bother listing many here because most would take more than ten million items to be used

    this.skippedIds.add('of');
  }

  skip(id) {
    this.skippedIds.add(id);
  }

  addReference(id) {
    const currentCount = this.references.get(id) || 0;
    this.references.set(id, currentCount + 1);
  }

  generateNewIds() {
    const entries = Array.from(this.references.entries()); // The most used original IDs should get the shortest new IDs.

    entries.sort((a, b) => b[1] - a[1]);
    let i = 0;
    let newId;

    for (const entry of entries) {
      const oldId = entry[0];

      while (true) {
        newId = generateId(i);

        if (this.skippedIds.has(newId)) {
          i++;
        } else {
          break;
        }
      }

      this.generatedIds.set(oldId, newId);
      i++;
    }
  }

  getNewId(originalId) {
    if (this.generatedIds.has(originalId)) {
      return this.generatedIds.get(originalId);
    }

    return originalId;
  }

}

const compress = projectData => {
  // projectData is modified in-place
  // The optimization here is not optimal. This is intentional.
  // We only compress block and comment IDs because we want to maintain 100% (not 99.99%; 100%) compatibility and be
  // truly lossless. Optimizing things like variable IDs will cause things such as the editor's backpack feature
  // to misbehave.
  // We use the same variable pool for all objects to avoid any possible issues if IDs are ever treated as unique
  // within a given project.
  const pool = new Pool();

  for (const target of projectData.targets) {
    // While we don't compress these IDs, we need to make sure that our compressed IDs
    // do not intersect, which could happen if the project was compressed with a
    // different tool.
    for (const variableId of Object.keys(target.variables)) {
      pool.skip(variableId);
    }

    for (const listId of Object.keys(target.lists)) {
      pool.skip(listId);
    }

    for (const broadcastId of Object.keys(target.broadcasts)) {
      pool.skip(broadcastId);
    }

    for (const blockId of Object.keys(target.blocks)) {
      const block = target.blocks[blockId];
      pool.addReference(blockId);

      if (Array.isArray(block)) {
        // Compressed native
        continue;
      }

      if (block.parent) {
        pool.addReference(block.parent);
      }

      if (block.next) {
        pool.addReference(block.next);
      }

      if (block.comment) {
        pool.addReference(block.comment);
      }

      for (const input of Object.values(block.inputs)) {
        for (let i = 1; i < input.length; i++) {
          const inputValue = input[i];

          if (typeof inputValue === 'string') {
            pool.addReference(inputValue);
          }
        }
      }
    }

    for (const commentId of Object.keys(target.comments)) {
      const comment = target.comments[commentId];
      pool.addReference(commentId);

      if (comment.blockId) {
        pool.addReference(comment.blockId);
      }
    }
  }

  pool.generateNewIds();

  for (const target of projectData.targets) {
    const newBlocks = {};
    const newComments = {};

    for (const blockId of Object.keys(target.blocks)) {
      const block = target.blocks[blockId];
      newBlocks[pool.getNewId(blockId)] = block;

      if (Array.isArray(block)) {
        // Compressed native
        continue;
      }

      if (block.parent) {
        block.parent = pool.getNewId(block.parent);
      }

      if (block.next) {
        block.next = pool.getNewId(block.next);
      }

      if (block.comment) {
        block.comment = pool.getNewId(block.comment);
      }

      for (const input of Object.values(block.inputs)) {
        for (let i = 1; i < input.length; i++) {
          const inputValue = input[i];

          if (typeof inputValue === 'string') {
            input[i] = pool.getNewId(inputValue);
          }
        }
      }
    }

    for (const commentId of Object.keys(target.comments)) {
      const comment = target.comments[commentId];
      newComments[pool.getNewId(commentId)] = comment;

      if (comment.blockId) {
        comment.blockId = pool.getNewId(comment.blockId);
      }
    }

    target.blocks = newBlocks;
    target.comments = newComments;
  }
};

module.exports = compress;

/***/ }),

/***/ "./node_modules/scratch-vm/src/serialization/tw-costume-import-export.js":
/*!*******************************************************************************!*\
  !*** ./node_modules/scratch-vm/src/serialization/tw-costume-import-export.js ***!
  \*******************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// We want to preserve the rotation center of exported SVGs when they are later imported.
// Unfortunately, the SVG itself does not have sufficient information to accomplish this.
// Instead we must add a small amount of extra information to the end of exported SVGs
// that can be read on import.
// Adding this comment in scratch-paint is not a viable approach because the user can
// open projects not made with TurboWarp and we want costumes exported from there to
// have their center saved even if they haven't been edited.
let _TextEncoder;

let _TextDecoder;

if (typeof TextEncoder === 'undefined') {
  _TextEncoder = __webpack_require__(/*! text-encoding */ "./src/scaffolding/text-encoding/index.js").TextEncoder;
  _TextDecoder = __webpack_require__(/*! text-encoding */ "./src/scaffolding/text-encoding/index.js").TextDecoder;
} else {
  _TextEncoder = TextEncoder;
  _TextDecoder = TextDecoder;
} // Using literal HTML comments tokens will cause this script to be very hard to inline in
// a <script> element, so we'll instead do this terrible hack which the minifier probably
// won't be able to optimize away.


const HTML_COMMENT_START = "<!".concat('-'.repeat(2));
const HTML_COMMENT_END = "".concat('-'.repeat(2), ">");
const regex = new RegExp("".concat(HTML_COMMENT_START, "rotationCenter:(-?[\\d\\.]+):(-?[\\d\\.]+)").concat(HTML_COMMENT_END, "$"));
/**
 * @param {string} svgString SVG source
 * @returns {[number, number]|null} The detected rotation center of the SVG, if any.
 */

const parseVectorMetadata = svgString => {
  // TODO: see if this is slow on large strings
  const match = svgString.match(regex);

  if (!match) {
    return null;
  }

  const detectedX = +match[1];
  const detectedY = +match[2];

  if (Number.isNaN(detectedX) || Number.isNaN(detectedY)) {
    return null;
  }

  return [detectedX, detectedY];
};
/**
 * @param {Costume} costume scratch-vm costume object
 * @returns {Uint8Array} Binary data to export
 */


const exportCostume = costume => {
  /** @type {Uint8Array} */
  const originalData = costume.asset.data;

  if (costume.dataFormat !== 'svg') {
    return originalData;
  }

  let decodedData = new _TextDecoder().decode(originalData); // It's okay that the regex isn't global because it can only match one item anyways.

  decodedData = decodedData.replace(regex, '');
  const centerX = costume.rotationCenterX;
  const centerY = costume.rotationCenterY;
  const extraData = "".concat(HTML_COMMENT_START, "rotationCenter:").concat(centerX, ":").concat(centerY).concat(HTML_COMMENT_END);
  decodedData += extraData;
  return new _TextEncoder().encode(decodedData);
};

module.exports = {
  parseVectorMetadata,
  exportCostume
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/sprites/rendered-target.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/sprites/rendered-target.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const MathUtil = __webpack_require__(/*! ../util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const Cast = __webpack_require__(/*! ../util/cast */ "./node_modules/scratch-vm/src/util/cast.js");

const Clone = __webpack_require__(/*! ../util/clone */ "./node_modules/scratch-vm/src/util/clone.js");

const Target = __webpack_require__(/*! ../engine/target */ "./node_modules/scratch-vm/src/engine/target.js");

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");
/**
 * Rendered target: instance of a sprite (clone), or the stage.
 */


class RenderedTarget extends Target {
  /**
   * @param {!Sprite} sprite Reference to the parent sprite.
   * @param {Runtime} runtime Reference to the runtime.
   * @constructor
   */
  constructor(sprite, runtime) {
    super(runtime, sprite.blocks);
    /**
     * Reference to the sprite that this is a render of.
     * @type {!Sprite}
     */

    this.sprite = sprite;
    /**
     * Reference to the global renderer for this VM, if one exists.
     * @type {?RenderWebGL}
     */

    this.renderer = null;

    if (this.runtime) {
      this.renderer = this.runtime.renderer;
    }
    /**
     * ID of the drawable for this rendered target,
     * returned by the renderer, if rendered.
     * @type {?Number}
     */


    this.drawableID = null;
    /**
     * Drag state of this rendered target. If true, x/y position can't be
     * changed by blocks.
     * @type {boolean}
     */

    this.dragging = false;
    /**
     * Map of current graphic effect values.
     * @type {!Object.<string, number>}
     */

    this.effects = {
      color: 0,
      fisheye: 0,
      whirl: 0,
      pixelate: 0,
      mosaic: 0,
      brightness: 0,
      ghost: 0
    };
    /**
     * Whether this represents an "original" non-clone rendered-target for a sprite,
     * i.e., created by the editor and not clone blocks.
     * @type {boolean}
     */

    this.isOriginal = true;
    /**
     * Whether this rendered target represents the Scratch stage.
     * @type {boolean}
     */

    this.isStage = false;
    /**
     * Scratch X coordinate. Currently should range from -240 to 240.
     * @type {Number}
     */

    this.x = 0;
    /**
     * Scratch Y coordinate. Currently should range from -180 to 180.
     * @type {number}
     */

    this.y = 0;
    /**
     * Scratch direction. Currently should range from -179 to 180.
     * @type {number}
     */

    this.direction = 90;
    /**
     * Whether the rendered target is draggable on the stage
     * @type {boolean}
     */

    this.draggable = false;
    /**
     * Whether the rendered target is currently visible.
     * @type {boolean}
     */

    this.visible = true;
    /**
     * Size of rendered target as a percent of costume size.
     * @type {number}
     */

    this.size = 100;
    /**
     * Currently selected costume index.
     * @type {number}
     */

    this.currentCostume = 0;
    /**
     * Current rotation style.
     * @type {!string}
     */

    this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
    /**
     * Loudness for sound playback for this target, as a percentage.
     * @type {number}
     */

    this.volume = 100;
    /**
     * Current tempo (used by the music extension).
     * This property is global to the project and stored in the stage.
     * @type {number}
     */

    this.tempo = 60;
    /**
     * The transparency of the video (used by extensions with camera input).
     * This property is global to the project and stored in the stage.
     * @type {number}
     */

    this.videoTransparency = 50;
    /**
     * The state of the video input (used by extensions with camera input).
     * This property is global to the project and stored in the stage.
     *
     * Defaults to ON. This setting does not turn the video by itself. A
     * video extension once loaded will set the video device to this
     * setting. Set to ON when a video extension is added in the editor the
     * video will start ON. If the extension is loaded as part of loading a
     * saved project the extension will see the value set when the stage
     * was loaded from the saved values including the video state.
     *
     * @type {string}
     */

    this.videoState = RenderedTarget.VIDEO_STATE.ON;
    /**
     * The language to use for speech synthesis, in the text2speech extension.
     * It is initialized to null so that on extension load, we can check for
     * this and try setting it using the editor locale.
     * @type {string}
     */

    this.textToSpeechLanguage = null; // Node-style event emitters have non-zero performance overhead compared to function calls, so we
    // replace some very high frequency events with these specific methods that are overridden elsewhere.

    this.onTargetMoved = null;
    this.onTargetVisualChange = null;
    this.interpolationData = null;
  }
  /**
   * Create a drawable with the this.renderer.
   * @param {boolean} layerGroup The layer group this drawable should be added to
   */


  initDrawable(layerGroup) {
    if (this.renderer) {
      this.drawableID = this.renderer.createDrawable(layerGroup);
    } // If we're a clone, start the hats.


    if (!this.isOriginal) {
      this.runtime.startHats('control_start_as_clone', null, this);
    }
  }

  get audioPlayer() {
    /* eslint-disable no-console */
    console.warn('get audioPlayer deprecated, please update to use .sprite.soundBank methods');
    console.warn(new Error('stack for debug').stack);
    /* eslint-enable no-console */

    const bank = this.sprite.soundBank;
    const audioPlayerProxy = {
      playSound: soundId => bank.play(this, soundId)
    };
    Object.defineProperty(this, 'audioPlayer', {
      configurable: false,
      enumerable: true,
      writable: false,
      value: audioPlayerProxy
    });
    return audioPlayerProxy;
  }
  /**
   * Initialize the audio player for this sprite or clone.
   */


  initAudio() {}
  /**
   * Rotation style for "all around"/spinning.
   * @type {string}
   */


  static get ROTATION_STYLE_ALL_AROUND() {
    return 'all around';
  }
  /**
   * Rotation style for "left-right"/flipping.
   * @type {string}
   */


  static get ROTATION_STYLE_LEFT_RIGHT() {
    return 'left-right';
  }
  /**
   * Rotation style for "no rotation."
   * @type {string}
   */


  static get ROTATION_STYLE_NONE() {
    return "don't rotate";
  }
  /**
   * Available states for video input.
   * @enum {string}
   */


  static get VIDEO_STATE() {
    return {
      OFF: 'off',
      ON: 'on',
      ON_FLIPPED: 'on-flipped'
    };
  }

  emitVisualChange() {
    if (this.onTargetVisualChange) {
      this.onTargetVisualChange(this);
    }
  }
  /**
   * Set the X and Y coordinates.
   * @param {!number} x New X coordinate, in Scratch coordinates.
   * @param {!number} y New Y coordinate, in Scratch coordinates.
   * @param {?boolean} force Force setting X/Y, in case of dragging
   */


  setXY(x, y, force) {
    // used by compiler
    if (this.isStage) return;
    if (this.dragging && !force) return;
    const oldX = this.x;
    const oldY = this.y;

    if (this.renderer) {
      const position = this.runtime.runtimeOptions.fencing ? this.renderer.getFencedPositionOfDrawable(this.drawableID, [x, y]) : [x, y];
      this.x = position[0];
      this.y = position[1];
      this.renderer.updateDrawablePosition(this.drawableID, position);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    } else {
      this.x = x;
      this.y = y;
    }

    if (this.onTargetMoved) {
      this.onTargetMoved(this, oldX, oldY, force);
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Get the rendered direction and scale, after applying rotation style.
   * @return {object<string, number>} Direction and scale to render.
   */


  _getRenderedDirectionAndScale() {
    // Default: no changes to `this.direction` or `this.scale`.
    let finalDirection = this.direction;
    let finalScale = [this.size, this.size];

    if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) {
      // Force rendered direction to be 90.
      finalDirection = 90;
    } else if (this.rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) {
      // Force rendered direction to be 90, and flip drawable if needed.
      finalDirection = 90;
      const scaleFlip = this.direction < 0 ? -1 : 1;
      finalScale = [scaleFlip * this.size, this.size];
    }

    return {
      direction: finalDirection,
      scale: finalScale
    };
  }
  /**
   * Set the direction.
   * @param {!number} direction New direction.
   */


  setDirection(direction) {
    // used by compiler
    if (this.isStage) {
      return;
    }

    if (!isFinite(direction)) {
      return;
    } // Keep direction between -179 and +180.


    this.direction = MathUtil.wrapClamp(direction, -179, 180);

    if (this.renderer) {
      const {
        direction: renderedDirection,
        scale
      } = this._getRenderedDirectionAndScale();

      this.renderer.updateDrawableDirectionScale(this.drawableID, renderedDirection, scale);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Set draggability; i.e., whether it's able to be dragged in the player
   * @param {!boolean} draggable True if should be draggable.
   */


  setDraggable(draggable) {
    if (this.isStage) return;
    this.draggable = !!draggable;
    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Set visibility; i.e., whether it's shown or hidden.
   * @param {!boolean} visible True if should be shown.
   */


  setVisible(visible) {
    // used by compiler
    if (this.isStage) {
      return;
    }

    this.visible = !!visible;

    if (this.renderer) {
      this.renderer.updateDrawableVisible(this.drawableID, this.visible);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Set size, as a percentage of the costume size.
   * @param {!number} size Size of rendered target, as % of costume size.
   */


  setSize(size) {
    // used by compiler
    if (this.isStage) {
      return;
    }

    if (this.renderer) {
      // Clamp to scales relative to costume and stage size.
      // See original ScratchSprite.as:setSize.
      const costumeSize = this.renderer.getCurrentSkinSize(this.drawableID);
      const origW = costumeSize[0];
      const origH = costumeSize[1];
      const fencing = this.runtime.runtimeOptions.fencing;
      const minScale = fencing ? Math.min(1, Math.max(5 / origW, 5 / origH)) : 0;
      const maxScale = fencing ? Math.min(1.5 * this.runtime.stageWidth / origW, 1.5 * this.runtime.stageHeight / origH) : Infinity;
      this.size = MathUtil.clamp(size / 100, minScale, maxScale) * 100;

      const {
        direction,
        scale
      } = this._getRenderedDirectionAndScale();

      this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    } else {
      // tw: setSize should update size even without a renderer
      // needed by tw-change-size-does-not-use-rounded-size.sb3 test
      this.size = size;
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Set a particular graphic effect value.
   * @param {!string} effectName Name of effect (see `RenderedTarget.prototype.effects`).
   * @param {!number} value Numerical magnitude of effect.
   */


  setEffect(effectName, value) {
    // used by compiler
    if (!this.effects.hasOwnProperty(effectName)) return;
    this.effects[effectName] = value;

    if (this.renderer) {
      this.renderer.updateDrawableEffect(this.drawableID, effectName, value);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }
  }
  /**
   * Clear all graphic effects on this rendered target.
   */


  clearEffects() {
    // used by compiler
    for (const effectName in this.effects) {
      if (!this.effects.hasOwnProperty(effectName)) continue;
      this.effects[effectName] = 0;
    }

    if (this.renderer) {
      for (const effectName in this.effects) {
        if (!this.effects.hasOwnProperty(effectName)) continue;
        this.renderer.updateDrawableEffect(this.drawableID, effectName, 0);
      }

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }
  }
  /**
   * Set the current costume.
   * @param {number} index New index of costume.
   */


  setCostume(index) {
    // Keep the costume index within possible values.
    index = Math.round(index);

    if (index === Infinity || index === -Infinity || !index) {
      index = 0;
    }

    this.currentCostume = MathUtil.wrapClamp(index, 0, this.sprite.costumes.length - 1);

    if (this.renderer) {
      const costume = this.sprite.costumes[this.currentCostume];
      this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Add a costume, taking care to avoid duplicate names.
   * @param {!object} costumeObject Object representing the costume.
   * @param {?int} index Index at which to add costume
   */


  addCostume(costumeObject, index) {
    if (typeof index === 'number' && !isNaN(index)) {
      this.sprite.addCostumeAt(costumeObject, index);
    } else {
      this.sprite.addCostumeAt(costumeObject, this.sprite.costumes.length);
    }
  }
  /**
   * Rename a costume, taking care to avoid duplicate names.
   * @param {int} costumeIndex - the index of the costume to be renamed.
   * @param {string} newName - the desired new name of the costume (will be modified if already in use).
   */


  renameCostume(costumeIndex, newName) {
    const usedNames = this.sprite.costumes.filter((costume, index) => costumeIndex !== index).map(costume => costume.name);
    const oldName = this.getCostumes()[costumeIndex].name;
    const newUnusedName = StringUtil.unusedName(newName, usedNames);
    this.getCostumes()[costumeIndex].name = newUnusedName;

    if (this.isStage) {
      // Since this is a backdrop, go through all targets and
      // update any blocks referencing the old backdrop name
      const targets = this.runtime.targets;

      for (let i = 0; i < targets.length; i++) {
        const currTarget = targets[i];
        currTarget.blocks.updateAssetName(oldName, newUnusedName, 'backdrop');
      }
    } else {
      this.blocks.updateAssetName(oldName, newUnusedName, 'costume');
    }
  }
  /**
   * Delete a costume by index.
   * @param {number} index Costume index to be deleted
   * @return {?object} The costume that was deleted or null
   * if the index was out of bounds of the costumes list or
   * this target only has one costume.
   */


  deleteCostume(index) {
    const originalCostumeCount = this.sprite.costumes.length;
    if (originalCostumeCount === 1) return null;

    if (index < 0 || index >= originalCostumeCount) {
      return null;
    }

    const deletedCostume = this.sprite.deleteCostumeAt(index);

    if (index === this.currentCostume && index === originalCostumeCount - 1) {
      this.setCostume(index - 1);
    } else if (index < this.currentCostume) {
      this.setCostume(this.currentCostume - 1);
    } else {
      this.setCostume(this.currentCostume);
    }

    this.runtime.requestTargetsUpdate(this);
    return deletedCostume;
  }
  /**
   * Add a sound, taking care to avoid duplicate names.
   * @param {!object} soundObject Object representing the sound.
   * @param {?int} index Index at which to add costume
   */


  addSound(soundObject, index) {
    const usedNames = this.sprite.sounds.map(sound => sound.name);
    soundObject.name = StringUtil.unusedName(soundObject.name, usedNames);

    if (typeof index === 'number' && !isNaN(index)) {
      this.sprite.sounds.splice(index, 0, soundObject);
    } else {
      this.sprite.sounds.push(soundObject);
    }
  }
  /**
   * Rename a sound, taking care to avoid duplicate names.
   * @param {int} soundIndex - the index of the sound to be renamed.
   * @param {string} newName - the desired new name of the sound (will be modified if already in use).
   */


  renameSound(soundIndex, newName) {
    const usedNames = this.sprite.sounds.filter((sound, index) => soundIndex !== index).map(sound => sound.name);
    const oldName = this.sprite.sounds[soundIndex].name;
    const newUnusedName = StringUtil.unusedName(newName, usedNames);
    this.sprite.sounds[soundIndex].name = newUnusedName;
    this.blocks.updateAssetName(oldName, newUnusedName, 'sound');
  }
  /**
   * Delete a sound by index.
   * @param {number} index Sound index to be deleted
   * @return {object} The deleted sound object, or null if no sound was deleted.
   */


  deleteSound(index) {
    // Make sure the sound index is not out of bounds
    if (index < 0 || index >= this.sprite.sounds.length) {
      return null;
    } // Delete the sound at the given index


    const deletedSound = this.sprite.sounds.splice(index, 1)[0];
    this.runtime.requestTargetsUpdate(this);
    return deletedSound;
  }
  /**
   * Update the rotation style.
   * @param {!string} rotationStyle New rotation style.
   */


  setRotationStyle(rotationStyle) {
    // used by compiler
    if (rotationStyle === RenderedTarget.ROTATION_STYLE_NONE) {
      this.rotationStyle = RenderedTarget.ROTATION_STYLE_NONE;
    } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_ALL_AROUND) {
      this.rotationStyle = RenderedTarget.ROTATION_STYLE_ALL_AROUND;
    } else if (rotationStyle === RenderedTarget.ROTATION_STYLE_LEFT_RIGHT) {
      this.rotationStyle = RenderedTarget.ROTATION_STYLE_LEFT_RIGHT;
    }

    if (this.renderer) {
      const {
        direction,
        scale
      } = this._getRenderedDirectionAndScale();

      this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Get a costume index of this rendered target, by name of the costume.
   * @param {?string} costumeName Name of a costume.
   * @return {number} Index of the named costume, or -1 if not present.
   */


  getCostumeIndexByName(costumeName) {
    const costumes = this.getCostumes();

    for (let i = 0; i < costumes.length; i++) {
      if (costumes[i].name === costumeName) {
        return i;
      }
    }

    return -1;
  }
  /**
   * Get a costume of this rendered target by id.
   * @return {object} current costume
   */


  getCurrentCostume() {
    return this.getCostumes()[this.currentCostume];
  }
  /**
   * Get full costume list
   * @return {object[]} list of costumes
   */


  getCostumes() {
    // used by compiler
    return this.sprite.costumes;
  }
  /**
   * Reorder costume list by moving costume at costumeIndex to newIndex.
   * @param {!number} costumeIndex Index of the costume to move.
   * @param {!number} newIndex New index for that costume.
   * @returns {boolean} If a change occurred (i.e. if the indices do not match)
   */


  reorderCostume(costumeIndex, newIndex) {
    newIndex = MathUtil.clamp(newIndex, 0, this.sprite.costumes.length - 1);
    costumeIndex = MathUtil.clamp(costumeIndex, 0, this.sprite.costumes.length - 1);
    if (newIndex === costumeIndex) return false;
    const currentCostume = this.getCurrentCostume();
    const costume = this.sprite.costumes[costumeIndex]; // Use the sprite method for deleting costumes because setCostume is handled manually

    this.sprite.deleteCostumeAt(costumeIndex);
    this.addCostume(costume, newIndex);
    this.currentCostume = this.getCostumeIndexByName(currentCostume.name);
    return true;
  }
  /**
   * Reorder sound list by moving sound at soundIndex to newIndex.
   * @param {!number} soundIndex Index of the sound to move.
   * @param {!number} newIndex New index for that sound.
   * @returns {boolean} If a change occurred (i.e. if the indices do not match)
   */


  reorderSound(soundIndex, newIndex) {
    newIndex = MathUtil.clamp(newIndex, 0, this.sprite.sounds.length - 1);
    soundIndex = MathUtil.clamp(soundIndex, 0, this.sprite.sounds.length - 1);
    if (newIndex === soundIndex) return false;
    const sound = this.sprite.sounds[soundIndex];
    this.deleteSound(soundIndex);
    this.addSound(sound, newIndex);
    return true;
  }
  /**
   * Get full sound list
   * @return {object[]} list of sounds
   */


  getSounds() {
    return this.sprite.sounds;
  }
  /**
   * Update all drawable properties for this rendered target.
   * Use when a batch has changed, e.g., when the drawable is first created.
   */


  updateAllDrawableProperties() {
    if (this.renderer) {
      const {
        direction,
        scale
      } = this._getRenderedDirectionAndScale();

      this.renderer.updateDrawablePosition(this.drawableID, [this.x, this.y]);
      this.renderer.updateDrawableDirectionScale(this.drawableID, direction, scale);
      this.renderer.updateDrawableVisible(this.drawableID, this.visible);
      const costume = this.getCostumes()[this.currentCostume];
      this.renderer.updateDrawableSkinId(this.drawableID, costume.skinId);

      for (const effectName in this.effects) {
        if (!this.effects.hasOwnProperty(effectName)) continue;
        this.renderer.updateDrawableEffect(this.drawableID, effectName, this.effects[effectName]);
      }

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }

    this.runtime.requestTargetsUpdate(this);
  }
  /**
   * Return the human-readable name for this rendered target, e.g., the sprite's name.
   * @override
   * @returns {string} Human-readable name.
   */


  getName() {
    return this.sprite.name;
  }
  /**
   * Return whether this rendered target is a sprite (not a clone, not the stage).
   * @return {boolean} True if not a clone and not the stage.
   */


  isSprite() {
    return !this.isStage && this.isOriginal;
  }
  /**
   * Return the rendered target's tight bounding box.
   * Includes top, left, bottom, right attributes in Scratch coordinates.
   * @return {?object} Tight bounding box, or null.
   */


  getBounds() {
    if (this.renderer) {
      return this.runtime.renderer.getBounds(this.drawableID);
    }

    return null;
  }
  /**
   * Return the bounding box around a slice of the top 8px of the rendered target.
   * Includes top, left, bottom, right attributes in Scratch coordinates.
   * @return {?object} Tight bounding box, or null.
   */


  getBoundsForBubble() {
    if (this.renderer) {
      return this.runtime.renderer.getBoundsForBubble(this.drawableID);
    }

    return null;
  }
  /**
   * Return whether this target is touching the mouse, an edge, or a sprite.
   * @param {string} requestedObject an id for mouse or edge, or a sprite name.
   * @return {boolean} True if the sprite is touching the object.
   */


  isTouchingObject(requestedObject) {
    // used by compiler
    if (requestedObject === '_mouse_') {
      if (!this.runtime.ioDevices.mouse) return false;
      const mouseX = this.runtime.ioDevices.mouse.getClientX();
      const mouseY = this.runtime.ioDevices.mouse.getClientY();
      return this.isTouchingPoint(mouseX, mouseY);
    } else if (requestedObject === '_edge_') {
      return this.isTouchingEdge();
    }

    return this.isTouchingSprite(requestedObject);
  }
  /**
   * Return whether touching a point.
   * @param {number} x X coordinate of test point.
   * @param {number} y Y coordinate of test point.
   * @return {boolean} True iff the rendered target is touching the point.
   */


  isTouchingPoint(x, y) {
    if (this.renderer) {
      return this.renderer.drawableTouching(this.drawableID, x, y);
    }

    return false;
  }
  /**
   * Return whether touching a stage edge.
   * @return {boolean} True iff the rendered target is touching the stage edge.
   */


  isTouchingEdge() {
    if (this.renderer) {
      const stageWidth = this.runtime.stageWidth;
      const stageHeight = this.runtime.stageHeight;
      const bounds = this.getBounds();

      if (bounds.left < -stageWidth / 2 || bounds.right > stageWidth / 2 || bounds.top > stageHeight / 2 || bounds.bottom < -stageHeight / 2) {
        return true;
      }
    }

    return false;
  }
  /**
   * Return whether touching any of a named sprite's clones.
   * @param {string} spriteName Name of the sprite.
   * @return {boolean} True iff touching a clone of the sprite.
   */


  isTouchingSprite(spriteName) {
    spriteName = Cast.toString(spriteName);
    const firstClone = this.runtime.getSpriteTargetByName(spriteName);

    if (!firstClone || !this.renderer) {
      return false;
    } // Filter out dragging targets. This means a sprite that is being dragged
    // can detect other sprites using touching <sprite>, but cannot be detected
    // by other sprites while it is being dragged. This matches Scratch 2.0 behavior.


    const drawableCandidates = firstClone.sprite.clones.filter(clone => !clone.dragging).map(clone => clone.drawableID);
    return this.renderer.isTouchingDrawables(this.drawableID, drawableCandidates);
  }
  /**
   * Return whether touching a color.
   * @param {Array.<number>} rgb [r,g,b], values between 0-255.
   * @return {Promise.<boolean>} True iff the rendered target is touching the color.
   */


  isTouchingColor(rgb) {
    // used by compiler
    if (this.renderer) {
      return this.renderer.isTouchingColor(this.drawableID, rgb);
    }

    return false;
  }
  /**
   * Return whether rendered target's color is touching a color.
   * @param {object} targetRgb {Array.<number>} [r,g,b], values between 0-255.
   * @param {object} maskRgb {Array.<number>} [r,g,b], values between 0-255.
   * @return {Promise.<boolean>} True iff the color is touching the color.
   */


  colorIsTouchingColor(targetRgb, maskRgb) {
    // used by compiler
    if (this.renderer) {
      return this.renderer.isTouchingColor(this.drawableID, targetRgb, maskRgb);
    }

    return false;
  }

  getLayerOrder() {
    if (this.renderer) {
      return this.renderer.getDrawableOrder(this.drawableID);
    }

    return null;
  }
  /**
   * Move to the front layer.
   */


  goToFront() {
    // This should only ever be used for sprites // used by compiler
    if (this.renderer) {
      // Let the renderer re-order the sprite based on its knowledge
      // of what layers are present
      this.renderer.setDrawableOrder(this.drawableID, Infinity, StageLayering.SPRITE_LAYER);
    }

    this.runtime.setExecutablePosition(this, Infinity);
  }
  /**
   * Move to the back layer.
   */


  goToBack() {
    // This should only ever be used for sprites // used by compiler
    if (this.renderer) {
      // Let the renderer re-order the sprite based on its knowledge
      // of what layers are present
      this.renderer.setDrawableOrder(this.drawableID, -Infinity, StageLayering.SPRITE_LAYER, false);
    }

    this.runtime.setExecutablePosition(this, -Infinity);
  }
  /**
   * Move forward a number of layers.
   * @param {number} nLayers How many layers to go forward.
   */


  goForwardLayers(nLayers) {
    // used by compiler
    if (this.renderer) {
      this.renderer.setDrawableOrder(this.drawableID, nLayers, StageLayering.SPRITE_LAYER, true);
    }

    this.runtime.moveExecutable(this, nLayers);
  }
  /**
   * Move backward a number of layers.
   * @param {number} nLayers How many layers to go backward.
   */


  goBackwardLayers(nLayers) {
    // used by compiler
    if (this.renderer) {
      this.renderer.setDrawableOrder(this.drawableID, -nLayers, StageLayering.SPRITE_LAYER, true);
    }

    this.runtime.moveExecutable(this, -nLayers);
  }
  /**
   * Move behind some other rendered target.
   * @param {!RenderedTarget} other Other rendered target to move behind.
   */


  goBehindOther(other) {
    if (this.renderer) {
      const otherLayer = this.renderer.setDrawableOrder(other.drawableID, 0, StageLayering.SPRITE_LAYER, true);
      this.renderer.setDrawableOrder(this.drawableID, otherLayer, StageLayering.SPRITE_LAYER);
    }

    const executionPosition = this.runtime.executableTargets.indexOf(other);
    this.runtime.setExecutablePosition(this, executionPosition);
  }
  /**
   * Keep a desired position within a fence.
   * @param {number} newX New desired X position.
   * @param {number} newY New desired Y position.
   * @param {object=} optFence Optional fence with left, right, top bottom.
   * @return {Array.<number>} Fenced X and Y coordinates.
   */


  keepInFence(newX, newY, optFence) {
    let fence = optFence;

    if (!fence) {
      fence = {
        left: -this.runtime.stageWidth / 2,
        right: this.runtime.stageWidth / 2,
        top: this.runtime.stageHeight / 2,
        bottom: -this.runtime.stageHeight / 2
      };
    }

    const bounds = this.getBounds();
    if (!bounds) return; // Adjust the known bounds to the target position.

    bounds.left += newX - this.x;
    bounds.right += newX - this.x;
    bounds.top += newY - this.y;
    bounds.bottom += newY - this.y; // Find how far we need to move the target position.

    let dx = 0;
    let dy = 0;

    if (bounds.left < fence.left) {
      dx += fence.left - bounds.left;
    }

    if (bounds.right > fence.right) {
      dx += fence.right - bounds.right;
    }

    if (bounds.top > fence.top) {
      dy += fence.top - bounds.top;
    }

    if (bounds.bottom < fence.bottom) {
      dy += fence.bottom - bounds.bottom;
    }

    return [newX + dx, newY + dy];
  }
  /**
   * Make a clone, copying any run-time properties.
   * If we've hit the global clone limit, returns null.
   * @return {RenderedTarget} New clone.
   */


  makeClone() {
    if (!this.runtime.clonesAvailable() || this.isStage) {
      return null; // Hit max clone limit, or this is the stage.
    }

    this.runtime.changeCloneCounter(1);
    const newClone = this.sprite.createClone(); // Copy all properties.

    newClone.x = this.x;
    newClone.y = this.y;
    newClone.direction = this.direction;
    newClone.draggable = this.draggable;
    newClone.visible = this.visible;
    newClone.size = this.size;
    newClone.currentCostume = this.currentCostume;
    newClone.rotationStyle = this.rotationStyle;
    newClone.effects = Clone.simple(this.effects);
    newClone.variables = this.duplicateVariables();
    newClone._edgeActivatedHatValues = Clone.simple(this._edgeActivatedHatValues);
    newClone.initDrawable(StageLayering.SPRITE_LAYER);
    newClone.updateAllDrawableProperties();
    return newClone;
  }
  /**
   * Make a duplicate using a duplicate sprite.
   * @return {RenderedTarget} New clone.
   */


  duplicate() {
    return this.sprite.duplicate().then(newSprite => {
      const newTarget = newSprite.createClone(); // Copy all properties.
      // @todo refactor with clone methods

      newTarget.x = (Math.random() - 0.5) * 400 / 2;
      newTarget.y = (Math.random() - 0.5) * 300 / 2;
      newTarget.direction = this.direction;
      newTarget.draggable = this.draggable;
      newTarget.visible = this.visible;
      newTarget.size = this.size;
      newTarget.currentCostume = this.currentCostume;
      newTarget.rotationStyle = this.rotationStyle;
      newTarget.effects = JSON.parse(JSON.stringify(this.effects));
      newTarget.variables = this.duplicateVariables(newTarget.blocks);
      newTarget.updateAllDrawableProperties();
      return newTarget;
    });
  }
  /**
   * Called when the project receives a "green flag."
   * For a rendered target, this clears graphic effects.
   */


  onGreenFlag() {
    this.clearEffects();
  }
  /**
   * Called when the project receives a "stop all"
   * Stop all sounds and clear graphic effects.
   */


  onStopAll() {
    this.clearEffects();
  }
  /**
   * Post/edit sprite info.
   * @param {object} data An object with sprite info data to set.
   */


  postSpriteInfo(data) {
    const force = data.hasOwnProperty('force') ? data.force : null;
    const isXChanged = data.hasOwnProperty('x');
    const isYChanged = data.hasOwnProperty('y');

    if (isXChanged || isYChanged) {
      this.setXY(isXChanged ? data.x : this.x, isYChanged ? data.y : this.y, force);
    }

    if (data.hasOwnProperty('direction')) {
      this.setDirection(data.direction);
    }

    if (data.hasOwnProperty('draggable')) {
      this.setDraggable(data.draggable);
    }

    if (data.hasOwnProperty('rotationStyle')) {
      this.setRotationStyle(data.rotationStyle);
    }

    if (data.hasOwnProperty('visible')) {
      this.setVisible(data.visible);
    }

    if (data.hasOwnProperty('size')) {
      this.setSize(data.size);
    }
  }
  /**
   * Put the sprite into the drag state. While in effect, setXY must be forced
   */


  startDrag() {
    this.dragging = true;
  }
  /**
   * Remove the sprite from the drag state.
   */


  stopDrag() {
    this.dragging = false;
  }
  /**
   * Serialize sprite info, used when emitting events about the sprite
   * @returns {object} Sprite data as a simple object
   */


  toJSON() {
    const costumes = this.getCostumes();
    return {
      id: this.id,
      name: this.getName(),
      isStage: this.isStage,
      x: this.x,
      y: this.y,
      size: this.size,
      direction: this.direction,
      draggable: this.draggable,
      currentCostume: this.currentCostume,
      costume: costumes[this.currentCostume],
      costumeCount: costumes.length,
      visible: this.visible,
      rotationStyle: this.rotationStyle,
      comments: this.comments,
      blocks: this.blocks._blocks,
      variables: this.variables,
      costumes: costumes,
      sounds: this.getSounds(),
      textToSpeechLanguage: this.textToSpeechLanguage,
      tempo: this.tempo,
      volume: this.volume,
      videoTransparency: this.videoTransparency,
      videoState: this.videoState
    };
  }
  /**
   * Dispose, destroying any run-time properties.
   */


  dispose() {
    if (!this.isOriginal) {
      this.runtime.changeCloneCounter(-1);
    }

    this.runtime.stopForTarget(this);
    this.runtime.removeExecutable(this);
    this.sprite.removeClone(this);

    if (this.renderer && this.drawableID !== null) {
      this.renderer.destroyDrawable(this.drawableID, this.isStage ? StageLayering.BACKGROUND_LAYER : StageLayering.SPRITE_LAYER);

      if (this.visible) {
        this.emitVisualChange();
        this.runtime.requestRedraw();
      }
    }
  }

}

module.exports = RenderedTarget;

/***/ }),

/***/ "./node_modules/scratch-vm/src/sprites/sprite.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/sprites/sprite.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const RenderedTarget = __webpack_require__(/*! ./rendered-target */ "./node_modules/scratch-vm/src/sprites/rendered-target.js");

const Blocks = __webpack_require__(/*! ../engine/blocks */ "./node_modules/scratch-vm/src/engine/blocks.js");

const {
  loadSoundFromAsset
} = __webpack_require__(/*! ../import/load-sound */ "./node_modules/scratch-vm/src/import/load-sound.js");

const {
  loadCostumeFromAsset
} = __webpack_require__(/*! ../import/load-costume */ "./node_modules/scratch-vm/src/import/load-costume.js");

const newBlockIds = __webpack_require__(/*! ../util/new-block-ids */ "./node_modules/scratch-vm/src/util/new-block-ids.js");

const StringUtil = __webpack_require__(/*! ../util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const StageLayering = __webpack_require__(/*! ../engine/stage-layering */ "./node_modules/scratch-vm/src/engine/stage-layering.js");

class Sprite {
  /**
   * Sprite to be used on the Scratch stage.
   * All clones of a sprite have shared blocks, shared costumes, shared variables,
   * shared sounds, etc.
   * @param {?Blocks} blocks Shared blocks object for all clones of sprite.
   * @param {Runtime} runtime Reference to the runtime.
   * @constructor
   */
  constructor(blocks, runtime) {
    this.runtime = runtime;

    if (!blocks) {
      // Shared set of blocks for all clones.
      blocks = new Blocks(runtime);
    }

    this.blocks = blocks;
    /**
     * Human-readable name for this sprite (and all clones).
     * @type {string}
     */

    this.name = '';
    /**
     * List of costumes for this sprite.
     * Each entry is an object, e.g.,
     * {
     *      skinId: 1,
     *      name: "Costume Name",
     *      bitmapResolution: 2,
     *      rotationCenterX: 0,
     *      rotationCenterY: 0
     * }
     * @type {Array.<!Object>}
     */

    this.costumes_ = [];
    /**
     * List of sounds for this sprite.
    */

    this.sounds = [];
    /**
     * List of clones for this sprite, including the original.
     * @type {Array.<!RenderedTarget>}
     */

    this.clones = [];
    this.soundBank = null;

    if (this.runtime && this.runtime.audioEngine) {
      this.soundBank = this.runtime.audioEngine.createBank();
    }
  }
  /**
   * Add an array of costumes, taking care to avoid duplicate names.
   * @param {!Array<object>} costumes Array of objects representing costumes.
   */


  set costumes(costumes) {
    this.costumes_ = [];

    for (const costume of costumes) {
      this.addCostumeAt(costume, this.costumes_.length);
    }
  }
  /**
   * Get full costume list
   * @return {object[]} list of costumes. Note that mutating the returned list will not
   *     mutate the list on the sprite. The sprite list should be mutated by calling
   *     addCostumeAt, deleteCostumeAt, or setting costumes.
   */


  get costumes() {
    return this.costumes_;
  }
  /**
   * Add a costume at the given index, taking care to avoid duplicate names.
   * @param {!object} costumeObject Object representing the costume.
   * @param {!int} index Index at which to add costume
   */


  addCostumeAt(costumeObject, index) {
    if (!costumeObject.name) {
      costumeObject.name = '';
    }

    const usedNames = this.costumes_.map(costume => costume.name);
    costumeObject.name = StringUtil.unusedName(costumeObject.name, usedNames);
    this.costumes_.splice(index, 0, costumeObject);
  }
  /**
   * Delete a costume by index.
   * @param {number} index Costume index to be deleted
   * @return {?object} The deleted costume
   */


  deleteCostumeAt(index) {
    return this.costumes.splice(index, 1)[0];
  }
  /**
   * Create a clone of this sprite.
   * @param {string=} optLayerGroup Optional layer group the clone's drawable should be added to
   * Defaults to the sprite layer group
   * @returns {!RenderedTarget} Newly created clone.
   */


  createClone(optLayerGroup) {
    const newClone = new RenderedTarget(this, this.runtime);
    newClone.isOriginal = this.clones.length === 0;
    this.clones.push(newClone);
    newClone.initAudio();

    if (newClone.isOriginal) {
      // Default to the sprite layer group if optLayerGroup is not provided
      const layerGroup = typeof optLayerGroup === 'string' ? optLayerGroup : StageLayering.SPRITE_LAYER;
      newClone.initDrawable(layerGroup);
      this.runtime.fireTargetWasCreated(newClone);
    } else {
      this.runtime.fireTargetWasCreated(newClone, this.clones[0]);
    }

    return newClone;
  }
  /**
   * Disconnect a clone from this sprite. The clone is unmodified.
   * In particular, the clone's dispose() method is not called.
   * @param {!RenderedTarget} clone - the clone to be removed.
   */


  removeClone(clone) {
    this.runtime.fireTargetWasRemoved(clone);
    const cloneIndex = this.clones.indexOf(clone);

    if (cloneIndex >= 0) {
      this.clones.splice(cloneIndex, 1);
    }
  }

  duplicate() {
    const newSprite = new Sprite(null, this.runtime);
    const blocksContainer = this.blocks._blocks;
    const originalBlocks = Object.keys(blocksContainer).map(key => blocksContainer[key]);
    const copiedBlocks = JSON.parse(JSON.stringify(originalBlocks));
    newBlockIds(copiedBlocks);
    copiedBlocks.forEach(block => {
      newSprite.blocks.createBlock(block);
    });
    const allNames = this.runtime.targets.map(t => t.sprite.name);
    newSprite.name = StringUtil.unusedName(this.name, allNames);
    const assetPromises = [];
    newSprite.costumes = this.costumes_.map(costume => {
      const newCostume = Object.assign({}, costume);
      assetPromises.push(loadCostumeFromAsset(newCostume, this.runtime));
      return newCostume;
    });
    newSprite.sounds = this.sounds.map(sound => {
      const newSound = Object.assign({}, sound);
      const soundAsset = sound.asset;
      assetPromises.push(loadSoundFromAsset(newSound, soundAsset, this.runtime, newSprite.soundBank));
      return newSound;
    });
    return Promise.all(assetPromises).then(() => newSprite);
  }

  dispose() {
    if (this.soundBank) {
      this.soundBank.dispose();
    }
  }

}

module.exports = Sprite;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/async-limiter.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/async-limiter.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class AsyncLimiter {
  constructor(callback, maxConcurrent) {
    this.callback = callback;
    this.maxConcurrent = maxConcurrent;
    this._current = 0;
    this._queue = [];
  }

  do() {
    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
      args[_key] = arguments[_key];
    }

    return new Promise((resolve, reject) => {
      this._queue.push([resolve, reject, args]);

      this._startNext();
    });
  }

  _startNext() {
    if (this._current >= this.maxConcurrent || this._queue.length === 0) {
      return;
    }

    this._current++;

    const [resolve, reject, args] = this._queue.shift();

    this.callback.apply(null, args).then(result => {
      resolve(result);
      this._current--;

      this._startNext();
    }).catch(error => {
      reject(error);
      this._current--;

      this._startNext();
    });
  }

}

module.exports = AsyncLimiter;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/base64-util.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/base64-util.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const atob = __webpack_require__(/*! atob */ "./node_modules/atob/browser-atob.js");

const btoa = __webpack_require__(/*! btoa */ "./node_modules/btoa/index.js");

class Base64Util {
  /**
   * Convert a base64 encoded string to a Uint8Array.
   * @param {string} base64 - a base64 encoded string.
   * @return {Uint8Array} - a decoded Uint8Array.
   */
  static base64ToUint8Array(base64) {
    const binaryString = atob(base64);
    const len = binaryString.length;
    const array = new Uint8Array(len);

    for (let i = 0; i < len; i++) {
      array[i] = binaryString.charCodeAt(i);
    }

    return array;
  }
  /**
   * Convert a Uint8Array to a base64 encoded string.
   * @param {Uint8Array} array - the array to convert.
   * @return {string} - the base64 encoded string.
   */


  static uint8ArrayToBase64(array) {
    let binary = '';
    const len = array.byteLength;

    for (let i = 0; i < len; i++) {
      binary += String.fromCharCode(array[i]);
    }

    return btoa(binary);
  }
  /**
  * Convert an array buffer to a base64 encoded string.
  * @param {array} buffer - an array buffer to convert.
  * @return {string} - the base64 encoded string.
  */


  static arrayBufferToBase64(buffer) {
    return Base64Util.uint8ArrayToBase64(new Uint8Array(buffer));
  }

}

module.exports = Base64Util;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/cast.js":
/*!**************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/cast.js ***!
  \**************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Color = __webpack_require__(/*! ../util/color */ "./node_modules/scratch-vm/src/util/color.js");
/**
 * @fileoverview
 * Utilities for casting and comparing Scratch data-types.
 * Scratch behaves slightly differently from JavaScript in many respects,
 * and these differences should be encapsulated below.
 * For example, in Scratch, add(1, join("hello", world")) -> 1.
 * This is because "hello world" is cast to 0.
 * In JavaScript, 1 + Number("hello" + "world") would give you NaN.
 * Use when coercing a value before computation.
 */

/**
 * Used internally by compare()
 * @param {*} val A value that evaluates to 0 in JS string-to-number conversation such as empty string, 0, or tab.
 * @returns {boolean} True if the value should not be treated as the number zero.
 */


const isNotActuallyZero = val => {
  if (typeof val !== 'string') return false;

  for (let i = 0; i < val.length; i++) {
    const code = val.charCodeAt(i); // '0'.charCodeAt(0) === 48
    // '\t'.charCodeAt(0) === 9
    // We include tab for compatibility with scratch-www's broken trim() polyfill.
    // https://github.com/TurboWarp/scratch-vm/issues/115
    // https://scratch.mit.edu/projects/788261699/

    if (code === 48 || code === 9) {
      return false;
    }
  }

  return true;
};

class Cast {
  /**
   * Scratch cast to number.
   * Treats NaN as 0.
   * In Scratch 2.0, this is captured by `interp.numArg.`
   * @param {*} value Value to cast to number.
   * @return {number} The Scratch-casted number value.
   */
  static toNumber(value) {
    // If value is already a number we don't need to coerce it with
    // Number().
    if (typeof value === 'number') {
      // Scratch treats NaN as 0, when needed as a number.
      // E.g., 0 + NaN -> 0.
      if (Number.isNaN(value)) {
        return 0;
      }

      return value;
    }

    const n = Number(value);

    if (Number.isNaN(n)) {
      // Scratch treats NaN as 0, when needed as a number.
      // E.g., 0 + NaN -> 0.
      return 0;
    }

    return n;
  }
  /**
   * Scratch cast to boolean.
   * In Scratch 2.0, this is captured by `interp.boolArg.`
   * Treats some string values differently from JavaScript.
   * @param {*} value Value to cast to boolean.
   * @return {boolean} The Scratch-casted boolean value.
   */


  static toBoolean(value) {
    // Already a boolean?
    if (typeof value === 'boolean') {
      return value;
    }

    if (typeof value === 'string') {
      // These specific strings are treated as false in Scratch.
      if (value === '' || value === '0' || value.toLowerCase() === 'false') {
        return false;
      } // All other strings treated as true.


      return true;
    } // Coerce other values and numbers.


    return Boolean(value);
  }
  /**
   * Scratch cast to string.
   * @param {*} value Value to cast to string.
   * @return {string} The Scratch-casted string value.
   */


  static toString(value) {
    return String(value);
  }
  /**
   * Cast any Scratch argument to an RGB color array to be used for the renderer.
   * @param {*} value Value to convert to RGB color array.
   * @return {Array.<number>} [r,g,b], values between 0-255.
   */


  static toRgbColorList(value) {
    const color = Cast.toRgbColorObject(value);
    return [color.r, color.g, color.b];
  }
  /**
   * Cast any Scratch argument to an RGB color object to be used for the renderer.
   * @param {*} value Value to convert to RGB color object.
   * @return {RGBOject} [r,g,b], values between 0-255.
   */


  static toRgbColorObject(value) {
    let color;

    if (typeof value === 'string' && value.substring(0, 1) === '#') {
      color = Color.hexToRgb(value); // If the color wasn't *actually* a hex color, cast to black

      if (!color) color = {
        r: 0,
        g: 0,
        b: 0,
        a: 255
      };
    } else {
      color = Color.decimalToRgb(Cast.toNumber(value));
    }

    return color;
  }
  /**
   * Determine if a Scratch argument is a white space string (or null / empty).
   * @param {*} val value to check.
   * @return {boolean} True if the argument is all white spaces or null / empty.
   */


  static isWhiteSpace(val) {
    return val === null || typeof val === 'string' && val.trim().length === 0;
  }
  /**
   * Compare two values, using Scratch cast, case-insensitive string compare, etc.
   * In Scratch 2.0, this is captured by `interp.compare.`
   * @param {*} v1 First value to compare.
   * @param {*} v2 Second value to compare.
   * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise.
   */


  static compare(v1, v2) {
    let n1 = Number(v1);
    let n2 = Number(v2);

    if (n1 === 0 && isNotActuallyZero(v1)) {
      n1 = NaN;
    } else if (n2 === 0 && isNotActuallyZero(v2)) {
      n2 = NaN;
    }

    if (isNaN(n1) || isNaN(n2)) {
      // At least one argument can't be converted to a number.
      // Scratch compares strings as case insensitive.
      const s1 = String(v1).toLowerCase();
      const s2 = String(v2).toLowerCase();

      if (s1 < s2) {
        return -1;
      } else if (s1 > s2) {
        return 1;
      }

      return 0;
    } // Handle the special case of Infinity


    if (n1 === Infinity && n2 === Infinity || n1 === -Infinity && n2 === -Infinity) {
      return 0;
    } // Compare as numbers.


    return n1 - n2;
  }
  /**
   * Determine if a Scratch argument number represents a round integer.
   * @param {*} val Value to check.
   * @return {boolean} True if number looks like an integer.
   */


  static isInt(val) {
    // Values that are already numbers.
    if (typeof val === 'number') {
      if (isNaN(val)) {
        // NaN is considered an integer.
        return true;
      } // True if it's "round" (e.g., 2.0 and 2).


      return val === Math.floor(val);
    } else if (typeof val === 'boolean') {
      // `True` and `false` always represent integer after Scratch cast.
      return true;
    } else if (typeof val === 'string') {
      // If it contains a decimal point, don't consider it an int.
      return val.indexOf('.') < 0;
    }

    return false;
  }

  static get LIST_INVALID() {
    return 'INVALID';
  }

  static get LIST_ALL() {
    return 'ALL';
  }
  /**
   * Compute a 1-based index into a list, based on a Scratch argument.
   * Two special cases may be returned:
   * LIST_ALL: if the block is referring to all of the items in the list.
   * LIST_INVALID: if the index was invalid in any way.
   * @param {*} index Scratch arg, including 1-based numbers or special cases.
   * @param {number} length Length of the list.
   * @param {boolean} acceptAll Whether it should accept "all" or not.
   * @return {(number|string)} 1-based index for list, LIST_ALL, or LIST_INVALID.
   */


  static toListIndex(index, length, acceptAll) {
    if (typeof index !== 'number') {
      if (index === 'all') {
        return acceptAll ? Cast.LIST_ALL : Cast.LIST_INVALID;
      }

      if (index === 'last') {
        if (length > 0) {
          return length;
        }

        return Cast.LIST_INVALID;
      } else if (index === 'random' || index === 'any') {
        if (length > 0) {
          return 1 + Math.floor(Math.random() * length);
        }

        return Cast.LIST_INVALID;
      }
    }

    index = Math.floor(Cast.toNumber(index));

    if (index < 1 || index > length) {
      return Cast.LIST_INVALID;
    }

    return index;
  }

}

module.exports = Cast;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/clone.js":
/*!***************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/clone.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Methods for cloning JavaScript objects.
 * @type {object}
 */
class Clone {
  /**
   * Deep-clone a "simple" object: one which can be fully expressed with JSON.
   * Non-JSON values, such as functions, will be stripped from the clone.
   * @param {object} original - the object to be cloned.
   * @returns {object} a deep clone of the original object.
   */
  static simple(original) {
    return JSON.parse(JSON.stringify(original));
  }

}

module.exports = Clone;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/color.js":
/*!***************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/color.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class Color {
  /**
   * @typedef {object} RGBObject - An object representing a color in RGB format.
   * @property {number} r - the red component, in the range [0, 255].
   * @property {number} g - the green component, in the range [0, 255].
   * @property {number} b - the blue component, in the range [0, 255].
   */

  /**
   * @typedef {object} HSVObject - An object representing a color in HSV format.
   * @property {number} h - hue, in the range [0-359).
   * @property {number} s - saturation, in the range [0,1].
   * @property {number} v - value, in the range [0,1].
   */

  /** @type {RGBObject} */
  static get RGB_BLACK() {
    return {
      r: 0,
      g: 0,
      b: 0
    };
  }
  /** @type {RGBObject} */


  static get RGB_WHITE() {
    return {
      r: 255,
      g: 255,
      b: 255
    };
  }
  /**
   * Convert a Scratch decimal color to a hex string, #RRGGBB.
   * @param {number} decimal RGB color as a decimal.
   * @return {string} RGB color as #RRGGBB hex string.
   */


  static decimalToHex(decimal) {
    if (decimal < 0) {
      decimal += 0xFFFFFF + 1;
    }

    let hex = Number(decimal).toString(16);
    hex = "#".concat('000000'.substr(0, 6 - hex.length)).concat(hex);
    return hex;
  }
  /**
   * Convert a Scratch decimal color to an RGB color object.
   * @param {number} decimal RGB color as decimal.
   * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   */


  static decimalToRgb(decimal) {
    const a = decimal >> 24 & 0xFF;
    const r = decimal >> 16 & 0xFF;
    const g = decimal >> 8 & 0xFF;
    const b = decimal & 0xFF;
    return {
      r: r,
      g: g,
      b: b,
      a: a > 0 ? a : 255
    };
  }
  /**
   * Convert a hex color (e.g., F00, #03F, #0033FF) to an RGB color object.
   * @param {!string} hex Hex representation of the color.
   * @return {RGBObject} null on failure, or rgb: {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   */


  static hexToRgb(hex) {
    if (hex.startsWith('#')) {
      hex = hex.substring(1);
    }

    const parsed = parseInt(hex, 16);

    if (isNaN(parsed)) {
      return null;
    }

    if (hex.length === 6) {
      return {
        r: parsed >> 16 & 0xff,
        g: parsed >> 8 & 0xff,
        b: parsed & 0xff
      };
    } else if (hex.length === 3) {
      const r = parsed >> 8 & 0xf;
      const g = parsed >> 4 & 0xf;
      const b = parsed & 0xf;
      return {
        r: r << 4 | r,
        g: g << 4 | g,
        b: b << 4 | b
      };
    }

    return null;
  }
  /**
   * Convert an RGB color object to a hex color.
   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   * @return {!string} Hex representation of the color.
   */


  static rgbToHex(rgb) {
    return Color.decimalToHex(Color.rgbToDecimal(rgb));
  }
  /**
   * Convert an RGB color object to a Scratch decimal color.
   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   * @return {!number} Number representing the color.
   */


  static rgbToDecimal(rgb) {
    return (rgb.r << 16) + (rgb.g << 8) + rgb.b;
  }
  /**
  * Convert a hex color (e.g., F00, #03F, #0033FF) to a decimal color number.
  * @param {!string} hex Hex representation of the color.
  * @return {!number} Number representing the color.
  */


  static hexToDecimal(hex) {
    return Color.rgbToDecimal(Color.hexToRgb(hex));
  }
  /**
   * Convert an HSV color to RGB format.
   * @param {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]}
   * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   */


  static hsvToRgb(hsv) {
    let h = hsv.h % 360;
    if (h < 0) h += 360;
    const s = Math.max(0, Math.min(hsv.s, 1));
    const v = Math.max(0, Math.min(hsv.v, 1));
    const i = Math.floor(h / 60);
    const f = h / 60 - i;
    const p = v * (1 - s);
    const q = v * (1 - s * f);
    const t = v * (1 - s * (1 - f));
    let r;
    let g;
    let b;

    switch (i) {
      default:
      case 0:
        r = v;
        g = t;
        b = p;
        break;

      case 1:
        r = q;
        g = v;
        b = p;
        break;

      case 2:
        r = p;
        g = v;
        b = t;
        break;

      case 3:
        r = p;
        g = q;
        b = v;
        break;

      case 4:
        r = t;
        g = p;
        b = v;
        break;

      case 5:
        r = v;
        g = p;
        b = q;
        break;
    }

    return {
      r: Math.floor(r * 255),
      g: Math.floor(g * 255),
      b: Math.floor(b * 255)
    };
  }
  /**
   * Convert an RGB color to HSV format.
   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.
   * @return {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]}
   */


  static rgbToHsv(rgb) {
    const r = rgb.r / 255;
    const g = rgb.g / 255;
    const b = rgb.b / 255;
    const x = Math.min(Math.min(r, g), b);
    const v = Math.max(Math.max(r, g), b); // For grays, hue will be arbitrarily reported as zero. Otherwise, calculate

    let h = 0;
    let s = 0;

    if (x !== v) {
      const f = r === x ? g - b : g === x ? b - r : r - g;
      const i = r === x ? 3 : g === x ? 5 : 1;
      h = (i - f / (v - x)) * 60 % 360;
      s = (v - x) / v;
    }

    return {
      h: h,
      s: s,
      v: v
    };
  }
  /**
   * Linear interpolation between rgb0 and rgb1.
   * @param {RGBObject} rgb0 - the color corresponding to fraction1 <= 0.
   * @param {RGBObject} rgb1 - the color corresponding to fraction1 >= 1.
   * @param {number} fraction1 - the interpolation parameter. If this is 0.5, for example, mix the two colors equally.
   * @return {RGBObject} the interpolated color.
   */


  static mixRgb(rgb0, rgb1, fraction1) {
    if (fraction1 <= 0) return rgb0;
    if (fraction1 >= 1) return rgb1;
    const fraction0 = 1 - fraction1;
    return {
      r: fraction0 * rgb0.r + fraction1 * rgb1.r,
      g: fraction0 * rgb0.g + fraction1 * rgb1.g,
      b: fraction0 * rgb0.b + fraction1 * rgb1.b
    };
  }

}

module.exports = Color;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/fetch-with-timeout.js":
/*!****************************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/fetch-with-timeout.js ***!
  \****************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Fetch a remote resource like `fetch` does, but with a time limit.
 * @param {Request|string} resource Remote resource to fetch.
 * @param {?object} init An options object containing any custom settings that you want to apply to the request.
 * @param {number} timeout The amount of time before the request is canceled, in milliseconds
 * @returns {Promise<Response>} The response from the server.
 */
const fetchWithTimeout = (resource, init, timeout) => {
  let timeoutID = null; // Not supported in Safari <11

  const controller = window.AbortController ? new window.AbortController() : null;
  const signal = controller ? controller.signal : null; // The fetch call races a timer.

  return Promise.race([fetch(resource, Object.assign({
    signal
  }, init)).then(response => {
    clearTimeout(timeoutID);
    return response;
  }), new Promise((resolve, reject) => {
    timeoutID = setTimeout(() => {
      if (controller) controller.abort();
      reject(new Error("Fetch timed out after ".concat(timeout, " ms")));
    }, timeout);
  })]);
};

module.exports = fetchWithTimeout;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/get-monitor-id.js":
/*!************************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/get-monitor-id.js ***!
  \************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * Returns a string representing a unique id for a monitored block
 * where a single reporter block can have more than one monitor
 * (and therefore more than one monitor block) associated
 * with it (e.g. when reporter blocks have inputs).
 * @param {string} baseId The base id to use for the different monitor blocks
 * @param {object} fields The monitor block's fields object.
 */
// TODO this function should eventually be the single place where all monitor
// IDs are obtained given an opcode for the reporter block and the list of
// selected parameters.
const getMonitorIdForBlockWithArgs = function getMonitorIdForBlockWithArgs(id, fields) {
  let fieldString = '';

  for (const fieldKey in fields) {
    let fieldValue = fields[fieldKey].value;

    if (fieldKey === 'CURRENTMENU') {
      // The 'sensing_current' block has field values in all caps.
      // However, when importing from scratch 2.0, these
      // could have gotten imported as lower case field values.
      // Normalize the field value here so that we don't ever
      // end up with a different monitor ID representing the same
      // block configuration
      // Note: we are not doing this for every block field that comes into
      // this function so as not to make the faulty assumption that block
      // field values coming in would be unique after being made lower case
      fieldValue = fieldValue.toLowerCase();
    }

    fieldString += "_".concat(fieldValue);
  }

  return "".concat(id).concat(fieldString);
};

module.exports = getMonitorIdForBlockWithArgs;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/jsonrpc.js":
/*!*****************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/jsonrpc.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class JSONRPC {
  constructor() {
    this._requestID = 0;
    this._openRequests = {};
  }
  /**
   * Make an RPC request and retrieve the result.
   * @param {string} method - the remote method to call.
   * @param {object} params - the parameters to pass to the remote method.
   * @returns {Promise} - a promise for the result of the call.
   */


  sendRemoteRequest(method, params) {
    const requestID = this._requestID++;
    const promise = new Promise((resolve, reject) => {
      this._openRequests[requestID] = {
        resolve,
        reject
      };
    });

    this._sendRequest(method, params, requestID);

    return promise;
  }
  /**
   * Make an RPC notification with no expectation of a result or callback.
   * @param {string} method - the remote method to call.
   * @param {object} params - the parameters to pass to the remote method.
   */


  sendRemoteNotification(method, params) {
    this._sendRequest(method, params);
  }
  /**
   * Handle an RPC request from remote, should return a result or Promise for result, if appropriate.
   * @param {string} method - the method requested by the remote caller.
   * @param {object} params - the parameters sent with the remote caller's request.
   */


  didReceiveCall() {
    throw new Error('Must override didReceiveCall');
  }

  _sendMessage() {
    throw new Error('Must override _sendMessage');
  }

  _sendRequest(method, params, id) {
    const request = {
      jsonrpc: '2.0',
      method,
      params
    };

    if (id !== null) {
      request.id = id;
    }

    this._sendMessage(request);
  }

  _handleMessage(json) {
    if (json.jsonrpc !== '2.0') {
      throw new Error("Bad or missing JSON-RPC version in message: ".concat(json));
    }

    if (json.hasOwnProperty('method')) {
      this._handleRequest(json);
    } else {
      this._handleResponse(json);
    }
  }

  _sendResponse(id, result, error) {
    const response = {
      jsonrpc: '2.0',
      id
    };

    if (error) {
      response.error = error;
    } else {
      response.result = result || null;
    }

    this._sendMessage(response);
  }

  _handleResponse(json) {
    const {
      result,
      error,
      id
    } = json;
    const openRequest = this._openRequests[id];
    delete this._openRequests[id];

    if (openRequest) {
      if (error) {
        openRequest.reject(error);
      } else {
        openRequest.resolve(result);
      }
    }
  }

  _handleRequest(json) {
    const {
      method,
      params,
      id
    } = json;
    const rawResult = this.didReceiveCall(method, params);

    if (id) {
      Promise.resolve(rawResult).then(result => {
        this._sendResponse(id, result);
      }, error => {
        this._sendResponse(id, null, error);
      });
    }
  }

}

module.exports = JSONRPC;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/log.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/log.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const minilog = __webpack_require__(/*! minilog */ "./node_modules/minilog/lib/web/index.js");

minilog.enable();
module.exports = minilog('vm');

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/math-util.js":
/*!*******************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/math-util.js ***!
  \*******************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class MathUtil {
  /**
   * Convert a value from degrees to radians.
   * @param {!number} deg Value in degrees.
   * @return {!number} Equivalent value in radians.
   */
  static degToRad(deg) {
    return deg * Math.PI / 180;
  }
  /**
   * Convert a value from radians to degrees.
   * @param {!number} rad Value in radians.
   * @return {!number} Equivalent value in degrees.
   */


  static radToDeg(rad) {
    return rad * 180 / Math.PI;
  }
  /**
   * Clamp a number between two limits.
   * If n < min, return min. If n > max, return max. Else, return n.
   * @param {!number} n Number to clamp.
   * @param {!number} min Minimum limit.
   * @param {!number} max Maximum limit.
   * @return {!number} Value of n clamped to min and max.
   */


  static clamp(n, min, max) {
    return Math.min(Math.max(n, min), max);
  }
  /**
   * Keep a number between two limits, wrapping "extra" into the range.
   * e.g., wrapClamp(7, 1, 5) == 2
   * wrapClamp(0, 1, 5) == 5
   * wrapClamp(-11, -10, 6) == 6, etc.
   * @param {!number} n Number to wrap.
   * @param {!number} min Minimum limit.
   * @param {!number} max Maximum limit.
   * @return {!number} Value of n wrapped between min and max.
   */


  static wrapClamp(n, min, max) {
    const range = max - min + 1;
    return n - Math.floor((n - min) / range) * range;
  }
  /**
   * Convert a value from tan function in degrees.
   * @param {!number} angle in degrees
   * @return {!number} Correct tan value
   */


  static tan(angle) {
    angle = angle % 360;

    switch (angle) {
      case -270:
      case 90:
        return Infinity;

      case -90:
      case 270:
        return -Infinity;

      default:
        return Math.round(Math.tan(Math.PI * angle / 180) * 1e10) / 1e10;
    }
  }
  /**
   * Given an array of unique numbers,
   * returns a reduced array such that each element of the reduced array
   * represents the position of that element in a sorted version of the
   * original array.
   * E.g. [5, 19. 13, 1] => [1, 3, 2, 0]
   * @param {Array<number>} elts The elements to sort and reduce
   * @return {Array<number>} The array of reduced orderings
   */


  static reducedSortOrdering(elts) {
    const sorted = elts.slice(0).sort((a, b) => a - b);
    return elts.map(e => sorted.indexOf(e));
  }
  /**
   * Return a random number given an inclusive range and a number in that
   * range that should be excluded.
   *
   * For instance, (1, 5, 3) will only pick 1, 2, 4, or 5 (with equal
   * probability)
   *
   * @param {number} lower - The lower bound (inlcusive)
   * @param {number} upper - The upper bound (inclusive), such that lower <= upper
   * @param {number} excluded - The number to exclude (MUST be in the range)
   * @return {number} A random integer in the range [lower, upper] that is not "excluded"
   */


  static inclusiveRandIntWithout(lower, upper, excluded) {
    // Note that subtraction is the number of items in the
    // inclusive range [lower, upper] minus 1 already
    // (e.g. in the set {3, 4, 5}, 5 - 3 = 2).
    const possibleOptions = upper - lower;
    const randInt = lower + Math.floor(Math.random() * possibleOptions);

    if (randInt >= excluded) {
      return randInt + 1;
    }

    return randInt;
  }
  /**
   * Scales a number from one range to another.
   * @param {number} i number to be scaled
   * @param {number} iMin input range minimum
   * @param {number} iMax input range maximum
   * @param {number} oMin output range minimum
   * @param {number} oMax output range maximum
   * @return {number} scaled number
   */


  static scale(i, iMin, iMax, oMin, oMax) {
    const p = (i - iMin) / (iMax - iMin);
    return p * (oMax - oMin) + oMin;
  }

}

module.exports = MathUtil;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/maybe-format-message.js":
/*!******************************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/maybe-format-message.js ***!
  \******************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");
/**
 * Check if `maybeMessage` looks like a message object, and if so pass it to `formatMessage`.
 * Otherwise, return `maybeMessage` as-is.
 * @param {*} maybeMessage - something that might be a message descriptor object.
 * @param {object} [args] - the arguments to pass to `formatMessage` if it gets called.
 * @param {string} [locale] - the locale to pass to `formatMessage` if it gets called.
 * @return {string|*} - the formatted message OR the original `maybeMessage` input.
 */


const maybeFormatMessage = function maybeFormatMessage(maybeMessage, args, locale) {
  if (maybeMessage && maybeMessage.id && maybeMessage.default) {
    return formatMessage(maybeMessage, args, locale);
  }

  return maybeMessage;
};

module.exports = maybeFormatMessage;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/new-block-ids.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/new-block-ids.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const uid = __webpack_require__(/*! ./uid */ "./node_modules/scratch-vm/src/util/uid.js");
/**
 * Mutate the given blocks to have new IDs and update all internal ID references.
 * Does not return anything to make it clear that the blocks are updated in-place.
 * @param {array} blocks - blocks to be mutated.
 */


module.exports = blocks => {
  const oldToNew = {}; // First update all top-level IDs and create old-to-new mapping

  for (let i = 0; i < blocks.length; i++) {
    const newId = uid();
    const oldId = blocks[i].id;
    blocks[i].id = oldToNew[oldId] = newId;
  } // Then go back through and update inputs (block/shadow)
  // and next/parent properties


  for (let i = 0; i < blocks.length; i++) {
    for (const key in blocks[i].inputs) {
      const input = blocks[i].inputs[key];
      input.block = oldToNew[input.block];
      input.shadow = oldToNew[input.shadow];
    }

    if (blocks[i].parent) {
      blocks[i].parent = oldToNew[blocks[i].parent];
    }

    if (blocks[i].next) {
      blocks[i].next = oldToNew[blocks[i].next];
    }
  }
};

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/rateLimiter.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/rateLimiter.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const Timer = __webpack_require__(/*! ../util/timer */ "./node_modules/scratch-vm/src/util/timer.js");

class RateLimiter {
  /**
   * A utility for limiting the rate of repetitive send operations, such as
   * bluetooth messages being sent to hardware devices. It uses the token bucket
   * strategy: a counter accumulates tokens at a steady rate, and each send costs
   * a token. If no tokens remain, it's not okay to send.
   * @param {number} maxRate the maximum number of sends allowed per second
   * @constructor
   */
  constructor(maxRate) {
    /**
     * The maximum number of tokens.
     * @type {number}
     */
    this._maxTokens = maxRate;
    /**
     * The interval in milliseconds for refilling one token. It is calculated
     * so that the tokens will be filled to maximum in one second.
     * @type {number}
     */

    this._refillInterval = 1000 / maxRate;
    /**
     * The current number of tokens in the bucket.
     * @type {number}
     */

    this._count = this._maxTokens;
    this._timer = new Timer();

    this._timer.start();
    /**
     * The last time in milliseconds when the token count was updated.
     * @type {number}
     */


    this._lastUpdateTime = this._timer.timeElapsed();
  }
  /**
   * Check if it is okay to send a message, by updating the token count,
   * taking a token and then checking if we are still under the rate limit.
   * @return {boolean} true if we are under the rate limit
   */


  okayToSend() {
    // Calculate the number of tokens to refill the bucket with, based on the
    // amount of time since the last refill.
    const now = this._timer.timeElapsed();

    const timeSinceRefill = now - this._lastUpdateTime;
    const refillCount = Math.floor(timeSinceRefill / this._refillInterval); // If we're adding at least one token, reset _lastUpdateTime to now.
    // Otherwise, don't reset it so that we can continue measuring time until
    // the next refill.

    if (refillCount > 0) {
      this._lastUpdateTime = now;
    } // Refill the tokens up to the maximum


    this._count = Math.min(this._maxTokens, this._count + refillCount); // If we have at least one token, use one, and it's okay to send.

    if (this._count > 0) {
      this._count--;
      return true;
    }

    return false;
  }

}

module.exports = RateLimiter;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/scratch-link-websocket.js":
/*!********************************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/scratch-link-websocket.js ***!
  \********************************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * This class provides a ScratchLinkSocket implementation using WebSockets,
 * attempting to connect with the locally installed Scratch-Link.
 *
 * To connect with ScratchLink without WebSockets, you must implement all of the
 * public methods in this class.
 * - open()
 * - close()
 * - setOn[Open|Close|Error]
 * - setHandleMessage
 * - sendMessage(msgObj)
 * - isOpen()
 */
class ScratchLinkWebSocket {
  constructor(type) {
    this._type = type;
    this._onOpen = null;
    this._onClose = null;
    this._onError = null;
    this._handleMessage = null;
    this._ws = null;
  }

  open() {
    if (!(this._onOpen && this._onClose && this._onError && this._handleMessage)) {
      throw new Error('Must set open, close, message and error handlers before calling open on the socket');
    }

    let pathname;

    switch (this._type) {
      case 'BLE':
        pathname = 'scratch/ble';
        break;

      case 'BT':
        pathname = 'scratch/bt';
        break;

      default:
        throw new Error("Unknown ScratchLink socket Type: ".concat(this._type));
    } // Try ws:// (the new way) and wss:// (the old way) simultaneously. If either connects, close the other. If we
    // were to try one and fall back to the other on failure, that could mean a delay of 30 seconds or more for
    // those who need the fallback.
    // If both connections fail we should report only one error.


    const setSocket = (socketToUse, socketToClose) => {
      socketToClose.onopen = socketToClose.onerror = null;
      socketToClose.close();
      this._ws = socketToUse;
      this._ws.onopen = this._onOpen;
      this._ws.onclose = this._onClose;
      this._ws.onerror = this._onError;
      this._ws.onmessage = this._onMessage.bind(this);
    };

    const ws = new WebSocket("ws://127.0.0.1:20111/".concat(pathname));
    const wss = new WebSocket("wss://device-manager.scratch.mit.edu:20110/".concat(pathname));
    const connectTimeout = setTimeout(() => {
      // neither socket succeeded before the timeout
      setSocket(ws, wss);

      this._ws.onerror(new Event('timeout'));
    }, 15 * 1000);

    ws.onopen = openEvent => {
      clearTimeout(connectTimeout);
      setSocket(ws, wss);

      this._ws.onopen(openEvent);
    };

    wss.onopen = openEvent => {
      clearTimeout(connectTimeout);
      setSocket(wss, ws);

      this._ws.onopen(openEvent);
    };

    let wsError;
    let wssError;

    const errorHandler = () => {
      // if only one has received an error, we haven't overall failed yet
      if (wsError && wssError) {
        clearTimeout(connectTimeout);
        setSocket(ws, wss);

        this._ws.onerror(wsError);
      }
    };

    ws.onerror = errorEvent => {
      wsError = errorEvent;
      errorHandler();
    };

    wss.onerror = errorEvent => {
      wssError = errorEvent;
      errorHandler();
    };
  }

  close() {
    this._ws.close();

    this._ws = null;
  }

  sendMessage(message) {
    const messageText = JSON.stringify(message);

    this._ws.send(messageText);
  }

  setOnOpen(fn) {
    this._onOpen = fn;
  }

  setOnClose(fn) {
    this._onClose = fn;
  }

  setOnError(fn) {
    this._onError = fn;
  }

  setHandleMessage(fn) {
    this._handleMessage = fn;
  }

  isOpen() {
    return this._ws && this._ws.readyState === this._ws.OPEN;
  }

  _onMessage(e) {
    const json = JSON.parse(e.data);

    this._handleMessage(json);
  }

}

module.exports = ScratchLinkWebSocket;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/string-util.js":
/*!*********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/string-util.js ***!
  \*********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-vm/src/util/log.js");

class StringUtil {
  static withoutTrailingDigits(s) {
    let i = s.length - 1;

    while (i >= 0 && '0123456789'.indexOf(s.charAt(i)) > -1) i--;

    return s.slice(0, i + 1);
  }

  static unusedName(name, existingNames) {
    if (existingNames.indexOf(name) < 0) return name;
    name = StringUtil.withoutTrailingDigits(name);
    let i = 2;

    while (existingNames.indexOf(name + i) >= 0) i++;

    return name + i;
  }
  /**
   * Split a string on the first occurrence of a split character.
   * @param {string} text - the string to split.
   * @param {string} separator - split the text on this character.
   * @returns {string[]} - the two parts of the split string, or [text, null] if no split character found.
   * @example
   * // returns ['foo', 'tar.gz']
   * splitFirst('foo.tar.gz', '.');
   * @example
   * // returns ['foo', null]
   * splitFirst('foo', '.');
   * @example
   * // returns ['foo', '']
   * splitFirst('foo.', '.');
   */


  static splitFirst(text, separator) {
    const index = text.indexOf(separator);

    if (index >= 0) {
      return [text.substring(0, index), text.substring(index + 1)];
    }

    return [text, null];
  }
  /**
   * A customized version of JSON.stringify that sets Infinity/NaN to 0,
   * instead of the default (null).
   * Needed because null is not of type number, but Infinity/NaN are, which
   * can lead to serialization producing JSON that isn't valid based on the parser schema.
   * It is also consistent with the behavior of saving 2.0 projects.
   * This is only needed when stringifying an object for saving.
   *
   * @param {!object} obj - The object to serialize
   * @return {!string} The JSON.stringified string with Infinity/NaN replaced with 0
   */


  static stringify(obj) {
    return JSON.stringify(obj, (_key, value) => {
      if (typeof value === 'number' && (value === Infinity || value === -Infinity || isNaN(value))) {
        return 0;
      }

      return value;
    });
  }
  /**
   * A function to replace unsafe characters (not allowed in XML) with safe ones. This is used
   * in cases where we're replacing non-user facing strings (e.g. variable IDs).
   * When replacing user facing strings, the xmlEscape utility function should be used
   * instead so that the user facing string does not change how it displays.
   * @param {!string | !Array.<string>} unsafe Unsafe string possibly containing unicode control characters.
   * In some cases this argument may be an array (e.g. hacked inputs from 2.0)
   * @return {string} String with control characters replaced.
   */


  static replaceUnsafeChars(unsafe) {
    if (typeof unsafe !== 'string') {
      if (Array.isArray(unsafe)) {
        // This happens when we have hacked blocks from 2.0
        // See #1030
        unsafe = String(unsafe);
      } else {
        log.error('Unexpected input recieved in replaceUnsafeChars');
        return unsafe;
      }
    }

    return unsafe.replace(/[<>&'"]/g, c => {
      switch (c) {
        case '<':
          return 'lt';

        case '>':
          return 'gt';

        case '&':
          return 'amp';

        case '\'':
          return 'apos';

        case '"':
          return 'quot';
      }
    });
  }

}

module.exports = StringUtil;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/timer.js":
/*!***************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/timer.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {/**
 * @fileoverview
 * A utility for accurately measuring time.
 * To use:
 * ---
 * var timer = new Timer();
 * timer.start();
 * ... pass some time ...
 * var timeDifference = timer.timeElapsed();
 * ---
 * Or, you can use the `time` and `relativeTime`
 * to do some measurement yourself.
 */
class Timer {
  constructor() {
    let nowObj = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Timer.nowObj;

    /**
     * Used to store the start time of a timer action.
     * Updated when calling `timer.start`.
     */
    this.startTime = 0;
    /**
     * Used to pass custom logic for determining the value for "now",
     * which is sometimes useful for compatibility with Scratch 2
     */

    this.nowObj = nowObj;
  }
  /**
   * Disable use of self.performance for now as it results in lower performance
   * However, instancing it like below (caching the self.performance to a local variable) negates most of the issues.
   * @type {boolean}
   */


  static get USE_PERFORMANCE() {
    return false;
  }
  /**
   * Legacy object to allow for us to call now to get the old style date time (for backwards compatibility)
   * @deprecated This is only called via the nowObj.now() if no other means is possible...
   */


  static get legacyDateCode() {
    return {
      now: function now() {
        return new Date().getTime();
      }
    };
  }
  /**
   * Use this object to route all time functions through single access points.
   */


  static get nowObj() {
    if (Timer.USE_PERFORMANCE && typeof self !== 'undefined' && self.performance && 'now' in self.performance) {
      return self.performance;
    } else if (Date.now) {
      return Date;
    }

    return Timer.legacyDateCode;
  }
  /**
   * Return the currently known absolute time, in ms precision.
   * @returns {number} ms elapsed since 1 January 1970 00:00:00 UTC.
   */


  time() {
    return this.nowObj.now();
  }
  /**
   * Returns a time accurate relative to other times produced by this function.
   * If possible, will use sub-millisecond precision.
   * If not, will use millisecond precision.
   * Not guaranteed to produce the same absolute values per-system.
   * @returns {number} ms-scale accurate time relative to other relative times.
   */


  relativeTime() {
    return this.nowObj.now();
  }
  /**
   * Start a timer for measuring elapsed time,
   * at the most accurate precision possible.
   */


  start() {
    this.startTime = this.nowObj.now();
  }

  timeElapsed() {
    return this.nowObj.now() - this.startTime;
  }
  /**
   * Call a handler function after a specified amount of time has elapsed.
   * @param {function} handler - function to call after the timeout
   * @param {number} timeout - number of milliseconds to delay before calling the handler
   * @returns {number} - the ID of the new timeout
   */


  setTimeout(handler, timeout) {
    return global.setTimeout(handler, timeout);
  }
  /**
   * Clear a timeout from the pending timeout pool.
   * @param {number} timeoutId - the ID returned by `setTimeout()`
   * @memberof Timer
   */


  clearTimeout(timeoutId) {
    global.clearTimeout(timeoutId);
  }

}

module.exports = Timer;
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/uid.js":
/*!*************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/uid.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/**
 * @fileoverview UID generator, from Blockly.
 */

/**
 * Legal characters for the unique ID.
 * Should be all on a US keyboard.  No XML special characters or control codes.
 * Removed $ due to issue 251.
 * @private
 */
const soup_ = '!#%()*+,-./:;=?@[]^_`{|}~' + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
/**
 * Generate a unique ID, from Blockly.  This should be globally unique.
 * 87 characters ^ 20 length > 128 bits (better than a UUID).
 * @return {string} A globally unique ID string.
 */

const uid = function uid() {
  const length = 20;
  const soupLength = soup_.length;
  const id = [];

  for (let i = 0; i < length; i++) {
    id[i] = soup_.charAt(Math.random() * soupLength);
  }

  return id.join('');
};

module.exports = uid;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/variable-util.js":
/*!***********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/variable-util.js ***!
  \***********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

class VariableUtil {
  static _mergeVarRefObjects(accum, obj2) {
    for (const id in obj2) {
      if (accum[id]) {
        accum[id] = accum[id].concat(obj2[id]);
      } else {
        accum[id] = obj2[id];
      }
    }

    return accum;
  }
  /**
   * Get all variable/list references in the given list of targets
   * in the project.
   * @param {Array.<Target>} targets The list of targets to get the variable
   * and list references from.
   * @param {boolean} shouldIncludeBroadcast Whether to include broadcast message fields.
   * @return {object} An object with variable ids as the keys and a list of block fields referencing
   * the variable.
   */


  static getAllVarRefsForTargets(targets, shouldIncludeBroadcast) {
    return targets.map(t => t.blocks.getAllVariableAndListReferences(null, shouldIncludeBroadcast)).reduce(VariableUtil._mergeVarRefObjects, {});
  }
  /**
   * Give all variable references provided a new id and possibly new name.
   * @param {Array<object>} referencesToUpdate Context of the change, the object containing variable
   * references to update.
   * @param {string} newId ID of the variable that the old references should be replaced with
   * @param {?string} optNewName New variable name to merge with. The old
   * variable name in the references being updated should be replaced with this new name.
   * If this parameter is not provided or is '', no name change occurs.
   */


  static updateVariableIdentifiers(referencesToUpdate, newId, optNewName) {
    referencesToUpdate.map(ref => {
      ref.referencingField.id = newId;

      if (optNewName) {
        ref.referencingField.value = optNewName;
      }

      return ref;
    });
  }

}

module.exports = VariableUtil;

/***/ }),

/***/ "./node_modules/scratch-vm/src/util/xml-escape.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-vm/src/util/xml-escape.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const log = __webpack_require__(/*! ./log */ "./node_modules/scratch-vm/src/util/log.js");
/**
 * Escape a string to be safe to use in XML content.
 * CC-BY-SA: hgoebl
 * https://stackoverflow.com/questions/7918868/
 * how-to-escape-xml-entities-in-javascript
 * @param {!string | !Array.<string>} unsafe Unsafe string.
 * @return {string} XML-escaped string, for use within an XML tag.
 */


const xmlEscape = function xmlEscape(unsafe) {
  if (typeof unsafe !== 'string') {
    if (Array.isArray(unsafe)) {
      // This happens when we have hacked blocks from 2.0
      // See #1030
      unsafe = String(unsafe);
    } else {
      log.error('Unexpected input recieved in replaceUnsafeChars');
      return unsafe;
    }
  }

  return unsafe.replace(/[<>&'"]/g, c => {
    switch (c) {
      case '<':
        return '&lt;';

      case '>':
        return '&gt;';

      case '&':
        return '&amp;';

      case '\'':
        return '&apos;';

      case '"':
        return '&quot;';
    }
  });
};

module.exports = xmlEscape;

/***/ }),

/***/ "./node_modules/scratch-vm/src/virtual-machine.js":
/*!********************************************************!*\
  !*** ./node_modules/scratch-vm/src/virtual-machine.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

let _TextEncoder;

if (typeof TextEncoder === 'undefined') {
  _TextEncoder = __webpack_require__(/*! text-encoding */ "./src/scaffolding/text-encoding/index.js").TextEncoder;
} else {
  /* global TextEncoder */
  _TextEncoder = TextEncoder;
}

const EventEmitter = __webpack_require__(/*! events */ "./node_modules/events/events.js");

const JSZip = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");

const Buffer = __webpack_require__(/*! buffer */ "./node_modules/buffer/index.js").Buffer;

const centralDispatch = __webpack_require__(/*! ./dispatch/central-dispatch */ "./node_modules/scratch-vm/src/dispatch/central-dispatch.js");

const ExtensionManager = __webpack_require__(/*! ./extension-support/extension-manager */ "./node_modules/scratch-vm/src/extension-support/extension-manager.js");

const log = __webpack_require__(/*! ./util/log */ "./node_modules/scratch-vm/src/util/log.js");

const MathUtil = __webpack_require__(/*! ./util/math-util */ "./node_modules/scratch-vm/src/util/math-util.js");

const Runtime = __webpack_require__(/*! ./engine/runtime */ "./node_modules/scratch-vm/src/engine/runtime.js");

const StringUtil = __webpack_require__(/*! ./util/string-util */ "./node_modules/scratch-vm/src/util/string-util.js");

const formatMessage = __webpack_require__(/*! format-message */ "./node_modules/format-message/index.js");

const Variable = __webpack_require__(/*! ./engine/variable */ "./node_modules/scratch-vm/src/engine/variable.js");

const newBlockIds = __webpack_require__(/*! ./util/new-block-ids */ "./node_modules/scratch-vm/src/util/new-block-ids.js");

const {
  loadCostume
} = __webpack_require__(/*! ./import/load-costume.js */ "./node_modules/scratch-vm/src/import/load-costume.js");

const {
  loadSound
} = __webpack_require__(/*! ./import/load-sound.js */ "./node_modules/scratch-vm/src/import/load-sound.js");

const {
  serializeSounds,
  serializeCostumes
} = __webpack_require__(/*! ./serialization/serialize-assets */ "./node_modules/scratch-vm/src/serialization/serialize-assets.js");

__webpack_require__(/*! canvas-toBlob */ "./node_modules/canvas-toBlob/canvas-toBlob.js");

const {
  exportCostume
} = __webpack_require__(/*! ./serialization/tw-costume-import-export */ "./node_modules/scratch-vm/src/serialization/tw-costume-import-export.js");

const Base64Util = __webpack_require__(/*! ./util/base64-util */ "./node_modules/scratch-vm/src/util/base64-util.js");

const RESERVED_NAMES = ['_mouse_', '_stage_', '_edge_', '_myself_', '_random_'];
const CORE_EXTENSIONS = [// 'motion',
  // 'looks',
  // 'sound',
  // 'events',
  // 'control',
  // 'sensing',
  // 'operators',
  // 'variables',
  // 'myBlocks'
]; // Disable missing translation warnings in console

formatMessage.setup({
  missingTranslation: 'ignore'
});

const createRuntimeService = runtime => {
  const service = {};
  service._refreshExtensionPrimitives = runtime._refreshExtensionPrimitives.bind(runtime);
  service._registerExtensionPrimitives = runtime._registerExtensionPrimitives.bind(runtime);
  return service;
};
/**
 * Handles connections between blocks, stage, and extensions.
 * @constructor
 */


class VirtualMachine extends EventEmitter {
  constructor() {
    super();
    /**
     * VM runtime, to store blocks, I/O devices, sprites/targets, etc.
     * @type {!Runtime}
     */

    this.runtime = new Runtime();
    centralDispatch.setService('runtime', createRuntimeService(this.runtime)).catch(e => {
      log.error("Failed to register runtime service: ".concat(JSON.stringify(e)));
    });
    /**
     * The "currently editing"/selected target ID for the VM.
     * Block events from any Blockly workspace are routed to this target.
     * @type {Target}
     */

    this.editingTarget = null;
    /**
     * The currently dragging target, for redirecting IO data.
     * @type {Target}
     */

    this._dragTarget = null; // Runtime emits are passed along as VM emits.

    this.runtime.on(Runtime.SCRIPT_GLOW_ON, glowData => {
      this.emit(Runtime.SCRIPT_GLOW_ON, glowData);
    });
    this.runtime.on(Runtime.SCRIPT_GLOW_OFF, glowData => {
      this.emit(Runtime.SCRIPT_GLOW_OFF, glowData);
    });
    this.runtime.on(Runtime.BLOCK_GLOW_ON, glowData => {
      this.emit(Runtime.BLOCK_GLOW_ON, glowData);
    });
    this.runtime.on(Runtime.BLOCK_GLOW_OFF, glowData => {
      this.emit(Runtime.BLOCK_GLOW_OFF, glowData);
    });
    this.runtime.on(Runtime.PROJECT_START, () => {
      this.emit(Runtime.PROJECT_START);
    });
    this.runtime.on(Runtime.PROJECT_RUN_START, () => {
      this.emit(Runtime.PROJECT_RUN_START);
    });
    this.runtime.on(Runtime.PROJECT_RUN_STOP, () => {
      this.emit(Runtime.PROJECT_RUN_STOP);
    });
    this.runtime.on(Runtime.PROJECT_CHANGED, () => {
      this.emit(Runtime.PROJECT_CHANGED);
    });
    this.runtime.on(Runtime.VISUAL_REPORT, visualReport => {
      this.emit(Runtime.VISUAL_REPORT, visualReport);
    });
    this.runtime.on(Runtime.TARGETS_UPDATE, emitProjectChanged => {
      this.emitTargetsUpdate(emitProjectChanged);
    });
    this.runtime.on(Runtime.MONITORS_UPDATE, monitorList => {
      this.emit(Runtime.MONITORS_UPDATE, monitorList);
    });
    this.runtime.on(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui => {
      this.emit(Runtime.BLOCK_DRAG_UPDATE, areBlocksOverGui);
    });
    this.runtime.on(Runtime.BLOCK_DRAG_END, (blocks, topBlockId) => {
      this.emit(Runtime.BLOCK_DRAG_END, blocks, topBlockId);
    });
    this.runtime.on(Runtime.EXTENSION_ADDED, categoryInfo => {
      this.emit(Runtime.EXTENSION_ADDED, categoryInfo);
    });
    this.runtime.on(Runtime.EXTENSION_FIELD_ADDED, (fieldName, fieldImplementation) => {
      this.emit(Runtime.EXTENSION_FIELD_ADDED, fieldName, fieldImplementation);
    });
    this.runtime.on(Runtime.BLOCKSINFO_UPDATE, categoryInfo => {
      this.emit(Runtime.BLOCKSINFO_UPDATE, categoryInfo);
    });
    this.runtime.on(Runtime.BLOCKS_NEED_UPDATE, () => {
      this.emitWorkspaceUpdate();
    });
    this.runtime.on(Runtime.TOOLBOX_EXTENSIONS_NEED_UPDATE, () => {
      this.extensionManager.refreshBlocks();
    });
    this.runtime.on(Runtime.PERIPHERAL_LIST_UPDATE, info => {
      this.emit(Runtime.PERIPHERAL_LIST_UPDATE, info);
    });
    this.runtime.on(Runtime.USER_PICKED_PERIPHERAL, info => {
      this.emit(Runtime.USER_PICKED_PERIPHERAL, info);
    });
    this.runtime.on(Runtime.PERIPHERAL_CONNECTED, () => this.emit(Runtime.PERIPHERAL_CONNECTED));
    this.runtime.on(Runtime.PERIPHERAL_REQUEST_ERROR, () => this.emit(Runtime.PERIPHERAL_REQUEST_ERROR));
    this.runtime.on(Runtime.PERIPHERAL_DISCONNECTED, () => this.emit(Runtime.PERIPHERAL_DISCONNECTED));
    this.runtime.on(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data => this.emit(Runtime.PERIPHERAL_CONNECTION_LOST_ERROR, data));
    this.runtime.on(Runtime.PERIPHERAL_SCAN_TIMEOUT, () => this.emit(Runtime.PERIPHERAL_SCAN_TIMEOUT));
    this.runtime.on(Runtime.MIC_LISTENING, listening => {
      this.emit(Runtime.MIC_LISTENING, listening);
    });
    this.runtime.on(Runtime.RUNTIME_STARTED, () => {
      this.emit(Runtime.RUNTIME_STARTED);
    });
    this.runtime.on(Runtime.RUNTIME_STOPPED, () => {
      this.emit(Runtime.RUNTIME_STOPPED);
    });
    this.runtime.on(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData => {
      this.emit(Runtime.HAS_CLOUD_DATA_UPDATE, hasCloudData);
    });
    this.runtime.on(Runtime.RUNTIME_OPTIONS_CHANGED, runtimeOptions => {
      this.emit(Runtime.RUNTIME_OPTIONS_CHANGED, runtimeOptions);
    });
    this.runtime.on(Runtime.COMPILER_OPTIONS_CHANGED, compilerOptions => {
      this.emit(Runtime.COMPILER_OPTIONS_CHANGED, compilerOptions);
    });
    this.runtime.on(Runtime.FRAMERATE_CHANGED, framerate => {
      this.emit(Runtime.FRAMERATE_CHANGED, framerate);
    });
    this.runtime.on(Runtime.INTERPOLATION_CHANGED, framerate => {
      this.emit(Runtime.INTERPOLATION_CHANGED, framerate);
    });
    this.runtime.on(Runtime.STAGE_SIZE_CHANGED, (width, height) => {
      this.emit(Runtime.STAGE_SIZE_CHANGED, width, height);
    });
    this.runtime.on(Runtime.COMPILE_ERROR, (target, error) => {
      this.emit(Runtime.COMPILE_ERROR, target, error);
    });
    this.runtime.on(Runtime.TURBO_MODE_OFF, () => {
      this.emit(Runtime.TURBO_MODE_OFF);
    });
    this.runtime.on(Runtime.TURBO_MODE_ON, () => {
      this.emit(Runtime.TURBO_MODE_ON);
    });
    this.extensionManager = new ExtensionManager(this);
    this.securityManager = this.extensionManager.securityManager;
    this.runtime.extensionManager = this.extensionManager; // Load core extensions

    for (const id of CORE_EXTENSIONS) {
      this.extensionManager.loadExtensionIdSync(id);
    }

    this.blockListener = this.blockListener.bind(this);
    this.flyoutBlockListener = this.flyoutBlockListener.bind(this);
    this.monitorBlockListener = this.monitorBlockListener.bind(this);
    this.variableListener = this.variableListener.bind(this);
  }
  /**
   * Start running the VM - do this before anything else.
   */


  start() {
    this.runtime.start();
  }
  /**
   * tw: Stop running the VM
   * Note: This only stops the loop. It will not stop any threads the next time the VM starts
   */


  stop() {
    this.runtime.stop();
  }
  /**
   * "Green flag" handler - start all threads starting with a green flag.
   */


  greenFlag() {
    this.runtime.greenFlag();
  }
  /**
   * Set whether the VM is in "turbo mode."
   * When true, loops don't yield to redraw.
   * @param {boolean} turboModeOn Whether turbo mode should be set.
   */


  setTurboMode(turboModeOn) {
    this.runtime.turboMode = !!turboModeOn;

    if (this.runtime.turboMode) {
      this.emit(Runtime.TURBO_MODE_ON);
    } else {
      this.emit(Runtime.TURBO_MODE_OFF);
    }
  }
  /**
   * Set whether the VM is in 2.0 "compatibility mode."
   * When true, ticks go at 2.0 speed (30 TPS).
   * @param {boolean} compatibilityModeOn Whether compatibility mode is set.
   */


  setCompatibilityMode(compatibilityModeOn) {
    this.runtime.setCompatibilityMode(!!compatibilityModeOn);
  }

  setFramerate(framerate) {
    this.runtime.setFramerate(framerate);
  }

  setInterpolation(interpolationEnabled) {
    this.runtime.setInterpolation(interpolationEnabled);
  }

  setRuntimeOptions(runtimeOptions) {
    this.runtime.setRuntimeOptions(runtimeOptions);
  }

  setCompilerOptions(compilerOptions) {
    this.runtime.setCompilerOptions(compilerOptions);
  }

  setStageSize(width, height) {
    this.runtime.setStageSize(width, height);
  }

  setInEditor(inEditor) {
    this.runtime.setInEditor(inEditor);
  }

  convertToPackagedRuntime() {
    this.runtime.convertToPackagedRuntime();
  }

  addAddonBlock(options) {
    this.runtime.addAddonBlock(options);
  }

  getAddonBlock(procedureCode) {
    return this.runtime.getAddonBlock(procedureCode);
  }

  storeProjectOptions() {
    this.runtime.storeProjectOptions();

    if (this.editingTarget.isStage) {
      this.emitWorkspaceUpdate();
    }
  }

  enableDebug() {
    this.runtime.enableDebug();
    return 'enabled debug mode';
  }
  /**
   * Stop all threads and running activities.
   */


  stopAll() {
    this.runtime.stopAll();
  }
  /**
   * Clear out current running project data.
   */


  clear() {
    this.runtime.dispose();
    this.editingTarget = null;
    this.emitTargetsUpdate(false
    /* Don't emit project change */
    );
  }
  /**
   * Get data for playground. Data comes back in an emitted event.
   */


  getPlaygroundData() {
    const instance = this; // Only send back thread data for the current editingTarget.

    const threadData = this.runtime.threads.filter(thread => thread.target === instance.editingTarget); // Remove the target key, since it's a circular reference.

    const filteredThreadData = JSON.stringify(threadData, (key, value) => {
      if (key === 'target' || key === 'blockContainer') return;
      return value;
    }, 2);
    this.emit('playgroundData', {
      blocks: this.editingTarget.blocks,
      threads: filteredThreadData
    });
  }
  /**
   * Post I/O data to the virtual devices.
   * @param {?string} device Name of virtual I/O device.
   * @param {object} data Any data object to post to the I/O device.
   */


  postIOData(device, data) {
    if (this.runtime.ioDevices[device]) {
      this.runtime.ioDevices[device].postData(data);
    }
  }

  setVideoProvider(videoProvider) {
    this.runtime.ioDevices.video.setProvider(videoProvider);
  }

  setCloudProvider(cloudProvider) {
    this.runtime.ioDevices.cloud.setProvider(cloudProvider);
  }
  /**
   * Tell the specified extension to scan for a peripheral.
   * @param {string} extensionId - the id of the extension.
   */


  scanForPeripheral(extensionId) {
    this.runtime.scanForPeripheral(extensionId);
  }
  /**
   * Connect to the extension's specified peripheral.
   * @param {string} extensionId - the id of the extension.
   * @param {number} peripheralId - the id of the peripheral.
   */


  connectPeripheral(extensionId, peripheralId) {
    this.runtime.connectPeripheral(extensionId, peripheralId);
  }
  /**
   * Disconnect from the extension's connected peripheral.
   * @param {string} extensionId - the id of the extension.
   */


  disconnectPeripheral(extensionId) {
    this.runtime.disconnectPeripheral(extensionId);
  }
  /**
   * Returns whether the extension has a currently connected peripheral.
   * @param {string} extensionId - the id of the extension.
   * @return {boolean} - whether the extension has a connected peripheral.
   */


  getPeripheralIsConnected(extensionId) {
    return this.runtime.getPeripheralIsConnected(extensionId);
  }
  /**
   * Load a Scratch project from a .sb, .sb2, .sb3 or json string.
   * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load.
   * @return {!Promise} Promise that resolves after targets are installed.
   */


  loadProject(input) {
    if (typeof input === 'object' && !(input instanceof ArrayBuffer) && !ArrayBuffer.isView(input)) {
      // If the input is an object and not any ArrayBuffer
      // or an ArrayBuffer view (this includes all typed arrays and DataViews)
      // turn the object into a JSON string, because we suspect
      // this is a project.json as an object
      // validate expects a string or buffer as input
      // TODO not sure if we need to check that it also isn't a data view
      input = JSON.stringify(input);
    }

    const validationPromise = new Promise((resolve, reject) => {
      const validate = __webpack_require__(/*! scratch-parser */ "./src/scaffolding/scratch-parser/index.js"); // The second argument of false below indicates to the validator that the
      // input should be parsed/validated as an entire project (and not a single sprite)


      validate(input, false, (error, res) => {
        if (error) {
          return reject(error);
        }

        resolve(res);
      });
    }).catch(error => {
      const {
        SB1File,
        ValidationError
      } = __webpack_require__(/*! scratch-sb1-converter */ "./node_modules/scratch-sb1-converter/index.js");

      try {
        const sb1 = new SB1File(input);
        const json = sb1.json;
        json.projectVersion = 2;
        return Promise.resolve([json, sb1.zip]);
      } catch (sb1Error) {
        if (sb1Error instanceof ValidationError || "".concat(sb1Error).includes('Non-ascii character in FixedAsciiString')) {// The input does not validate as a Scratch 1 file.
        } else {
          // The project appears to be a Scratch 1 file but it
          // could not be successfully translated into a Scratch 2
          // project.
          return Promise.reject(sb1Error);
        }
      } // Throw original error since the input does not appear to be
      // an SB1File.


      return Promise.reject(error);
    });
    return validationPromise.then(validatedInput => this.deserializeProject(validatedInput[0], validatedInput[1])).then(() => this.runtime.emitProjectLoaded()).catch(error => {
      // Intentionally rejecting here (want errors to be handled by caller)
      if (error.hasOwnProperty('validationError')) {
        return Promise.reject(JSON.stringify(error));
      }

      return Promise.reject(error);
    });
  }
  /**
   * Load a project from the Scratch web site, by ID.
   * @param {string} id - the ID of the project to download, as a string.
   */


  downloadProjectId(id) {
    const storage = this.runtime.storage;

    if (!storage) {
      log.error('No storage module present; cannot load project: ', id);
      return;
    }

    const vm = this;
    const promise = storage.load(storage.AssetType.Project, id);
    promise.then(projectAsset => {
      if (!projectAsset) {
        log.error("Failed to fetch project with id: ".concat(id));
        return null;
      }

      return vm.loadProject(projectAsset.data);
    });
  }
  /**
   * @returns {JSZip} JSZip zip object representing the sb3.
   */


  _saveProjectZip() {
    const soundDescs = serializeSounds(this.runtime);
    const costumeDescs = serializeCostumes(this.runtime);
    const projectJson = this.toJSON(); // TODO want to eventually move zip creation out of here, and perhaps
    // into scratch-storage

    const zip = new JSZip(); // Put everything in a zip file

    zip.file('project.json', projectJson);

    this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip);

    return zip;
  }
  /**
   * @param {JSZip.OutputType} [type] JSZip output type. Defaults to 'blob' for Scratch compatibility.
   * @returns {Promise<unknown>} Compressed sb3 file in a type determined by the type argument.
   */


  saveProjectSb3(type) {
    return this._saveProjectZip().generateAsync({
      type: type || 'blob',
      mimeType: 'application/x.scratch.sb3',
      compression: 'DEFLATE'
    });
  }
  /**
   * @param {JSZip.OutputType} [type] JSZip output type. Defaults to 'arraybuffer'.
   * @returns {StreamHelper} JSZip StreamHelper object generating the compressed sb3.
   * See: https://stuk.github.io/jszip/documentation/api_streamhelper.html
   */


  saveProjectSb3Stream(type) {
    return this._saveProjectZip().generateInternalStream({
      type: type || 'arraybuffer',
      mimeType: 'application/x.scratch.sb3',
      compression: 'DEFLATE'
    });
  }
  /**
   * tw: Serialize the project into a map of files without actually zipping the project.
   * The buffers returned are the exact same ones used internally, not copies. Avoid directly
   * manipulating them (except project.json, which is created by this function).
   * @returns {Record<string, Uint8Array>} Map of file name to the raw data for that file.
   */


  saveProjectSb3DontZip() {
    const soundDescs = serializeSounds(this.runtime);
    const costumeDescs = serializeCostumes(this.runtime);
    const projectJson = this.toJSON();
    const files = {
      'project.json': new _TextEncoder().encode(projectJson)
    };

    for (const fileDesc of soundDescs.concat(costumeDescs)) {
      files[fileDesc.fileName] = fileDesc.fileContent;
    }

    return files;
  }
  /*
   * @type {Array<object>} Array of all costumes and sounds currently in the runtime
   */


  get assets() {
    return this.runtime.targets.reduce((acc, target) => acc.concat(target.sprite.sounds.map(sound => sound.asset)).concat(target.sprite.costumes.map(costume => costume.asset)), []);
  }

  _addFileDescsToZip(fileDescs, zip) {
    for (let i = 0; i < fileDescs.length; i++) {
      const currFileDesc = fileDescs[i];
      zip.file(currFileDesc.fileName, currFileDesc.fileContent);
    }
  }
  /**
   * Exports a sprite in the sprite3 format.
   * @param {string} targetId ID of the target to export
   * @param {string=} optZipType Optional type that the resulting
   * zip should be outputted in. Options are: base64, binarystring,
   * array, uint8array, arraybuffer, blob, or nodebuffer. Defaults to
   * blob if argument not provided.
   * See https://stuk.github.io/jszip/documentation/api_jszip/generate_async.html#type-option
   * for more information about these options.
   * @return {object} A generated zip of the sprite and its assets in the format
   * specified by optZipType or blob by default.
   */


  exportSprite(targetId, optZipType) {
    const soundDescs = serializeSounds(this.runtime, targetId);
    const costumeDescs = serializeCostumes(this.runtime, targetId);
    const spriteJson = this.toJSON(targetId);
    const zip = new JSZip();
    zip.file('sprite.json', spriteJson);

    this._addFileDescsToZip(soundDescs.concat(costumeDescs), zip);

    return zip.generateAsync({
      type: typeof optZipType === 'string' ? optZipType : 'blob',
      mimeType: 'application/x.scratch.sprite3',
      compression: 'DEFLATE',
      compressionOptions: {
        level: 6
      }
    });
  }
  /**
   * Export project or sprite as a Scratch 3.0 JSON representation.
   * @param {string=} optTargetId - Optional id of a sprite to serialize
   * @param {*} serializationOptions Options to pass to the serializer
   * @return {string} Serialized state of the runtime.
   */


  toJSON(optTargetId, serializationOptions) {
    const sb3 = __webpack_require__(/*! ./serialization/sb3 */ "./node_modules/scratch-vm/src/serialization/sb3.js");

    return StringUtil.stringify(sb3.serialize(this.runtime, optTargetId, serializationOptions));
  } // TODO do we still need this function? Keeping it here so as not to introduce
  // a breaking change.

  /**
   * Load a project from a Scratch JSON representation.
   * @param {string} json JSON string representing a project.
   * @returns {Promise} Promise that resolves after the project has loaded
   */


  fromJSON(json) {
    log.warning('fromJSON is now just a wrapper around loadProject, please use that function instead.');
    return this.loadProject(json);
  }
  /**
   * Load a project from a Scratch JSON representation.
   * @param {string} projectJSON JSON string representing a project.
   * @param {?JSZip} zip Optional zipped project containing assets to be loaded.
   * @returns {Promise} Promise that resolves after the project has loaded
   */


  deserializeProject(projectJSON, zip) {
    // Clear the current runtime
    this.clear();

    if (typeof performance !== 'undefined') {
      performance.mark('scratch-vm-deserialize-start');
    }

    const runtime = this.runtime;

    const deserializePromise = function deserializePromise() {
      const projectVersion = projectJSON.projectVersion;

      if (projectVersion === 2) {
        const sb2 = __webpack_require__(/*! ./serialization/sb2 */ "./node_modules/scratch-vm/src/serialization/sb2.js");

        return sb2.deserialize(projectJSON, runtime, false, zip);
      }

      if (projectVersion === 3) {
        const sb3 = __webpack_require__(/*! ./serialization/sb3 */ "./node_modules/scratch-vm/src/serialization/sb3.js");

        return sb3.deserialize(projectJSON, runtime, zip);
      }

      return Promise.reject('Unable to verify Scratch Project version.');
    };

    return deserializePromise().then(_ref => {
      let {
        targets,
        extensions
      } = _ref;

      if (typeof performance !== 'undefined') {
        performance.mark('scratch-vm-deserialize-end');

        try {
          performance.measure('scratch-vm-deserialize', 'scratch-vm-deserialize-start', 'scratch-vm-deserialize-end');
        } catch (e) {
          // performance.measure() will throw an error if the start deserialize
          // marker was removed from memory before we finished deserializing
          // the project. We've seen this happen a couple times when loading
          // very large projects.
          log.error(e);
        }
      }

      return this.installTargets(targets, extensions, true);
    });
  }
  /**
   * @param {string[]} extensionIDs The IDs of the extensions
   * @param {Map<string, string>} extensionURLs A map of extension ID to URL
   */


  async _loadExtensions(extensionIDs) {
    let extensionURLs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : new Map();
    const extensionPromises = [];

    for (const extensionID of extensionIDs) {
      if (this.extensionManager.isExtensionLoaded(extensionID)) {// Already loaded
      } else if (this.extensionManager.isBuiltinExtension(extensionID)) {
        // Builtin extension
        this.extensionManager.loadExtensionIdSync(extensionID);
        continue;
      } else {
        // Custom extension
        const url = extensionURLs.get(extensionID);

        if (!url) {
          throw new Error("Unknown extension: ".concat(extensionID));
        }

        if (await this.securityManager.canLoadExtensionFromProject(url)) {
          extensionPromises.push(this.extensionManager.loadExtensionURL(url));
        } else {
          throw new Error("Permission to load extension denied: ".concat(extensionID));
        }
      }
    }

    return Promise.all(extensionPromises);
  }
  /**
   * Install `deserialize` results: zero or more targets after the extensions (if any) used by those targets.
   * @param {Array.<Target>} targets - the targets to be installed
   * @param {ImportedExtensionsInfo} extensions - metadata about extensions used by these targets
   * @param {boolean} wholeProject - set to true if installing a whole project, as opposed to a single sprite.
   * @returns {Promise} resolved once targets have been installed
   */


  async installTargets(targets, extensions, wholeProject) {
    await this.extensionManager.allAsyncExtensionsLoaded();
    targets = targets.filter(target => !!target);
    return this._loadExtensions(extensions.extensionIDs, extensions.extensionURLs).then(() => {
      targets.forEach(target => {
        this.runtime.addTarget(target);

        /** @type RenderedTarget */
        target.updateAllDrawableProperties(); // Ensure unique sprite name

        if (target.isSprite()) this.renameSprite(target.id, target.getName());
      }); // Sort the executable targets by layerOrder.
      // Remove layerOrder property after use.

      this.runtime.executableTargets.sort((a, b) => a.layerOrder - b.layerOrder);
      targets.forEach(target => {
        delete target.layerOrder;
      }); // Select the first target for editing, e.g., the first sprite.

      if (wholeProject && targets.length > 1) {
        this.editingTarget = targets[1];
      } else {
        this.editingTarget = targets[0];
      }

      if (!wholeProject) {
        this.editingTarget.fixUpVariableReferences();
      }

      if (wholeProject) {
        this.runtime.parseProjectOptions();
      } // Update the VM user's knowledge of targets and blocks on the workspace.


      this.emitTargetsUpdate(false
      /* Don't emit project change */
      );
      this.emitWorkspaceUpdate();
      this.runtime.setEditingTarget(this.editingTarget);
      this.runtime.ioDevices.cloud.setStage(this.runtime.getTargetForStage());
    });
  }
  /**
   * Add a sprite, this could be .sprite2 or .sprite3. Unpack and validate
   * such a file first.
   * @param {string | object} input A json string, object, or ArrayBuffer representing the project to load.
   * @return {!Promise} Promise that resolves after targets are installed.
   */


  addSprite(input) {
    const errorPrefix = 'Sprite Upload Error:';

    if (typeof input === 'object' && !(input instanceof ArrayBuffer) && !ArrayBuffer.isView(input)) {
      // If the input is an object and not any ArrayBuffer
      // or an ArrayBuffer view (this includes all typed arrays and DataViews)
      // turn the object into a JSON string, because we suspect
      // this is a project.json as an object
      // validate expects a string or buffer as input
      // TODO not sure if we need to check that it also isn't a data view
      input = JSON.stringify(input);
    }

    const validationPromise = new Promise((resolve, reject) => {
      const validate = __webpack_require__(/*! scratch-parser */ "./src/scaffolding/scratch-parser/index.js"); // The second argument of true below indicates to the parser/validator
      // that the given input should be treated as a single sprite and not
      // an entire project


      validate(input, true, (error, res) => {
        if (error) return reject(error);
        resolve(res);
      });
    });
    return validationPromise.then(validatedInput => {
      const projectVersion = validatedInput[0].projectVersion;

      if (projectVersion === 2) {
        return this._addSprite2(validatedInput[0], validatedInput[1]);
      }

      if (projectVersion === 3) {
        return this._addSprite3(validatedInput[0], validatedInput[1]);
      }

      return Promise.reject("".concat(errorPrefix, " Unable to verify sprite version."));
    }).then(() => this.runtime.emitProjectChanged()).catch(error => {
      // Intentionally rejecting here (want errors to be handled by caller)
      if (error.hasOwnProperty('validationError')) {
        return Promise.reject(JSON.stringify(error));
      }

      return Promise.reject("".concat(errorPrefix, " ").concat(error));
    });
  }
  /**
   * Add a single sprite from the "Sprite2" (i.e., SB2 sprite) format.
   * @param {object} sprite Object representing 2.0 sprite to be added.
   * @param {?ArrayBuffer} zip Optional zip of assets being referenced by json
   * @returns {Promise} Promise that resolves after the sprite is added
   */


  _addSprite2(sprite, zip) {
    // Validate & parse
    const sb2 = __webpack_require__(/*! ./serialization/sb2 */ "./node_modules/scratch-vm/src/serialization/sb2.js");

    return sb2.deserialize(sprite, this.runtime, true, zip).then(_ref2 => {
      let {
        targets,
        extensions
      } = _ref2;
      return this.installTargets(targets, extensions, false);
    });
  }
  /**
   * Add a single sb3 sprite.
   * @param {object} sprite Object rperesenting 3.0 sprite to be added.
   * @param {?ArrayBuffer} zip Optional zip of assets being referenced by target json
   * @returns {Promise} Promise that resolves after the sprite is added
   */


  _addSprite3(sprite, zip) {
    // Validate & parse
    const sb3 = __webpack_require__(/*! ./serialization/sb3 */ "./node_modules/scratch-vm/src/serialization/sb3.js");

    return sb3.deserialize(sprite, this.runtime, zip, true).then(_ref3 => {
      let {
        targets,
        extensions
      } = _ref3;
      return this.installTargets(targets, extensions, false);
    });
  }
  /**
   * Add a costume to the current editing target.
   * @param {string} md5ext - the MD5 and extension of the costume to be loaded.
   * @param {!object} costumeObject Object representing the costume.
   * @property {int} skinId - the ID of the costume's render skin, once installed.
   * @property {number} rotationCenterX - the X component of the costume's origin.
   * @property {number} rotationCenterY - the Y component of the costume's origin.
   * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.
   * @param {string} optTargetId - the id of the target to add to, if not the editing target.
   * @param {string} optVersion - if this is 2, load costume as sb2, otherwise load costume as sb3.
   * @returns {?Promise} - a promise that resolves when the costume has been added
   */


  addCostume(md5ext, costumeObject, optTargetId, optVersion) {
    const target = optTargetId ? this.runtime.getTargetById(optTargetId) : this.editingTarget;

    if (target) {
      return loadCostume(md5ext, costumeObject, this.runtime, optVersion).then(() => {
        target.addCostume(costumeObject);
        target.setCostume(target.getCostumes().length - 1);
        this.runtime.emitProjectChanged();
      });
    } // If the target cannot be found by id, return a rejected promise


    return Promise.reject();
  }
  /**
   * Add a costume loaded from the library to the current editing target.
   * @param {string} md5ext - the MD5 and extension of the costume to be loaded.
   * @param {!object} costumeObject Object representing the costume.
   * @property {int} skinId - the ID of the costume's render skin, once installed.
   * @property {number} rotationCenterX - the X component of the costume's origin.
   * @property {number} rotationCenterY - the Y component of the costume's origin.
   * @property {number} [bitmapResolution] - the resolution scale for a bitmap costume.
   * @returns {?Promise} - a promise that resolves when the costume has been added
   */


  addCostumeFromLibrary(md5ext, costumeObject) {
    if (!this.editingTarget) return Promise.reject();
    return this.addCostume(md5ext, costumeObject, this.editingTarget.id, 2
    /* optVersion */
    );
  }
  /**
   * Duplicate the costume at the given index. Add it at that index + 1.
   * @param {!int} costumeIndex Index of costume to duplicate
   * @returns {?Promise} - a promise that resolves when the costume has been decoded and added
   */


  duplicateCostume(costumeIndex) {
    const originalCostume = this.editingTarget.getCostumes()[costumeIndex];
    const clone = Object.assign({}, originalCostume);
    const md5ext = "".concat(clone.assetId, ".").concat(clone.dataFormat);
    return loadCostume(md5ext, clone, this.runtime).then(() => {
      this.editingTarget.addCostume(clone, costumeIndex + 1);
      this.editingTarget.setCostume(costumeIndex + 1);
      this.emitTargetsUpdate();
    });
  }
  /**
   * Duplicate the sound at the given index. Add it at that index + 1.
   * @param {!int} soundIndex Index of sound to duplicate
   * @returns {?Promise} - a promise that resolves when the sound has been decoded and added
   */


  duplicateSound(soundIndex) {
    const originalSound = this.editingTarget.getSounds()[soundIndex];
    const clone = Object.assign({}, originalSound);
    return loadSound(clone, this.runtime, this.editingTarget.sprite.soundBank).then(() => {
      this.editingTarget.addSound(clone, soundIndex + 1);
      this.emitTargetsUpdate();
    });
  }
  /**
   * Rename a costume on the current editing target.
   * @param {int} costumeIndex - the index of the costume to be renamed.
   * @param {string} newName - the desired new name of the costume (will be modified if already in use).
   */


  renameCostume(costumeIndex, newName) {
    this.editingTarget.renameCostume(costumeIndex, newName);
    this.emitTargetsUpdate();
  }
  /**
   * Delete a costume from the current editing target.
   * @param {int} costumeIndex - the index of the costume to be removed.
   * @return {?function} A function to restore the deleted costume, or null,
   * if no costume was deleted.
   */


  deleteCostume(costumeIndex) {
    const deletedCostume = this.editingTarget.deleteCostume(costumeIndex);

    if (deletedCostume) {
      const target = this.editingTarget;
      this.runtime.emitProjectChanged();
      return () => {
        target.addCostume(deletedCostume);
        this.emitTargetsUpdate();
      };
    }

    return null;
  }
  /**
   * Add a sound to the current editing target.
   * @param {!object} soundObject Object representing the costume.
   * @param {string} optTargetId - the id of the target to add to, if not the editing target.
   * @returns {?Promise} - a promise that resolves when the sound has been decoded and added
   */


  addSound(soundObject, optTargetId) {
    const target = optTargetId ? this.runtime.getTargetById(optTargetId) : this.editingTarget;

    if (target) {
      return loadSound(soundObject, this.runtime, target.sprite.soundBank).then(() => {
        target.addSound(soundObject);
        this.emitTargetsUpdate();
      });
    } // If the target cannot be found by id, return a rejected promise


    return Promise.reject(new Error("No target with ID: ".concat(optTargetId)));
  }
  /**
   * Rename a sound on the current editing target.
   * @param {int} soundIndex - the index of the sound to be renamed.
   * @param {string} newName - the desired new name of the sound (will be modified if already in use).
   */


  renameSound(soundIndex, newName) {
    this.editingTarget.renameSound(soundIndex, newName);
    this.emitTargetsUpdate();
  }
  /**
   * Get a sound buffer from the audio engine.
   * @param {int} soundIndex - the index of the sound to be got.
   * @return {AudioBuffer} the sound's audio buffer.
   */


  getSoundBuffer(soundIndex) {
    const id = this.editingTarget.sprite.sounds[soundIndex].soundId;

    if (id && this.runtime && this.runtime.audioEngine) {
      return this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer;
    }

    return null;
  }
  /**
   * Update a sound buffer.
   * @param {int} soundIndex - the index of the sound to be updated.
   * @param {AudioBuffer} newBuffer - new audio buffer for the audio engine.
   * @param {ArrayBuffer} soundEncoding - the new (wav) encoded sound to be stored
   */


  updateSoundBuffer(soundIndex, newBuffer, soundEncoding) {
    const sound = this.editingTarget.sprite.sounds[soundIndex];
    if (sound && sound.broken) delete sound.broken;
    const id = sound ? sound.soundId : null;

    if (id && this.runtime && this.runtime.audioEngine) {
      this.editingTarget.sprite.soundBank.getSoundPlayer(id).buffer = newBuffer;
    } // Update sound in runtime


    if (soundEncoding) {
      // Now that we updated the sound, the format should also be updated
      // so that the sound can eventually be decoded the right way.
      // Sounds that were formerly 'adpcm', but were updated in sound editor
      // will not get decoded by the audio engine correctly unless the format
      // is updated as below.
      sound.format = '';
      const storage = this.runtime.storage;
      sound.asset = storage.createAsset(storage.AssetType.Sound, storage.DataFormat.WAV, soundEncoding, null, true // generate md5
      );
      sound.assetId = sound.asset.assetId;
      sound.dataFormat = storage.DataFormat.WAV;
      sound.md5 = "".concat(sound.assetId, ".").concat(sound.dataFormat);
      sound.sampleCount = newBuffer.length;
      sound.rate = newBuffer.sampleRate;
    } // If soundEncoding is null, it's because gui had a problem
    // encoding the updated sound. We don't want to store anything in this
    // case, and gui should have logged an error.


    this.emitTargetsUpdate();
  }
  /**
   * Delete a sound from the current editing target.
   * @param {int} soundIndex - the index of the sound to be removed.
   * @return {?Function} A function to restore the sound that was deleted,
   * or null, if no sound was deleted.
   */


  deleteSound(soundIndex) {
    const target = this.editingTarget;
    const deletedSound = this.editingTarget.deleteSound(soundIndex);

    if (deletedSound) {
      this.runtime.emitProjectChanged();

      const restoreFun = () => {
        target.addSound(deletedSound);
        this.emitTargetsUpdate();
      };

      return restoreFun;
    }

    return null;
  }
  /**
   * Get a string representation of the image from storage.
   * @param {int} costumeIndex - the index of the costume to be got.
   * @return {string} the costume's SVG string if it's SVG,
   *     a dataURI if it's a PNG or JPG, or null if it couldn't be found or decoded.
   */


  getCostume(costumeIndex) {
    const asset = this.editingTarget.getCostumes()[costumeIndex].asset;
    if (!asset || !this.runtime || !this.runtime.storage) return null;
    const format = asset.dataFormat;

    if (format === this.runtime.storage.DataFormat.SVG) {
      return asset.decodeText();
    } else if (format === this.runtime.storage.DataFormat.PNG || format === this.runtime.storage.DataFormat.JPG) {
      return asset.encodeDataURI();
    }

    log.error("Unhandled format: ".concat(asset.dataFormat));
    return null;
  }
  /**
   * TW: Get the raw binary data to use when exporting a costume to the user's local file system.
   * @param {Costume} costumeObject scratch-vm costume object
   * @returns {Uint8Array}
   */


  getExportedCostume(costumeObject) {
    return exportCostume(costumeObject);
  }
  /**
   * TW: Get a base64 string to use when exporting a costume to the user's local file system.
   * @param {Costume} costumeObject scratch-vm costume object
   * @returns {string} base64 string. Not a data: URI.
   */


  getExportedCostumeBase64(costumeObject) {
    const binaryData = this.getExportedCostume(costumeObject);
    return Base64Util.uint8ArrayToBase64(binaryData);
  }
  /**
   * Update a costume with the given bitmap
   * @param {!int} costumeIndex - the index of the costume to be updated.
   * @param {!ImageData} bitmap - new bitmap for the renderer.
   * @param {!number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner
   * @param {!number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner
   * @param {!number} bitmapResolution 1 for bitmaps that have 1 pixel per unit of stage,
   *     2 for double-resolution bitmaps
   */


  updateBitmap(costumeIndex, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) {
    return this._updateBitmap(this.editingTarget.getCostumes()[costumeIndex], bitmap, rotationCenterX, rotationCenterY, bitmapResolution);
  }

  _updateBitmap(costume, bitmap, rotationCenterX, rotationCenterY, bitmapResolution) {
    if (!(costume && this.runtime && this.runtime.renderer)) return;
    if (costume && costume.broken) delete costume.broken;
    costume.rotationCenterX = rotationCenterX;
    costume.rotationCenterY = rotationCenterY; // If the bitmap originally had a zero width or height, use that value

    const bitmapWidth = bitmap.sourceWidth === 0 ? 0 : bitmap.width;
    const bitmapHeight = bitmap.sourceHeight === 0 ? 0 : bitmap.height; // @todo: updateBitmapSkin does not take ImageData

    const canvas = document.createElement('canvas');
    canvas.width = bitmapWidth;
    canvas.height = bitmapHeight;
    const context = canvas.getContext('2d');
    context.putImageData(bitmap, 0, 0); // Divide by resolution because the renderer's definition of the rotation center
    // is the rotation center divided by the bitmap resolution

    this.runtime.renderer.updateBitmapSkin(costume.skinId, canvas, bitmapResolution, [rotationCenterX / bitmapResolution, rotationCenterY / bitmapResolution]); // @todo there should be a better way to get from ImageData to a decodable storage format

    canvas.toBlob(blob => {
      const reader = new FileReader();
      reader.addEventListener('loadend', () => {
        const storage = this.runtime.storage;
        costume.dataFormat = storage.DataFormat.PNG;
        costume.bitmapResolution = bitmapResolution;
        costume.size = [bitmapWidth, bitmapHeight];
        costume.asset = storage.createAsset(storage.AssetType.ImageBitmap, costume.dataFormat, Buffer.from(reader.result), null, // id
        true // generate md5
        );
        costume.assetId = costume.asset.assetId;
        costume.md5 = "".concat(costume.assetId, ".").concat(costume.dataFormat);
        this.emitTargetsUpdate();
      }); // Bitmaps with a zero width or height return null for their blob

      if (blob) {
        reader.readAsArrayBuffer(blob);
      }
    });
  }
  /**
   * Update a costume with the given SVG
   * @param {int} costumeIndex - the index of the costume to be updated.
   * @param {string} svg - new SVG for the renderer.
   * @param {number} rotationCenterX x of point about which the costume rotates, relative to its upper left corner
   * @param {number} rotationCenterY y of point about which the costume rotates, relative to its upper left corner
   */


  updateSvg(costumeIndex, svg, rotationCenterX, rotationCenterY) {
    return this._updateSvg(this.editingTarget.getCostumes()[costumeIndex], svg, rotationCenterX, rotationCenterY);
  }

  _updateSvg(costume, svg, rotationCenterX, rotationCenterY) {
    if (costume && costume.broken) delete costume.broken;

    if (costume && this.runtime && this.runtime.renderer) {
      costume.rotationCenterX = rotationCenterX;
      costume.rotationCenterY = rotationCenterY;
      this.runtime.renderer.updateSVGSkin(costume.skinId, svg, [rotationCenterX, rotationCenterY]);
      costume.size = this.runtime.renderer.getSkinSize(costume.skinId);
    }

    const storage = this.runtime.storage; // If we're in here, we've edited an svg in the vector editor,
    // so the dataFormat should be 'svg'

    costume.dataFormat = storage.DataFormat.SVG;
    costume.bitmapResolution = 1;
    costume.asset = storage.createAsset(storage.AssetType.ImageVector, costume.dataFormat, new _TextEncoder().encode(svg), null, true // generate md5
    );
    costume.assetId = costume.asset.assetId;
    costume.md5 = "".concat(costume.assetId, ".").concat(costume.dataFormat);
    this.emitTargetsUpdate();
  }
  /**
   * Add a backdrop to the stage.
   * @param {string} md5ext - the MD5 and extension of the backdrop to be loaded.
   * @param {!object} backdropObject Object representing the backdrop.
   * @property {int} skinId - the ID of the backdrop's render skin, once installed.
   * @property {number} rotationCenterX - the X component of the backdrop's origin.
   * @property {number} rotationCenterY - the Y component of the backdrop's origin.
   * @property {number} [bitmapResolution] - the resolution scale for a bitmap backdrop.
   * @returns {?Promise} - a promise that resolves when the backdrop has been added
   */


  addBackdrop(md5ext, backdropObject) {
    return loadCostume(md5ext, backdropObject, this.runtime).then(() => {
      const stage = this.runtime.getTargetForStage();
      stage.addCostume(backdropObject);
      stage.setCostume(stage.getCostumes().length - 1);
      this.runtime.emitProjectChanged();
    });
  }
  /**
   * Rename a sprite.
   * @param {string} targetId ID of a target whose sprite to rename.
   * @param {string} newName New name of the sprite.
   */


  renameSprite(targetId, newName) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      if (!target.isSprite()) {
        throw new Error('Cannot rename non-sprite targets.');
      }

      const sprite = target.sprite;

      if (!sprite) {
        throw new Error('No sprite associated with this target.');
      }

      if (newName && RESERVED_NAMES.indexOf(newName) === -1) {
        const names = this.runtime.targets.filter(runtimeTarget => runtimeTarget.isSprite() && runtimeTarget.id !== target.id).map(runtimeTarget => runtimeTarget.sprite.name);
        const oldName = sprite.name;
        const newUnusedName = StringUtil.unusedName(newName, names);
        sprite.name = newUnusedName;

        if (oldName === newUnusedName) {
          return;
        }

        const allTargets = this.runtime.targets;

        for (let i = 0; i < allTargets.length; i++) {
          const currTarget = allTargets[i];
          currTarget.blocks.updateAssetName(oldName, newName, 'sprite');
        }

        if (newUnusedName !== oldName) this.emitTargetsUpdate();
      }
    } else {
      throw new Error('No target with the provided id.');
    }
  }
  /**
   * Delete a sprite and all its clones.
   * @param {string} targetId ID of a target whose sprite to delete.
   * @return {Function} Returns a function to restore the sprite that was deleted
   */


  deleteSprite(targetId) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      const targetIndexBeforeDelete = this.runtime.targets.map(t => t.id).indexOf(target.id);

      if (!target.isSprite()) {
        throw new Error('Cannot delete non-sprite targets.');
      }

      const sprite = target.sprite;

      if (!sprite) {
        throw new Error('No sprite associated with this target.');
      }

      const spritePromise = this.exportSprite(targetId, 'uint8array');

      const restoreSprite = () => spritePromise.then(spriteBuffer => this.addSprite(spriteBuffer)); // Remove monitors from the runtime state and remove the
      // target-specific monitored blocks (e.g. local variables)


      target.deleteMonitors();
      const currentEditingTarget = this.editingTarget;

      for (let i = 0; i < sprite.clones.length; i++) {
        const clone = sprite.clones[i];
        this.runtime.stopForTarget(sprite.clones[i]);
        this.runtime.disposeTarget(sprite.clones[i]); // Ensure editing target is switched if we are deleting it.

        if (clone === currentEditingTarget) {
          const nextTargetIndex = Math.min(this.runtime.targets.length - 1, targetIndexBeforeDelete);

          if (this.runtime.targets.length > 0) {
            this.setEditingTarget(this.runtime.targets[nextTargetIndex].id);
          } else {
            this.editingTarget = null;
          }
        }
      } // Sprite object should be deleted by GC.


      this.emitTargetsUpdate();
      return restoreSprite;
    }

    throw new Error('No target with the provided id.');
  }
  /**
   * Duplicate a sprite.
   * @param {string} targetId ID of a target whose sprite to duplicate.
   * @returns {Promise} Promise that resolves when duplicated target has
   *     been added to the runtime.
   */


  duplicateSprite(targetId) {
    const target = this.runtime.getTargetById(targetId);

    if (!target) {
      throw new Error('No target with the provided id.');
    } else if (!target.isSprite()) {
      throw new Error('Cannot duplicate non-sprite targets.');
    } else if (!target.sprite) {
      throw new Error('No sprite associated with this target.');
    }

    return target.duplicate().then(newTarget => {
      this.runtime.addTarget(newTarget);
      newTarget.goBehindOther(target);
      this.setEditingTarget(newTarget.id);
    });
  }
  /**
   * Set the audio engine for the VM/runtime
   * @param {!AudioEngine} audioEngine The audio engine to attach
   */


  attachAudioEngine(audioEngine) {
    this.runtime.attachAudioEngine(audioEngine);
  }
  /**
   * Set the renderer for the VM/runtime
   * @param {!RenderWebGL} renderer The renderer to attach
   */


  attachRenderer(renderer) {
    this.runtime.attachRenderer(renderer);
  }
  /**
   * @returns {RenderWebGL} The renderer attached to the vm
   */


  get renderer() {
    return this.runtime && this.runtime.renderer;
  } // @deprecated


  attachV2SVGAdapter() {}
  /**
   * Set the bitmap adapter for the VM/runtime, which converts scratch 2
   * bitmaps to scratch 3 bitmaps. (Scratch 3 bitmaps are all bitmap resolution 2)
   * @param {!function} bitmapAdapter The adapter to attach
   */


  attachV2BitmapAdapter(bitmapAdapter) {
    this.runtime.attachV2BitmapAdapter(bitmapAdapter);
  }
  /**
   * Set the storage module for the VM/runtime
   * @param {!ScratchStorage} storage The storage module to attach
   */


  attachStorage(storage) {
    this.runtime.attachStorage(storage);
  }
  /**
   * set the current locale and builtin messages for the VM
   * @param {!string} locale       current locale
   * @param {!object} messages     builtin messages map for current locale
   * @returns {Promise} Promise that resolves when all the blocks have been
   *     updated for a new locale (or empty if locale hasn't changed.)
   */


  setLocale(locale, messages) {
    if (locale !== formatMessage.setup().locale) {
      formatMessage.setup({
        locale: locale,
        translations: {
          [locale]: messages
        }
      });
    }

    return this.extensionManager.refreshBlocks();
  }
  /**
   * get the current locale for the VM
   * @returns {string} the current locale in the VM
   */


  getLocale() {
    return formatMessage.setup().locale;
  }
  /**
   * Handle a Blockly event for the current editing target.
   * @param {!Blockly.Event} e Any Blockly event.
   */


  blockListener(e) {
    if (this.editingTarget) {
      this.editingTarget.blocks.blocklyListen(e);
    }
  }
  /**
   * Handle a Blockly event for the flyout.
   * @param {!Blockly.Event} e Any Blockly event.
   */


  flyoutBlockListener(e) {
    this.runtime.flyoutBlocks.blocklyListen(e);
  }
  /**
   * Handle a Blockly event for the flyout to be passed to the monitor container.
   * @param {!Blockly.Event} e Any Blockly event.
   */


  monitorBlockListener(e) {
    // Filter events by type, since monitor blocks only need to listen to these events.
    // Monitor blocks shouldn't be destroyed when flyout blocks are deleted.
    if (['create', 'change'].indexOf(e.type) !== -1) {
      this.runtime.monitorBlocks.blocklyListen(e);
    }
  }
  /**
   * Handle a Blockly event for the variable map.
   * @param {!Blockly.Event} e Any Blockly event.
   */


  variableListener(e) {
    // Filter events by type, since blocks only needs to listen to these
    // var events.
    if (['var_create', 'var_rename', 'var_delete'].indexOf(e.type) !== -1) {
      this.runtime.getTargetForStage().blocks.blocklyListen(e);
    }
  }
  /**
   * Set an editing target. An editor UI can use this function to switch
   * between editing different targets, sprites, etc.
   * After switching the editing target, the VM may emit updates
   * to the list of targets and any attached workspace blocks
   * (see `emitTargetsUpdate` and `emitWorkspaceUpdate`).
   * @param {string} targetId Id of target to set as editing.
   */


  setEditingTarget(targetId) {
    // Has the target id changed? If not, exit.
    if (this.editingTarget && targetId === this.editingTarget.id) {
      return;
    }

    const target = this.runtime.getTargetById(targetId);

    if (target) {
      this.editingTarget = target; // Emit appropriate UI updates.

      this.emitTargetsUpdate(false
      /* Don't emit project change */
      );
      this.emitWorkspaceUpdate();
      this.runtime.setEditingTarget(target);
    }
  }
  /**
   * Called when blocks are dragged from one sprite to another. Adds the blocks to the
   * workspace of the given target.
   * @param {!Array<object>} blocks Blocks to add.
   * @param {!string} targetId Id of target to add blocks to.
   * @param {?string} optFromTargetId Optional target id indicating that blocks are being
   * shared from that target. This is needed for resolving any potential variable conflicts.
   * @return {!Promise} Promise that resolves when the extensions and blocks have been added.
   */


  shareBlocksToTarget(blocks, targetId, optFromTargetId) {
    const sb3 = __webpack_require__(/*! ./serialization/sb3 */ "./node_modules/scratch-vm/src/serialization/sb3.js");

    const copiedBlocks = JSON.parse(JSON.stringify(blocks));
    newBlockIds(copiedBlocks);
    const target = this.runtime.getTargetById(targetId);

    if (optFromTargetId) {
      // If the blocks are being shared from another target,
      // resolve any possible variable conflicts that may arise.
      const fromTarget = this.runtime.getTargetById(optFromTargetId);
      fromTarget.resolveVariableSharingConflictsWithTarget(copiedBlocks, target);
    } // Create a unique set of extensionIds that are not yet loaded


    const extensionIDs = new Set(copiedBlocks.map(b => sb3.getExtensionIdForOpcode(b.opcode)).filter(id => !!id) // Remove ids that do not exist
    .filter(id => !this.extensionManager.isExtensionLoaded(id)) // and remove loaded extensions
    );
    return this._loadExtensions(extensionIDs).then(() => {
      copiedBlocks.forEach(block => {
        target.blocks.createBlock(block);
      });
      target.blocks.updateTargetSpecificBlocks(target.isStage);
    });
  }
  /**
   * Called when costumes are dragged from editing target to another target.
   * Sets the newly added costume as the current costume.
   * @param {!number} costumeIndex Index of the costume of the editing target to share.
   * @param {!string} targetId Id of target to add the costume.
   * @return {Promise} Promise that resolves when the new costume has been loaded.
   */


  shareCostumeToTarget(costumeIndex, targetId) {
    const originalCostume = this.editingTarget.getCostumes()[costumeIndex];
    const clone = Object.assign({}, originalCostume);
    const md5ext = "".concat(clone.assetId, ".").concat(clone.dataFormat);
    return loadCostume(md5ext, clone, this.runtime).then(() => {
      const target = this.runtime.getTargetById(targetId);

      if (target) {
        target.addCostume(clone);
        target.setCostume(target.getCostumes().length - 1);
      }
    });
  }
  /**
   * Called when sounds are dragged from editing target to another target.
   * @param {!number} soundIndex Index of the sound of the editing target to share.
   * @param {!string} targetId Id of target to add the sound.
   * @return {Promise} Promise that resolves when the new sound has been loaded.
   */


  shareSoundToTarget(soundIndex, targetId) {
    const originalSound = this.editingTarget.getSounds()[soundIndex];
    const clone = Object.assign({}, originalSound);
    const target = this.runtime.getTargetById(targetId);
    return loadSound(clone, this.runtime, target.sprite.soundBank).then(() => {
      if (target) {
        target.addSound(clone);
        this.emitTargetsUpdate();
      }
    });
  }
  /**
   * Repopulate the workspace with the blocks of the current editingTarget. This
   * allows us to get around bugs like gui#413.
   */


  refreshWorkspace() {
    if (this.editingTarget) {
      this.emitWorkspaceUpdate();
      this.runtime.setEditingTarget(this.editingTarget);
      this.emitTargetsUpdate(false
      /* Don't emit project change */
      );
    }
  }
  /**
   * Emit metadata about available targets.
   * An editor UI could use this to display a list of targets and show
   * the currently editing one.
   * @param {bool} triggerProjectChange If true, also emit a project changed event.
   * Disabled selectively by updates that don't affect project serialization.
   * Defaults to true.
   */


  emitTargetsUpdate(triggerProjectChange) {
    if (typeof triggerProjectChange === 'undefined') triggerProjectChange = true;
    let lazyTargetList;

    const getTargetListLazily = () => {
      if (!lazyTargetList) {
        lazyTargetList = this.runtime.targets.filter( // Don't report clones.
        target => !target.hasOwnProperty('isOriginal') || target.isOriginal).map(target => target.toJSON());
      }

      return lazyTargetList;
    };

    this.emit('targetsUpdate', {
      // [[target id, human readable target name], ...].
      get targetList() {
        return getTargetListLazily();
      },

      // Currently editing target id.
      editingTarget: this.editingTarget ? this.editingTarget.id : null
    });

    if (triggerProjectChange) {
      this.runtime.emitProjectChanged();
    }
  }
  /**
   * Emit an Blockly/scratch-blocks compatible XML representation
   * of the current editing target's blocks.
   */


  emitWorkspaceUpdate() {
    // Create a list of broadcast message Ids according to the stage variables
    const stageVariables = this.runtime.getTargetForStage().variables;
    let messageIds = [];

    for (const varId in stageVariables) {
      if (stageVariables[varId].type === Variable.BROADCAST_MESSAGE_TYPE) {
        messageIds.push(varId);
      }
    } // Go through all blocks on all targets, removing referenced
    // broadcast ids from the list.


    for (let i = 0; i < this.runtime.targets.length; i++) {
      const currTarget = this.runtime.targets[i];
      const currBlocks = currTarget.blocks._blocks;

      for (const blockId in currBlocks) {
        if (currBlocks[blockId].fields.BROADCAST_OPTION) {
          const id = currBlocks[blockId].fields.BROADCAST_OPTION.id;
          const index = messageIds.indexOf(id);

          if (index !== -1) {
            messageIds = messageIds.slice(0, index).concat(messageIds.slice(index + 1));
          }
        }
      }
    } // Anything left in messageIds is not referenced by a block, so delete it.


    for (let i = 0; i < messageIds.length; i++) {
      const id = messageIds[i];
      delete this.runtime.getTargetForStage().variables[id];
    }

    const globalVarMap = Object.assign({}, this.runtime.getTargetForStage().variables);
    const localVarMap = this.editingTarget.isStage ? Object.create(null) : Object.assign({}, this.editingTarget.variables);
    const globalVariables = Object.keys(globalVarMap).map(k => globalVarMap[k]);
    const localVariables = Object.keys(localVarMap).map(k => localVarMap[k]);
    const workspaceComments = Object.keys(this.editingTarget.comments).map(k => this.editingTarget.comments[k]).filter(c => c.blockId === null);
    const xmlString = "<xml xmlns=\"http://www.w3.org/1999/xhtml\">\n                            <variables>\n                                ".concat(globalVariables.map(v => v.toXML()).join(), "\n                                ").concat(localVariables.map(v => v.toXML(true)).join(), "\n                            </variables>\n                            ").concat(workspaceComments.map(c => c.toXML()).join(), "\n                            ").concat(this.editingTarget.blocks.toXML(this.editingTarget.comments), "\n                        </xml>");
    this.emit('workspaceUpdate', {
      xml: xmlString
    });
  }
  /**
   * Get a target id for a drawable id. Useful for interacting with the renderer
   * @param {int} drawableId The drawable id to request the target id for
   * @returns {?string} The target id, if found. Will also be null if the target found is the stage.
   */


  getTargetIdForDrawableId(drawableId) {
    const target = this.runtime.getTargetByDrawableId(drawableId);

    if (target && target.hasOwnProperty('id') && target.hasOwnProperty('isStage') && !target.isStage) {
      return target.id;
    }

    return null;
  }
  /**
   * Reorder target by index. Return whether a change was made.
   * @param {!string} targetIndex Index of the target.
   * @param {!number} newIndex index that the target should be moved to.
   * @returns {boolean} Whether a target was reordered.
   */


  reorderTarget(targetIndex, newIndex) {
    let targets = this.runtime.targets;
    targetIndex = MathUtil.clamp(targetIndex, 0, targets.length - 1);
    newIndex = MathUtil.clamp(newIndex, 0, targets.length - 1);
    if (targetIndex === newIndex) return false;
    const target = targets[targetIndex];
    targets = targets.slice(0, targetIndex).concat(targets.slice(targetIndex + 1));
    targets.splice(newIndex, 0, target);
    this.runtime.targets = targets;
    this.emitTargetsUpdate();
    return true;
  }
  /**
   * Reorder the costumes of a target if it exists. Return whether it succeeded.
   * @param {!string} targetId ID of the target which owns the costumes.
   * @param {!number} costumeIndex index of the costume to move.
   * @param {!number} newIndex index that the costume should be moved to.
   * @returns {boolean} Whether a costume was reordered.
   */


  reorderCostume(targetId, costumeIndex, newIndex) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      const reorderSuccessful = target.reorderCostume(costumeIndex, newIndex);

      if (reorderSuccessful) {
        this.runtime.emitProjectChanged();
      }

      return reorderSuccessful;
    }

    return false;
  }
  /**
   * Reorder the sounds of a target if it exists. Return whether it occured.
   * @param {!string} targetId ID of the target which owns the sounds.
   * @param {!number} soundIndex index of the sound to move.
   * @param {!number} newIndex index that the sound should be moved to.
   * @returns {boolean} Whether a sound was reordered.
   */


  reorderSound(targetId, soundIndex, newIndex) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      const reorderSuccessful = target.reorderSound(soundIndex, newIndex);

      if (reorderSuccessful) {
        this.runtime.emitProjectChanged();
      }

      return reorderSuccessful;
    }

    return false;
  }
  /**
   * Put a target into a "drag" state, during which its X/Y positions will be unaffected
   * by blocks.
   * @param {string} targetId The id for the target to put into a drag state
   */


  startDrag(targetId) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      this._dragTarget = target;
      target.startDrag();
    }
  }
  /**
   * Remove a target from a drag state, so blocks may begin affecting X/Y position again
   * @param {string} targetId The id for the target to remove from the drag state
   */


  stopDrag(targetId) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      this._dragTarget = null;
      target.stopDrag();
      this.setEditingTarget(target.sprite && target.sprite.clones[0] ? target.sprite.clones[0].id : target.id);
    }
  }
  /**
   * Post/edit sprite info for the current editing target or the drag target.
   * @param {object} data An object with sprite info data to set.
   */


  postSpriteInfo(data) {
    if (this._dragTarget) {
      this._dragTarget.postSpriteInfo(data);
    } else {
      this.editingTarget.postSpriteInfo(data);
    } // Post sprite info means the gui has changed something about a sprite,
    // either through the sprite info pane fields (e.g. direction, size) or
    // through dragging a sprite on the stage
    // Emit a project changed event.


    this.runtime.emitProjectChanged();
  }
  /**
   * Set a target's variable's value. Return whether it succeeded.
   * @param {!string} targetId ID of the target which owns the variable.
   * @param {!string} variableId ID of the variable to set.
   * @param {!*} value The new value of that variable.
   * @returns {boolean} whether the target and variable were found and updated.
   */


  setVariableValue(targetId, variableId, value) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      const variable = target.lookupVariableById(variableId);

      if (variable) {
        variable.value = value;

        if (variable.isCloud) {
          this.runtime.ioDevices.cloud.requestUpdateVariable(variable.name, variable.value);
        }

        return true;
      }
    }

    return false;
  }
  /**
   * Get a target's variable's value. Return null if the target or variable does not exist.
   * @param {!string} targetId ID of the target which owns the variable.
   * @param {!string} variableId ID of the variable to set.
   * @returns {?*} The value of the variable, or null if it could not be looked up.
   */


  getVariableValue(targetId, variableId) {
    const target = this.runtime.getTargetById(targetId);

    if (target) {
      const variable = target.lookupVariableById(variableId);

      if (variable) {
        return variable.value;
      }
    }

    return null;
  }
  /**
   * Allow VM consumer to configure the ScratchLink socket creator.
   * @param {Function} factory The custom ScratchLink socket factory.
   */


  configureScratchLinkSocketFactory(factory) {
    this.runtime.configureScratchLinkSocketFactory(factory);
  }

}

module.exports = VirtualMachine;

/***/ }),

/***/ "./node_modules/setimmediate/setImmediate.js":
/*!***************************************************!*\
  !*** ./node_modules/setimmediate/setImmediate.js ***!
  \***************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global, process) {(function (global, undefined) {
    "use strict";

    if (global.setImmediate) {
        return;
    }

    var nextHandle = 1; // Spec says greater than zero
    var tasksByHandle = {};
    var currentlyRunningATask = false;
    var doc = global.document;
    var registerImmediate;

    function setImmediate(callback) {
      // Callback can either be a function or a string
      if (typeof callback !== "function") {
        callback = new Function("" + callback);
      }
      // Copy function arguments
      var args = new Array(arguments.length - 1);
      for (var i = 0; i < args.length; i++) {
          args[i] = arguments[i + 1];
      }
      // Store and register the task
      var task = { callback: callback, args: args };
      tasksByHandle[nextHandle] = task;
      registerImmediate(nextHandle);
      return nextHandle++;
    }

    function clearImmediate(handle) {
        delete tasksByHandle[handle];
    }

    function run(task) {
        var callback = task.callback;
        var args = task.args;
        switch (args.length) {
        case 0:
            callback();
            break;
        case 1:
            callback(args[0]);
            break;
        case 2:
            callback(args[0], args[1]);
            break;
        case 3:
            callback(args[0], args[1], args[2]);
            break;
        default:
            callback.apply(undefined, args);
            break;
        }
    }

    function runIfPresent(handle) {
        // From the spec: "Wait until any invocations of this algorithm started before this one have completed."
        // So if we're currently running a task, we'll need to delay this invocation.
        if (currentlyRunningATask) {
            // Delay by doing a setTimeout. setImmediate was tried instead, but in Firefox 7 it generated a
            // "too much recursion" error.
            setTimeout(runIfPresent, 0, handle);
        } else {
            var task = tasksByHandle[handle];
            if (task) {
                currentlyRunningATask = true;
                try {
                    run(task);
                } finally {
                    clearImmediate(handle);
                    currentlyRunningATask = false;
                }
            }
        }
    }

    function installNextTickImplementation() {
        registerImmediate = function(handle) {
            process.nextTick(function () { runIfPresent(handle); });
        };
    }

    function canUsePostMessage() {
        // The test against `importScripts` prevents this implementation from being installed inside a web worker,
        // where `global.postMessage` means something completely different and can't be used for this purpose.
        if (global.postMessage && !global.importScripts) {
            var postMessageIsAsynchronous = true;
            var oldOnMessage = global.onmessage;
            global.onmessage = function() {
                postMessageIsAsynchronous = false;
            };
            global.postMessage("", "*");
            global.onmessage = oldOnMessage;
            return postMessageIsAsynchronous;
        }
    }

    function installPostMessageImplementation() {
        // Installs an event handler on `global` for the `message` event: see
        // * https://developer.mozilla.org/en/DOM/window.postMessage
        // * http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html#crossDocumentMessages

        var messagePrefix = "setImmediate$" + Math.random() + "$";
        var onGlobalMessage = function(event) {
            if (event.source === global &&
                typeof event.data === "string" &&
                event.data.indexOf(messagePrefix) === 0) {
                runIfPresent(+event.data.slice(messagePrefix.length));
            }
        };

        if (global.addEventListener) {
            global.addEventListener("message", onGlobalMessage, false);
        } else {
            global.attachEvent("onmessage", onGlobalMessage);
        }

        registerImmediate = function(handle) {
            global.postMessage(messagePrefix + handle, "*");
        };
    }

    function installMessageChannelImplementation() {
        var channel = new MessageChannel();
        channel.port1.onmessage = function(event) {
            var handle = event.data;
            runIfPresent(handle);
        };

        registerImmediate = function(handle) {
            channel.port2.postMessage(handle);
        };
    }

    function installReadyStateChangeImplementation() {
        var html = doc.documentElement;
        registerImmediate = function(handle) {
            // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
            // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
            var script = doc.createElement("script");
            script.onreadystatechange = function () {
                runIfPresent(handle);
                script.onreadystatechange = null;
                html.removeChild(script);
                script = null;
            };
            html.appendChild(script);
        };
    }

    function installSetTimeoutImplementation() {
        registerImmediate = function(handle) {
            setTimeout(runIfPresent, 0, handle);
        };
    }

    // If supported, we should attach to the prototype of global, since that is where setTimeout et al. live.
    var attachTo = Object.getPrototypeOf && Object.getPrototypeOf(global);
    attachTo = attachTo && attachTo.setTimeout ? attachTo : global;

    // Don't get fooled by e.g. browserify environments.
    if ({}.toString.call(global.process) === "[object process]") {
        // For Node.js before 0.9
        installNextTickImplementation();

    } else if (canUsePostMessage()) {
        // For non-IE10 modern browsers
        installPostMessageImplementation();

    } else if (global.MessageChannel) {
        // For web workers, where supported
        installMessageChannelImplementation();

    } else if (doc && "onreadystatechange" in doc.createElement("script")) {
        // For IE 6–8
        installReadyStateChangeImplementation();

    } else {
        // For older browsers
        installSetTimeoutImplementation();
    }

    attachTo.setImmediate = setImmediate;
    attachTo.clearImmediate = clearImmediate;
}(typeof self === "undefined" ? typeof global === "undefined" ? this : global : self));

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"), __webpack_require__(/*! ./../process/browser.js */ "./node_modules/process/browser.js")))

/***/ }),

/***/ "./node_modules/startaudiocontext/StartAudioContext.js":
/*!*************************************************************!*\
  !*** ./node_modules/startaudiocontext/StartAudioContext.js ***!
  \*************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/**
 *  StartAudioContext.js
 *  @author Yotam Mann
 *  @license http://opensource.org/licenses/MIT MIT License
 *  @copyright 2016 Yotam Mann
 */
(function (root, factory) {
	if (true) {
		!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
				__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
				(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__))
	 } else {}
}(this, function () {

	//TAP LISTENER/////////////////////////////////////////////////////////////

	/**
	 * @class  Listens for non-dragging tap ends on the given element
	 * @param {Element} element
	 * @internal
	 */
	var TapListener = function(element, context){

		this._dragged = false

		this._element = element

		this._bindedMove = this._moved.bind(this)
		this._bindedEnd = this._ended.bind(this, context)

		element.addEventListener("touchstart", this._bindedEnd)
		element.addEventListener("touchmove", this._bindedMove)
		element.addEventListener("touchend", this._bindedEnd)
		element.addEventListener("mouseup", this._bindedEnd)
	}

	/**
	 * drag move event
	 */
	TapListener.prototype._moved = function(e){
		this._dragged = true
	};

	/**
	 * tap ended listener
	 */
	TapListener.prototype._ended = function(context){
		if (!this._dragged){
			startContext(context)
		}
		this._dragged = false
	};

	/**
	 * remove all the bound events
	 */
	TapListener.prototype.dispose = function(){
		this._element.removeEventListener("touchstart", this._bindedEnd)
		this._element.removeEventListener("touchmove", this._bindedMove)
		this._element.removeEventListener("touchend", this._bindedEnd)
		this._element.removeEventListener("mouseup", this._bindedEnd)
		this._bindedMove = null
		this._bindedEnd = null
		this._element = null
	};

	//END TAP LISTENER/////////////////////////////////////////////////////////

	/**
	 * Plays a silent sound and also invoke the "resume" method
	 * @param {AudioContext} context
	 * @private
	 */
	function startContext(context){
		// this accomplishes the iOS specific requirement
		var buffer = context.createBuffer(1, 1, context.sampleRate)
		var source = context.createBufferSource()
		source.buffer = buffer
		source.connect(context.destination)
		source.start(0)

		// resume the audio context
		if (context.resume){
			context.resume()
		}
	}

	/**
	 * Returns true if the audio context is started
	 * @param  {AudioContext}  context
	 * @return {Boolean}
	 * @private
	 */
	function isStarted(context){
		 return context.state === "running"
	}

	/**
	 * Invokes the callback as soon as the AudioContext
	 * is started
	 * @param  {AudioContext}   context
	 * @param  {Function} callback
	 */
	function onStarted(context, callback){

		function checkLoop(){
			if (isStarted(context)){
				callback()
			} else {
				requestAnimationFrame(checkLoop)
				if (context.resume){
					context.resume()
				}
			}
		}

		if (isStarted(context)){
			callback()
		} else {
			checkLoop()
		}
	}

	/**
	 * Add a tap listener to the audio context
	 * @param  {Array|Element|String|jQuery} element
	 * @param {Array} tapListeners
	 */
	function bindTapListener(element, tapListeners, context){
		if (Array.isArray(element) || (NodeList && element instanceof NodeList)){
			for (var i = 0; i < element.length; i++){
				bindTapListener(element[i], tapListeners, context)
			}
		} else if (typeof element === "string"){
			bindTapListener(document.querySelectorAll(element), tapListeners, context)
		} else if (element.jquery && typeof element.toArray === "function"){
			bindTapListener(element.toArray(), tapListeners, context)
		} else if (Element && element instanceof Element){
			//if it's an element, create a TapListener
			var tap = new TapListener(element, context)
			tapListeners.push(tap)
		} 
	}

	/**
	 * @param {AudioContext} context The AudioContext to start.
	 * @param {Array|String|Element|jQuery=} elements For iOS, the list of elements
	 *                                               to bind tap event listeners
	 *                                               which will start the AudioContext. If
	 *                                               no elements are given, it will bind
	 *                                               to the document.body.
	 * @param {Function=} callback The callback to invoke when the AudioContext is started.
	 * @return {Promise} The promise is invoked when the AudioContext
	 *                       is started.
	 */
	function StartAudioContext(context, elements, callback){

		//the promise is invoked when the AudioContext is started
		var promise = new Promise(function(success) {
			onStarted(context, success)
		})

		// The TapListeners bound to the elements
		var tapListeners = []

		// add all the tap listeners
		if (!elements){
			elements = document.body
		}
		bindTapListener(elements, tapListeners, context)

		//dispose all these tap listeners when the context is started
		promise.then(function(){
			for (var i = 0; i < tapListeners.length; i++){
				tapListeners[i].dispose()
			}
			tapListeners = null

			if (callback){
				callback()
			}
		})

		return promise
	}

	return StartAudioContext
}))

/***/ }),

/***/ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js":
/*!****************************************************************************!*\
  !*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
  \****************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


var isOldIE = function isOldIE() {
  var memo;
  return function memorize() {
    if (typeof memo === 'undefined') {
      // Test for IE <= 9 as proposed by Browserhacks
      // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
      // Tests for existence of standard globals is to allow style-loader
      // to operate correctly into non-standard environments
      // @see https://github.com/webpack-contrib/style-loader/issues/177
      memo = Boolean(window && document && document.all && !window.atob);
    }

    return memo;
  };
}();

var getTarget = function getTarget() {
  var memo = {};
  return function memorize(target) {
    if (typeof memo[target] === 'undefined') {
      var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself

      if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
        try {
          // This will throw an exception if access to iframe is blocked
          // due to cross-origin restrictions
          styleTarget = styleTarget.contentDocument.head;
        } catch (e) {
          // istanbul ignore next
          styleTarget = null;
        }
      }

      memo[target] = styleTarget;
    }

    return memo[target];
  };
}();

var stylesInDom = [];

function getIndexByIdentifier(identifier) {
  var result = -1;

  for (var i = 0; i < stylesInDom.length; i++) {
    if (stylesInDom[i].identifier === identifier) {
      result = i;
      break;
    }
  }

  return result;
}

function modulesToDom(list, options) {
  var idCountMap = {};
  var identifiers = [];

  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var id = options.base ? item[0] + options.base : item[0];
    var count = idCountMap[id] || 0;
    var identifier = "".concat(id, " ").concat(count);
    idCountMap[id] = count + 1;
    var index = getIndexByIdentifier(identifier);
    var obj = {
      css: item[1],
      media: item[2],
      sourceMap: item[3]
    };

    if (index !== -1) {
      stylesInDom[index].references++;
      stylesInDom[index].updater(obj);
    } else {
      stylesInDom.push({
        identifier: identifier,
        updater: addStyle(obj, options),
        references: 1
      });
    }

    identifiers.push(identifier);
  }

  return identifiers;
}

function insertStyleElement(options) {
  var style = document.createElement('style');
  var attributes = options.attributes || {};

  if (typeof attributes.nonce === 'undefined') {
    var nonce =  true ? __webpack_require__.nc : undefined;

    if (nonce) {
      attributes.nonce = nonce;
    }
  }

  Object.keys(attributes).forEach(function (key) {
    style.setAttribute(key, attributes[key]);
  });

  if (typeof options.insert === 'function') {
    options.insert(style);
  } else {
    var target = getTarget(options.insert || 'head');

    if (!target) {
      throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
    }

    target.appendChild(style);
  }

  return style;
}

function removeStyleElement(style) {
  // istanbul ignore if
  if (style.parentNode === null) {
    return false;
  }

  style.parentNode.removeChild(style);
}
/* istanbul ignore next  */


var replaceText = function replaceText() {
  var textStore = [];
  return function replace(index, replacement) {
    textStore[index] = replacement;
    return textStore.filter(Boolean).join('\n');
  };
}();

function applyToSingletonTag(style, index, remove, obj) {
  var css = remove ? '' : obj.media ? "@media ".concat(obj.media, " {").concat(obj.css, "}") : obj.css; // For old IE

  /* istanbul ignore if  */

  if (style.styleSheet) {
    style.styleSheet.cssText = replaceText(index, css);
  } else {
    var cssNode = document.createTextNode(css);
    var childNodes = style.childNodes;

    if (childNodes[index]) {
      style.removeChild(childNodes[index]);
    }

    if (childNodes.length) {
      style.insertBefore(cssNode, childNodes[index]);
    } else {
      style.appendChild(cssNode);
    }
  }
}

function applyToTag(style, options, obj) {
  var css = obj.css;
  var media = obj.media;
  var sourceMap = obj.sourceMap;

  if (media) {
    style.setAttribute('media', media);
  } else {
    style.removeAttribute('media');
  }

  if (sourceMap && typeof btoa !== 'undefined') {
    css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  } // For old IE

  /* istanbul ignore if  */


  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    while (style.firstChild) {
      style.removeChild(style.firstChild);
    }

    style.appendChild(document.createTextNode(css));
  }
}

var singleton = null;
var singletonCounter = 0;

function addStyle(obj, options) {
  var style;
  var update;
  var remove;

  if (options.singleton) {
    var styleIndex = singletonCounter++;
    style = singleton || (singleton = insertStyleElement(options));
    update = applyToSingletonTag.bind(null, style, styleIndex, false);
    remove = applyToSingletonTag.bind(null, style, styleIndex, true);
  } else {
    style = insertStyleElement(options);
    update = applyToTag.bind(null, style, options);

    remove = function remove() {
      removeStyleElement(style);
    };
  }

  update(obj);
  return function updateStyle(newObj) {
    if (newObj) {
      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {
        return;
      }

      update(obj = newObj);
    } else {
      remove();
    }
  };
}

module.exports = function (list, options) {
  options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  // tags it will allow on a page

  if (!options.singleton && typeof options.singleton !== 'boolean') {
    options.singleton = isOldIE();
  }

  list = list || [];
  var lastIdentifiers = modulesToDom(list, options);
  return function update(newList) {
    newList = newList || [];

    if (Object.prototype.toString.call(newList) !== '[object Array]') {
      return;
    }

    for (var i = 0; i < lastIdentifiers.length; i++) {
      var identifier = lastIdentifiers[i];
      var index = getIndexByIdentifier(identifier);
      stylesInDom[index].references--;
    }

    var newLastIdentifiers = modulesToDom(newList, options);

    for (var _i = 0; _i < lastIdentifiers.length; _i++) {
      var _identifier = lastIdentifiers[_i];

      var _index = getIndexByIdentifier(_identifier);

      if (stylesInDom[_index].references === 0) {
        stylesInDom[_index].updater();

        stylesInDom.splice(_index, 1);
      }
    }

    lastIdentifiers = newLastIdentifiers;
  };
};

/***/ }),

/***/ "./node_modules/timers-browserify/main.js":
/*!************************************************!*\
  !*** ./node_modules/timers-browserify/main.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(global) {var scope = (typeof global !== "undefined" && global) ||
            (typeof self !== "undefined" && self) ||
            window;
var apply = Function.prototype.apply;

// DOM APIs, for completeness

exports.setTimeout = function() {
  return new Timeout(apply.call(setTimeout, scope, arguments), clearTimeout);
};
exports.setInterval = function() {
  return new Timeout(apply.call(setInterval, scope, arguments), clearInterval);
};
exports.clearTimeout =
exports.clearInterval = function(timeout) {
  if (timeout) {
    timeout.close();
  }
};

function Timeout(id, clearFn) {
  this._id = id;
  this._clearFn = clearFn;
}
Timeout.prototype.unref = Timeout.prototype.ref = function() {};
Timeout.prototype.close = function() {
  this._clearFn.call(scope, this._id);
};

// Does not start the time, just sets up the members needed.
exports.enroll = function(item, msecs) {
  clearTimeout(item._idleTimeoutId);
  item._idleTimeout = msecs;
};

exports.unenroll = function(item) {
  clearTimeout(item._idleTimeoutId);
  item._idleTimeout = -1;
};

exports._unrefActive = exports.active = function(item) {
  clearTimeout(item._idleTimeoutId);

  var msecs = item._idleTimeout;
  if (msecs >= 0) {
    item._idleTimeoutId = setTimeout(function onTimeout() {
      if (item._onTimeout)
        item._onTimeout();
    }, msecs);
  }
};

// setimmediate attaches itself to the global object
__webpack_require__(/*! setimmediate */ "./node_modules/setimmediate/setImmediate.js");
// On some exotic environments, it's not clear which object `setimmediate` was
// able to install onto.  Search each possibility in the same order as the
// `setimmediate` library.
exports.setImmediate = (typeof self !== "undefined" && self.setImmediate) ||
                       (typeof global !== "undefined" && global.setImmediate) ||
                       (this && this.setImmediate);
exports.clearImmediate = (typeof self !== "undefined" && self.clearImmediate) ||
                         (typeof global !== "undefined" && global.clearImmediate) ||
                         (this && this.clearImmediate);

/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))

/***/ }),

/***/ "./node_modules/tiny-inflate/index.js":
/*!********************************************!*\
  !*** ./node_modules/tiny-inflate/index.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

var TINF_OK = 0;
var TINF_DATA_ERROR = -3;

function Tree() {
  this.table = new Uint16Array(16);   /* table of code length counts */
  this.trans = new Uint16Array(288);  /* code -> symbol translation table */
}

function Data(source, dest) {
  this.source = source;
  this.sourceIndex = 0;
  this.tag = 0;
  this.bitcount = 0;
  
  this.dest = dest;
  this.destLen = 0;
  
  this.ltree = new Tree();  /* dynamic length/symbol tree */
  this.dtree = new Tree();  /* dynamic distance tree */
}

/* --------------------------------------------------- *
 * -- uninitialized global data (static structures) -- *
 * --------------------------------------------------- */

var sltree = new Tree();
var sdtree = new Tree();

/* extra bits and base tables for length codes */
var length_bits = new Uint8Array(30);
var length_base = new Uint16Array(30);

/* extra bits and base tables for distance codes */
var dist_bits = new Uint8Array(30);
var dist_base = new Uint16Array(30);

/* special ordering of code length codes */
var clcidx = new Uint8Array([
  16, 17, 18, 0, 8, 7, 9, 6,
  10, 5, 11, 4, 12, 3, 13, 2,
  14, 1, 15
]);

/* used by tinf_decode_trees, avoids allocations every call */
var code_tree = new Tree();
var lengths = new Uint8Array(288 + 32);

/* ----------------------- *
 * -- utility functions -- *
 * ----------------------- */

/* build extra bits and base tables */
function tinf_build_bits_base(bits, base, delta, first) {
  var i, sum;

  /* build bits table */
  for (i = 0; i < delta; ++i) bits[i] = 0;
  for (i = 0; i < 30 - delta; ++i) bits[i + delta] = i / delta | 0;

  /* build base table */
  for (sum = first, i = 0; i < 30; ++i) {
    base[i] = sum;
    sum += 1 << bits[i];
  }
}

/* build the fixed huffman trees */
function tinf_build_fixed_trees(lt, dt) {
  var i;

  /* build fixed length tree */
  for (i = 0; i < 7; ++i) lt.table[i] = 0;

  lt.table[7] = 24;
  lt.table[8] = 152;
  lt.table[9] = 112;

  for (i = 0; i < 24; ++i) lt.trans[i] = 256 + i;
  for (i = 0; i < 144; ++i) lt.trans[24 + i] = i;
  for (i = 0; i < 8; ++i) lt.trans[24 + 144 + i] = 280 + i;
  for (i = 0; i < 112; ++i) lt.trans[24 + 144 + 8 + i] = 144 + i;

  /* build fixed distance tree */
  for (i = 0; i < 5; ++i) dt.table[i] = 0;

  dt.table[5] = 32;

  for (i = 0; i < 32; ++i) dt.trans[i] = i;
}

/* given an array of code lengths, build a tree */
var offs = new Uint16Array(16);

function tinf_build_tree(t, lengths, off, num) {
  var i, sum;

  /* clear code length count table */
  for (i = 0; i < 16; ++i) t.table[i] = 0;

  /* scan symbol lengths, and sum code length counts */
  for (i = 0; i < num; ++i) t.table[lengths[off + i]]++;

  t.table[0] = 0;

  /* compute offset table for distribution sort */
  for (sum = 0, i = 0; i < 16; ++i) {
    offs[i] = sum;
    sum += t.table[i];
  }

  /* create code->symbol translation table (symbols sorted by code) */
  for (i = 0; i < num; ++i) {
    if (lengths[off + i]) t.trans[offs[lengths[off + i]]++] = i;
  }
}

/* ---------------------- *
 * -- decode functions -- *
 * ---------------------- */

/* get one bit from source stream */
function tinf_getbit(d) {
  /* check if tag is empty */
  if (!d.bitcount--) {
    /* load next tag */
    d.tag = d.source[d.sourceIndex++];
    d.bitcount = 7;
  }

  /* shift bit out of tag */
  var bit = d.tag & 1;
  d.tag >>>= 1;

  return bit;
}

/* read a num bit value from a stream and add base */
function tinf_read_bits(d, num, base) {
  if (!num)
    return base;

  while (d.bitcount < 24) {
    d.tag |= d.source[d.sourceIndex++] << d.bitcount;
    d.bitcount += 8;
  }

  var val = d.tag & (0xffff >>> (16 - num));
  d.tag >>>= num;
  d.bitcount -= num;
  return val + base;
}

/* given a data stream and a tree, decode a symbol */
function tinf_decode_symbol(d, t) {
  while (d.bitcount < 24) {
    d.tag |= d.source[d.sourceIndex++] << d.bitcount;
    d.bitcount += 8;
  }
  
  var sum = 0, cur = 0, len = 0;
  var tag = d.tag;

  /* get more bits while code value is above sum */
  do {
    cur = 2 * cur + (tag & 1);
    tag >>>= 1;
    ++len;

    sum += t.table[len];
    cur -= t.table[len];
  } while (cur >= 0);
  
  d.tag = tag;
  d.bitcount -= len;

  return t.trans[sum + cur];
}

/* given a data stream, decode dynamic trees from it */
function tinf_decode_trees(d, lt, dt) {
  var hlit, hdist, hclen;
  var i, num, length;

  /* get 5 bits HLIT (257-286) */
  hlit = tinf_read_bits(d, 5, 257);

  /* get 5 bits HDIST (1-32) */
  hdist = tinf_read_bits(d, 5, 1);

  /* get 4 bits HCLEN (4-19) */
  hclen = tinf_read_bits(d, 4, 4);

  for (i = 0; i < 19; ++i) lengths[i] = 0;

  /* read code lengths for code length alphabet */
  for (i = 0; i < hclen; ++i) {
    /* get 3 bits code length (0-7) */
    var clen = tinf_read_bits(d, 3, 0);
    lengths[clcidx[i]] = clen;
  }

  /* build code length tree */
  tinf_build_tree(code_tree, lengths, 0, 19);

  /* decode code lengths for the dynamic trees */
  for (num = 0; num < hlit + hdist;) {
    var sym = tinf_decode_symbol(d, code_tree);

    switch (sym) {
      case 16:
        /* copy previous code length 3-6 times (read 2 bits) */
        var prev = lengths[num - 1];
        for (length = tinf_read_bits(d, 2, 3); length; --length) {
          lengths[num++] = prev;
        }
        break;
      case 17:
        /* repeat code length 0 for 3-10 times (read 3 bits) */
        for (length = tinf_read_bits(d, 3, 3); length; --length) {
          lengths[num++] = 0;
        }
        break;
      case 18:
        /* repeat code length 0 for 11-138 times (read 7 bits) */
        for (length = tinf_read_bits(d, 7, 11); length; --length) {
          lengths[num++] = 0;
        }
        break;
      default:
        /* values 0-15 represent the actual code lengths */
        lengths[num++] = sym;
        break;
    }
  }

  /* build dynamic trees */
  tinf_build_tree(lt, lengths, 0, hlit);
  tinf_build_tree(dt, lengths, hlit, hdist);
}

/* ----------------------------- *
 * -- block inflate functions -- *
 * ----------------------------- */

/* given a stream and two trees, inflate a block of data */
function tinf_inflate_block_data(d, lt, dt) {
  while (1) {
    var sym = tinf_decode_symbol(d, lt);

    /* check for end of block */
    if (sym === 256) {
      return TINF_OK;
    }

    if (sym < 256) {
      d.dest[d.destLen++] = sym;
    } else {
      var length, dist, offs;
      var i;

      sym -= 257;

      /* possibly get more bits from length code */
      length = tinf_read_bits(d, length_bits[sym], length_base[sym]);

      dist = tinf_decode_symbol(d, dt);

      /* possibly get more bits from distance code */
      offs = d.destLen - tinf_read_bits(d, dist_bits[dist], dist_base[dist]);

      /* copy match */
      for (i = offs; i < offs + length; ++i) {
        d.dest[d.destLen++] = d.dest[i];
      }
    }
  }
}

/* inflate an uncompressed block of data */
function tinf_inflate_uncompressed_block(d) {
  var length, invlength;
  var i;
  
  /* unread from bitbuffer */
  while (d.bitcount > 8) {
    d.sourceIndex--;
    d.bitcount -= 8;
  }

  /* get length */
  length = d.source[d.sourceIndex + 1];
  length = 256 * length + d.source[d.sourceIndex];

  /* get one's complement of length */
  invlength = d.source[d.sourceIndex + 3];
  invlength = 256 * invlength + d.source[d.sourceIndex + 2];

  /* check length */
  if (length !== (~invlength & 0x0000ffff))
    return TINF_DATA_ERROR;

  d.sourceIndex += 4;

  /* copy block */
  for (i = length; i; --i)
    d.dest[d.destLen++] = d.source[d.sourceIndex++];

  /* make sure we start next block on a byte boundary */
  d.bitcount = 0;

  return TINF_OK;
}

/* inflate stream from source to dest */
function tinf_uncompress(source, dest) {
  var d = new Data(source, dest);
  var bfinal, btype, res;

  do {
    /* read final block flag */
    bfinal = tinf_getbit(d);

    /* read block type (2 bits) */
    btype = tinf_read_bits(d, 2, 0);

    /* decompress block */
    switch (btype) {
      case 0:
        /* decompress uncompressed block */
        res = tinf_inflate_uncompressed_block(d);
        break;
      case 1:
        /* decompress block with fixed huffman trees */
        res = tinf_inflate_block_data(d, sltree, sdtree);
        break;
      case 2:
        /* decompress block with dynamic huffman trees */
        tinf_decode_trees(d, d.ltree, d.dtree);
        res = tinf_inflate_block_data(d, d.ltree, d.dtree);
        break;
      default:
        res = TINF_DATA_ERROR;
    }

    if (res !== TINF_OK)
      throw new Error('Data error');

  } while (!bfinal);

  if (d.destLen < d.dest.length) {
    if (typeof d.dest.slice === 'function')
      return d.dest.slice(0, d.destLen);
    else
      return d.dest.subarray(0, d.destLen);
  }
  
  return d.dest;
}

/* -------------------- *
 * -- initialization -- *
 * -------------------- */

/* build fixed huffman trees */
tinf_build_fixed_trees(sltree, sdtree);

/* build extra bits and base tables */
tinf_build_bits_base(length_bits, length_base, 4, 3);
tinf_build_bits_base(dist_bits, dist_base, 2, 1);

/* fix a special case */
length_bits[28] = 0;
length_base[28] = 258;

module.exports = tinf_uncompress;


/***/ }),

/***/ "./node_modules/transformation-matrix/build-umd/transformation-matrix.min.js":
/*!***********************************************************************************!*\
  !*** ./node_modules/transformation-matrix/build-umd/transformation-matrix.min.js ***!
  \***********************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

!function(r,n){ true?module.exports=n():undefined}(window,function(){return function(r){var n={};function t(e){if(n[e])return n[e].exports;var o=n[e]={i:e,l:!1,exports:{}};return r[e].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=r,t.c=n,t.d=function(r,n,e){t.o(r,n)||Object.defineProperty(r,n,{enumerable:!0,get:e})},t.r=function(r){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(r,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(r,"__esModule",{value:!0})},t.t=function(r,n){if(1&n&&(r=t(r)),8&n)return r;if(4&n&&"object"==typeof r&&r&&r.__esModule)return r;var e=Object.create(null);if(t.r(e),Object.defineProperty(e,"default",{enumerable:!0,value:r}),2&n&&"string"!=typeof r)for(var o in r)t.d(e,o,function(n){return r[n]}.bind(null,o));return e},t.n=function(r){var n=r&&r.__esModule?function(){return r.default}:function(){return r};return t.d(n,"a",n),n},t.o=function(r,n){return Object.prototype.hasOwnProperty.call(r,n)},t.p="",t(t.s=0)}([function(r,n,t){"use strict";function e(r,n){return Array.isArray(n)?[r.a*n[0]+r.c*n[1]+r.e,r.b*n[0]+r.d*n[1]+r.f]:{x:r.a*n.x+r.c*n.y+r.e,y:r.b*n.x+r.d*n.y+r.f}}function o(r,n){return n.map(function(n){return e(r,n)})}function u(r){return{a:parseFloat(r.a),b:parseFloat(r.b),c:parseFloat(r.c),d:parseFloat(r.d),e:parseFloat(r.e),f:parseFloat(r.f)}}t.r(n);var a=/^matrix\(\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*,\s*([0-9_+-.e]+)\s*\)$/i;function i(r){var n=r.match(a);if(null===n||n.length<7)throw new Error("'"+r+"' is not a matrix");return{a:parseFloat(n[1]),b:parseFloat(n[2]),c:parseFloat(n[3]),d:parseFloat(n[4]),e:parseFloat(n[5]),f:parseFloat(n[6])}}function f(){return{a:1,c:0,e:0,b:0,d:1,f:0}}function c(r){var n=r.a,t=r.b,e=r.c,o=r.d,u=r.e,a=r.f,i=n*o-t*e;return{a:o/i,b:t/-i,c:e/-i,d:n/i,e:(o*u-e*a)/-i,f:(t*u-n*a)/i}}var d="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(r){return typeof r}:function(r){return r&&"function"==typeof Symbol&&r.constructor===Symbol&&r!==Symbol.prototype?"symbol":typeof r},s=function(r){return"number"==typeof r&&!isNaN(r)&&isFinite(r)},l=function(r){return null!=r&&"object"===(void 0===r?"undefined":d(r))};function p(r){return l(r)&&r.hasOwnProperty("a")&&s(r.a)&&r.hasOwnProperty("b")&&s(r.b)&&r.hasOwnProperty("c")&&s(r.c)&&r.hasOwnProperty("d")&&s(r.d)&&r.hasOwnProperty("e")&&s(r.e)&&r.hasOwnProperty("f")&&s(r.f)}function y(r){return void 0===r}function b(r){return{a:1,c:0,e:r,b:0,d:1,f:arguments.length>1&&void 0!==arguments[1]?arguments[1]:0}}function v(){for(var r=arguments.length,n=Array(r),t=0;t<r;t++)n[t]=arguments[t];var e=function(r,n){return{a:r.a*n.a+r.c*n.b,c:r.a*n.c+r.c*n.d,e:r.a*n.e+r.c*n.f+r.e,b:r.b*n.a+r.d*n.b,d:r.b*n.c+r.d*n.d,f:r.b*n.e+r.d*n.f+r.f}};switch((n=Array.isArray(n[0])?n[0]:n).length){case 0:throw new Error("no matrices provided");case 1:return n[0];case 2:return e(n[0],n[1]);default:var o=function(r){return Array.isArray(r)?r:Array.from(r)}(n),u=o[0],a=o[1],i=o.slice(2),f=e(u,a);return v.apply(void 0,[f].concat(function(r){if(Array.isArray(r)){for(var n=0,t=Array(r.length);n<r.length;n++)t[n]=r[n];return t}return Array.from(r)}(i)))}}function m(){return v.apply(void 0,arguments)}var h=Math.cos,x=Math.sin,g=Math.PI;function w(r,n,t){var e=h(r),o=x(r),u={a:e,c:-o,e:0,b:o,d:e,f:0};return y(n)||y(t)?u:v([b(n,t),u,b(-n,-t)])}function P(r){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0,t=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;return w(r*g/180,n,t)}function S(r){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:void 0;return y(n)&&(n=r),{a:r,c:0,e:0,b:0,d:n,f:0}}function O(r,n){return{a:1,c:r,e:0,b:n,d:1,f:0}}var A=Math.tan;function F(r,n){return{a:1,c:A(r),e:0,b:A(n),d:1,f:0}}function M(r,n){return F(r*Math.PI/180,n*Math.PI/180)}function j(r){return T(r)}function _(r){return T(r)}function T(r){return"matrix("+r.a+","+r.b+","+r.c+","+r.d+","+r.e+","+r.f+")"}t.d(n,"applyToPoint",function(){return e}),t.d(n,"applyToPoints",function(){return o}),t.d(n,"fromObject",function(){return u}),t.d(n,"fromString",function(){return i}),t.d(n,"identity",function(){return f}),t.d(n,"inverse",function(){return c}),t.d(n,"isAffineMatrix",function(){return p}),t.d(n,"rotate",function(){return w}),t.d(n,"rotateDEG",function(){return P}),t.d(n,"scale",function(){return S}),t.d(n,"shear",function(){return O}),t.d(n,"skew",function(){return F}),t.d(n,"skewDEG",function(){return M}),t.d(n,"toCSS",function(){return j}),t.d(n,"toSVG",function(){return _}),t.d(n,"toString",function(){return T}),t.d(n,"transform",function(){return v}),t.d(n,"compose",function(){return m}),t.d(n,"translate",function(){return b})}])});
//# sourceMappingURL=transformation-matrix.min.js.map

/***/ }),

/***/ "./node_modules/twgl.js/dist/4.x/twgl-full.js":
/*!****************************************************!*\
  !*** ./node_modules/twgl.js/dist/4.x/twgl-full.js ***!
  \****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/*!
 * @license twgl.js 4.4.0 Copyright (c) 2015, Gregg Tavares All Rights Reserved.
 * Available via the MIT license.
 * see: http://github.com/greggman/twgl.js for details
 */
(function webpackUniversalModuleDefinition(root, factory) {
	if(true)
		module.exports = factory();
	else {}
})(typeof self !== 'undefined' ? self : this, function() {
return /******/ (function(modules) { // webpackBootstrap
/******/ 	// The module cache
/******/ 	var installedModules = {};
/******/
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/
/******/ 		// Check if module is in cache
/******/ 		if(installedModules[moduleId]) {
/******/ 			return installedModules[moduleId].exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = installedModules[moduleId] = {
/******/ 			i: moduleId,
/******/ 			l: false,
/******/ 			exports: {}
/******/ 		};
/******/
/******/ 		// Execute the module function
/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ 		// Flag the module as loaded
/******/ 		module.l = true;
/******/
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/
/******/
/******/ 	// expose the modules object (__webpack_modules__)
/******/ 	__webpack_require__.m = modules;
/******/
/******/ 	// expose the module cache
/******/ 	__webpack_require__.c = installedModules;
/******/
/******/ 	// define getter function for harmony exports
/******/ 	__webpack_require__.d = function(exports, name, getter) {
/******/ 		if(!__webpack_require__.o(exports, name)) {
/******/ 			Object.defineProperty(exports, name, {
/******/ 				configurable: false,
/******/ 				enumerable: true,
/******/ 				get: getter
/******/ 			});
/******/ 		}
/******/ 	};
/******/
/******/ 	// getDefaultExport function for compatibility with non-harmony modules
/******/ 	__webpack_require__.n = function(module) {
/******/ 		var getter = module && module.__esModule ?
/******/ 			function getDefault() { return module['default']; } :
/******/ 			function getModuleExports() { return module; };
/******/ 		__webpack_require__.d(getter, 'a', getter);
/******/ 		return getter;
/******/ 	};
/******/
/******/ 	// Object.prototype.hasOwnProperty.call
/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ 	// __webpack_public_path__
/******/ 	__webpack_require__.p = "";
/******/
/******/ 	// Load entry module and return exports
/******/ 	return __webpack_require__(__webpack_require__.s = 9);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


exports.__esModule = true;
exports.copyExistingProperties = copyExistingProperties;
exports.copyNamedProperties = copyNamedProperties;
exports.isBuffer = isBuffer;
exports.isRenderbuffer = isRenderbuffer;
exports.isShader = isShader;
exports.isTexture = isTexture;
exports.isSampler = isSampler;
exports.warn = exports.error = void 0;

var _globalObject = _interopRequireDefault(__webpack_require__(2));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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

/**
 * Copy named properties
 *
 * @param {string[]} names names of properties to copy
 * @param {object} src object to copy properties from
 * @param {object} dst object to copy properties to
 */
function copyNamedProperties(names, src, dst) {
  names.forEach(function (name) {
    var value = src[name];

    if (value !== undefined) {
      dst[name] = value;
    }
  });
}
/**
 * Copies properties from source to dest only if a matching key is in dest
 *
 * @param {Object.<string, ?>} src the source
 * @param {Object.<string, ?>} dst the dest
 */


function copyExistingProperties(src, dst) {
  Object.keys(dst).forEach(function (key) {
    if (dst.hasOwnProperty(key) && src.hasOwnProperty(key)) {
      dst[key] = src[key];
    }
  });
}

var error = _globalObject.default.console && _globalObject.default.console.error && typeof _globalObject.default.console.error === "function" ? _globalObject.default.console.error.bind(_globalObject.default.console) : function () {};
exports.error = error;
var warn = _globalObject.default.console && _globalObject.default.console.warn && typeof _globalObject.default.console.warn === "function" ? _globalObject.default.console.warn.bind(_globalObject.default.console) : function () {};
exports.warn = warn;
var repBuffer;

function isBuffer(gl, t) {
  if (!repBuffer) {
    repBuffer = gl.createBuffer();
  }

  return t instanceof repBuffer.constructor;
}

var repRenderbuffer;

function isRenderbuffer(gl, t) {
  if (!repRenderbuffer) {
    repRenderbuffer = gl.createRenderbuffer();
  }

  return t instanceof repRenderbuffer.constructor;
}

var repShader;

function isShader(gl, t) {
  if (!repShader) {
    repShader = gl.createShader(gl.VERTEX_SHADER);
  }

  return t instanceof repShader.constructor;
}

var repTexture;

function isTexture(gl, t) {
  if (!repTexture) {
    repTexture = gl.createTexture();
  }

  return t instanceof repTexture.constructor;
}

var repSampler;

function isSampler(gl, t) {
  if (!repSampler) {
    if (gl.createSampler) {
      repSampler = gl.createSampler();
    } else {
      return false; // it can't be a sampler if this is not WebGL2
    }
  }

  return t instanceof repSampler.constructor;
}

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

"use strict";


exports.__esModule = true;
exports.getGLTypeForTypedArray = getGLTypeForTypedArray;
exports.getGLTypeForTypedArrayType = getGLTypeForTypedArrayType;
exports.getTypedArrayTypeForGLType = getTypedArrayTypeForGLType;
exports.isArrayBuffer = void 0;

var _globalObject = _interopRequireDefault(__webpack_require__(2));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

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

/**
 * Low level shader typed array related functions
 *
 * You should generally not need to use these functions. They are provided
 * for those cases where you're doing something out of the ordinary
 * and you need lower level access.
 *
 * For backward compatibily they are available at both `twgl.typedArray` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/typedArray
 */
// make sure we don't see a global gl
var gl = undefined; // eslint-disable-line

/* DataType */

var BYTE = 0x1400;
var UNSIGNED_BYTE = 0x1401;
var SHORT = 0x1402;
var UNSIGNED_SHORT = 0x1403;
var INT = 0x1404;
var UNSIGNED_INT = 0x1405;
var FLOAT = 0x1406;
var UNSIGNED_SHORT_4_4_4_4 = 0x8033;
var UNSIGNED_SHORT_5_5_5_1 = 0x8034;
var UNSIGNED_SHORT_5_6_5 = 0x8363;
var HALF_FLOAT = 0x140B;
var UNSIGNED_INT_2_10_10_10_REV = 0x8368;
var UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B;
var UNSIGNED_INT_5_9_9_9_REV = 0x8C3E;
var FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD;
var UNSIGNED_INT_24_8 = 0x84FA;
var glTypeToTypedArray = {};
{
  var tt = glTypeToTypedArray;
  tt[BYTE] = Int8Array;
  tt[UNSIGNED_BYTE] = Uint8Array;
  tt[SHORT] = Int16Array;
  tt[UNSIGNED_SHORT] = Uint16Array;
  tt[INT] = Int32Array;
  tt[UNSIGNED_INT] = Uint32Array;
  tt[FLOAT] = Float32Array;
  tt[UNSIGNED_SHORT_4_4_4_4] = Uint16Array;
  tt[UNSIGNED_SHORT_5_5_5_1] = Uint16Array;
  tt[UNSIGNED_SHORT_5_6_5] = Uint16Array;
  tt[HALF_FLOAT] = Uint16Array;
  tt[UNSIGNED_INT_2_10_10_10_REV] = Uint32Array;
  tt[UNSIGNED_INT_10F_11F_11F_REV] = Uint32Array;
  tt[UNSIGNED_INT_5_9_9_9_REV] = Uint32Array;
  tt[FLOAT_32_UNSIGNED_INT_24_8_REV] = Uint32Array;
  tt[UNSIGNED_INT_24_8] = Uint32Array;
}
/**
 * Get the GL type for a typedArray
 * @param {ArrayBuffer|ArrayBufferView} typedArray a typedArray
 * @return {number} the GL type for array. For example pass in an `Int8Array` and `gl.BYTE` will
 *   be returned. Pass in a `Uint32Array` and `gl.UNSIGNED_INT` will be returned
 * @memberOf module:twgl/typedArray
 */

function getGLTypeForTypedArray(typedArray) {
  if (typedArray instanceof Int8Array) {
    return BYTE;
  } // eslint-disable-line


  if (typedArray instanceof Uint8Array) {
    return UNSIGNED_BYTE;
  } // eslint-disable-line


  if (typedArray instanceof Uint8ClampedArray) {
    return UNSIGNED_BYTE;
  } // eslint-disable-line


  if (typedArray instanceof Int16Array) {
    return SHORT;
  } // eslint-disable-line


  if (typedArray instanceof Uint16Array) {
    return UNSIGNED_SHORT;
  } // eslint-disable-line


  if (typedArray instanceof Int32Array) {
    return INT;
  } // eslint-disable-line


  if (typedArray instanceof Uint32Array) {
    return UNSIGNED_INT;
  } // eslint-disable-line


  if (typedArray instanceof Float32Array) {
    return FLOAT;
  } // eslint-disable-line


  throw "unsupported typed array type";
}
/**
 * Get the GL type for a typedArray type
 * @param {ArrayBufferViewType} typedArrayType a typedArray constructor
 * @return {number} the GL type for type. For example pass in `Int8Array` and `gl.BYTE` will
 *   be returned. Pass in `Uint32Array` and `gl.UNSIGNED_INT` will be returned
 * @memberOf module:twgl/typedArray
 */


function getGLTypeForTypedArrayType(typedArrayType) {
  if (typedArrayType === Int8Array) {
    return BYTE;
  } // eslint-disable-line


  if (typedArrayType === Uint8Array) {
    return UNSIGNED_BYTE;
  } // eslint-disable-line


  if (typedArrayType === Uint8ClampedArray) {
    return UNSIGNED_BYTE;
  } // eslint-disable-line


  if (typedArrayType === Int16Array) {
    return SHORT;
  } // eslint-disable-line


  if (typedArrayType === Uint16Array) {
    return UNSIGNED_SHORT;
  } // eslint-disable-line


  if (typedArrayType === Int32Array) {
    return INT;
  } // eslint-disable-line


  if (typedArrayType === Uint32Array) {
    return UNSIGNED_INT;
  } // eslint-disable-line


  if (typedArrayType === Float32Array) {
    return FLOAT;
  } // eslint-disable-line


  throw "unsupported typed array type";
}
/**
 * Get the typed array constructor for a given GL type
 * @param {number} type the GL type. (eg: `gl.UNSIGNED_INT`)
 * @return {function} the constructor for a the corresponding typed array. (eg. `Uint32Array`).
 * @memberOf module:twgl/typedArray
 */


function getTypedArrayTypeForGLType(type) {
  var CTOR = glTypeToTypedArray[type];

  if (!CTOR) {
    throw "unknown gl type";
  }

  return CTOR;
}

var isArrayBuffer = _globalObject.default.SharedArrayBuffer ? function isArrayBufferOrSharedArrayBuffer(a) {
  return a && a.buffer && (a.buffer instanceof ArrayBuffer || a.buffer instanceof _globalObject.default.SharedArrayBuffer);
} : function isArrayBuffer(a) {
  return a && a.buffer && a.buffer instanceof ArrayBuffer;
};
exports.isArrayBuffer = isArrayBuffer;

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

"use strict";


exports.__esModule = true;
exports.default = void 0;
var global = typeof global !== 'undefined' // eslint-disable-line
? global // eslint-disable-line
: typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : {};
exports.default = global;

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

"use strict";


exports.__esModule = true;
exports.add = add;
exports.copy = copy;
exports.create = create;
exports.cross = cross;
exports.distance = distance;
exports.distanceSq = distanceSq;
exports.divide = divide;
exports.divScalar = divScalar;
exports.dot = dot;
exports.lerp = lerp;
exports.length = length;
exports.lengthSq = lengthSq;
exports.mulScalar = mulScalar;
exports.multiply = multiply;
exports.negate = negate;
exports.normalize = normalize;
exports.setDefaultType = setDefaultType;
exports.subtract = subtract;

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

/**
 *
 * Vec3 math math functions.
 *
 * Almost all functions take an optional `dst` argument. If it is not passed in the
 * functions will create a new Vec3. In other words you can do this
 *
 *     var v = v3.cross(v1, v2);  // Creates a new Vec3 with the cross product of v1 x v2.
 *
 * or
 *
 *     var v3 = v3.create();
 *     v3.cross(v1, v2, v);  // Puts the cross product of v1 x v2 in v
 *
 * The first style is often easier but depending on where it's used it generates garbage where
 * as there is almost never allocation with the second style.
 *
 * It is always save to pass any vector as the destination. So for example
 *
 *     v3.cross(v1, v2, v1);  // Puts the cross product of v1 x v2 in v1
 *
 * @module twgl/v3
 */
var VecType = Float32Array;
/**
 * A JavaScript array with 3 values or a Float32Array with 3 values.
 * When created by the library will create the default type which is `Float32Array`
 * but can be set by calling {@link module:twgl/v3.setDefaultType}.
 * @typedef {(number[]|Float32Array)} Vec3
 * @memberOf module:twgl/v3
 */

/**
 * Sets the type this library creates for a Vec3
 * @param {constructor} ctor the constructor for the type. Either `Float32Array` or `Array`
 * @return {constructor} previous constructor for Vec3
 */

function setDefaultType(ctor) {
  var oldType = VecType;
  VecType = ctor;
  return oldType;
}
/**
 * Creates a vec3; may be called with x, y, z to set initial values.
 * @return {Vec3} the created vector
 * @memberOf module:twgl/v3
 */


function create(x, y, z) {
  var dst = new VecType(3);

  if (x) {
    dst[0] = x;
  }

  if (y) {
    dst[1] = y;
  }

  if (z) {
    dst[2] = z;
  }

  return dst;
}
/**
 * Adds two vectors; assumes a and b have the same dimension.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @memberOf module:twgl/v3
 */


function add(a, b, dst) {
  dst = dst || new VecType(3);
  dst[0] = a[0] + b[0];
  dst[1] = a[1] + b[1];
  dst[2] = a[2] + b[2];
  return dst;
}
/**
 * Subtracts two vectors.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @memberOf module:twgl/v3
 */


function subtract(a, b, dst) {
  dst = dst || new VecType(3);
  dst[0] = a[0] - b[0];
  dst[1] = a[1] - b[1];
  dst[2] = a[2] - b[2];
  return dst;
}
/**
 * Performs linear interpolation on two vectors.
 * Given vectors a and b and interpolation coefficient t, returns
 * (1 - t) * a + t * b.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {number} t Interpolation coefficient.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @memberOf module:twgl/v3
 */


function lerp(a, b, t, dst) {
  dst = dst || new VecType(3);
  dst[0] = (1 - t) * a[0] + t * b[0];
  dst[1] = (1 - t) * a[1] + t * b[1];
  dst[2] = (1 - t) * a[2] + t * b[2];
  return dst;
}
/**
 * Mutiplies a vector by a scalar.
 * @param {module:twgl/v3.Vec3} v The vector.
 * @param {number} k The scalar.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} dst.
 * @memberOf module:twgl/v3
 */


function mulScalar(v, k, dst) {
  dst = dst || new VecType(3);
  dst[0] = v[0] * k;
  dst[1] = v[1] * k;
  dst[2] = v[2] * k;
  return dst;
}
/**
 * Divides a vector by a scalar.
 * @param {module:twgl/v3.Vec3} v The vector.
 * @param {number} k The scalar.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} dst.
 * @memberOf module:twgl/v3
 */


function divScalar(v, k, dst) {
  dst = dst || new VecType(3);
  dst[0] = v[0] / k;
  dst[1] = v[1] / k;
  dst[2] = v[2] / k;
  return dst;
}
/**
 * Computes the cross product of two vectors; assumes both vectors have
 * three entries.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} The vector a cross b.
 * @memberOf module:twgl/v3
 */


function cross(a, b, dst) {
  dst = dst || new VecType(3);
  var t1 = a[2] * b[0] - a[0] * b[2];
  var t2 = a[0] * b[1] - a[1] * b[0];
  dst[0] = a[1] * b[2] - a[2] * b[1];
  dst[1] = t1;
  dst[2] = t2;
  return dst;
}
/**
 * Computes the dot product of two vectors; assumes both vectors have
 * three entries.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @return {number} dot product
 * @memberOf module:twgl/v3
 */


function dot(a, b) {
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
/**
 * Computes the length of vector
 * @param {module:twgl/v3.Vec3} v vector.
 * @return {number} length of vector.
 * @memberOf module:twgl/v3
 */


function length(v) {
  return Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
}
/**
 * Computes the square of the length of vector
 * @param {module:twgl/v3.Vec3} v vector.
 * @return {number} square of the length of vector.
 * @memberOf module:twgl/v3
 */


function lengthSq(v) {
  return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
}
/**
 * Computes the distance between 2 points
 * @param {module:twgl/v3.Vec3} a vector.
 * @param {module:twgl/v3.Vec3} b vector.
 * @return {number} distance between a and b
 * @memberOf module:twgl/v3
 */


function distance(a, b) {
  var dx = a[0] - b[0];
  var dy = a[1] - b[1];
  var dz = a[2] - b[2];
  return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
 * Computes the square of the distance between 2 points
 * @param {module:twgl/v3.Vec3} a vector.
 * @param {module:twgl/v3.Vec3} b vector.
 * @return {number} square of the distance between a and b
 * @memberOf module:twgl/v3
 */


function distanceSq(a, b) {
  var dx = a[0] - b[0];
  var dy = a[1] - b[1];
  var dz = a[2] - b[2];
  return dx * dx + dy * dy + dz * dz;
}
/**
 * Divides a vector by its Euclidean length and returns the quotient.
 * @param {module:twgl/v3.Vec3} a The vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} The normalized vector.
 * @memberOf module:twgl/v3
 */


function normalize(a, dst) {
  dst = dst || new VecType(3);
  var lenSq = a[0] * a[0] + a[1] * a[1] + a[2] * a[2];
  var len = Math.sqrt(lenSq);

  if (len > 0.00001) {
    dst[0] = a[0] / len;
    dst[1] = a[1] / len;
    dst[2] = a[2] / len;
  } else {
    dst[0] = 0;
    dst[1] = 0;
    dst[2] = 0;
  }

  return dst;
}
/**
 * Negates a vector.
 * @param {module:twgl/v3.Vec3} v The vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} -v.
 * @memberOf module:twgl/v3
 */


function negate(v, dst) {
  dst = dst || new VecType(3);
  dst[0] = -v[0];
  dst[1] = -v[1];
  dst[2] = -v[2];
  return dst;
}
/**
 * Copies a vector.
 * @param {module:twgl/v3.Vec3} v The vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} A copy of v.
 * @memberOf module:twgl/v3
 */


function copy(v, dst) {
  dst = dst || new VecType(3);
  dst[0] = v[0];
  dst[1] = v[1];
  dst[2] = v[2];
  return dst;
}
/**
 * Multiplies a vector by another vector (component-wise); assumes a and
 * b have the same length.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} The vector of products of entries of a and
 *     b.
 * @memberOf module:twgl/v3
 */


function multiply(a, b, dst) {
  dst = dst || new VecType(3);
  dst[0] = a[0] * b[0];
  dst[1] = a[1] * b[1];
  dst[2] = a[2] * b[2];
  return dst;
}
/**
 * Divides a vector by another vector (component-wise); assumes a and
 * b have the same length.
 * @param {module:twgl/v3.Vec3} a Operand vector.
 * @param {module:twgl/v3.Vec3} b Operand vector.
 * @param {module:twgl/v3.Vec3} [dst] vector to hold result. If not new one is created..
 * @return {module:twgl/v3.Vec3} The vector of quotients of entries of a and
 *     b.
 * @memberOf module:twgl/v3
 */


function divide(a, b, dst) {
  dst = dst || new VecType(3);
  dst[0] = a[0] / b[0];
  dst[1] = a[1] / b[1];
  dst[2] = a[2] / b[2];
  return dst;
}

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

"use strict";


exports.__esModule = true;
exports.isWebGL1 = isWebGL1;
exports.isWebGL2 = isWebGL2;
exports.glEnumToString = void 0;

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

/**
 * Gets the gl version as a number
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @return {number} version of gl
 */
//function getVersionAsNumber(gl) {
//  return parseFloat(gl.getParameter(gl.VERSION).substr(6));
//}

/**
 * Check if context is WebGL 2.0
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @return {bool} true if it's WebGL 2.0
 * @memberOf module:twgl
 */
function isWebGL2(gl) {
  // This is the correct check but it's slow
  //  return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0") === 0;
  // This might also be the correct check but I'm assuming it's slow-ish
  // return gl instanceof WebGL2RenderingContext;
  return !!gl.texStorage2D;
}
/**
 * Check if context is WebGL 1.0
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @return {bool} true if it's WebGL 1.0
 * @memberOf module:twgl
 */


function isWebGL1(gl) {
  // This is the correct check but it's slow
  // const version = getVersionAsNumber(gl);
  // return version <= 1.0 && version > 0.0;  // because as of 2016/5 Edge returns 0.96
  // This might also be the correct check but I'm assuming it's slow-ish
  // return gl instanceof WebGLRenderingContext;
  return !gl.texStorage2D;
}
/**
 * Gets a string for WebGL enum
 *
 * Note: Several enums are the same. Without more
 * context (which function) it's impossible to always
 * give the correct enum. As it is, for matching values
 * it gives all enums. Checking the WebGL2RenderingContext
 * that means
 *
 *      0     = ZERO | POINT | NONE | NO_ERROR
 *      1     = ONE | LINES | SYNC_FLUSH_COMMANDS_BIT
 *      32777 = BLEND_EQUATION_RGB | BLEND_EQUATION_RGB
 *      36662 = COPY_READ_BUFFER | COPY_READ_BUFFER_BINDING
 *      36663 = COPY_WRITE_BUFFER | COPY_WRITE_BUFFER_BINDING
 *      36006 = FRAMEBUFFER_BINDING | DRAW_FRAMEBUFFER_BINDING
 *
 * It's also not useful for bits really unless you pass in individual bits.
 * In other words
 *
 *     const bits = gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT;
 *     twgl.glEnumToString(gl, bits);  // not going to work
 *
 * Note that some enums only exist on extensions. If you
 * want them to show up you need to pass the extension at least
 * once. For example
 *
 *     const ext = gl.getExtension('WEBGL_compressed_texture_s3tc`);
 *     if (ext) {
 *        twgl.glEnumToString(ext, 0);  // just prime the function
 *
 *        ..later..
 *
 *        const internalFormat = ext.COMPRESSED_RGB_S3TC_DXT1_EXT;
 *        console.log(twgl.glEnumToString(gl, internalFormat));
 *
 * Notice I didn't have to pass the extension the second time. This means
 * you can have place that generically gets an enum for texture formats for example.
 * and as long as you primed the function with the extensions
 *
 * If you're using `twgl.addExtensionsToContext` to enable your extensions
 * then twgl will automatically get the extension's enums.
 *
 * @param {WebGLRenderingContext|Extension} gl A WebGLRenderingContext or any extension object
 * @param {number} value the value of the enum you want to look up.
 * @memberOf module:twgl
 */


var glEnumToString = function () {
  var haveEnumsForType = {};
  var enums = {};

  function addEnums(gl) {
    var type = gl.constructor.name;

    if (!haveEnumsForType[type]) {
      for (var key in gl) {
        if (typeof gl[key] === 'number') {
          var existing = enums[gl[key]];
          enums[gl[key]] = existing ? "".concat(existing, " | ").concat(key) : key;
        }
      }

      haveEnumsForType[type] = true;
    }
  }

  return function glEnumToString(gl, value) {
    addEnums(gl);
    return enums[value] || "0x" + value.toString(16);
  };
}();

exports.glEnumToString = glEnumToString;

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

"use strict";


exports.__esModule = true;
exports.createAttributeSetters = createAttributeSetters;
exports.createProgram = createProgram;
exports.createProgramFromScripts = createProgramFromScripts;
exports.createProgramFromSources = createProgramFromSources;
exports.createProgramInfo = createProgramInfo;
exports.createProgramInfoFromProgram = createProgramInfoFromProgram;
exports.createUniformSetters = createUniformSetters;
exports.createUniformBlockSpecFromProgram = createUniformBlockSpecFromProgram;
exports.createUniformBlockInfoFromProgram = createUniformBlockInfoFromProgram;
exports.createUniformBlockInfo = createUniformBlockInfo;
exports.createTransformFeedback = createTransformFeedback;
exports.createTransformFeedbackInfo = createTransformFeedbackInfo;
exports.bindTransformFeedbackInfo = bindTransformFeedbackInfo;
exports.setAttributes = setAttributes;
exports.setBuffersAndAttributes = setBuffersAndAttributes;
exports.setUniforms = setUniforms;
exports.setUniformBlock = setUniformBlock;
exports.setBlockUniforms = setBlockUniforms;
exports.bindUniformBlock = bindUniformBlock;

var utils = _interopRequireWildcard(__webpack_require__(4));

var helper = _interopRequireWildcard(__webpack_require__(0));

var _globalObject = _interopRequireDefault(__webpack_require__(2));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Low level shader program related functions
 *
 * You should generally not need to use these functions. They are provided
 * for those cases where you're doing something out of the ordinary
 * and you need lower level access.
 *
 * For backward compatibily they are available at both `twgl.programs` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/programs
 */
var error = helper.error;
var warn = helper.warn;
var getElementById = _globalObject.default && _globalObject.default.document && _globalObject.default.document.getElementById ? _globalObject.default.document.getElementById.bind(_globalObject.default.document) : function () {
  return null;
};
var FLOAT = 0x1406;
var FLOAT_VEC2 = 0x8B50;
var FLOAT_VEC3 = 0x8B51;
var FLOAT_VEC4 = 0x8B52;
var INT = 0x1404;
var INT_VEC2 = 0x8B53;
var INT_VEC3 = 0x8B54;
var INT_VEC4 = 0x8B55;
var BOOL = 0x8B56;
var BOOL_VEC2 = 0x8B57;
var BOOL_VEC3 = 0x8B58;
var BOOL_VEC4 = 0x8B59;
var FLOAT_MAT2 = 0x8B5A;
var FLOAT_MAT3 = 0x8B5B;
var FLOAT_MAT4 = 0x8B5C;
var SAMPLER_2D = 0x8B5E;
var SAMPLER_CUBE = 0x8B60;
var SAMPLER_3D = 0x8B5F;
var SAMPLER_2D_SHADOW = 0x8B62;
var FLOAT_MAT2x3 = 0x8B65;
var FLOAT_MAT2x4 = 0x8B66;
var FLOAT_MAT3x2 = 0x8B67;
var FLOAT_MAT3x4 = 0x8B68;
var FLOAT_MAT4x2 = 0x8B69;
var FLOAT_MAT4x3 = 0x8B6A;
var SAMPLER_2D_ARRAY = 0x8DC1;
var SAMPLER_2D_ARRAY_SHADOW = 0x8DC4;
var SAMPLER_CUBE_SHADOW = 0x8DC5;
var UNSIGNED_INT = 0x1405;
var UNSIGNED_INT_VEC2 = 0x8DC6;
var UNSIGNED_INT_VEC3 = 0x8DC7;
var UNSIGNED_INT_VEC4 = 0x8DC8;
var INT_SAMPLER_2D = 0x8DCA;
var INT_SAMPLER_3D = 0x8DCB;
var INT_SAMPLER_CUBE = 0x8DCC;
var INT_SAMPLER_2D_ARRAY = 0x8DCF;
var UNSIGNED_INT_SAMPLER_2D = 0x8DD2;
var UNSIGNED_INT_SAMPLER_3D = 0x8DD3;
var UNSIGNED_INT_SAMPLER_CUBE = 0x8DD4;
var UNSIGNED_INT_SAMPLER_2D_ARRAY = 0x8DD7;
var TEXTURE_2D = 0x0DE1;
var TEXTURE_CUBE_MAP = 0x8513;
var TEXTURE_3D = 0x806F;
var TEXTURE_2D_ARRAY = 0x8C1A;
var typeMap = {};
/**
 * Returns the corresponding bind point for a given sampler type
 */

function getBindPointForSamplerType(gl, type) {
  return typeMap[type].bindPoint;
} // This kind of sucks! If you could compose functions as in `var fn = gl[name];`
// this code could be a lot smaller but that is sadly really slow (T_T)


function floatSetter(gl, location) {
  return function (v) {
    gl.uniform1f(location, v);
  };
}

function floatArraySetter(gl, location) {
  return function (v) {
    gl.uniform1fv(location, v);
  };
}

function floatVec2Setter(gl, location) {
  return function (v) {
    gl.uniform2fv(location, v);
  };
}

function floatVec3Setter(gl, location) {
  return function (v) {
    gl.uniform3fv(location, v);
  };
}

function floatVec4Setter(gl, location) {
  return function (v) {
    gl.uniform4fv(location, v);
  };
}

function intSetter(gl, location) {
  return function (v) {
    gl.uniform1i(location, v);
  };
}

function intArraySetter(gl, location) {
  return function (v) {
    gl.uniform1iv(location, v);
  };
}

function intVec2Setter(gl, location) {
  return function (v) {
    gl.uniform2iv(location, v);
  };
}

function intVec3Setter(gl, location) {
  return function (v) {
    gl.uniform3iv(location, v);
  };
}

function intVec4Setter(gl, location) {
  return function (v) {
    gl.uniform4iv(location, v);
  };
}

function uintSetter(gl, location) {
  return function (v) {
    gl.uniform1ui(location, v);
  };
}

function uintArraySetter(gl, location) {
  return function (v) {
    gl.uniform1uiv(location, v);
  };
}

function uintVec2Setter(gl, location) {
  return function (v) {
    gl.uniform2uiv(location, v);
  };
}

function uintVec3Setter(gl, location) {
  return function (v) {
    gl.uniform3uiv(location, v);
  };
}

function uintVec4Setter(gl, location) {
  return function (v) {
    gl.uniform4uiv(location, v);
  };
}

function floatMat2Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix2fv(location, false, v);
  };
}

function floatMat3Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix3fv(location, false, v);
  };
}

function floatMat4Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix4fv(location, false, v);
  };
}

function floatMat23Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix2x3fv(location, false, v);
  };
}

function floatMat32Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix3x2fv(location, false, v);
  };
}

function floatMat24Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix2x4fv(location, false, v);
  };
}

function floatMat42Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix4x2fv(location, false, v);
  };
}

function floatMat34Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix3x4fv(location, false, v);
  };
}

function floatMat43Setter(gl, location) {
  return function (v) {
    gl.uniformMatrix4x3fv(location, false, v);
  };
}

function samplerSetter(gl, type, unit, location) {
  var bindPoint = getBindPointForSamplerType(gl, type);
  return utils.isWebGL2(gl) ? function (textureOrPair) {
    var texture;
    var sampler;

    if (helper.isTexture(gl, textureOrPair)) {
      texture = textureOrPair;
      sampler = null;
    } else {
      texture = textureOrPair.texture;
      sampler = textureOrPair.sampler;
    }

    gl.uniform1i(location, unit);
    gl.activeTexture(gl.TEXTURE0 + unit);
    gl.bindTexture(bindPoint, texture);
    gl.bindSampler(unit, sampler);
  } : function (texture) {
    gl.uniform1i(location, unit);
    gl.activeTexture(gl.TEXTURE0 + unit);
    gl.bindTexture(bindPoint, texture);
  };
}

function samplerArraySetter(gl, type, unit, location, size) {
  var bindPoint = getBindPointForSamplerType(gl, type);
  var units = new Int32Array(size);

  for (var ii = 0; ii < size; ++ii) {
    units[ii] = unit + ii;
  }

  return utils.isWebGL2(gl) ? function (textures) {
    gl.uniform1iv(location, units);
    textures.forEach(function (textureOrPair, index) {
      gl.activeTexture(gl.TEXTURE0 + units[index]);
      var texture;
      var sampler;

      if (helper.isTexture(gl, textureOrPair)) {
        texture = textureOrPair;
        sampler = null;
      } else {
        texture = textureOrPair.texture;
        sampler = textureOrPair.sampler;
      }

      gl.bindSampler(unit, sampler);
      gl.bindTexture(bindPoint, texture);
    });
  } : function (textures) {
    gl.uniform1iv(location, units);
    textures.forEach(function (texture, index) {
      gl.activeTexture(gl.TEXTURE0 + units[index]);
      gl.bindTexture(bindPoint, texture);
    });
  };
}

typeMap[FLOAT] = {
  Type: Float32Array,
  size: 4,
  setter: floatSetter,
  arraySetter: floatArraySetter
};
typeMap[FLOAT_VEC2] = {
  Type: Float32Array,
  size: 8,
  setter: floatVec2Setter
};
typeMap[FLOAT_VEC3] = {
  Type: Float32Array,
  size: 12,
  setter: floatVec3Setter
};
typeMap[FLOAT_VEC4] = {
  Type: Float32Array,
  size: 16,
  setter: floatVec4Setter
};
typeMap[INT] = {
  Type: Int32Array,
  size: 4,
  setter: intSetter,
  arraySetter: intArraySetter
};
typeMap[INT_VEC2] = {
  Type: Int32Array,
  size: 8,
  setter: intVec2Setter
};
typeMap[INT_VEC3] = {
  Type: Int32Array,
  size: 12,
  setter: intVec3Setter
};
typeMap[INT_VEC4] = {
  Type: Int32Array,
  size: 16,
  setter: intVec4Setter
};
typeMap[UNSIGNED_INT] = {
  Type: Uint32Array,
  size: 4,
  setter: uintSetter,
  arraySetter: uintArraySetter
};
typeMap[UNSIGNED_INT_VEC2] = {
  Type: Uint32Array,
  size: 8,
  setter: uintVec2Setter
};
typeMap[UNSIGNED_INT_VEC3] = {
  Type: Uint32Array,
  size: 12,
  setter: uintVec3Setter
};
typeMap[UNSIGNED_INT_VEC4] = {
  Type: Uint32Array,
  size: 16,
  setter: uintVec4Setter
};
typeMap[BOOL] = {
  Type: Uint32Array,
  size: 4,
  setter: intSetter,
  arraySetter: intArraySetter
};
typeMap[BOOL_VEC2] = {
  Type: Uint32Array,
  size: 8,
  setter: intVec2Setter
};
typeMap[BOOL_VEC3] = {
  Type: Uint32Array,
  size: 12,
  setter: intVec3Setter
};
typeMap[BOOL_VEC4] = {
  Type: Uint32Array,
  size: 16,
  setter: intVec4Setter
};
typeMap[FLOAT_MAT2] = {
  Type: Float32Array,
  size: 16,
  setter: floatMat2Setter
};
typeMap[FLOAT_MAT3] = {
  Type: Float32Array,
  size: 36,
  setter: floatMat3Setter
};
typeMap[FLOAT_MAT4] = {
  Type: Float32Array,
  size: 64,
  setter: floatMat4Setter
};
typeMap[FLOAT_MAT2x3] = {
  Type: Float32Array,
  size: 24,
  setter: floatMat23Setter
};
typeMap[FLOAT_MAT2x4] = {
  Type: Float32Array,
  size: 32,
  setter: floatMat24Setter
};
typeMap[FLOAT_MAT3x2] = {
  Type: Float32Array,
  size: 24,
  setter: floatMat32Setter
};
typeMap[FLOAT_MAT3x4] = {
  Type: Float32Array,
  size: 48,
  setter: floatMat34Setter
};
typeMap[FLOAT_MAT4x2] = {
  Type: Float32Array,
  size: 32,
  setter: floatMat42Setter
};
typeMap[FLOAT_MAT4x3] = {
  Type: Float32Array,
  size: 48,
  setter: floatMat43Setter
};
typeMap[SAMPLER_2D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D
};
typeMap[SAMPLER_CUBE] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_CUBE_MAP
};
typeMap[SAMPLER_3D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_3D
};
typeMap[SAMPLER_2D_SHADOW] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D
};
typeMap[SAMPLER_2D_ARRAY] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D_ARRAY
};
typeMap[SAMPLER_2D_ARRAY_SHADOW] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D_ARRAY
};
typeMap[SAMPLER_CUBE_SHADOW] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_CUBE_MAP
};
typeMap[INT_SAMPLER_2D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D
};
typeMap[INT_SAMPLER_3D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_3D
};
typeMap[INT_SAMPLER_CUBE] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_CUBE_MAP
};
typeMap[INT_SAMPLER_2D_ARRAY] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D_ARRAY
};
typeMap[UNSIGNED_INT_SAMPLER_2D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D
};
typeMap[UNSIGNED_INT_SAMPLER_3D] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_3D
};
typeMap[UNSIGNED_INT_SAMPLER_CUBE] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_CUBE_MAP
};
typeMap[UNSIGNED_INT_SAMPLER_2D_ARRAY] = {
  Type: null,
  size: 0,
  setter: samplerSetter,
  arraySetter: samplerArraySetter,
  bindPoint: TEXTURE_2D_ARRAY
};

function floatAttribSetter(gl, index) {
  return function (b) {
    gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
    gl.enableVertexAttribArray(index);
    gl.vertexAttribPointer(index, b.numComponents || b.size, b.type || gl.FLOAT, b.normalize || false, b.stride || 0, b.offset || 0);

    if (b.divisor !== undefined) {
      gl.vertexAttribDivisor(index, b.divisor);
    }
  };
}

function intAttribSetter(gl, index) {
  return function (b) {
    gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
    gl.enableVertexAttribArray(index);
    gl.vertexAttribIPointer(index, b.numComponents || b.size, b.type || gl.INT, b.stride || 0, b.offset || 0);

    if (b.divisor !== undefined) {
      gl.vertexAttribDivisor(index, b.divisor);
    }
  };
}

function matAttribSetter(gl, index, typeInfo) {
  var defaultSize = typeInfo.size;
  var count = typeInfo.count;
  return function (b) {
    gl.bindBuffer(gl.ARRAY_BUFFER, b.buffer);
    var numComponents = b.size || b.numComponents || defaultSize;
    var size = numComponents / count;
    var type = b.type || gl.FLOAT;
    var typeInfo = typeMap[type];
    var stride = typeInfo.size * numComponents;
    var normalize = b.normalize || false;
    var offset = b.offset || 0;
    var rowOffset = stride / count;

    for (var i = 0; i < count; ++i) {
      gl.enableVertexAttribArray(index + i);
      gl.vertexAttribPointer(index + i, size, type, normalize, stride, offset + rowOffset * i);

      if (b.divisor !== undefined) {
        gl.vertexAttribDivisor(index + i, b.divisor);
      }
    }
  };
}

var attrTypeMap = {};
attrTypeMap[FLOAT] = {
  size: 4,
  setter: floatAttribSetter
};
attrTypeMap[FLOAT_VEC2] = {
  size: 8,
  setter: floatAttribSetter
};
attrTypeMap[FLOAT_VEC3] = {
  size: 12,
  setter: floatAttribSetter
};
attrTypeMap[FLOAT_VEC4] = {
  size: 16,
  setter: floatAttribSetter
};
attrTypeMap[INT] = {
  size: 4,
  setter: intAttribSetter
};
attrTypeMap[INT_VEC2] = {
  size: 8,
  setter: intAttribSetter
};
attrTypeMap[INT_VEC3] = {
  size: 12,
  setter: intAttribSetter
};
attrTypeMap[INT_VEC4] = {
  size: 16,
  setter: intAttribSetter
};
attrTypeMap[UNSIGNED_INT] = {
  size: 4,
  setter: intAttribSetter
};
attrTypeMap[UNSIGNED_INT_VEC2] = {
  size: 8,
  setter: intAttribSetter
};
attrTypeMap[UNSIGNED_INT_VEC3] = {
  size: 12,
  setter: intAttribSetter
};
attrTypeMap[UNSIGNED_INT_VEC4] = {
  size: 16,
  setter: intAttribSetter
};
attrTypeMap[BOOL] = {
  size: 4,
  setter: intAttribSetter
};
attrTypeMap[BOOL_VEC2] = {
  size: 8,
  setter: intAttribSetter
};
attrTypeMap[BOOL_VEC3] = {
  size: 12,
  setter: intAttribSetter
};
attrTypeMap[BOOL_VEC4] = {
  size: 16,
  setter: intAttribSetter
};
attrTypeMap[FLOAT_MAT2] = {
  size: 4,
  setter: matAttribSetter,
  count: 2
};
attrTypeMap[FLOAT_MAT3] = {
  size: 9,
  setter: matAttribSetter,
  count: 3
};
attrTypeMap[FLOAT_MAT4] = {
  size: 16,
  setter: matAttribSetter,
  count: 4
}; // make sure we don't see a global gl

var gl = undefined; // eslint-disable-line

/**
 * Error Callback
 * @callback ErrorCallback
 * @param {string} msg error message.
 * @param {number} [lineOffset] amount to add to line number
 * @memberOf module:twgl
 */

function addLineNumbers(src, lineOffset) {
  lineOffset = lineOffset || 0;
  ++lineOffset;
  return src.split("\n").map(function (line, ndx) {
    return ndx + lineOffset + ": " + line;
  }).join("\n");
}

var spaceRE = /^[ \t]*\n/;
/**
 * Loads a shader.
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {string} shaderSource The shader source.
 * @param {number} shaderType The type of shader.
 * @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors.
 * @return {WebGLShader} The created shader.
 */

function loadShader(gl, shaderSource, shaderType, opt_errorCallback) {
  var errFn = opt_errorCallback || error; // Create the shader object

  var shader = gl.createShader(shaderType); // Remove the first end of line because WebGL 2.0 requires
  // #version 300 es
  // as the first line. No whitespace allowed before that line
  // so
  //
  // <script>
  // #version 300 es
  // </scri'+'pt>
  //
  // Has one line before it which is invalid according to GLSL ES 3.00
  //

  var lineOffset = 0;

  if (spaceRE.test(shaderSource)) {
    lineOffset = 1;
    shaderSource = shaderSource.replace(spaceRE, '');
  } // Load the shader source


  gl.shaderSource(shader, shaderSource); // Compile the shader

  gl.compileShader(shader); // Check the compile status

  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);

  if (!compiled) {
    // Something went wrong during compilation; get the error
    var lastError = gl.getShaderInfoLog(shader);
    errFn(addLineNumbers(shaderSource, lineOffset) + "\n*** Error compiling shader: " + lastError);
    gl.deleteShader(shader);
    return null;
  }

  return shader;
}
/**
 * @typedef {Object} ProgramOptions
 * @property {function(string)} [errorCallback] callback for errors
 * @property {Object.<string,number>} [attribLocations] a attribute name to location map
 * @property {(module:twgl.BufferInfo|Object.<string,module:twgl.AttribInfo>|string[])} [transformFeedbackVaryings] If passed
 *   a BufferInfo will use the attribs names inside. If passed an object of AttribInfos will use the names from that object. Otherwise
 *   you can pass an array of names.
 * @property {number} [transformFeedbackMode] the mode to pass `gl.transformFeedbackVaryings`. Defaults to `SEPARATE_ATTRIBS`.
 * @memberOf module:twgl
 */

/**
 * Gets the program options based on all these optional arguments
 * @param {module:twgl.ProgramOptions|string[]} [opt_attribs] Options for the program or an array of attribs names. Locations will be assigned by index if not passed in
 * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
 * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
 *        on error. If you want something else pass an callback. It's passed an error message.
 * @return {module:twgl.ProgramOptions} an instance of ProgramOptions based on the arguments pased on
 */


function getProgramOptions(opt_attribs, opt_locations, opt_errorCallback) {
  var transformFeedbackVaryings;

  if (typeof opt_locations === 'function') {
    opt_errorCallback = opt_locations;
    opt_locations = undefined;
  }

  if (typeof opt_attribs === 'function') {
    opt_errorCallback = opt_attribs;
    opt_attribs = undefined;
  } else if (opt_attribs && !Array.isArray(opt_attribs)) {
    // If we have an errorCallback we can just return this object
    // Otherwise we need to construct one with default errorCallback
    if (opt_attribs.errorCallback) {
      return opt_attribs;
    }

    var opt = opt_attribs;
    opt_errorCallback = opt.errorCallback;
    opt_attribs = opt.attribLocations;
    transformFeedbackVaryings = opt.transformFeedbackVaryings;
  }

  var options = {
    errorCallback: opt_errorCallback || error,
    transformFeedbackVaryings: transformFeedbackVaryings
  };

  if (opt_attribs) {
    var attribLocations = {};

    if (Array.isArray(opt_attribs)) {
      opt_attribs.forEach(function (attrib, ndx) {
        attribLocations[attrib] = opt_locations ? opt_locations[ndx] : ndx;
      });
    } else {
      attribLocations = opt_attribs;
    }

    options.attribLocations = attribLocations;
  }

  return options;
}

var defaultShaderType = ["VERTEX_SHADER", "FRAGMENT_SHADER"];

function getShaderTypeFromScriptType(scriptType) {
  if (scriptType.indexOf("frag") >= 0) {
    return gl.FRAGMENT_SHADER;
  } else if (scriptType.indexOf("vert") >= 0) {
    return gl.VERTEX_SHADER;
  }

  return undefined;
}

function deleteShaders(gl, shaders) {
  shaders.forEach(function (shader) {
    gl.deleteShader(shader);
  });
}
/**
 * Creates a program, attaches (and/or compiles) shaders, binds attrib locations, links the
 * program and calls useProgram.
 *
 * NOTE: There are 4 signatures for this function
 *
 *     twgl.createProgram(gl, [vs, fs], options);
 *     twgl.createProgram(gl, [vs, fs], opt_errFunc);
 *     twgl.createProgram(gl, [vs, fs], opt_attribs, opt_errFunc);
 *     twgl.createProgram(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
 *
 * @param {WebGLShader[]|string[]} shaders The shaders to attach, or element ids for their source, or strings that contain their source
 * @param {module:twgl.ProgramOptions|string[]} [opt_attribs] Options for the program or an array of attribs names. Locations will be assigned by index if not passed in
 * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
 * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors. By default it just prints an error to the console
 *        on error. If you want something else pass an callback. It's passed an error message.
 * @return {WebGLProgram?} the created program or null if error.
 * @memberOf module:twgl/programs
 */


function createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback) {
  var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
  var realShaders = [];
  var newShaders = [];

  for (var ndx = 0; ndx < shaders.length; ++ndx) {
    var shader = shaders[ndx];

    if (typeof shader === 'string') {
      var elem = getElementById(shader);
      var src = elem ? elem.text : shader;
      var type = gl[defaultShaderType[ndx]];

      if (elem && elem.type) {
        type = getShaderTypeFromScriptType(elem.type) || type;
      }

      shader = loadShader(gl, src, type, progOptions.errorCallback);
      newShaders.push(shader);
    }

    if (helper.isShader(gl, shader)) {
      realShaders.push(shader);
    }
  }

  if (realShaders.length !== shaders.length) {
    progOptions.errorCallback("not enough shaders for program");
    deleteShaders(gl, newShaders);
    return null;
  }

  var program = gl.createProgram();
  realShaders.forEach(function (shader) {
    gl.attachShader(program, shader);
  });

  if (progOptions.attribLocations) {
    Object.keys(progOptions.attribLocations).forEach(function (attrib) {
      gl.bindAttribLocation(program, progOptions.attribLocations[attrib], attrib);
    });
  }

  var varyings = progOptions.transformFeedbackVaryings;

  if (varyings) {
    if (varyings.attribs) {
      varyings = varyings.attribs;
    }

    if (!Array.isArray(varyings)) {
      varyings = Object.keys(varyings);
    }

    gl.transformFeedbackVaryings(program, varyings, progOptions.transformFeedbackMode || gl.SEPARATE_ATTRIBS);
  }

  gl.linkProgram(program); // Check the link status

  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);

  if (!linked) {
    // something went wrong with the link
    var lastError = gl.getProgramInfoLog(program);
    progOptions.errorCallback("Error in program linking:" + lastError);
    gl.deleteProgram(program);
    deleteShaders(gl, newShaders);
    return null;
  }

  return program;
}
/**
 * Loads a shader from a script tag.
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {string} scriptId The id of the script tag.
 * @param {number} [opt_shaderType] The type of shader. If not passed in it will
 *     be derived from the type of the script tag.
 * @param {module:twgl.ErrorCallback} [opt_errorCallback] callback for errors.
 * @return {WebGLShader?} The created shader or null if error.
 */


function createShaderFromScript(gl, scriptId, opt_shaderType, opt_errorCallback) {
  var shaderSource = "";
  var shaderScript = getElementById(scriptId);

  if (!shaderScript) {
    throw "*** Error: unknown script element" + scriptId;
  }

  shaderSource = shaderScript.text;
  var shaderType = opt_shaderType || getShaderTypeFromScriptType(shaderScript.type);

  if (!shaderType) {
    throw "*** Error: unknown shader type";
  }

  return loadShader(gl, shaderSource, shaderType, opt_errorCallback);
}
/**
 * Creates a program from 2 script tags.
 *
 * NOTE: There are 4 signatures for this function
 *
 *     twgl.createProgramFromScripts(gl, [vs, fs], opt_options);
 *     twgl.createProgramFromScripts(gl, [vs, fs], opt_errFunc);
 *     twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_errFunc);
 *     twgl.createProgramFromScripts(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext
 *        to use.
 * @param {string[]} shaderScriptIds Array of ids of the script
 *        tags for the shaders. The first is assumed to be the
 *        vertex shader, the second the fragment shader.
 * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
 * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
 * @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
 *        on error. If you want something else pass an callback. It's passed an error message.
 * @return {WebGLProgram} The created program.
 * @memberOf module:twgl/programs
 */


function createProgramFromScripts(gl, shaderScriptIds, opt_attribs, opt_locations, opt_errorCallback) {
  var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
  var shaders = [];

  for (var ii = 0; ii < shaderScriptIds.length; ++ii) {
    var shader = createShaderFromScript(gl, shaderScriptIds[ii], gl[defaultShaderType[ii]], progOptions.errorCallback);

    if (!shader) {
      return null;
    }

    shaders.push(shader);
  }

  return createProgram(gl, shaders, progOptions);
}
/**
 * Creates a program from 2 sources.
 *
 * NOTE: There are 4 signatures for this function
 *
 *     twgl.createProgramFromSource(gl, [vs, fs], opt_options);
 *     twgl.createProgramFromSource(gl, [vs, fs], opt_errFunc);
 *     twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_errFunc);
 *     twgl.createProgramFromSource(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext
 *        to use.
 * @param {string[]} shaderSources Array of sources for the
 *        shaders. The first is assumed to be the vertex shader,
 *        the second the fragment shader.
 * @param {string[]} [opt_attribs] An array of attribs names. Locations will be assigned by index if not passed in
 * @param {number[]} [opt_locations] The locations for the. A parallel array to opt_attribs letting you assign locations.
 * @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
 *        on error. If you want something else pass an callback. It's passed an error message.
 * @return {WebGLProgram} The created program.
 * @memberOf module:twgl/programs
 */


function createProgramFromSources(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
  var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
  var shaders = [];

  for (var ii = 0; ii < shaderSources.length; ++ii) {
    var shader = loadShader(gl, shaderSources[ii], gl[defaultShaderType[ii]], progOptions.errorCallback);

    if (!shader) {
      return null;
    }

    shaders.push(shader);
  }

  return createProgram(gl, shaders, progOptions);
}
/**
 * Returns true if attribute/uniform is a reserved/built in
 *
 * It makes no sense to me why GL returns these because it's
 * illegal to call `gl.getUniformLocation` and `gl.getAttribLocation`
 * with names that start with `gl_` (and `webgl_` in WebGL)
 *
 * I can only assume they are there because they might count
 * when computing the number of uniforms/attributes used when you want to
 * know if you are near the limit. That doesn't really make sense
 * to me but the fact that these get returned are in the spec.
 *
 * @param {WebGLActiveInfo} info As returned from `gl.getActiveUniform` or
 *    `gl.getActiveAttrib`.
 * @return {bool} true if it's reserved
 */


function isBuiltIn(info) {
  var name = info.name;
  return name.startsWith("gl_") || name.startsWith("webgl_");
}
/**
 * Creates setter functions for all uniforms of a shader
 * program.
 *
 * @see {@link module:twgl.setUniforms}
 *
 * @param {WebGLProgram} program the program to create setters for.
 * @returns {Object.<string, function>} an object with a setter by name for each uniform
 * @memberOf module:twgl/programs
 */


function createUniformSetters(gl, program) {
  var textureUnit = 0;
  /**
   * Creates a setter for a uniform of the given program with it's
   * location embedded in the setter.
   * @param {WebGLProgram} program
   * @param {WebGLUniformInfo} uniformInfo
   * @returns {function} the created setter.
   */

  function createUniformSetter(program, uniformInfo) {
    var location = gl.getUniformLocation(program, uniformInfo.name);
    var isArray = uniformInfo.size > 1 && uniformInfo.name.substr(-3) === "[0]";
    var type = uniformInfo.type;
    var typeInfo = typeMap[type];

    if (!typeInfo) {
      throw "unknown type: 0x" + type.toString(16); // we should never get here.
    }

    var setter;

    if (typeInfo.bindPoint) {
      // it's a sampler
      var unit = textureUnit;
      textureUnit += uniformInfo.size;

      if (isArray) {
        setter = typeInfo.arraySetter(gl, type, unit, location, uniformInfo.size);
      } else {
        setter = typeInfo.setter(gl, type, unit, location, uniformInfo.size);
      }
    } else {
      if (typeInfo.arraySetter && isArray) {
        setter = typeInfo.arraySetter(gl, location);
      } else {
        setter = typeInfo.setter(gl, location);
      }
    }

    setter.location = location;
    return setter;
  }

  var uniformSetters = {};
  var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);

  for (var ii = 0; ii < numUniforms; ++ii) {
    var uniformInfo = gl.getActiveUniform(program, ii);

    if (isBuiltIn(uniformInfo)) {
      continue;
    }

    var name = uniformInfo.name; // remove the array suffix.

    if (name.substr(-3) === "[0]") {
      name = name.substr(0, name.length - 3);
    }

    var setter = createUniformSetter(program, uniformInfo);
    uniformSetters[name] = setter;
  }

  return uniformSetters;
}
/**
 * @typedef {Object} TransformFeedbackInfo
 * @property {number} index index of transform feedback
 * @property {number} type GL type
 * @property {number} size 1 - 4
 * @memberOf module:twgl
 */

/**
 * Create TransformFeedbackInfo for passing to bind/unbindTransformFeedbackInfo.
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {WebGLProgram} program an existing WebGLProgram.
 * @return {Object<string, module:twgl.TransformFeedbackInfo>}
 * @memberOf module:twgl
 */


function createTransformFeedbackInfo(gl, program) {
  var info = {};
  var numVaryings = gl.getProgramParameter(program, gl.TRANSFORM_FEEDBACK_VARYINGS);

  for (var ii = 0; ii < numVaryings; ++ii) {
    var varying = gl.getTransformFeedbackVarying(program, ii);
    info[varying.name] = {
      index: ii,
      type: varying.type,
      size: varying.size
    };
  }

  return info;
}
/**
 * Binds buffers for transform feedback.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {(module:twgl.ProgramInfo|Object<string, module:twgl.TransformFeedbackInfo>)} transformFeedbackInfo A ProgramInfo or TransformFeedbackInfo.
 * @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos.
 * @memberOf module:twgl
 */


function bindTransformFeedbackInfo(gl, transformFeedbackInfo, bufferInfo) {
  if (transformFeedbackInfo.transformFeedbackInfo) {
    transformFeedbackInfo = transformFeedbackInfo.transformFeedbackInfo;
  }

  if (bufferInfo.attribs) {
    bufferInfo = bufferInfo.attribs;
  }

  for (var name in bufferInfo) {
    var varying = transformFeedbackInfo[name];

    if (varying) {
      var buf = bufferInfo[name];

      if (buf.offset) {
        gl.bindBufferRange(gl.TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer, buf.offset, buf.size);
      } else {
        gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, varying.index, buf.buffer);
      }
    }
  }
}
/**
 * Unbinds buffers afetr transform feedback.
 *
 * Buffers can not be bound to 2 bind points so if you try to bind a buffer used
 * in a transform feedback as an ARRAY_BUFFER for an attribute it will fail.
 *
 * This function unbinds all buffers that were bound with {@link module:twgl.bindTransformFeedbackInfo}.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {(module:twgl.ProgramInfo|Object<string, module:twgl.TransformFeedbackInfo>)} transformFeedbackInfo A ProgramInfo or TransformFeedbackInfo.
 * @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos.
 */


function unbindTransformFeedbackInfo(gl, transformFeedbackInfo, bufferInfo) {
  if (transformFeedbackInfo.transformFeedbackInfo) {
    transformFeedbackInfo = transformFeedbackInfo.transformFeedbackInfo;
  }

  if (bufferInfo.attribs) {
    bufferInfo = bufferInfo.attribs;
  }

  for (var name in bufferInfo) {
    var varying = transformFeedbackInfo[name];

    if (varying) {
      gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, varying.index, null);
    }
  }
}
/**
 * Creates a transform feedback and sets the buffers
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo}
 * @param {(module:twgl.BufferInfo|Object<string, module:twgl.AttribInfo>)} [bufferInfo] A BufferInfo or set of AttribInfos.
 * @return {WebGLTransformFeedback} the created transform feedback
 * @memberOf module:twgl
 */


function createTransformFeedback(gl, programInfo, bufferInfo) {
  var tf = gl.createTransformFeedback();
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, tf);
  gl.useProgram(programInfo.program);
  bindTransformFeedbackInfo(gl, programInfo, bufferInfo);
  gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, null); // This is only needed because of a bug in Chrome 56. Will remove
  // when chrome fixes it.

  unbindTransformFeedbackInfo(gl, programInfo, bufferInfo);
  return tf;
}
/**
 * @typedef {Object} UniformData
 * @property {number} type The WebGL type enum for this uniform
 * @property {number} size The number of elements for this uniform
 * @property {number} blockNdx The block index this uniform appears in
 * @property {number} offset The byte offset in the block for this uniform's value
 * @memberOf module:twgl
 */

/**
 * The specification for one UniformBlockObject
 *
 * @typedef {Object} BlockSpec
 * @property {number} index The index of the block.
 * @property {number} size The size in bytes needed for the block
 * @property {number[]} uniformIndices The indices of the uniforms used by the block. These indices
 *    correspond to entries in a UniformData array in the {@link module:twgl.UniformBlockSpec}.
 * @property {bool} usedByVertexShader Self explanitory
 * @property {bool} usedByFragmentShader Self explanitory
 * @property {bool} used Self explanitory
 * @memberOf module:twgl
 */

/**
 * A `UniformBlockSpec` represents the data needed to create and bind
 * UniformBlockObjects for a given program
 *
 * @typedef {Object} UniformBlockSpec
 * @property {Object.<string, module:twgl.BlockSpec> blockSpecs The BlockSpec for each block by block name
 * @property {UniformData[]} uniformData An array of data for each uniform by uniform index.
 * @memberOf module:twgl
 */

/**
 * Creates a UniformBlockSpec for the given program.
 *
 * A UniformBlockSpec represents the data needed to create and bind
 * UniformBlockObjects
 *
 * @param {WebGL2RenderingContext} gl A WebGL2 Rendering Context
 * @param {WebGLProgram} program A WebGLProgram for a successfully linked program
 * @return {module:twgl.UniformBlockSpec} The created UniformBlockSpec
 * @memberOf module:twgl/programs
 */


function createUniformBlockSpecFromProgram(gl, program) {
  var numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
  var uniformData = [];
  var uniformIndices = [];

  for (var ii = 0; ii < numUniforms; ++ii) {
    uniformIndices.push(ii);
    uniformData.push({});
    var uniformInfo = gl.getActiveUniform(program, ii);

    if (isBuiltIn(uniformInfo)) {
      break;
    } // REMOVE [0]?


    uniformData[ii].name = uniformInfo.name;
  }

  [["UNIFORM_TYPE", "type"], ["UNIFORM_SIZE", "size"], // num elements
  ["UNIFORM_BLOCK_INDEX", "blockNdx"], ["UNIFORM_OFFSET", "offset"]].forEach(function (pair) {
    var pname = pair[0];
    var key = pair[1];
    gl.getActiveUniforms(program, uniformIndices, gl[pname]).forEach(function (value, ndx) {
      uniformData[ndx][key] = value;
    });
  });
  var blockSpecs = {};
  var numUniformBlocks = gl.getProgramParameter(program, gl.ACTIVE_UNIFORM_BLOCKS);

  for (var _ii = 0; _ii < numUniformBlocks; ++_ii) {
    var name = gl.getActiveUniformBlockName(program, _ii);
    var blockSpec = {
      index: _ii,
      usedByVertexShader: gl.getActiveUniformBlockParameter(program, _ii, gl.UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER),
      usedByFragmentShader: gl.getActiveUniformBlockParameter(program, _ii, gl.UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER),
      size: gl.getActiveUniformBlockParameter(program, _ii, gl.UNIFORM_BLOCK_DATA_SIZE),
      uniformIndices: gl.getActiveUniformBlockParameter(program, _ii, gl.UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES)
    };
    blockSpec.used = blockSpec.usedByVertexSahder || blockSpec.usedByFragmentShader;
    blockSpecs[name] = blockSpec;
  }

  return {
    blockSpecs: blockSpecs,
    uniformData: uniformData
  };
}

var arraySuffixRE = /\[\d+\]\.$/; // better way to check?

/**
 * Represents a UniformBlockObject including an ArrayBuffer with all the uniform values
 * and a corresponding WebGLBuffer to hold those values on the GPU
 *
 * @typedef {Object} UniformBlockInfo
 * @property {string} name The name of the block
 * @property {ArrayBuffer} array The array buffer that contains the uniform values
 * @property {Float32Array} asFloat A float view on the array buffer. This is useful
 *    inspecting the contents of the buffer in the debugger.
 * @property {WebGLBuffer} buffer A WebGL buffer that will hold a copy of the uniform values for rendering.
 * @property {number} [offset] offset into buffer
 * @property {Object.<string, ArrayBufferView>} uniforms A uniform name to ArrayBufferView map.
 *   each Uniform has a correctly typed `ArrayBufferView` into array at the correct offset
 *   and length of that uniform. So for example a float uniform would have a 1 float `Float32Array`
 *   view. A single mat4 would have a 16 element `Float32Array` view. An ivec2 would have an
 *   `Int32Array` view, etc.
 * @memberOf module:twgl
 */

/**
 * Creates a `UniformBlockInfo` for the specified block
 *
 * Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy
 * `UniformBlockInfo` is returned**. This is because when debugging GLSL
 * it is common to comment out large portions of a shader or for example set
 * the final output to a constant. When that happens blocks get optimized out.
 * If this function did not create dummy blocks your code would crash when debugging.
 *
 * @param {WebGL2RenderingContext} gl A WebGL2RenderingContext
 * @param {WebGLProgram} program A WebGLProgram
 * @param {module:twgl.UniformBlockSpec} uinformBlockSpec. A UniformBlockSpec as returned
 *     from {@link module:twgl.createUniformBlockSpecFromProgram}.
 * @param {string} blockName The name of the block.
 * @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo
 * @memberOf module:twgl/programs
 */

function createUniformBlockInfoFromProgram(gl, program, uniformBlockSpec, blockName) {
  var blockSpecs = uniformBlockSpec.blockSpecs;
  var uniformData = uniformBlockSpec.uniformData;
  var blockSpec = blockSpecs[blockName];

  if (!blockSpec) {
    warn("no uniform block object named:", blockName);
    return {
      name: blockName,
      uniforms: {}
    };
  }

  var array = new ArrayBuffer(blockSpec.size);
  var buffer = gl.createBuffer();
  var uniformBufferIndex = blockSpec.index;
  gl.bindBuffer(gl.UNIFORM_BUFFER, buffer);
  gl.uniformBlockBinding(program, blockSpec.index, uniformBufferIndex);
  var prefix = blockName + ".";

  if (arraySuffixRE.test(prefix)) {
    prefix = prefix.replace(arraySuffixRE, ".");
  }

  var uniforms = {};
  blockSpec.uniformIndices.forEach(function (uniformNdx) {
    var data = uniformData[uniformNdx];
    var typeInfo = typeMap[data.type];
    var Type = typeInfo.Type;
    var length = data.size * typeInfo.size;
    var name = data.name;

    if (name.substr(0, prefix.length) === prefix) {
      name = name.substr(prefix.length);
    }

    uniforms[name] = new Type(array, data.offset, length / Type.BYTES_PER_ELEMENT);
  });
  return {
    name: blockName,
    array: array,
    asFloat: new Float32Array(array),
    // for debugging
    buffer: buffer,
    uniforms: uniforms
  };
}
/**
 * Creates a `UniformBlockInfo` for the specified block
 *
 * Note: **If the blockName matches no existing blocks a warning is printed to the console and a dummy
 * `UniformBlockInfo` is returned**. This is because when debugging GLSL
 * it is common to comment out large portions of a shader or for example set
 * the final output to a constant. When that happens blocks get optimized out.
 * If this function did not create dummy blocks your code would crash when debugging.
 *
 * @param {WebGL2RenderingContext} gl A WebGL2RenderingContext
 * @param {module:twgl.ProgramInfo} programInfo a `ProgramInfo`
 *     as returned from {@link module:twgl.createProgramInfo}
 * @param {string} blockName The name of the block.
 * @return {module:twgl.UniformBlockInfo} The created UniformBlockInfo
 * @memberOf module:twgl/programs
 */


function createUniformBlockInfo(gl, programInfo, blockName) {
  return createUniformBlockInfoFromProgram(gl, programInfo.program, programInfo.uniformBlockSpec, blockName);
}
/**
 * Binds a unform block to the matching uniform block point.
 * Matches by blocks by name so blocks must have the same name not just the same
 * structure.
 *
 * If you have changed any values and you upload the valus into the corresponding WebGLBuffer
 * call {@link module:twgl.setUniformBlock} instead.
 *
 * @param {WebGL2RenderingContext} gl A WebGL 2 rendering context.
 * @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo`
 *     as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as
 *     returned from {@link module:twgl.createUniformBlockSpecFromProgram}.
 * @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from
 *     {@link module:twgl.createUniformBlockInfo}.
 * @return {bool} true if buffer was bound. If the programInfo has no block with the same block name
 *     no buffer is bound.
 * @memberOf module:twgl/programs
 */


function bindUniformBlock(gl, programInfo, uniformBlockInfo) {
  var uniformBlockSpec = programInfo.uniformBlockSpec || programInfo;
  var blockSpec = uniformBlockSpec.blockSpecs[uniformBlockInfo.name];

  if (blockSpec) {
    var bufferBindIndex = blockSpec.index;
    gl.bindBufferRange(gl.UNIFORM_BUFFER, bufferBindIndex, uniformBlockInfo.buffer, uniformBlockInfo.offset || 0, uniformBlockInfo.array.byteLength);
    return true;
  }

  return false;
}
/**
 * Uploads the current uniform values to the corresponding WebGLBuffer
 * and binds that buffer to the program's corresponding bind point for the uniform block object.
 *
 * If you haven't changed any values and you only need to bind the uniform block object
 * call {@link module:twgl.bindUniformBlock} instead.
 *
 * @param {WebGL2RenderingContext} gl A WebGL 2 rendering context.
 * @param {(module:twgl.ProgramInfo|module:twgl.UniformBlockSpec)} programInfo a `ProgramInfo`
 *     as returned from {@link module:twgl.createProgramInfo} or or `UniformBlockSpec` as
 *     returned from {@link module:twgl.createUniformBlockSpecFromProgram}.
 * @param {module:twgl.UniformBlockInfo} uniformBlockInfo a `UniformBlockInfo` as returned from
 *     {@link module:twgl.createUniformBlockInfo}.
 * @memberOf module:twgl/programs
 */


function setUniformBlock(gl, programInfo, uniformBlockInfo) {
  if (bindUniformBlock(gl, programInfo, uniformBlockInfo)) {
    gl.bufferData(gl.UNIFORM_BUFFER, uniformBlockInfo.array, gl.DYNAMIC_DRAW);
  }
}
/**
 * Sets values of a uniform block object
 *
 * @param {module:twgl.UniformBlockInfo} uniformBlockInfo A UniformBlockInfo as returned by {@link module:twgl.createUniformBlockInfo}.
 * @param {Object.<string, ?>} values A uniform name to value map where the value is correct for the given
 *    type of uniform. So for example given a block like
 *
 *       uniform SomeBlock {
 *         float someFloat;
 *         vec2 someVec2;
 *         vec3 someVec3Array[2];
 *         int someInt;
 *       }
 *
 *  You can set the values of the uniform block with
 *
 *       twgl.setBlockUniforms(someBlockInfo, {
 *          someFloat: 12.3,
 *          someVec2: [1, 2],
 *          someVec3Array: [1, 2, 3, 4, 5, 6],
 *          someInt: 5,
 *       }
 *
 *  Arrays can be JavaScript arrays or typed arrays
 *
 *  Any name that doesn't match will be ignored
 * @memberOf module:twgl/programs
 */


function setBlockUniforms(uniformBlockInfo, values) {
  var uniforms = uniformBlockInfo.uniforms;

  for (var name in values) {
    var array = uniforms[name];

    if (array) {
      var value = values[name];

      if (value.length) {
        array.set(value);
      } else {
        array[0] = value;
      }
    }
  }
}
/**
 * Set uniforms and binds related textures.
 *
 * example:
 *
 *     const programInfo = createProgramInfo(
 *         gl, ["some-vs", "some-fs"]);
 *
 *     const tex1 = gl.createTexture();
 *     const tex2 = gl.createTexture();
 *
 *     ... assume we setup the textures with data ...
 *
 *     const uniforms = {
 *       u_someSampler: tex1,
 *       u_someOtherSampler: tex2,
 *       u_someColor: [1,0,0,1],
 *       u_somePosition: [0,1,1],
 *       u_someMatrix: [
 *         1,0,0,0,
 *         0,1,0,0,
 *         0,0,1,0,
 *         0,0,0,0,
 *       ],
 *     };
 *
 *     gl.useProgram(program);
 *
 * This will automatically bind the textures AND set the
 * uniforms.
 *
 *     twgl.setUniforms(programInfo, uniforms);
 *
 * For the example above it is equivalent to
 *
 *     var texUnit = 0;
 *     gl.activeTexture(gl.TEXTURE0 + texUnit);
 *     gl.bindTexture(gl.TEXTURE_2D, tex1);
 *     gl.uniform1i(u_someSamplerLocation, texUnit++);
 *     gl.activeTexture(gl.TEXTURE0 + texUnit);
 *     gl.bindTexture(gl.TEXTURE_2D, tex2);
 *     gl.uniform1i(u_someSamplerLocation, texUnit++);
 *     gl.uniform4fv(u_someColorLocation, [1, 0, 0, 1]);
 *     gl.uniform3fv(u_somePositionLocation, [0, 1, 1]);
 *     gl.uniformMatrix4fv(u_someMatrix, false, [
 *         1,0,0,0,
 *         0,1,0,0,
 *         0,0,1,0,
 *         0,0,0,0,
 *       ]);
 *
 * Note it is perfectly reasonable to call `setUniforms` multiple times. For example
 *
 *     const uniforms = {
 *       u_someSampler: tex1,
 *       u_someOtherSampler: tex2,
 *     };
 *
 *     const moreUniforms {
 *       u_someColor: [1,0,0,1],
 *       u_somePosition: [0,1,1],
 *       u_someMatrix: [
 *         1,0,0,0,
 *         0,1,0,0,
 *         0,0,1,0,
 *         0,0,0,0,
 *       ],
 *     };
 *
 *     twgl.setUniforms(programInfo, uniforms);
 *     twgl.setUniforms(programInfo, moreUniforms);
 *
 * You can also add WebGLSamplers to uniform samplers as in
 *
 *     const uniforms = {
 *       u_someSampler: {
 *         texture: someWebGLTexture,
 *         sampler: someWebGLSampler,
 *       },
 *     };
 *
 * In which case both the sampler and texture will be bound to the
 * same unit.
 *
 * @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters a `ProgramInfo` as returned from `createProgramInfo` or the setters returned from
 *        `createUniformSetters`.
 * @param {Object.<string, ?>} values an object with values for the
 *        uniforms.
 *   You can pass multiple objects by putting them in an array or by calling with more arguments.For example
 *
 *     const sharedUniforms = {
 *       u_fogNear: 10,
 *       u_projection: ...
 *       ...
 *     };
 *
 *     const localUniforms = {
 *       u_world: ...
 *       u_diffuseColor: ...
 *     };
 *
 *     twgl.setUniforms(programInfo, sharedUniforms, localUniforms);
 *
 *     // is the same as
 *
 *     twgl.setUniforms(programInfo, [sharedUniforms, localUniforms]);
 *
 *     // is the same as
 *
 *     twgl.setUniforms(programInfo, sharedUniforms);
 *     twgl.setUniforms(programInfo, localUniforms};
 *
 * @memberOf module:twgl/programs
 */


function setUniforms(setters, values) {
  // eslint-disable-line
  var actualSetters = setters.uniformSetters || setters;
  var numArgs = arguments.length;

  for (var andx = 1; andx < numArgs; ++andx) {
    var vals = arguments[andx];

    if (Array.isArray(vals)) {
      var numValues = vals.length;

      for (var ii = 0; ii < numValues; ++ii) {
        setUniforms(actualSetters, vals[ii]);
      }
    } else {
      for (var name in vals) {
        var setter = actualSetters[name];

        if (setter) {
          setter(vals[name]);
        }
      }
    }
  }
}
/**
 * Creates setter functions for all attributes of a shader
 * program. You can pass this to {@link module:twgl.setBuffersAndAttributes} to set all your buffers and attributes.
 *
 * @see {@link module:twgl.setAttributes} for example
 * @param {WebGLProgram} program the program to create setters for.
 * @return {Object.<string, function>} an object with a setter for each attribute by name.
 * @memberOf module:twgl/programs
 */


function createAttributeSetters(gl, program) {
  var attribSetters = {};
  var numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);

  for (var ii = 0; ii < numAttribs; ++ii) {
    var attribInfo = gl.getActiveAttrib(program, ii);

    if (isBuiltIn(attribInfo)) {
      continue;
    }

    var index = gl.getAttribLocation(program, attribInfo.name);
    var typeInfo = attrTypeMap[attribInfo.type];
    var setter = typeInfo.setter(gl, index, typeInfo);
    setter.location = index;
    attribSetters[attribInfo.name] = setter;
  }

  return attribSetters;
}
/**
 * Sets attributes and binds buffers (deprecated... use {@link module:twgl.setBuffersAndAttributes})
 *
 * Example:
 *
 *     const program = createProgramFromScripts(
 *         gl, ["some-vs", "some-fs");
 *
 *     const attribSetters = createAttributeSetters(program);
 *
 *     const positionBuffer = gl.createBuffer();
 *     const texcoordBuffer = gl.createBuffer();
 *
 *     const attribs = {
 *       a_position: {buffer: positionBuffer, numComponents: 3},
 *       a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
 *     };
 *
 *     gl.useProgram(program);
 *
 * This will automatically bind the buffers AND set the
 * attributes.
 *
 *     setAttributes(attribSetters, attribs);
 *
 * Properties of attribs. For each attrib you can add
 * properties:
 *
 * *   type: the type of data in the buffer. Default = gl.FLOAT
 * *   normalize: whether or not to normalize the data. Default = false
 * *   stride: the stride. Default = 0
 * *   offset: offset into the buffer. Default = 0
 * *   divisor: the divisor for instances. Default = undefined
 *
 * For example if you had 3 value float positions, 2 value
 * float texcoord and 4 value uint8 colors you'd setup your
 * attribs like this
 *
 *     const attribs = {
 *       a_position: {buffer: positionBuffer, numComponents: 3},
 *       a_texcoord: {buffer: texcoordBuffer, numComponents: 2},
 *       a_color: {
 *         buffer: colorBuffer,
 *         numComponents: 4,
 *         type: gl.UNSIGNED_BYTE,
 *         normalize: true,
 *       },
 *     };
 *
 * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
 * @param {Object.<string, module:twgl.AttribInfo>} buffers AttribInfos mapped by attribute name.
 * @memberOf module:twgl/programs
 * @deprecated use {@link module:twgl.setBuffersAndAttributes}
 */


function setAttributes(setters, buffers) {
  for (var name in buffers) {
    var setter = setters[name];

    if (setter) {
      setter(buffers[name]);
    }
  }
}
/**
 * Sets attributes and buffers including the `ELEMENT_ARRAY_BUFFER` if appropriate
 *
 * Example:
 *
 *     const programInfo = createProgramInfo(
 *         gl, ["some-vs", "some-fs");
 *
 *     const arrays = {
 *       position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
 *       texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
 *     };
 *
 *     const bufferInfo = createBufferInfoFromArrays(gl, arrays);
 *
 *     gl.useProgram(programInfo.program);
 *
 * This will automatically bind the buffers AND set the
 * attributes.
 *
 *     setBuffersAndAttributes(gl, programInfo, bufferInfo);
 *
 * For the example above it is equivilent to
 *
 *     gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
 *     gl.enableVertexAttribArray(a_positionLocation);
 *     gl.vertexAttribPointer(a_positionLocation, 3, gl.FLOAT, false, 0, 0);
 *     gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
 *     gl.enableVertexAttribArray(a_texcoordLocation);
 *     gl.vertexAttribPointer(a_texcoordLocation, 4, gl.FLOAT, false, 0, 0);
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
 * @param {(module:twgl.ProgramInfo|Object.<string, function>)} setters A `ProgramInfo` as returned from {@link module:twgl.createProgrmaInfo} or Attribute setters as returned from {@link module:twgl.createAttributeSetters}
 * @param {(module:twgl.BufferInfo|module:twgl.vertexArrayInfo)} buffers a `BufferInfo` as returned from {@link module:twgl.createBufferInfoFromArrays}.
 *   or a `VertexArrayInfo` as returned from {@link module:twgl.createVertexArrayInfo}
 * @memberOf module:twgl/programs
 */


function setBuffersAndAttributes(gl, programInfo, buffers) {
  if (buffers.vertexArrayObject) {
    gl.bindVertexArray(buffers.vertexArrayObject);
  } else {
    setAttributes(programInfo.attribSetters || programInfo, buffers.attribs);

    if (buffers.indices) {
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
    }
  }
}
/**
 * @typedef {Object} ProgramInfo
 * @property {WebGLProgram} program A shader program
 * @property {Object<string, function>} uniformSetters object of setters as returned from createUniformSetters,
 * @property {Object<string, function>} attribSetters object of setters as returned from createAttribSetters,
 * @propetty {module:twgl.UniformBlockSpec} [uniformBlockSpace] a uniform block spec for making UniformBlockInfos with createUniformBlockInfo etc..
 * @property {Object<string, module:twgl.TransformFeedbackInfo>} [transformFeedbackInfo] info for transform feedbacks
 * @memberOf module:twgl
 */

/**
 * Creates a ProgramInfo from an existing program.
 *
 * A ProgramInfo contains
 *
 *     programInfo = {
 *        program: WebGLProgram,
 *        uniformSetters: object of setters as returned from createUniformSetters,
 *        attribSetters: object of setters as returned from createAttribSetters,
 *     }
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext
 *        to use.
 * @param {WebGLProgram} program an existing WebGLProgram.
 * @return {module:twgl.ProgramInfo} The created ProgramInfo.
 * @memberOf module:twgl/programs
 */


function createProgramInfoFromProgram(gl, program) {
  var uniformSetters = createUniformSetters(gl, program);
  var attribSetters = createAttributeSetters(gl, program);
  var programInfo = {
    program: program,
    uniformSetters: uniformSetters,
    attribSetters: attribSetters
  };

  if (utils.isWebGL2(gl)) {
    programInfo.uniformBlockSpec = createUniformBlockSpecFromProgram(gl, program);
    programInfo.transformFeedbackInfo = createTransformFeedbackInfo(gl, program);
  }

  return programInfo;
}
/**
 * Creates a ProgramInfo from 2 sources.
 *
 * A ProgramInfo contains
 *
 *     programInfo = {
 *        program: WebGLProgram,
 *        uniformSetters: object of setters as returned from createUniformSetters,
 *        attribSetters: object of setters as returned from createAttribSetters,
 *     }
 *
 * NOTE: There are 4 signatures for this function
 *
 *     twgl.createProgramInfo(gl, [vs, fs], options);
 *     twgl.createProgramInfo(gl, [vs, fs], opt_errFunc);
 *     twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_errFunc);
 *     twgl.createProgramInfo(gl, [vs, fs], opt_attribs, opt_locations, opt_errFunc);
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext
 *        to use.
 * @param {string[]} shaderSources Array of sources for the
 *        shaders or ids. The first is assumed to be the vertex shader,
 *        the second the fragment shader.
 * @param {module:twgl.ProgramOptions|string[]} [opt_attribs] Options for the program or an array of attribs names. Locations will be assigned by index if not passed in
 * @param {number[]} [opt_locations] The locations for the attributes. A parallel array to opt_attribs letting you assign locations.
 * @param {module:twgl.ErrorCallback} opt_errorCallback callback for errors. By default it just prints an error to the console
 *        on error. If you want something else pass an callback. It's passed an error message.
 * @return {module:twgl.ProgramInfo?} The created ProgramInfo or null if it failed to link or compile
 * @memberOf module:twgl/programs
 */


function createProgramInfo(gl, shaderSources, opt_attribs, opt_locations, opt_errorCallback) {
  var progOptions = getProgramOptions(opt_attribs, opt_locations, opt_errorCallback);
  var good = true;
  shaderSources = shaderSources.map(function (source) {
    // Lets assume if there is no \n it's an id
    if (source.indexOf("\n") < 0) {
      var script = getElementById(source);

      if (!script) {
        progOptions.errorCallback("no element with id: " + source);
        good = false;
      } else {
        source = script.text;
      }
    }

    return source;
  });

  if (!good) {
    return null;
  }

  var program = createProgramFromSources(gl, shaderSources, progOptions);

  if (!program) {
    return null;
  }

  return createProgramInfoFromProgram(gl, program);
}

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

"use strict";


exports.__esModule = true;
exports.axisRotate = axisRotate;
exports.axisRotation = axisRotation;
exports.copy = copy;
exports.frustum = frustum;
exports.getAxis = getAxis;
exports.getTranslation = getTranslation;
exports.identity = identity;
exports.inverse = inverse;
exports.lookAt = lookAt;
exports.multiply = multiply;
exports.negate = negate;
exports.ortho = ortho;
exports.perspective = perspective;
exports.rotateX = rotateX;
exports.rotateY = rotateY;
exports.rotateZ = rotateZ;
exports.rotationX = rotationX;
exports.rotationY = rotationY;
exports.rotationZ = rotationZ;
exports.scale = scale;
exports.scaling = scaling;
exports.setAxis = setAxis;
exports.setDefaultType = setDefaultType;
exports.setTranslation = setTranslation;
exports.transformDirection = transformDirection;
exports.transformNormal = transformNormal;
exports.transformPoint = transformPoint;
exports.translate = translate;
exports.translation = translation;
exports.transpose = transpose;

var v3 = _interopRequireWildcard(__webpack_require__(3));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * 4x4 Matrix math math functions.
 *
 * Almost all functions take an optional `dst` argument. If it is not passed in the
 * functions will create a new matrix. In other words you can do this
 *
 *     const mat = m4.translation([1, 2, 3]);  // Creates a new translation matrix
 *
 * or
 *
 *     const mat = m4.create();
 *     m4.translation([1, 2, 3], mat);  // Puts translation matrix in mat.
 *
 * The first style is often easier but depending on where it's used it generates garbage where
 * as there is almost never allocation with the second style.
 *
 * It is always save to pass any matrix as the destination. So for example
 *
 *     const mat = m4.identity();
 *     const trans = m4.translation([1, 2, 3]);
 *     m4.multiply(mat, trans, mat);  // Multiplies mat * trans and puts result in mat.
 *
 * @module twgl/m4
 */
var MatType = Float32Array;
var tempV3a = v3.create();
var tempV3b = v3.create();
var tempV3c = v3.create();
/**
 * A JavaScript array with 16 values or a Float32Array with 16 values.
 * When created by the library will create the default type which is `Float32Array`
 * but can be set by calling {@link module:twgl/m4.setDefaultType}.
 * @typedef {(number[]|Float32Array)} Mat4
 * @memberOf module:twgl/m4
 */

/**
 * Sets the type this library creates for a Mat4
 * @param {constructor} ctor the constructor for the type. Either `Float32Array` or `Array`
 * @return {constructor} previous constructor for Mat4
 */

function setDefaultType(ctor) {
  var oldType = MatType;
  MatType = ctor;
  return oldType;
}
/**
 * Negates a matrix.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} -m.
 * @memberOf module:twgl/m4
 */


function negate(m, dst) {
  dst = dst || new MatType(16);
  dst[0] = -m[0];
  dst[1] = -m[1];
  dst[2] = -m[2];
  dst[3] = -m[3];
  dst[4] = -m[4];
  dst[5] = -m[5];
  dst[6] = -m[6];
  dst[7] = -m[7];
  dst[8] = -m[8];
  dst[9] = -m[9];
  dst[10] = -m[10];
  dst[11] = -m[11];
  dst[12] = -m[12];
  dst[13] = -m[13];
  dst[14] = -m[14];
  dst[15] = -m[15];
  return dst;
}
/**
 * Copies a matrix.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {module:twgl/m4.Mat4} [dst] The matrix.
 * @return {module:twgl/m4.Mat4} A copy of m.
 * @memberOf module:twgl/m4
 */


function copy(m, dst) {
  dst = dst || new MatType(16);
  dst[0] = m[0];
  dst[1] = m[1];
  dst[2] = m[2];
  dst[3] = m[3];
  dst[4] = m[4];
  dst[5] = m[5];
  dst[6] = m[6];
  dst[7] = m[7];
  dst[8] = m[8];
  dst[9] = m[9];
  dst[10] = m[10];
  dst[11] = m[11];
  dst[12] = m[12];
  dst[13] = m[13];
  dst[14] = m[14];
  dst[15] = m[15];
  return dst;
}
/**
 * Creates an n-by-n identity matrix.
 *
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} An n-by-n identity matrix.
 * @memberOf module:twgl/m4
 */


function identity(dst) {
  dst = dst || new MatType(16);
  dst[0] = 1;
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = 1;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = 1;
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Takes the transpose of a matrix.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The transpose of m.
 * @memberOf module:twgl/m4
 */


function transpose(m, dst) {
  dst = dst || new MatType(16);

  if (dst === m) {
    var t;
    t = m[1];
    m[1] = m[4];
    m[4] = t;
    t = m[2];
    m[2] = m[8];
    m[8] = t;
    t = m[3];
    m[3] = m[12];
    m[12] = t;
    t = m[6];
    m[6] = m[9];
    m[9] = t;
    t = m[7];
    m[7] = m[13];
    m[13] = t;
    t = m[11];
    m[11] = m[14];
    m[14] = t;
    return dst;
  }

  var m00 = m[0 * 4 + 0];
  var m01 = m[0 * 4 + 1];
  var m02 = m[0 * 4 + 2];
  var m03 = m[0 * 4 + 3];
  var m10 = m[1 * 4 + 0];
  var m11 = m[1 * 4 + 1];
  var m12 = m[1 * 4 + 2];
  var m13 = m[1 * 4 + 3];
  var m20 = m[2 * 4 + 0];
  var m21 = m[2 * 4 + 1];
  var m22 = m[2 * 4 + 2];
  var m23 = m[2 * 4 + 3];
  var m30 = m[3 * 4 + 0];
  var m31 = m[3 * 4 + 1];
  var m32 = m[3 * 4 + 2];
  var m33 = m[3 * 4 + 3];
  dst[0] = m00;
  dst[1] = m10;
  dst[2] = m20;
  dst[3] = m30;
  dst[4] = m01;
  dst[5] = m11;
  dst[6] = m21;
  dst[7] = m31;
  dst[8] = m02;
  dst[9] = m12;
  dst[10] = m22;
  dst[11] = m32;
  dst[12] = m03;
  dst[13] = m13;
  dst[14] = m23;
  dst[15] = m33;
  return dst;
}
/**
 * Computes the inverse of a 4-by-4 matrix.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The inverse of m.
 * @memberOf module:twgl/m4
 */


function inverse(m, dst) {
  dst = dst || new MatType(16);
  var m00 = m[0 * 4 + 0];
  var m01 = m[0 * 4 + 1];
  var m02 = m[0 * 4 + 2];
  var m03 = m[0 * 4 + 3];
  var m10 = m[1 * 4 + 0];
  var m11 = m[1 * 4 + 1];
  var m12 = m[1 * 4 + 2];
  var m13 = m[1 * 4 + 3];
  var m20 = m[2 * 4 + 0];
  var m21 = m[2 * 4 + 1];
  var m22 = m[2 * 4 + 2];
  var m23 = m[2 * 4 + 3];
  var m30 = m[3 * 4 + 0];
  var m31 = m[3 * 4 + 1];
  var m32 = m[3 * 4 + 2];
  var m33 = m[3 * 4 + 3];
  var tmp_0 = m22 * m33;
  var tmp_1 = m32 * m23;
  var tmp_2 = m12 * m33;
  var tmp_3 = m32 * m13;
  var tmp_4 = m12 * m23;
  var tmp_5 = m22 * m13;
  var tmp_6 = m02 * m33;
  var tmp_7 = m32 * m03;
  var tmp_8 = m02 * m23;
  var tmp_9 = m22 * m03;
  var tmp_10 = m02 * m13;
  var tmp_11 = m12 * m03;
  var tmp_12 = m20 * m31;
  var tmp_13 = m30 * m21;
  var tmp_14 = m10 * m31;
  var tmp_15 = m30 * m11;
  var tmp_16 = m10 * m21;
  var tmp_17 = m20 * m11;
  var tmp_18 = m00 * m31;
  var tmp_19 = m30 * m01;
  var tmp_20 = m00 * m21;
  var tmp_21 = m20 * m01;
  var tmp_22 = m00 * m11;
  var tmp_23 = m10 * m01;
  var t0 = tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31 - (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
  var t1 = tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31 - (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
  var t2 = tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31 - (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
  var t3 = tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21 - (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);
  var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);
  dst[0] = d * t0;
  dst[1] = d * t1;
  dst[2] = d * t2;
  dst[3] = d * t3;
  dst[4] = d * (tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30 - (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30));
  dst[5] = d * (tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30 - (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30));
  dst[6] = d * (tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30 - (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30));
  dst[7] = d * (tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20 - (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20));
  dst[8] = d * (tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33 - (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33));
  dst[9] = d * (tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33 - (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33));
  dst[10] = d * (tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33 - (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33));
  dst[11] = d * (tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23 - (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23));
  dst[12] = d * (tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12 - (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22));
  dst[13] = d * (tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22 - (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02));
  dst[14] = d * (tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02 - (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12));
  dst[15] = d * (tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12 - (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02));
  return dst;
}
/**
 * Multiplies two 4-by-4 matrices with a on the left and b on the right
 * @param {module:twgl/m4.Mat4} a The matrix on the left.
 * @param {module:twgl/m4.Mat4} b The matrix on the right.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The matrix product of a and b.
 * @memberOf module:twgl/m4
 */


function multiply(a, b, dst) {
  dst = dst || new MatType(16);
  var a00 = a[0];
  var a01 = a[1];
  var a02 = a[2];
  var a03 = a[3];
  var a10 = a[4 + 0];
  var a11 = a[4 + 1];
  var a12 = a[4 + 2];
  var a13 = a[4 + 3];
  var a20 = a[8 + 0];
  var a21 = a[8 + 1];
  var a22 = a[8 + 2];
  var a23 = a[8 + 3];
  var a30 = a[12 + 0];
  var a31 = a[12 + 1];
  var a32 = a[12 + 2];
  var a33 = a[12 + 3];
  var b00 = b[0];
  var b01 = b[1];
  var b02 = b[2];
  var b03 = b[3];
  var b10 = b[4 + 0];
  var b11 = b[4 + 1];
  var b12 = b[4 + 2];
  var b13 = b[4 + 3];
  var b20 = b[8 + 0];
  var b21 = b[8 + 1];
  var b22 = b[8 + 2];
  var b23 = b[8 + 3];
  var b30 = b[12 + 0];
  var b31 = b[12 + 1];
  var b32 = b[12 + 2];
  var b33 = b[12 + 3];
  dst[0] = a00 * b00 + a10 * b01 + a20 * b02 + a30 * b03;
  dst[1] = a01 * b00 + a11 * b01 + a21 * b02 + a31 * b03;
  dst[2] = a02 * b00 + a12 * b01 + a22 * b02 + a32 * b03;
  dst[3] = a03 * b00 + a13 * b01 + a23 * b02 + a33 * b03;
  dst[4] = a00 * b10 + a10 * b11 + a20 * b12 + a30 * b13;
  dst[5] = a01 * b10 + a11 * b11 + a21 * b12 + a31 * b13;
  dst[6] = a02 * b10 + a12 * b11 + a22 * b12 + a32 * b13;
  dst[7] = a03 * b10 + a13 * b11 + a23 * b12 + a33 * b13;
  dst[8] = a00 * b20 + a10 * b21 + a20 * b22 + a30 * b23;
  dst[9] = a01 * b20 + a11 * b21 + a21 * b22 + a31 * b23;
  dst[10] = a02 * b20 + a12 * b21 + a22 * b22 + a32 * b23;
  dst[11] = a03 * b20 + a13 * b21 + a23 * b22 + a33 * b23;
  dst[12] = a00 * b30 + a10 * b31 + a20 * b32 + a30 * b33;
  dst[13] = a01 * b30 + a11 * b31 + a21 * b32 + a31 * b33;
  dst[14] = a02 * b30 + a12 * b31 + a22 * b32 + a32 * b33;
  dst[15] = a03 * b30 + a13 * b31 + a23 * b32 + a33 * b33;
  return dst;
}
/**
 * Sets the translation component of a 4-by-4 matrix to the given
 * vector.
 * @param {module:twgl/m4.Mat4} a The matrix.
 * @param {Vec3} v The vector.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} a once modified.
 * @memberOf module:twgl/m4
 */


function setTranslation(a, v, dst) {
  dst = dst || identity();

  if (a !== dst) {
    dst[0] = a[0];
    dst[1] = a[1];
    dst[2] = a[2];
    dst[3] = a[3];
    dst[4] = a[4];
    dst[5] = a[5];
    dst[6] = a[6];
    dst[7] = a[7];
    dst[8] = a[8];
    dst[9] = a[9];
    dst[10] = a[10];
    dst[11] = a[11];
  }

  dst[12] = v[0];
  dst[13] = v[1];
  dst[14] = v[2];
  dst[15] = 1;
  return dst;
}
/**
 * Returns the translation component of a 4-by-4 matrix as a vector with 3
 * entries.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} [dst] vector..
 * @return {Vec3} The translation component of m.
 * @memberOf module:twgl/m4
 */


function getTranslation(m, dst) {
  dst = dst || v3.create();
  dst[0] = m[12];
  dst[1] = m[13];
  dst[2] = m[14];
  return dst;
}
/**
 * Returns an axis of a 4x4 matrix as a vector with 3 entries
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {number} axis The axis 0 = x, 1 = y, 2 = z;
 * @return {Vec3} [dst] vector.
 * @return {Vec3} The axis component of m.
 * @memberOf module:twgl/m4
 */


function getAxis(m, axis, dst) {
  dst = dst || v3.create();
  var off = axis * 4;
  dst[0] = m[off + 0];
  dst[1] = m[off + 1];
  dst[2] = m[off + 2];
  return dst;
}
/**
 * Sets an axis of a 4x4 matrix as a vector with 3 entries
 * @param {Vec3} v the axis vector
 * @param {number} axis The axis  0 = x, 1 = y, 2 = z;
 * @param {module:twgl/m4.Mat4} [dst] The matrix to set. If none a new one is created
 * @return {module:twgl/m4.Mat4} dst
 * @memberOf module:twgl/m4
 */


function setAxis(a, v, axis, dst) {
  if (dst !== a) {
    dst = copy(a, dst);
  }

  var off = axis * 4;
  dst[off + 0] = v[0];
  dst[off + 1] = v[1];
  dst[off + 2] = v[2];
  return dst;
}
/**
 * Computes a 4-by-4 perspective transformation matrix given the angular height
 * of the frustum, the aspect ratio, and the near and far clipping planes.  The
 * arguments define a frustum extending in the negative z direction.  The given
 * angle is the vertical angle of the frustum, and the horizontal angle is
 * determined to produce the given aspect ratio.  The arguments near and far are
 * the distances to the near and far clipping planes.  Note that near and far
 * are not z coordinates, but rather they are distances along the negative
 * z-axis.  The matrix generated sends the viewing frustum to the unit box.
 * We assume a unit box extending from -1 to 1 in the x and y dimensions and
 * from 0 to 1 in the z dimension.
 * @param {number} fieldOfViewYInRadians The camera angle from top to bottom (in radians).
 * @param {number} aspect The aspect ratio width / height.
 * @param {number} zNear The depth (negative z coordinate)
 *     of the near clipping plane.
 * @param {number} zFar The depth (negative z coordinate)
 *     of the far clipping plane.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The perspective matrix.
 * @memberOf module:twgl/m4
 */


function perspective(fieldOfViewYInRadians, aspect, zNear, zFar, dst) {
  dst = dst || new MatType(16);
  var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewYInRadians);
  var rangeInv = 1.0 / (zNear - zFar);
  dst[0] = f / aspect;
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = f;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = (zNear + zFar) * rangeInv;
  dst[11] = -1;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = zNear * zFar * rangeInv * 2;
  dst[15] = 0;
  return dst;
}
/**
 * Computes a 4-by-4 othogonal transformation matrix given the left, right,
 * bottom, and top dimensions of the near clipping plane as well as the
 * near and far clipping plane distances.
 * @param {number} left Left side of the near clipping plane viewport.
 * @param {number} right Right side of the near clipping plane viewport.
 * @param {number} top Top of the near clipping plane viewport.
 * @param {number} bottom Bottom of the near clipping plane viewport.
 * @param {number} near The depth (negative z coordinate)
 *     of the near clipping plane.
 * @param {number} far The depth (negative z coordinate)
 *     of the far clipping plane.
 * @param {module:twgl/m4.Mat4} [dst] Output matrix.
 * @return {module:twgl/m4.Mat4} The perspective matrix.
 * @memberOf module:twgl/m4
 */


function ortho(left, right, bottom, top, near, far, dst) {
  dst = dst || new MatType(16);
  dst[0] = 2 / (right - left);
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = 2 / (top - bottom);
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = 2 / (near - far);
  dst[11] = 0;
  dst[12] = (right + left) / (left - right);
  dst[13] = (top + bottom) / (bottom - top);
  dst[14] = (far + near) / (near - far);
  dst[15] = 1;
  return dst;
}
/**
 * Computes a 4-by-4 perspective transformation matrix given the left, right,
 * top, bottom, near and far clipping planes. The arguments define a frustum
 * extending in the negative z direction. The arguments near and far are the
 * distances to the near and far clipping planes. Note that near and far are not
 * z coordinates, but rather they are distances along the negative z-axis. The
 * matrix generated sends the viewing frustum to the unit box. We assume a unit
 * box extending from -1 to 1 in the x and y dimensions and from 0 to 1 in the z
 * dimension.
 * @param {number} left The x coordinate of the left plane of the box.
 * @param {number} right The x coordinate of the right plane of the box.
 * @param {number} bottom The y coordinate of the bottom plane of the box.
 * @param {number} top The y coordinate of the right plane of the box.
 * @param {number} near The negative z coordinate of the near plane of the box.
 * @param {number} far The negative z coordinate of the far plane of the box.
 * @param {module:twgl/m4.Mat4} [dst] Output matrix.
 * @return {module:twgl/m4.Mat4} The perspective projection matrix.
 * @memberOf module:twgl/m4
 */


function frustum(left, right, bottom, top, near, far, dst) {
  dst = dst || new MatType(16);
  var dx = right - left;
  var dy = top - bottom;
  var dz = near - far;
  dst[0] = 2 * near / dx;
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = 2 * near / dy;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = (left + right) / dx;
  dst[9] = (top + bottom) / dy;
  dst[10] = far / dz;
  dst[11] = -1;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = near * far / dz;
  dst[15] = 0;
  return dst;
}
/**
 * Computes a 4-by-4 look-at transformation.
 *
 * This is a matrix which positions the camera itself. If you want
 * a view matrix (a matrix which moves things in front of the camera)
 * take the inverse of this.
 *
 * @param {Vec3} eye The position of the eye.
 * @param {Vec3} target The position meant to be viewed.
 * @param {Vec3} up A vector pointing up.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The look-at matrix.
 * @memberOf module:twgl/m4
 */


function lookAt(eye, target, up, dst) {
  dst = dst || new MatType(16);
  var xAxis = tempV3a;
  var yAxis = tempV3b;
  var zAxis = tempV3c;
  v3.normalize(v3.subtract(eye, target, zAxis), zAxis);
  v3.normalize(v3.cross(up, zAxis, xAxis), xAxis);
  v3.normalize(v3.cross(zAxis, xAxis, yAxis), yAxis);
  dst[0] = xAxis[0];
  dst[1] = xAxis[1];
  dst[2] = xAxis[2];
  dst[3] = 0;
  dst[4] = yAxis[0];
  dst[5] = yAxis[1];
  dst[6] = yAxis[2];
  dst[7] = 0;
  dst[8] = zAxis[0];
  dst[9] = zAxis[1];
  dst[10] = zAxis[2];
  dst[11] = 0;
  dst[12] = eye[0];
  dst[13] = eye[1];
  dst[14] = eye[2];
  dst[15] = 1;
  return dst;
}
/**
 * Creates a 4-by-4 matrix which translates by the given vector v.
 * @param {Vec3} v The vector by
 *     which to translate.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The translation matrix.
 * @memberOf module:twgl/m4
 */


function translation(v, dst) {
  dst = dst || new MatType(16);
  dst[0] = 1;
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = 1;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = 1;
  dst[11] = 0;
  dst[12] = v[0];
  dst[13] = v[1];
  dst[14] = v[2];
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix by translation by the given vector v.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} v The vector by
 *     which to translate.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function translate(m, v, dst) {
  dst = dst || new MatType(16);
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  var m00 = m[0];
  var m01 = m[1];
  var m02 = m[2];
  var m03 = m[3];
  var m10 = m[1 * 4 + 0];
  var m11 = m[1 * 4 + 1];
  var m12 = m[1 * 4 + 2];
  var m13 = m[1 * 4 + 3];
  var m20 = m[2 * 4 + 0];
  var m21 = m[2 * 4 + 1];
  var m22 = m[2 * 4 + 2];
  var m23 = m[2 * 4 + 3];
  var m30 = m[3 * 4 + 0];
  var m31 = m[3 * 4 + 1];
  var m32 = m[3 * 4 + 2];
  var m33 = m[3 * 4 + 3];

  if (m !== dst) {
    dst[0] = m00;
    dst[1] = m01;
    dst[2] = m02;
    dst[3] = m03;
    dst[4] = m10;
    dst[5] = m11;
    dst[6] = m12;
    dst[7] = m13;
    dst[8] = m20;
    dst[9] = m21;
    dst[10] = m22;
    dst[11] = m23;
  }

  dst[12] = m00 * v0 + m10 * v1 + m20 * v2 + m30;
  dst[13] = m01 * v0 + m11 * v1 + m21 * v2 + m31;
  dst[14] = m02 * v0 + m12 * v1 + m22 * v2 + m32;
  dst[15] = m03 * v0 + m13 * v1 + m23 * v2 + m33;
  return dst;
}
/**
 * Creates a 4-by-4 matrix which rotates around the x-axis by the given angle.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The rotation matrix.
 * @memberOf module:twgl/m4
 */


function rotationX(angleInRadians, dst) {
  dst = dst || new MatType(16);
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[0] = 1;
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = c;
  dst[6] = s;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = -s;
  dst[10] = c;
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix by a rotation around the x-axis by the given
 * angle.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function rotateX(m, angleInRadians, dst) {
  dst = dst || new MatType(16);
  var m10 = m[4];
  var m11 = m[5];
  var m12 = m[6];
  var m13 = m[7];
  var m20 = m[8];
  var m21 = m[9];
  var m22 = m[10];
  var m23 = m[11];
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[4] = c * m10 + s * m20;
  dst[5] = c * m11 + s * m21;
  dst[6] = c * m12 + s * m22;
  dst[7] = c * m13 + s * m23;
  dst[8] = c * m20 - s * m10;
  dst[9] = c * m21 - s * m11;
  dst[10] = c * m22 - s * m12;
  dst[11] = c * m23 - s * m13;

  if (m !== dst) {
    dst[0] = m[0];
    dst[1] = m[1];
    dst[2] = m[2];
    dst[3] = m[3];
    dst[12] = m[12];
    dst[13] = m[13];
    dst[14] = m[14];
    dst[15] = m[15];
  }

  return dst;
}
/**
 * Creates a 4-by-4 matrix which rotates around the y-axis by the given angle.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The rotation matrix.
 * @memberOf module:twgl/m4
 */


function rotationY(angleInRadians, dst) {
  dst = dst || new MatType(16);
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[0] = c;
  dst[1] = 0;
  dst[2] = -s;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = 1;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = s;
  dst[9] = 0;
  dst[10] = c;
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix by a rotation around the y-axis by the given
 * angle.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function rotateY(m, angleInRadians, dst) {
  dst = dst || new MatType(16);
  var m00 = m[0 * 4 + 0];
  var m01 = m[0 * 4 + 1];
  var m02 = m[0 * 4 + 2];
  var m03 = m[0 * 4 + 3];
  var m20 = m[2 * 4 + 0];
  var m21 = m[2 * 4 + 1];
  var m22 = m[2 * 4 + 2];
  var m23 = m[2 * 4 + 3];
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[0] = c * m00 - s * m20;
  dst[1] = c * m01 - s * m21;
  dst[2] = c * m02 - s * m22;
  dst[3] = c * m03 - s * m23;
  dst[8] = c * m20 + s * m00;
  dst[9] = c * m21 + s * m01;
  dst[10] = c * m22 + s * m02;
  dst[11] = c * m23 + s * m03;

  if (m !== dst) {
    dst[4] = m[4];
    dst[5] = m[5];
    dst[6] = m[6];
    dst[7] = m[7];
    dst[12] = m[12];
    dst[13] = m[13];
    dst[14] = m[14];
    dst[15] = m[15];
  }

  return dst;
}
/**
 * Creates a 4-by-4 matrix which rotates around the z-axis by the given angle.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The rotation matrix.
 * @memberOf module:twgl/m4
 */


function rotationZ(angleInRadians, dst) {
  dst = dst || new MatType(16);
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[0] = c;
  dst[1] = s;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = -s;
  dst[5] = c;
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = 1;
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix by a rotation around the z-axis by the given
 * angle.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function rotateZ(m, angleInRadians, dst) {
  dst = dst || new MatType(16);
  var m00 = m[0 * 4 + 0];
  var m01 = m[0 * 4 + 1];
  var m02 = m[0 * 4 + 2];
  var m03 = m[0 * 4 + 3];
  var m10 = m[1 * 4 + 0];
  var m11 = m[1 * 4 + 1];
  var m12 = m[1 * 4 + 2];
  var m13 = m[1 * 4 + 3];
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  dst[0] = c * m00 + s * m10;
  dst[1] = c * m01 + s * m11;
  dst[2] = c * m02 + s * m12;
  dst[3] = c * m03 + s * m13;
  dst[4] = c * m10 - s * m00;
  dst[5] = c * m11 - s * m01;
  dst[6] = c * m12 - s * m02;
  dst[7] = c * m13 - s * m03;

  if (m !== dst) {
    dst[8] = m[8];
    dst[9] = m[9];
    dst[10] = m[10];
    dst[11] = m[11];
    dst[12] = m[12];
    dst[13] = m[13];
    dst[14] = m[14];
    dst[15] = m[15];
  }

  return dst;
}
/**
 * Creates a 4-by-4 matrix which rotates around the given axis by the given
 * angle.
 * @param {Vec3} axis The axis
 *     about which to rotate.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} A matrix which rotates angle radians
 *     around the axis.
 * @memberOf module:twgl/m4
 */


function axisRotation(axis, angleInRadians, dst) {
  dst = dst || new MatType(16);
  var x = axis[0];
  var y = axis[1];
  var z = axis[2];
  var n = Math.sqrt(x * x + y * y + z * z);
  x /= n;
  y /= n;
  z /= n;
  var xx = x * x;
  var yy = y * y;
  var zz = z * z;
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  var oneMinusCosine = 1 - c;
  dst[0] = xx + (1 - xx) * c;
  dst[1] = x * y * oneMinusCosine + z * s;
  dst[2] = x * z * oneMinusCosine - y * s;
  dst[3] = 0;
  dst[4] = x * y * oneMinusCosine - z * s;
  dst[5] = yy + (1 - yy) * c;
  dst[6] = y * z * oneMinusCosine + x * s;
  dst[7] = 0;
  dst[8] = x * z * oneMinusCosine + y * s;
  dst[9] = y * z * oneMinusCosine - x * s;
  dst[10] = zz + (1 - zz) * c;
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix by rotation around the given axis by the
 * given angle.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} axis The axis
 *     about which to rotate.
 * @param {number} angleInRadians The angle by which to rotate (in radians).
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function axisRotate(m, axis, angleInRadians, dst) {
  dst = dst || new MatType(16);
  var x = axis[0];
  var y = axis[1];
  var z = axis[2];
  var n = Math.sqrt(x * x + y * y + z * z);
  x /= n;
  y /= n;
  z /= n;
  var xx = x * x;
  var yy = y * y;
  var zz = z * z;
  var c = Math.cos(angleInRadians);
  var s = Math.sin(angleInRadians);
  var oneMinusCosine = 1 - c;
  var r00 = xx + (1 - xx) * c;
  var r01 = x * y * oneMinusCosine + z * s;
  var r02 = x * z * oneMinusCosine - y * s;
  var r10 = x * y * oneMinusCosine - z * s;
  var r11 = yy + (1 - yy) * c;
  var r12 = y * z * oneMinusCosine + x * s;
  var r20 = x * z * oneMinusCosine + y * s;
  var r21 = y * z * oneMinusCosine - x * s;
  var r22 = zz + (1 - zz) * c;
  var m00 = m[0];
  var m01 = m[1];
  var m02 = m[2];
  var m03 = m[3];
  var m10 = m[4];
  var m11 = m[5];
  var m12 = m[6];
  var m13 = m[7];
  var m20 = m[8];
  var m21 = m[9];
  var m22 = m[10];
  var m23 = m[11];
  dst[0] = r00 * m00 + r01 * m10 + r02 * m20;
  dst[1] = r00 * m01 + r01 * m11 + r02 * m21;
  dst[2] = r00 * m02 + r01 * m12 + r02 * m22;
  dst[3] = r00 * m03 + r01 * m13 + r02 * m23;
  dst[4] = r10 * m00 + r11 * m10 + r12 * m20;
  dst[5] = r10 * m01 + r11 * m11 + r12 * m21;
  dst[6] = r10 * m02 + r11 * m12 + r12 * m22;
  dst[7] = r10 * m03 + r11 * m13 + r12 * m23;
  dst[8] = r20 * m00 + r21 * m10 + r22 * m20;
  dst[9] = r20 * m01 + r21 * m11 + r22 * m21;
  dst[10] = r20 * m02 + r21 * m12 + r22 * m22;
  dst[11] = r20 * m03 + r21 * m13 + r22 * m23;

  if (m !== dst) {
    dst[12] = m[12];
    dst[13] = m[13];
    dst[14] = m[14];
    dst[15] = m[15];
  }

  return dst;
}
/**
 * Creates a 4-by-4 matrix which scales in each dimension by an amount given by
 * the corresponding entry in the given vector; assumes the vector has three
 * entries.
 * @param {Vec3} v A vector of
 *     three entries specifying the factor by which to scale in each dimension.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} The scaling matrix.
 * @memberOf module:twgl/m4
 */


function scaling(v, dst) {
  dst = dst || new MatType(16);
  dst[0] = v[0];
  dst[1] = 0;
  dst[2] = 0;
  dst[3] = 0;
  dst[4] = 0;
  dst[5] = v[1];
  dst[6] = 0;
  dst[7] = 0;
  dst[8] = 0;
  dst[9] = 0;
  dst[10] = v[2];
  dst[11] = 0;
  dst[12] = 0;
  dst[13] = 0;
  dst[14] = 0;
  dst[15] = 1;
  return dst;
}
/**
 * Modifies the given 4-by-4 matrix, scaling in each dimension by an amount
 * given by the corresponding entry in the given vector; assumes the vector has
 * three entries.
 * @param {module:twgl/m4.Mat4} m The matrix to be modified.
 * @param {Vec3} v A vector of three entries specifying the
 *     factor by which to scale in each dimension.
 * @param {module:twgl/m4.Mat4} [dst] matrix to hold result. If none new one is created..
 * @return {module:twgl/m4.Mat4} m once modified.
 * @memberOf module:twgl/m4
 */


function scale(m, v, dst) {
  dst = dst || new MatType(16);
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  dst[0] = v0 * m[0 * 4 + 0];
  dst[1] = v0 * m[0 * 4 + 1];
  dst[2] = v0 * m[0 * 4 + 2];
  dst[3] = v0 * m[0 * 4 + 3];
  dst[4] = v1 * m[1 * 4 + 0];
  dst[5] = v1 * m[1 * 4 + 1];
  dst[6] = v1 * m[1 * 4 + 2];
  dst[7] = v1 * m[1 * 4 + 3];
  dst[8] = v2 * m[2 * 4 + 0];
  dst[9] = v2 * m[2 * 4 + 1];
  dst[10] = v2 * m[2 * 4 + 2];
  dst[11] = v2 * m[2 * 4 + 3];

  if (m !== dst) {
    dst[12] = m[12];
    dst[13] = m[13];
    dst[14] = m[14];
    dst[15] = m[15];
  }

  return dst;
}
/**
 * Takes a 4-by-4 matrix and a vector with 3 entries,
 * interprets the vector as a point, transforms that point by the matrix, and
 * returns the result as a vector with 3 entries.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} v The point.
 * @param {Vec3} dst optional vec3 to store result
 * @return {Vec3} dst or new vec3 if not provided
 * @memberOf module:twgl/m4
 */


function transformPoint(m, v, dst) {
  dst = dst || v3.create();
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  var d = v0 * m[0 * 4 + 3] + v1 * m[1 * 4 + 3] + v2 * m[2 * 4 + 3] + m[3 * 4 + 3];
  dst[0] = (v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0] + m[3 * 4 + 0]) / d;
  dst[1] = (v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1] + m[3 * 4 + 1]) / d;
  dst[2] = (v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2] + m[3 * 4 + 2]) / d;
  return dst;
}
/**
 * Takes a 4-by-4 matrix and a vector with 3 entries, interprets the vector as a
 * direction, transforms that direction by the matrix, and returns the result;
 * assumes the transformation of 3-dimensional space represented by the matrix
 * is parallel-preserving, i.e. any combination of rotation, scaling and
 * translation, but not a perspective distortion. Returns a vector with 3
 * entries.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} v The direction.
 * @param {Vec3} dst optional Vec3 to store result
 * @return {Vec3} dst or new Vec3 if not provided
 * @memberOf module:twgl/m4
 */


function transformDirection(m, v, dst) {
  dst = dst || v3.create();
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  dst[0] = v0 * m[0 * 4 + 0] + v1 * m[1 * 4 + 0] + v2 * m[2 * 4 + 0];
  dst[1] = v0 * m[0 * 4 + 1] + v1 * m[1 * 4 + 1] + v2 * m[2 * 4 + 1];
  dst[2] = v0 * m[0 * 4 + 2] + v1 * m[1 * 4 + 2] + v2 * m[2 * 4 + 2];
  return dst;
}
/**
 * Takes a 4-by-4 matrix m and a vector v with 3 entries, interprets the vector
 * as a normal to a surface, and computes a vector which is normal upon
 * transforming that surface by the matrix. The effect of this function is the
 * same as transforming v (as a direction) by the inverse-transpose of m.  This
 * function assumes the transformation of 3-dimensional space represented by the
 * matrix is parallel-preserving, i.e. any combination of rotation, scaling and
 * translation, but not a perspective distortion.  Returns a vector with 3
 * entries.
 * @param {module:twgl/m4.Mat4} m The matrix.
 * @param {Vec3} v The normal.
 * @param {Vec3} [dst] The direction.
 * @return {Vec3} The transformed direction.
 * @memberOf module:twgl/m4
 */


function transformNormal(m, v, dst) {
  dst = dst || v3.create();
  var mi = inverse(m);
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2];
  dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2];
  dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2];
  return dst;
}

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

"use strict";


exports.__esModule = true;
exports.createAttribsFromArrays = createAttribsFromArrays;
exports.createBuffersFromArrays = createBuffersFromArrays;
exports.createBufferFromArray = createBufferFromArray;
exports.createBufferFromTypedArray = createBufferFromTypedArray;
exports.createBufferInfoFromArrays = createBufferInfoFromArrays;
exports.setAttribInfoBufferFromArray = setAttribInfoBufferFromArray;
exports.setAttributePrefix = setAttributePrefix;
exports.setAttributeDefaults_ = setDefaults;
exports.getNumComponents_ = getNumComponents;
exports.getArray_ = getArray;

var typedArrays = _interopRequireWildcard(__webpack_require__(1));

var helper = _interopRequireWildcard(__webpack_require__(0));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Low level attribute and buffer related functions
 *
 * You should generally not need to use these functions. They are provided
 * for those cases where you're doing something out of the ordinary
 * and you need lower level access.
 *
 * For backward compatibily they are available at both `twgl.attributes` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/attributes
 */
// make sure we don't see a global gl
var gl = undefined; // eslint-disable-line

var defaults = {
  attribPrefix: ""
};
/**
 * Sets the default attrib prefix
 *
 * When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_`
 * as it makes it clear where they came from. But, when building geometry I prefer using unprefixed names.
 *
 * In otherwords I'll create arrays of geometry like this
 *
 *     var arrays = {
 *       position: ...
 *       normal: ...
 *       texcoord: ...
 *     };
 *
 * But need those mapped to attributes and my attributes start with `a_`.
 *
 * @deprecated see {@link module:twgl.setDefaults}
 * @param {string} prefix prefix for attribs
 * @memberOf module:twgl/attributes
 */

function setAttributePrefix(prefix) {
  defaults.attribPrefix = prefix;
}

function setDefaults(newDefaults) {
  helper.copyExistingProperties(newDefaults, defaults);
}

function setBufferFromTypedArray(gl, type, buffer, array, drawType) {
  gl.bindBuffer(type, buffer);
  gl.bufferData(type, array, drawType || gl.STATIC_DRAW);
}
/**
 * Given typed array creates a WebGLBuffer and copies the typed array
 * into it.
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @param {ArrayBuffer|SharedArrayBuffer|ArrayBufferView|WebGLBuffer} typedArray the typed array. Note: If a WebGLBuffer is passed in it will just be returned. No action will be taken
 * @param {number} [type] the GL bind type for the buffer. Default = `gl.ARRAY_BUFFER`.
 * @param {number} [drawType] the GL draw type for the buffer. Default = 'gl.STATIC_DRAW`.
 * @return {WebGLBuffer} the created WebGLBuffer
 * @memberOf module:twgl/attributes
 */


function createBufferFromTypedArray(gl, typedArray, type, drawType) {
  if (helper.isBuffer(gl, typedArray)) {
    return typedArray;
  }

  type = type || gl.ARRAY_BUFFER;
  var buffer = gl.createBuffer();
  setBufferFromTypedArray(gl, type, buffer, typedArray, drawType);
  return buffer;
}

function isIndices(name) {
  return name === "indices";
} // This is really just a guess. Though I can't really imagine using
// anything else? Maybe for some compression?


function getNormalizationForTypedArray(typedArray) {
  if (typedArray instanceof Int8Array) {
    return true;
  } // eslint-disable-line


  if (typedArray instanceof Uint8Array) {
    return true;
  } // eslint-disable-line


  return false;
} // This is really just a guess. Though I can't really imagine using
// anything else? Maybe for some compression?


function getNormalizationForTypedArrayType(typedArrayType) {
  if (typedArrayType === Int8Array) {
    return true;
  } // eslint-disable-line


  if (typedArrayType === Uint8Array) {
    return true;
  } // eslint-disable-line


  return false;
}

function getArray(array) {
  return array.length ? array : array.data;
}

var texcoordRE = /coord|texture/i;
var colorRE = /color|colour/i;

function guessNumComponentsFromName(name, length) {
  var numComponents;

  if (texcoordRE.test(name)) {
    numComponents = 2;
  } else if (colorRE.test(name)) {
    numComponents = 4;
  } else {
    numComponents = 3; // position, normals, indices ...
  }

  if (length % numComponents > 0) {
    throw "Can not guess numComponents for attribute '" + name + "'. Tried " + numComponents + " but " + length + " values is not evenly divisible by " + numComponents + ". You should specify it.";
  }

  return numComponents;
}

function getNumComponents(array, arrayName) {
  return array.numComponents || array.size || guessNumComponentsFromName(arrayName, getArray(array).length);
}

function makeTypedArray(array, name) {
  if (typedArrays.isArrayBuffer(array)) {
    return array;
  }

  if (typedArrays.isArrayBuffer(array.data)) {
    return array.data;
  }

  if (Array.isArray(array)) {
    array = {
      data: array
    };
  }

  var Type = array.type;

  if (!Type) {
    if (isIndices(name)) {
      Type = Uint16Array;
    } else {
      Type = Float32Array;
    }
  }

  return new Type(array.data);
}
/**
 * The info for an attribute. This is effectively just the arguments to `gl.vertexAttribPointer` plus the WebGLBuffer
 * for the attribute.
 *
 * @typedef {Object} AttribInfo
 * @property {number} [numComponents] the number of components for this attribute.
 * @property {number} [size] synonym for `numComponents`.
 * @property {number} [type] the type of the attribute (eg. `gl.FLOAT`, `gl.UNSIGNED_BYTE`, etc...) Default = `gl.FLOAT`
 * @property {boolean} [normalize] whether or not to normalize the data. Default = false
 * @property {number} [offset] offset into buffer in bytes. Default = 0
 * @property {number} [stride] the stride in bytes per element. Default = 0
 * @property {number} [divisor] the divisor in instances. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor
 *    where as anything else = do call it with this value
 * @property {WebGLBuffer} buffer the buffer that contains the data for this attribute
 * @property {number} [drawType] the draw type passed to gl.bufferData. Default = gl.STATIC_DRAW
 * @memberOf module:twgl
 */

/**
 * Use this type of array spec when TWGL can't guess the type or number of compoments of an array
 * @typedef {Object} FullArraySpec
 * @property {(number|number[]|ArrayBufferView)} data The data of the array. A number alone becomes the number of elements of type.
 * @property {number} [numComponents] number of components for `vertexAttribPointer`. Default is based on the name of the array.
 *    If `coord` is in the name assumes `numComponents = 2`.
 *    If `color` is in the name assumes `numComponents = 4`.
 *    otherwise assumes `numComponents = 3`
 * @property {constructor} type The type. This is only used if `data` is a JavaScript array. It is the constructor for the typedarray. (eg. `Uint8Array`).
 * For example if you want colors in a `Uint8Array` you might have a `FullArraySpec` like `{ type: Uint8Array, data: [255,0,255,255, ...], }`.
 * @property {number} [size] synonym for `numComponents`.
 * @property {boolean} [normalize] normalize for `vertexAttribPointer`. Default is true if type is `Int8Array` or `Uint8Array` otherwise false.
 * @property {number} [stride] stride for `vertexAttribPointer`. Default = 0
 * @property {number} [offset] offset for `vertexAttribPointer`. Default = 0
 * @property {number} [divisor] divisor for `vertexAttribDivisor`. Default = undefined. Note: undefined = don't call gl.vertexAttribDivisor
 *    where as anything else = do call it with this value
 * @property {string} [attrib] name of attribute this array maps to. Defaults to same name as array prefixed by the default attribPrefix.
 * @property {string} [name] synonym for `attrib`.
 * @property {string} [attribName] synonym for `attrib`.
 * @memberOf module:twgl
 */

/**
 * An individual array in {@link module:twgl.Arrays}
 *
 * When passed to {@link module:twgl.createBufferInfoFromArrays} if an ArraySpec is `number[]` or `ArrayBufferView`
 * the types will be guessed based on the name. `indices` will be `Uint16Array`, everything else will
 * be `Float32Array`. If an ArraySpec is a number it's the number of floats for an empty (zeroed) buffer.
 *
 * @typedef {(number|number[]|ArrayBufferView|module:twgl.FullArraySpec)} ArraySpec
 * @memberOf module:twgl
 */

/**
 * This is a JavaScript object of arrays by name. The names should match your shader's attributes. If your
 * attributes have a common prefix you can specify it by calling {@link module:twgl.setAttributePrefix}.
 *
 *     Bare JavaScript Arrays
 *
 *         var arrays = {
 *            position: [-1, 1, 0],
 *            normal: [0, 1, 0],
 *            ...
 *         }
 *
 *     Bare TypedArrays
 *
 *         var arrays = {
 *            position: new Float32Array([-1, 1, 0]),
 *            color: new Uint8Array([255, 128, 64, 255]),
 *            ...
 *         }
 *
 * *   Will guess at `numComponents` if not specified based on name.
 *
 *     If `coord` is in the name assumes `numComponents = 2`
 *
 *     If `color` is in the name assumes `numComponents = 4`
 *
 *     otherwise assumes `numComponents = 3`
 *
 * Objects with various fields. See {@link module:twgl.FullArraySpec}.
 *
 *     var arrays = {
 *       position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
 *       texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
 *       normal:   { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],     },
 *       indices:  { numComponents: 3, data: [0, 1, 2, 1, 2, 3],                       },
 *     };
 *
 * @typedef {Object.<string, module:twgl.ArraySpec>} Arrays
 * @memberOf module:twgl
 */

/**
 * Creates a set of attribute data and WebGLBuffers from set of arrays
 *
 * Given
 *
 *      var arrays = {
 *        position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
 *        texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
 *        normal:   { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],     },
 *        color:    { numComponents: 4, data: [255, 255, 255, 255, 255, 0, 0, 255, 0, 0, 255, 255], type: Uint8Array, },
 *        indices:  { numComponents: 3, data: [0, 1, 2, 1, 2, 3],                       },
 *      };
 *
 * returns something like
 *
 *      var attribs = {
 *        position: { numComponents: 3, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
 *        texcoord: { numComponents: 2, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
 *        normal:   { numComponents: 3, type: gl.FLOAT,         normalize: false, buffer: WebGLBuffer, },
 *        color:    { numComponents: 4, type: gl.UNSIGNED_BYTE, normalize: true,  buffer: WebGLBuffer, },
 *      };
 *
 * notes:
 *
 * *   Arrays can take various forms
 *
 *     Bare JavaScript Arrays
 *
 *         var arrays = {
 *            position: [-1, 1, 0],
 *            normal: [0, 1, 0],
 *            ...
 *         }
 *
 *     Bare TypedArrays
 *
 *         var arrays = {
 *            position: new Float32Array([-1, 1, 0]),
 *            color: new Uint8Array([255, 128, 64, 255]),
 *            ...
 *         }
 *
 * *   Will guess at `numComponents` if not specified based on name.
 *
 *     If `coord` is in the name assumes `numComponents = 2`
 *
 *     If `color` is in the name assumes `numComponents = 4`
 *
 *     otherwise assumes `numComponents = 3`
 *
 * @param {WebGLRenderingContext} gl The webgl rendering context.
 * @param {module:twgl.Arrays} arrays The arrays
 * @return {Object.<string, module:twgl.AttribInfo>} the attribs
 * @memberOf module:twgl/attributes
 */


function createAttribsFromArrays(gl, arrays) {
  var attribs = {};
  Object.keys(arrays).forEach(function (arrayName) {
    if (!isIndices(arrayName)) {
      var array = arrays[arrayName];
      var attribName = array.attrib || array.name || array.attribName || defaults.attribPrefix + arrayName;
      var buffer;
      var type;
      var normalization;
      var numComponents;
      var numValues;

      if (typeof array === "number" || typeof array.data === "number") {
        numValues = array.data || array;
        var arrayType = array.type || Float32Array;
        var numBytes = numValues * arrayType.BYTES_PER_ELEMENT;
        type = typedArrays.getGLTypeForTypedArrayType(arrayType);
        normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArrayType(arrayType);
        numComponents = array.numComponents || array.size || guessNumComponentsFromName(arrayName, numValues);
        buffer = gl.createBuffer();
        gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
        gl.bufferData(gl.ARRAY_BUFFER, numBytes, array.drawType || gl.STATIC_DRAW);
      } else {
        var typedArray = makeTypedArray(array, arrayName);
        buffer = createBufferFromTypedArray(gl, typedArray, undefined, array.drawType);
        type = typedArrays.getGLTypeForTypedArray(typedArray);
        normalization = array.normalize !== undefined ? array.normalize : getNormalizationForTypedArray(typedArray);
        numComponents = getNumComponents(array, arrayName);
        numValues = typedArray.length;
      }

      attribs[attribName] = {
        buffer: buffer,
        numComponents: numComponents,
        type: type,
        normalize: normalization,
        stride: array.stride || 0,
        offset: array.offset || 0,
        divisor: array.divisor === undefined ? undefined : array.divisor,
        drawType: array.drawType
      };
    }
  });
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  return attribs;
}
/**
 * Sets the contents of a buffer attached to an attribInfo
 *
 * This is helper function to dynamically update a buffer.
 *
 * Let's say you make a bufferInfo
 *
 *     var arrays = {
 *        position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
 *        texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
 *        normal:   new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
 *        indices:  new Uint16Array([0, 1, 2, 1, 2, 3]),
 *     };
 *     var bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
 *
 *  And you want to dynamically upate the positions. You could do this
 *
 *     // assuming arrays.position has already been updated with new data.
 *     twgl.setAttribInfoBufferFromArray(gl, bufferInfo.attribs.position, arrays.position);
 *
 * @param {WebGLRenderingContext} gl
 * @param {AttribInfo} attribInfo The attribInfo who's buffer contents to set. NOTE: If you have an attribute prefix
 *   the name of the attribute will include the prefix.
 * @param {ArraySpec} array Note: it is arguably ineffient to pass in anything but a typed array because anything
 *    else will have to be converted to a typed array before it can be used by WebGL. During init time that
 *    inefficiency is usually not important but if you're updating data dynamically best to be efficient.
 * @param {number} [offset] an optional offset into the buffer. This is only an offset into the WebGL buffer
 *    not the array. To pass in an offset into the array itself use a typed array and create an `ArrayBufferView`
 *    for the portion of the array you want to use.
 *
 *        var someArray = new Float32Array(1000); // an array with 1000 floats
 *        var someSubArray = new Float32Array(someArray.buffer, offsetInBytes, sizeInUnits); // a view into someArray
 *
 *    Now you can pass `someSubArray` into setAttribInfoBufferFromArray`
 * @memberOf module:twgl/attributes
 */


function setAttribInfoBufferFromArray(gl, attribInfo, array, offset) {
  array = makeTypedArray(array);

  if (offset !== undefined) {
    gl.bindBuffer(gl.ARRAY_BUFFER, attribInfo.buffer);
    gl.bufferSubData(gl.ARRAY_BUFFER, offset, array);
  } else {
    setBufferFromTypedArray(gl, gl.ARRAY_BUFFER, attribInfo.buffer, array, attribInfo.drawType);
  }
}

function getBytesPerValueForGLType(gl, type) {
  if (type === gl.BYTE) return 1; // eslint-disable-line

  if (type === gl.UNSIGNED_BYTE) return 1; // eslint-disable-line

  if (type === gl.SHORT) return 2; // eslint-disable-line

  if (type === gl.UNSIGNED_SHORT) return 2; // eslint-disable-line

  if (type === gl.INT) return 4; // eslint-disable-line

  if (type === gl.UNSIGNED_INT) return 4; // eslint-disable-line

  if (type === gl.FLOAT) return 4; // eslint-disable-line

  return 0;
}
/**
 * tries to get the number of elements from a set of arrays.
 */


var positionKeys = ['position', 'positions', 'a_position'];

function getNumElementsFromNonIndexedArrays(arrays) {
  var key;

  for (var _ii = 0; _ii < positionKeys.length; ++_ii) {
    key = positionKeys[_ii];

    if (key in arrays) {
      break;
    }
  }

  if (ii === positionKeys.length) {
    key = Object.keys(arrays)[0];
  }

  var array = arrays[key];
  var length = getArray(array).length;
  var numComponents = getNumComponents(array, key);
  var numElements = length / numComponents;

  if (length % numComponents > 0) {
    throw "numComponents " + numComponents + " not correct for length " + length;
  }

  return numElements;
}

function getNumElementsFromAttributes(gl, attribs) {
  var key;
  var ii;

  for (ii = 0; ii < positionKeys.length; ++ii) {
    key = positionKeys[ii];

    if (key in attribs) {
      break;
    }

    key = defaults.attribPrefix + key;

    if (key in attribs) {
      break;
    }
  }

  if (ii === positionKeys.length) {
    key = Object.keys(attribs)[0];
  }

  var attrib = attribs[key];
  gl.bindBuffer(gl.ARRAY_BUFFER, attrib.buffer);
  var numBytes = gl.getBufferParameter(gl.ARRAY_BUFFER, gl.BUFFER_SIZE);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  var bytesPerValue = getBytesPerValueForGLType(gl, attrib.type);
  var totalElements = numBytes / bytesPerValue;
  var numComponents = attrib.numComponents || attrib.size; // TODO: check stride

  var numElements = totalElements / numComponents;

  if (numElements % 1 !== 0) {
    throw "numComponents " + numComponents + " not correct for length " + length;
  }

  return numElements;
}
/**
 * @typedef {Object} BufferInfo
 * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
 * @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc..
 * @property {WebGLBuffer} [indices] The indices `ELEMENT_ARRAY_BUFFER` if any indices exist.
 * @property {Object.<string, module:twgl.AttribInfo>} [attribs] The attribs approriate to call `setAttributes`
 * @memberOf module:twgl
 */

/**
 * Creates a BufferInfo from an object of arrays.
 *
 * This can be passed to {@link module:twgl.setBuffersAndAttributes} and to
 * {@link module:twgl:drawBufferInfo}.
 *
 * Given an object like
 *
 *     var arrays = {
 *       position: { numComponents: 3, data: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0], },
 *       texcoord: { numComponents: 2, data: [0, 0, 0, 1, 1, 0, 1, 1],                 },
 *       normal:   { numComponents: 3, data: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],     },
 *       indices:  { numComponents: 3, data: [0, 1, 2, 1, 2, 3],                       },
 *     };
 *
 *  Creates an BufferInfo like this
 *
 *     bufferInfo = {
 *       numElements: 4,        // or whatever the number of elements is
 *       indices: WebGLBuffer,  // this property will not exist if there are no indices
 *       attribs: {
 *         a_position: { buffer: WebGLBuffer, numComponents: 3, },
 *         a_normal:   { buffer: WebGLBuffer, numComponents: 3, },
 *         a_texcoord: { buffer: WebGLBuffer, numComponents: 2, },
 *       },
 *     };
 *
 *  The properties of arrays can be JavaScript arrays in which case the number of components
 *  will be guessed.
 *
 *     var arrays = {
 *        position: [0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0],
 *        texcoord: [0, 0, 0, 1, 1, 0, 1, 1],
 *        normal:   [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
 *        indices:  [0, 1, 2, 1, 2, 3],
 *     };
 *
 *  They can also by TypedArrays
 *
 *     var arrays = {
 *        position: new Float32Array([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]),
 *        texcoord: new Float32Array([0, 0, 0, 1, 1, 0, 1, 1]),
 *        normal:   new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]),
 *        indices:  new Uint16Array([0, 1, 2, 1, 2, 3]),
 *     };
 *
 *  Or augmentedTypedArrays
 *
 *     var positions = createAugmentedTypedArray(3, 4);
 *     var texcoords = createAugmentedTypedArray(2, 4);
 *     var normals   = createAugmentedTypedArray(3, 4);
 *     var indices   = createAugmentedTypedArray(3, 2, Uint16Array);
 *
 *     positions.push([0, 0, 0, 10, 0, 0, 0, 10, 0, 10, 10, 0]);
 *     texcoords.push([0, 0, 0, 1, 1, 0, 1, 1]);
 *     normals.push([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
 *     indices.push([0, 1, 2, 1, 2, 3]);
 *
 *     var arrays = {
 *        position: positions,
 *        texcoord: texcoords,
 *        normal:   normals,
 *        indices:  indices,
 *     };
 *
 * For the last example it is equivalent to
 *
 *     var bufferInfo = {
 *       attribs: {
 *         a_position: { numComponents: 3, buffer: gl.createBuffer(), },
 *         a_texcoods: { numComponents: 2, buffer: gl.createBuffer(), },
 *         a_normals: { numComponents: 3, buffer: gl.createBuffer(), },
 *       },
 *       indices: gl.createBuffer(),
 *       numElements: 6,
 *     };
 *
 *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_position.buffer);
 *     gl.bufferData(gl.ARRAY_BUFFER, arrays.position, gl.STATIC_DRAW);
 *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_texcoord.buffer);
 *     gl.bufferData(gl.ARRAY_BUFFER, arrays.texcoord, gl.STATIC_DRAW);
 *     gl.bindBuffer(gl.ARRAY_BUFFER, bufferInfo.attribs.a_normal.buffer);
 *     gl.bufferData(gl.ARRAY_BUFFER, arrays.normal, gl.STATIC_DRAW);
 *     gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufferInfo.indices);
 *     gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, arrays.indices, gl.STATIC_DRAW);
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @param {module:twgl.Arrays} arrays Your data
 * @return {module:twgl.BufferInfo} A BufferInfo
 * @memberOf module:twgl/attributes
 */


function createBufferInfoFromArrays(gl, arrays) {
  var bufferInfo = {
    attribs: createAttribsFromArrays(gl, arrays)
  };
  var indices = arrays.indices;

  if (indices) {
    var newIndices = makeTypedArray(indices, "indices");
    bufferInfo.indices = createBufferFromTypedArray(gl, newIndices, gl.ELEMENT_ARRAY_BUFFER);
    bufferInfo.numElements = newIndices.length;
    bufferInfo.elementType = typedArrays.getGLTypeForTypedArray(newIndices);
  } else {
    bufferInfo.numElements = getNumElementsFromAttributes(gl, bufferInfo.attribs);
  }

  return bufferInfo;
}
/**
 * Creates a buffer from an array, typed array, or array spec
 *
 * Given something like this
 *
 *     [1, 2, 3],
 *
 * or
 *
 *     new Uint16Array([1,2,3]);
 *
 * or
 *
 *     {
 *        data: [1, 2, 3],
 *        type: Uint8Array,
 *     }
 *
 * returns a WebGLBuffer that constains the given data.
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
 * @param {module:twgl.ArraySpec} array an array, typed array, or array spec.
 * @param {string} arrayName name of array. Used to guess the type if type can not be dervied other wise.
 * @return {WebGLBuffer} a WebGLBuffer containing the data in array.
 * @memberOf module:twgl/attributes
 */


function createBufferFromArray(gl, array, arrayName) {
  var type = arrayName === "indices" ? gl.ELEMENT_ARRAY_BUFFER : gl.ARRAY_BUFFER;
  var typedArray = makeTypedArray(array, arrayName);
  return createBufferFromTypedArray(gl, typedArray, type);
}
/**
 * Creates buffers from arrays or typed arrays
 *
 * Given something like this
 *
 *     var arrays = {
 *        positions: [1, 2, 3],
 *        normals: [0, 0, 1],
 *     }
 *
 * returns something like
 *
 *     buffers = {
 *       positions: WebGLBuffer,
 *       normals: WebGLBuffer,
 *     }
 *
 * If the buffer is named 'indices' it will be made an ELEMENT_ARRAY_BUFFER.
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext.
 * @param {module:twgl.Arrays} arrays
 * @return {Object<string, WebGLBuffer>} returns an object with one WebGLBuffer per array
 * @memberOf module:twgl/attributes
 */


function createBuffersFromArrays(gl, arrays) {
  var buffers = {};
  Object.keys(arrays).forEach(function (key) {
    buffers[key] = createBufferFromArray(gl, arrays[key], key);
  }); // Ugh!

  if (arrays.indices) {
    buffers.numElements = arrays.indices.length;
    buffers.elementType = typedArrays.getGLTypeForTypedArray(makeTypedArray(arrays.indices), 'indices');
  } else {
    buffers.numElements = getNumElementsFromNonIndexedArrays(arrays);
  }

  return buffers;
}

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

"use strict";


exports.__esModule = true;
exports.setTextureDefaults_ = setDefaults;
exports.createSampler = createSampler;
exports.createSamplers = createSamplers;
exports.setSamplerParameters = setSamplerParameters;
exports.createTexture = createTexture;
exports.setEmptyTexture = setEmptyTexture;
exports.setTextureFromArray = setTextureFromArray;
exports.loadTextureFromUrl = loadTextureFromUrl;
exports.setTextureFromElement = setTextureFromElement;
exports.setTextureFilteringForSize = setTextureFilteringForSize;
exports.setTextureParameters = setTextureParameters;
exports.setDefaultTextureColor = setDefaultTextureColor;
exports.createTextures = createTextures;
exports.resizeTexture = resizeTexture;
exports.getNumComponentsForFormat = getNumComponentsForFormat;
exports.getBytesPerElementForInternalFormat = getBytesPerElementForInternalFormat;

var utils = _interopRequireWildcard(__webpack_require__(4));

var typedArrays = _interopRequireWildcard(__webpack_require__(1));

var helper = _interopRequireWildcard(__webpack_require__(0));

var _globalObject = _interopRequireDefault(__webpack_require__(2));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Low level texture related functions
 *
 * You should generally not need to use these functions. They are provided
 * for those cases where you're doing something out of the ordinary
 * and you need lower level access.
 *
 * For backward compatibily they are available at both `twgl.textures` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/textures
 */
// make sure we don't see a global gl
var gl = undefined; // eslint-disable-line

var defaults = {
  textureColor: new Uint8Array([128, 192, 255, 255]),
  textureOptions: {},
  crossOrigin: undefined
};
var isArrayBuffer = typedArrays.isArrayBuffer; // Should we make this on demand?

var ctx = _globalObject.default.document && _globalObject.default.document.createElement ? _globalObject.default.document.createElement("canvas").getContext("2d") : null; // NOTE: Chrome supports 2D canvas in a Worker (behind flag as of v64 but
//       not only does Firefox NOT support it but Firefox freezes immediately
//       if you try to create one instead of just returning null and continuing.
//  : (global.OffscreenCanvas && (new global.OffscreenCanvas(1, 1)).getContext("2d"));  // OffscreenCanvas may not support 2d
// NOTE: We can maybe remove some of the need for the 2d canvas. In WebGL2
// we can use the various unpack settings. Otherwise we could try using
// the ability of an imagebitmap to be cut. Unfortunately cutting an imagebitmap
// is async and the current TWGL code expects a non-Async result though that
// might not be a problem. ImageBitmap though is not available in Edge or Safari
// as of 2018-01-02

/* PixelFormat */

var ALPHA = 0x1906;
var RGB = 0x1907;
var RGBA = 0x1908;
var LUMINANCE = 0x1909;
var LUMINANCE_ALPHA = 0x190A;
var DEPTH_COMPONENT = 0x1902;
var DEPTH_STENCIL = 0x84F9;
/* TextureWrapMode */

var REPEAT = 0x2901; // eslint-disable-line

var MIRRORED_REPEAT = 0x8370; // eslint-disable-line

/* TextureMagFilter */

var NEAREST = 0x2600; // eslint-disable-line

/* TextureMinFilter */

var NEAREST_MIPMAP_NEAREST = 0x2700; // eslint-disable-line

var LINEAR_MIPMAP_NEAREST = 0x2701; // eslint-disable-line

var NEAREST_MIPMAP_LINEAR = 0x2702; // eslint-disable-line

var LINEAR_MIPMAP_LINEAR = 0x2703; // eslint-disable-line

var R8 = 0x8229;
var R8_SNORM = 0x8F94;
var R16F = 0x822D;
var R32F = 0x822E;
var R8UI = 0x8232;
var R8I = 0x8231;
var RG16UI = 0x823A;
var RG16I = 0x8239;
var RG32UI = 0x823C;
var RG32I = 0x823B;
var RG8 = 0x822B;
var RG8_SNORM = 0x8F95;
var RG16F = 0x822F;
var RG32F = 0x8230;
var RG8UI = 0x8238;
var RG8I = 0x8237;
var R16UI = 0x8234;
var R16I = 0x8233;
var R32UI = 0x8236;
var R32I = 0x8235;
var RGB8 = 0x8051;
var SRGB8 = 0x8C41;
var RGB565 = 0x8D62;
var RGB8_SNORM = 0x8F96;
var R11F_G11F_B10F = 0x8C3A;
var RGB9_E5 = 0x8C3D;
var RGB16F = 0x881B;
var RGB32F = 0x8815;
var RGB8UI = 0x8D7D;
var RGB8I = 0x8D8F;
var RGB16UI = 0x8D77;
var RGB16I = 0x8D89;
var RGB32UI = 0x8D71;
var RGB32I = 0x8D83;
var RGBA8 = 0x8058;
var SRGB8_ALPHA8 = 0x8C43;
var RGBA8_SNORM = 0x8F97;
var RGB5_A1 = 0x8057;
var RGBA4 = 0x8056;
var RGB10_A2 = 0x8059;
var RGBA16F = 0x881A;
var RGBA32F = 0x8814;
var RGBA8UI = 0x8D7C;
var RGBA8I = 0x8D8E;
var RGB10_A2UI = 0x906F;
var RGBA16UI = 0x8D76;
var RGBA16I = 0x8D88;
var RGBA32I = 0x8D82;
var RGBA32UI = 0x8D70;
var DEPTH_COMPONENT16 = 0x81A5;
var DEPTH_COMPONENT24 = 0x81A6;
var DEPTH_COMPONENT32F = 0x8CAC;
var DEPTH32F_STENCIL8 = 0x8CAD;
var DEPTH24_STENCIL8 = 0x88F0;
/* DataType */

var BYTE = 0x1400;
var UNSIGNED_BYTE = 0x1401;
var SHORT = 0x1402;
var UNSIGNED_SHORT = 0x1403;
var INT = 0x1404;
var UNSIGNED_INT = 0x1405;
var FLOAT = 0x1406;
var UNSIGNED_SHORT_4_4_4_4 = 0x8033;
var UNSIGNED_SHORT_5_5_5_1 = 0x8034;
var UNSIGNED_SHORT_5_6_5 = 0x8363;
var HALF_FLOAT = 0x140B;
var HALF_FLOAT_OES = 0x8D61; // Thanks Khronos for making this different >:(

var UNSIGNED_INT_2_10_10_10_REV = 0x8368;
var UNSIGNED_INT_10F_11F_11F_REV = 0x8C3B;
var UNSIGNED_INT_5_9_9_9_REV = 0x8C3E;
var FLOAT_32_UNSIGNED_INT_24_8_REV = 0x8DAD;
var UNSIGNED_INT_24_8 = 0x84FA;
var RG = 0x8227;
var RG_INTEGER = 0x8228;
var RED = 0x1903;
var RED_INTEGER = 0x8D94;
var RGB_INTEGER = 0x8D98;
var RGBA_INTEGER = 0x8D99;
var formatInfo = {};
{
  // NOTE: this is named `numColorComponents` vs `numComponents` so we can let Uglify mangle
  // the name.
  var f = formatInfo;
  f[ALPHA] = {
    numColorComponents: 1
  };
  f[LUMINANCE] = {
    numColorComponents: 1
  };
  f[LUMINANCE_ALPHA] = {
    numColorComponents: 2
  };
  f[RGB] = {
    numColorComponents: 3
  };
  f[RGBA] = {
    numColorComponents: 4
  };
  f[RED] = {
    numColorComponents: 1
  };
  f[RED_INTEGER] = {
    numColorComponents: 1
  };
  f[RG] = {
    numColorComponents: 2
  };
  f[RG_INTEGER] = {
    numColorComponents: 2
  };
  f[RGB] = {
    numColorComponents: 3
  };
  f[RGB_INTEGER] = {
    numColorComponents: 3
  };
  f[RGBA] = {
    numColorComponents: 4
  };
  f[RGBA_INTEGER] = {
    numColorComponents: 4
  };
  f[DEPTH_COMPONENT] = {
    numColorComponents: 1
  };
  f[DEPTH_STENCIL] = {
    numColorComponents: 2
  };
}
var textureInternalFormatInfo = {};
{
  // NOTE: these properties need unique names so we can let Uglify mangle the name.
  var t = textureInternalFormatInfo; // unsized formats

  t[ALPHA] = {
    textureFormat: ALPHA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [1, 2, 2, 4],
    type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT]
  };
  t[LUMINANCE] = {
    textureFormat: LUMINANCE,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [1, 2, 2, 4],
    type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT]
  };
  t[LUMINANCE_ALPHA] = {
    textureFormat: LUMINANCE_ALPHA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [2, 4, 4, 8],
    type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT]
  };
  t[RGB] = {
    textureFormat: RGB,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [3, 6, 6, 12, 2],
    type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT, UNSIGNED_SHORT_5_6_5]
  };
  t[RGBA] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [4, 8, 8, 16, 2, 2],
    type: [UNSIGNED_BYTE, HALF_FLOAT, HALF_FLOAT_OES, FLOAT, UNSIGNED_SHORT_4_4_4_4, UNSIGNED_SHORT_5_5_5_1]
  }; // sized formats

  t[R8] = {
    textureFormat: RED,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 1,
    type: UNSIGNED_BYTE
  };
  t[R8_SNORM] = {
    textureFormat: RED,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: 1,
    type: BYTE
  };
  t[R16F] = {
    textureFormat: RED,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [4, 2],
    type: [FLOAT, HALF_FLOAT]
  };
  t[R32F] = {
    textureFormat: RED,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 4,
    type: FLOAT
  };
  t[R8UI] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 1,
    type: UNSIGNED_BYTE
  };
  t[R8I] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 1,
    type: BYTE
  };
  t[R16UI] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 2,
    type: UNSIGNED_SHORT
  };
  t[R16I] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 2,
    type: SHORT
  };
  t[R32UI] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_INT
  };
  t[R32I] = {
    textureFormat: RED_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: INT
  };
  t[RG8] = {
    textureFormat: RG,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 2,
    type: UNSIGNED_BYTE
  };
  t[RG8_SNORM] = {
    textureFormat: RG,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: 2,
    type: BYTE
  };
  t[RG16F] = {
    textureFormat: RG,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [8, 4],
    type: [FLOAT, HALF_FLOAT]
  };
  t[RG32F] = {
    textureFormat: RG,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 8,
    type: FLOAT
  };
  t[RG8UI] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 2,
    type: UNSIGNED_BYTE
  };
  t[RG8I] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 2,
    type: BYTE
  };
  t[RG16UI] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_SHORT
  };
  t[RG16I] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: SHORT
  };
  t[RG32UI] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 8,
    type: UNSIGNED_INT
  };
  t[RG32I] = {
    textureFormat: RG_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 8,
    type: INT
  };
  t[RGB8] = {
    textureFormat: RGB,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 3,
    type: UNSIGNED_BYTE
  };
  t[SRGB8] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: 3,
    type: UNSIGNED_BYTE
  };
  t[RGB565] = {
    textureFormat: RGB,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [3, 2],
    type: [UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5]
  };
  t[RGB8_SNORM] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: 3,
    type: BYTE
  };
  t[R11F_G11F_B10F] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [12, 6, 4],
    type: [FLOAT, HALF_FLOAT, UNSIGNED_INT_10F_11F_11F_REV]
  };
  t[RGB9_E5] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [12, 6, 4],
    type: [FLOAT, HALF_FLOAT, UNSIGNED_INT_5_9_9_9_REV]
  };
  t[RGB16F] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [12, 6],
    type: [FLOAT, HALF_FLOAT]
  };
  t[RGB32F] = {
    textureFormat: RGB,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 12,
    type: FLOAT
  };
  t[RGB8UI] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 3,
    type: UNSIGNED_BYTE
  };
  t[RGB8I] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 3,
    type: BYTE
  };
  t[RGB16UI] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 6,
    type: UNSIGNED_SHORT
  };
  t[RGB16I] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 6,
    type: SHORT
  };
  t[RGB32UI] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 12,
    type: UNSIGNED_INT
  };
  t[RGB32I] = {
    textureFormat: RGB_INTEGER,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 12,
    type: INT
  };
  t[RGBA8] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 4,
    type: UNSIGNED_BYTE
  };
  t[SRGB8_ALPHA8] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 4,
    type: UNSIGNED_BYTE
  };
  t[RGBA8_SNORM] = {
    textureFormat: RGBA,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: 4,
    type: BYTE
  };
  t[RGB5_A1] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [4, 2, 4],
    type: [UNSIGNED_BYTE, UNSIGNED_SHORT_5_5_5_1, UNSIGNED_INT_2_10_10_10_REV]
  };
  t[RGBA4] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: [4, 2],
    type: [UNSIGNED_BYTE, UNSIGNED_SHORT_4_4_4_4]
  };
  t[RGB10_A2] = {
    textureFormat: RGBA,
    colorRenderable: true,
    textureFilterable: true,
    bytesPerElement: 4,
    type: UNSIGNED_INT_2_10_10_10_REV
  };
  t[RGBA16F] = {
    textureFormat: RGBA,
    colorRenderable: false,
    textureFilterable: true,
    bytesPerElement: [16, 8],
    type: [FLOAT, HALF_FLOAT]
  };
  t[RGBA32F] = {
    textureFormat: RGBA,
    colorRenderable: false,
    textureFilterable: false,
    bytesPerElement: 16,
    type: FLOAT
  };
  t[RGBA8UI] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_BYTE
  };
  t[RGBA8I] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: BYTE
  };
  t[RGB10_A2UI] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_INT_2_10_10_10_REV
  };
  t[RGBA16UI] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 8,
    type: UNSIGNED_SHORT
  };
  t[RGBA16I] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 8,
    type: SHORT
  };
  t[RGBA32I] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 16,
    type: INT
  };
  t[RGBA32UI] = {
    textureFormat: RGBA_INTEGER,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 16,
    type: UNSIGNED_INT
  }; // Sized Internal

  t[DEPTH_COMPONENT16] = {
    textureFormat: DEPTH_COMPONENT,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: [2, 4],
    type: [UNSIGNED_SHORT, UNSIGNED_INT]
  };
  t[DEPTH_COMPONENT24] = {
    textureFormat: DEPTH_COMPONENT,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_INT
  };
  t[DEPTH_COMPONENT32F] = {
    textureFormat: DEPTH_COMPONENT,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: FLOAT
  };
  t[DEPTH24_STENCIL8] = {
    textureFormat: DEPTH_STENCIL,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: UNSIGNED_INT_24_8
  };
  t[DEPTH32F_STENCIL8] = {
    textureFormat: DEPTH_STENCIL,
    colorRenderable: true,
    textureFilterable: false,
    bytesPerElement: 4,
    type: FLOAT_32_UNSIGNED_INT_24_8_REV
  };
  Object.keys(t).forEach(function (internalFormat) {
    var info = t[internalFormat];
    info.bytesPerElementMap = {};

    if (Array.isArray(info.bytesPerElement)) {
      info.bytesPerElement.forEach(function (bytesPerElement, ndx) {
        var type = info.type[ndx];
        info.bytesPerElementMap[type] = bytesPerElement;
      });
    } else {
      var type = info.type;
      info.bytesPerElementMap[type] = info.bytesPerElement;
    }
  });
}
/**
 * Gets the number of bytes per element for a given internalFormat / type
 * @param {number} internalFormat The internalFormat parameter from texImage2D etc..
 * @param {number} type The type parameter for texImage2D etc..
 * @return {number} the number of bytes per element for the given internalFormat, type combo
 * @memberOf module:twgl/textures
 */

function getBytesPerElementForInternalFormat(internalFormat, type) {
  var info = textureInternalFormatInfo[internalFormat];

  if (!info) {
    throw "unknown internal format";
  }

  var bytesPerElement = info.bytesPerElementMap[type];

  if (bytesPerElement === undefined) {
    throw "unknown internal format";
  }

  return bytesPerElement;
}
/**
 * Gets the format for a given internalFormat
 *
 * @param {number} internalFormat The internal format
 * @return {{format:number, type:number}} the corresponding format and type
 */


function getFormatAndTypeForInternalFormat(internalFormat) {
  var info = textureInternalFormatInfo[internalFormat];

  if (!info) {
    throw "unknown internal format";
  }

  return {
    format: info.textureFormat,
    type: Array.isArray(info.type) ? info.type[0] : info.type
  };
}
/**
 * Returns true if value is power of 2
 * @param {number} value number to check.
 * @return true if value is power of 2
 */


function isPowerOf2(value) {
  return (value & value - 1) === 0;
}
/**
 * Gets whether or not we can generate mips for the given format
 * @param {number} internalFormat The internalFormat parameter from texImage2D etc..
 * @param {number} type The type parameter for texImage2D etc..
 * @return {boolean} true if we can generate mips
 */


function canGenerateMipmap(gl, width, height, internalFormat
/*, type */
) {
  if (!utils.isWebGL2(gl)) {
    return isPowerOf2(width) && isPowerOf2(height);
  }

  var info = textureInternalFormatInfo[internalFormat];

  if (!info) {
    throw "unknown internal format";
  }

  return info.colorRenderable && info.textureFilterable;
}
/**
 * Gets whether or not we can generate mips for the given format
 * @param {number} internalFormat The internalFormat parameter from texImage2D etc..
 * @param {number} type The type parameter for texImage2D etc..
 * @return {boolean} true if we can generate mips
 */


function canFilter(internalFormat
/*, type */
) {
  var info = textureInternalFormatInfo[internalFormat];

  if (!info) {
    throw "unknown internal format";
  }

  return info.textureFilterable;
}
/**
 * Gets the number of compontents for a given image format.
 * @param {number} format the format.
 * @return {number} the number of components for the format.
 * @memberOf module:twgl/textures
 */


function getNumComponentsForFormat(format) {
  var info = formatInfo[format];

  if (!info) {
    throw "unknown format: " + format;
  }

  return info.numColorComponents;
}
/**
 * Gets the texture type for a given array type.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @return {number} the gl texture type
 */


function getTextureTypeForArrayType(gl, src, defaultType) {
  if (isArrayBuffer(src)) {
    return typedArrays.getGLTypeForTypedArray(src);
  }

  return defaultType || gl.UNSIGNED_BYTE;
}

function guessDimensions(gl, target, width, height, numElements) {
  if (numElements % 1 !== 0) {
    throw "can't guess dimensions";
  }

  if (!width && !height) {
    var size = Math.sqrt(numElements / (target === gl.TEXTURE_CUBE_MAP ? 6 : 1));

    if (size % 1 === 0) {
      width = size;
      height = size;
    } else {
      width = numElements;
      height = 1;
    }
  } else if (!height) {
    height = numElements / width;

    if (height % 1) {
      throw "can't guess dimensions";
    }
  } else if (!width) {
    width = numElements / height;

    if (width % 1) {
      throw "can't guess dimensions";
    }
  }

  return {
    width: width,
    height: height
  };
}
/**
 * Sets the default texture color.
 *
 * The default texture color is used when loading textures from
 * urls. Because the URL will be loaded async we'd like to be
 * able to use the texture immediately. By putting a 1x1 pixel
 * color in the texture we can start using the texture before
 * the URL has loaded.
 *
 * @param {number[]} color Array of 4 values in the range 0 to 1
 * @deprecated see {@link module:twgl.setDefaults}
 * @memberOf module:twgl/textures
 */


function setDefaultTextureColor(color) {
  defaults.textureColor = new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]);
}

function setDefaults(newDefaults) {
  helper.copyExistingProperties(newDefaults, defaults);

  if (newDefaults.textureColor) {
    setDefaultTextureColor(newDefaults.textureColor);
  }
}
/**
 * A function to generate the source for a texture.
 * @callback TextureFunc
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @param {module:twgl.TextureOptions} options the texture options
 * @return {*} Returns any of the things documentented for `src` for {@link module:twgl.TextureOptions}.
 * @memberOf module:twgl
 */

/**
 * Texture options passed to most texture functions. Each function will use whatever options
 * are appropriate for its needs. This lets you pass the same options to all functions.
 *
 * Note: A `TexImageSource` is defined in the WebGL spec as a `HTMLImageElement`, `HTMLVideoElement`,
 * `HTMLCanvasElement`, `ImageBitmap`, or `ImageData`.
 *
 * @typedef {Object} TextureOptions
 * @property {number} [target] the type of texture `gl.TEXTURE_2D` or `gl.TEXTURE_CUBE_MAP`. Defaults to `gl.TEXTURE_2D`.
 * @property {number} [level] the mip level to affect. Defaults to 0. Note, if set auto will be considered false unless explicitly set to true.
 * @property {number} [width] the width of the texture. Only used if src is an array or typed array or null.
 * @property {number} [height] the height of a texture. Only used if src is an array or typed array or null.
 * @property {number} [depth] the depth of a texture. Only used if src is an array or type array or null and target is `TEXTURE_3D` .
 * @property {number} [min] the min filter setting (eg. `gl.LINEAR`). Defaults to `gl.NEAREST_MIPMAP_LINEAR`
 *     or if texture is not a power of 2 on both dimensions then defaults to `gl.LINEAR`.
 * @property {number} [mag] the mag filter setting (eg. `gl.LINEAR`). Defaults to `gl.LINEAR`
 * @property {number} [minMag] both the min and mag filter settings.
 * @property {number} [internalFormat] internal format for texture. Defaults to `gl.RGBA`
 * @property {number} [format] format for texture. Defaults to `gl.RGBA`.
 * @property {number} [type] type for texture. Defaults to `gl.UNSIGNED_BYTE` unless `src` is ArrayBufferView. If `src`
 *     is ArrayBufferView defaults to type that matches ArrayBufferView type.
 * @property {number} [wrap] Texture wrapping for both S and T (and R if TEXTURE_3D or WebGLSampler). Defaults to `gl.REPEAT` for 2D unless src is WebGL1 and src not npot and `gl.CLAMP_TO_EDGE` for cube
 * @property {number} [wrapS] Texture wrapping for S. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
 * @property {number} [wrapT] Texture wrapping for T. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
 * @property {number} [wrapR] Texture wrapping for R. Defaults to `gl.REPEAT` and `gl.CLAMP_TO_EDGE` for cube. If set takes precedence over `wrap`.
 * @property {number} [minLod] TEXTURE_MIN_LOD setting
 * @property {number} [maxLod] TEXTURE_MAX_LOD setting
 * @property {number} [baseLevel] TEXTURE_BASE_LEVEL setting
 * @property {number} [maxLevel] TEXTURE_MAX_LEVEL setting
 * @property {number} [unpackAlignment] The `gl.UNPACK_ALIGNMENT` used when uploading an array. Defaults to 1.
 * @property {number} [premultiplyAlpha] Whether or not to premultiply alpha. Defaults to whatever the current setting is.
 *     This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
 *     the current setting for specific textures.
 * @property {number} [flipY] Whether or not to flip the texture vertically on upload. Defaults to whatever the current setting is.
 *     This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
 *     the current setting for specific textures.
 * @property {number} [colorspaceConversion] Whether or not to let the browser do colorspace conversion of the texture on upload. Defaults to whatever the current setting is.
 *     This lets you set it once before calling `twgl.createTexture` or `twgl.createTextures` and only override
 *     the current setting for specific textures.
 * @property {(number[]|ArrayBufferView)} color color used as temporary 1x1 pixel color for textures loaded async when src is a string.
 *    If it's a JavaScript array assumes color is 0 to 1 like most GL colors as in `[1, 0, 0, 1] = red=1, green=0, blue=0, alpha=0`.
 *    Defaults to `[0.5, 0.75, 1, 1]`. See {@link module:twgl.setDefaultTextureColor}. If `false` texture is set. Can be used to re-load a texture
 * @property {boolean} [auto] If `undefined` or `true`, in WebGL1, texture filtering is set automatically for non-power of 2 images and
 *    mips are generated for power of 2 images. In WebGL2 mips are generated if they can be. Note: if `level` is set above
 *    then then `auto` is assumed to be `false` unless explicity set to `true`.
 * @property {number[]} [cubeFaceOrder] The order that cube faces are pulled out of an img or set of images. The default is
 *
 *     [gl.TEXTURE_CUBE_MAP_POSITIVE_X,
 *      gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
 *      gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
 *      gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
 *      gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
 *      gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]
 *
 * @property {(number[]|ArrayBufferView|TexImageSource|TexImageSource[]|string|string[]|module:twgl.TextureFunc)} [src] source for texture
 *
 *    If `string` then it's assumed to be a URL to an image. The image will be downloaded async. A usable
 *    1x1 pixel texture will be returned immediatley. The texture will be updated once the image has downloaded.
 *    If `target` is `gl.TEXTURE_CUBE_MAP` will attempt to divide image into 6 square pieces. 1x6, 6x1, 3x2, 2x3.
 *    The pieces will be uploaded in `cubeFaceOrder`
 *
 *    If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_CUBE_MAP` then it must have 6 entries, one for each face of a cube map.
 *
 *    If `string[]` or `TexImageSource[]` and target is `gl.TEXTURE_2D_ARRAY` then eact entry is a slice of the a 2d array texture
 *    and will be scaled to the specified width and height OR to the size of the first image that loads.
 *
 *    If `TexImageSource` then it wil be used immediately to create the contents of the texture. Examples `HTMLImageElement`,
 *    `HTMLCanvasElement`, `HTMLVideoElement`.
 *
 *    If `number[]` or `ArrayBufferView` it's assumed to be data for a texture. If `width` or `height` is
 *    not specified it is guessed as follows. First the number of elements is computed by `src.length / numComponents`
 *    where `numComponents` is derived from `format`. If `target` is `gl.TEXTURE_CUBE_MAP` then `numElements` is divided
 *    by 6. Then
 *
 *    *   If neither `width` nor `height` are specified and `sqrt(numElements)` is an integer then width and height
 *        are set to `sqrt(numElements)`. Otherwise `width = numElements` and `height = 1`.
 *
 *    *   If only one of `width` or `height` is specified then the other equals `numElements / specifiedDimension`.
 *
 * If `number[]` will be converted to `type`.
 *
 * If `src` is a function it will be called with a `WebGLRenderingContext` and these options.
 * Whatever it returns is subject to these rules. So it can return a string url, an `HTMLElement`
 * an array etc...
 *
 * If `src` is undefined then an empty texture will be created of size `width` by `height`.
 *
 * @property {string} [crossOrigin] What to set the crossOrigin property of images when they are downloaded.
 *    default: undefined. Also see {@link module:twgl.setDefaults}.
 *
 * @memberOf module:twgl
 */
// NOTE: While querying GL is considered slow it's not remotely as slow
// as uploading a texture. On top of that you're unlikely to call this in
// a perf critical loop. Even if upload a texture every frame that's unlikely
// to be more than 1 or 2 textures a frame. In other words, the benefits of
// making the API easy to use outweigh any supposed perf benefits
//
// Also note I get that having one global of these is bad practice.
// As long as it's used correctly it means no garbage which probably
// doesn't matter when dealing with textures but old habits die hard.


var lastPackState = {};
/**
 * Saves any packing state that will be set based on the options.
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 */

function savePackState(gl, options) {
  if (options.colorspaceConversion !== undefined) {
    lastPackState.colorspaceConversion = gl.getParameter(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL);
    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, options.colorspaceConversion);
  }

  if (options.premultiplyAlpha !== undefined) {
    lastPackState.premultiplyAlpha = gl.getParameter(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL);
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, options.premultiplyAlpha);
  }

  if (options.flipY !== undefined) {
    lastPackState.flipY = gl.getParameter(gl.UNPACK_FLIP_Y_WEBGL);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, options.flipY);
  }
}
/**
 * Restores any packing state that was set based on the options.
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 */


function restorePackState(gl, options) {
  if (options.colorspaceConversion !== undefined) {
    gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, lastPackState.colorspaceConversion);
  }

  if (options.premultiplyAlpha !== undefined) {
    gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, lastPackState.premultiplyAlpha);
  }

  if (options.flipY !== undefined) {
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, lastPackState.flipY);
  }
}
/**
 * Saves state related to data size
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 */


function saveSkipState(gl) {
  lastPackState.unpackAlignment = gl.getParameter(gl.UNPACK_ALIGNMENT);

  if (utils.isWebGL2(gl)) {
    lastPackState.unpackRowLength = gl.getParameter(gl.UNPACK_ROW_LENGTH);
    lastPackState.unpackImageHeight = gl.getParameter(gl.UNPACK_IMAGE_HEIGHT);
    lastPackState.unpackSkipPixels = gl.getParameter(gl.UNPACK_SKIP_PIXELS);
    lastPackState.unpackSkipRows = gl.getParameter(gl.UNPACK_SKIP_ROWS);
    lastPackState.unpackSkipImages = gl.getParameter(gl.UNPACK_SKIP_IMAGES);
  }
}
/**
 * Restores state related to data size
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 */


function restoreSkipState(gl) {
  gl.pixelStorei(gl.UNPACK_ALIGNMENT, lastPackState.unpackAlignment);

  if (utils.isWebGL2(gl)) {
    gl.pixelStorei(gl.UNPACK_ROW_LENGTH, lastPackState.unpackRowLength);
    gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, lastPackState.unpackImageHeight);
    gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, lastPackState.unpackSkipPixels);
    gl.pixelStorei(gl.UNPACK_SKIP_ROWS, lastPackState.unpackSkipRows);
    gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, lastPackState.unpackSkipImages);
  }
}
/**
 * Sets the parameters of a texture or sampler
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {number|WebGLSampler} target texture target or sampler
 * @param {function()} parameteriFn texParamteri or samplerParameteri fn
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 */


function setTextureSamplerParameters(gl, target, parameteriFn, options) {
  if (options.minMag) {
    parameteriFn.call(gl, target, gl.TEXTURE_MIN_FILTER, options.minMag);
    parameteriFn.call(gl, target, gl.TEXTURE_MAG_FILTER, options.minMag);
  }

  if (options.min) {
    parameteriFn.call(gl, target, gl.TEXTURE_MIN_FILTER, options.min);
  }

  if (options.mag) {
    parameteriFn.call(gl, target, gl.TEXTURE_MAG_FILTER, options.mag);
  }

  if (options.wrap) {
    parameteriFn.call(gl, target, gl.TEXTURE_WRAP_S, options.wrap);
    parameteriFn.call(gl, target, gl.TEXTURE_WRAP_T, options.wrap);

    if (target === gl.TEXTURE_3D || helper.isSampler(gl, target)) {
      parameteriFn.call(gl, target, gl.TEXTURE_WRAP_R, options.wrap);
    }
  }

  if (options.wrapR) {
    parameteriFn.call(gl, target, gl.TEXTURE_WRAP_R, options.wrapR);
  }

  if (options.wrapS) {
    parameteriFn.call(gl, target, gl.TEXTURE_WRAP_S, options.wrapS);
  }

  if (options.wrapT) {
    parameteriFn.call(gl, target, gl.TEXTURE_WRAP_T, options.wrapT);
  }

  if (options.minLod) {
    parameteriFn.call(gl, target, gl.TEXTURE_MIN_LOD, options.minLod);
  }

  if (options.maxLod) {
    parameteriFn.call(gl, target, gl.TEXTURE_MAX_LOD, options.maxLod);
  }

  if (options.baseLevel) {
    parameteriFn.call(gl, target, gl.TEXTURE_BASE_LEVEL, options.baseLevel);
  }

  if (options.maxLevel) {
    parameteriFn.call(gl, target, gl.TEXTURE_MAX_LEVEL, options.maxLevel);
  }
}
/**
 * Sets the texture parameters of a texture.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @memberOf module:twgl/textures
 */


function setTextureParameters(gl, tex, options) {
  var target = options.target || gl.TEXTURE_2D;
  gl.bindTexture(target, tex);
  setTextureSamplerParameters(gl, target, gl.texParameteri, options);
}
/**
 * Sets the sampler parameters of a sampler.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLSampler} sampler the WebGLSampler to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @memberOf module:twgl/textures
 */


function setSamplerParameters(gl, sampler, options) {
  setTextureSamplerParameters(gl, sampler, gl.samplerParameteri, options);
}
/**
 * Creates a new sampler object and sets parameters.
 *
 * Example:
 *
 *      const sampler = twgl.createSampler(gl, {
 *        minMag: gl.NEAREST,         // sets both TEXTURE_MIN_FILTER and TEXTURE_MAG_FILTER
 *        wrap: gl.CLAMP_TO_NEAREST,  // sets both TEXTURE_WRAP_S and TEXTURE_WRAP_T and TEXTURE_WRAP_R
 *      });
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per sampler.
 * @return {Object.<string,WebGLSampler>} the created samplers by name
 */


function createSampler(gl, options) {
  var sampler = gl.createSampler();
  setSamplerParameters(gl, sampler, options);
  return sampler;
}
/**
 * Creates a multiple sampler objects and sets parameters on each.
 *
 * Example:
 *
 *      const samplers = twgl.createSamplers(gl, {
 *        nearest: {
 *          minMag: gl.NEAREST,
 *        },
 *        nearestClampS: {
 *          minMag: gl.NEAREST,
 *          wrapS: gl.CLAMP_TO_NEAREST,
 *        },
 *        linear: {
 *          minMag: gl.LINEAR,
 *        },
 *        nearestClamp: {
 *          minMag: gl.NEAREST,
 *          wrap: gl.CLAMP_TO_EDGE,
 *        },
 *        linearClamp: {
 *          minMag: gl.LINEAR,
 *          wrap: gl.CLAMP_TO_EDGE,
 *        },
 *        linearClampT: {
 *          minMag: gl.LINEAR,
 *          wrapT: gl.CLAMP_TO_EDGE,
 *        },
 *      });
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set on the sampler
 */


function createSamplers(gl, samplerOptions) {
  var samplers = {};
  Object.keys(samplerOptions).forEach(function (name) {
    samplers[name] = createSampler(gl, samplerOptions[name]);
  });
  return samplers;
}
/**
 * Makes a 1x1 pixel
 * If no color is passed in uses the default color which can be set by calling `setDefaultTextureColor`.
 * @param {(number[]|ArrayBufferView)} [color] The color using 0-1 values
 * @return {Uint8Array} Unit8Array with color.
 */


function make1Pixel(color) {
  color = color || defaults.textureColor;

  if (isArrayBuffer(color)) {
    return color;
  }

  return new Uint8Array([color[0] * 255, color[1] * 255, color[2] * 255, color[3] * 255]);
}
/**
 * Sets filtering or generates mips for texture based on width or height
 * If width or height is not passed in uses `options.width` and//or `options.height`
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @param {number} [width] width of texture
 * @param {number} [height] height of texture
 * @param {number} [internalFormat] The internalFormat parameter from texImage2D etc..
 * @param {number} [type] The type parameter for texImage2D etc..
 * @memberOf module:twgl/textures
 */


function setTextureFilteringForSize(gl, tex, options, width, height, internalFormat, type) {
  options = options || defaults.textureOptions;
  internalFormat = internalFormat || gl.RGBA;
  type = type || gl.UNSIGNED_BYTE;
  var target = options.target || gl.TEXTURE_2D;
  width = width || options.width;
  height = height || options.height;
  gl.bindTexture(target, tex);

  if (canGenerateMipmap(gl, width, height, internalFormat, type)) {
    gl.generateMipmap(target);
  } else {
    var filtering = canFilter(internalFormat, type) ? gl.LINEAR : gl.NEAREST;
    gl.texParameteri(target, gl.TEXTURE_MIN_FILTER, filtering);
    gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, filtering);
    gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  }
}

function shouldAutomaticallySetTextureFilteringForSize(options) {
  return options.auto === true || options.auto === undefined && options.level === undefined;
}
/**
 * Gets an array of cubemap face enums
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @return {number[]} cubemap face enums
 */


function getCubeFaceOrder(gl, options) {
  options = options || {};
  return options.cubeFaceOrder || [gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z];
}
/**
 * @typedef {Object} FaceInfo
 * @property {number} face gl enum for texImage2D
 * @property {number} ndx face index (0 - 5) into source data
 * @ignore
 */

/**
 * Gets an array of FaceInfos
 * There's a bug in some NVidia drivers that will crash the driver if
 * `gl.TEXTURE_CUBE_MAP_POSITIVE_X` is not uploaded first. So, we take
 * the user's desired order from his faces to WebGL and make sure we
 * do the faces in WebGL order
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @return {FaceInfo[]} cubemap face infos. Arguably the `face` property of each element is redundent but
 *    it's needed internally to sort the array of `ndx` properties by `face`.
 */


function getCubeFacesWithNdx(gl, options) {
  var faces = getCubeFaceOrder(gl, options); // work around bug in NVidia drivers. We have to upload the first face first else the driver crashes :(

  var facesWithNdx = faces.map(function (face, ndx) {
    return {
      face: face,
      ndx: ndx
    };
  });
  facesWithNdx.sort(function (a, b) {
    return a.face - b.face;
  });
  return facesWithNdx;
}
/**
 * Set a texture from the contents of an element. Will also set
 * texture filtering or generate mips based on the dimensions of the element
 * unless `options.auto === false`. If `target === gl.TEXTURE_CUBE_MAP` will
 * attempt to slice image into 1x6, 2x3, 3x2, or 6x1 images, one for each face.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {HTMLElement} element a canvas, img, or video element.
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @memberOf module:twgl/textures
 * @kind function
 */


function setTextureFromElement(gl, tex, element, options) {
  options = options || defaults.textureOptions;
  var target = options.target || gl.TEXTURE_2D;
  var level = options.level || 0;
  var width = element.width;
  var height = element.height;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type = options.type || formatType.type;
  savePackState(gl, options);
  gl.bindTexture(target, tex);

  if (target === gl.TEXTURE_CUBE_MAP) {
    // guess the parts
    var imgWidth = element.width;
    var imgHeight = element.height;
    var size;
    var slices;

    if (imgWidth / 6 === imgHeight) {
      // It's 6x1
      size = imgHeight;
      slices = [0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0];
    } else if (imgHeight / 6 === imgWidth) {
      // It's 1x6
      size = imgWidth;
      slices = [0, 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5];
    } else if (imgWidth / 3 === imgHeight / 2) {
      // It's 3x2
      size = imgWidth / 3;
      slices = [0, 0, 1, 0, 2, 0, 0, 1, 1, 1, 2, 1];
    } else if (imgWidth / 2 === imgHeight / 3) {
      // It's 2x3
      size = imgWidth / 2;
      slices = [0, 0, 1, 0, 0, 1, 1, 1, 0, 2, 1, 2];
    } else {
      throw "can't figure out cube map from element: " + (element.src ? element.src : element.nodeName);
    }

    if (ctx) {
      ctx.canvas.width = size;
      ctx.canvas.height = size;
      width = size;
      height = size;
      getCubeFacesWithNdx(gl, options).forEach(function (f) {
        var xOffset = slices[f.ndx * 2 + 0] * size;
        var yOffset = slices[f.ndx * 2 + 1] * size;
        ctx.drawImage(element, xOffset, yOffset, size, size, 0, 0, size, size);
        gl.texImage2D(f.face, level, internalFormat, format, type, ctx.canvas);
      }); // Free up the canvas memory

      ctx.canvas.width = 1;
      ctx.canvas.height = 1;
    } else if (_globalObject.default.createImageBitmap) {
      // NOTE: It seems like we should prefer ImageBitmap because unlike canvas it's
      // note lossy? (alpha is not premultiplied? although I'm not sure what
      width = size;
      height = size;
      getCubeFacesWithNdx(gl, options).forEach(function (f) {
        var xOffset = slices[f.ndx * 2 + 0] * size;
        var yOffset = slices[f.ndx * 2 + 1] * size; // We can't easily use a default texture color here as it would have to match
        // the type across all faces where as with a 2D one there's only one face
        // so we're replacing everything all at once. It also has to be the correct size.
        // On the other hand we need all faces to be the same size so as one face loads
        // the rest match else the texture will be unrenderable.

        gl.texImage2D(f.face, level, internalFormat, size, size, 0, format, type, null);

        _globalObject.default.createImageBitmap(element, xOffset, yOffset, size, size, {
          premultiplyAlpha: 'none',
          colorSpaceConversion: 'none'
        }).then(function (imageBitmap) {
          savePackState(gl, options);
          gl.bindTexture(target, tex);
          gl.texImage2D(f.face, level, internalFormat, format, type, imageBitmap);
          restorePackState(gl, options);

          if (shouldAutomaticallySetTextureFilteringForSize(options)) {
            setTextureFilteringForSize(gl, tex, options, width, height, internalFormat, type);
          }
        });
      });
    }
  } else if (target === gl.TEXTURE_3D || target === gl.TEXTURE_2D_ARRAY) {
    var smallest = Math.min(element.width, element.height);
    var largest = Math.max(element.width, element.height);
    var depth = largest / smallest;

    if (depth % 1 !== 0) {
      throw "can not compute 3D dimensions of element";
    }

    var xMult = element.width === largest ? 1 : 0;
    var yMult = element.height === largest ? 1 : 0;
    saveSkipState(gl);
    gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
    gl.pixelStorei(gl.UNPACK_ROW_LENGTH, element.width);
    gl.pixelStorei(gl.UNPACK_IMAGE_HEIGHT, 0);
    gl.pixelStorei(gl.UNPACK_SKIP_IMAGES, 0);
    gl.texImage3D(target, level, internalFormat, smallest, smallest, smallest, 0, format, type, null);

    for (var d = 0; d < depth; ++d) {
      var srcX = d * smallest * xMult;
      var srcY = d * smallest * yMult;
      gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, srcX);
      gl.pixelStorei(gl.UNPACK_SKIP_ROWS, srcY);
      gl.texSubImage3D(target, level, 0, 0, d, smallest, smallest, 1, format, type, element);
    }

    restoreSkipState(gl);
  } else {
    gl.texImage2D(target, level, internalFormat, format, type, element);
  }

  restorePackState(gl, options);

  if (shouldAutomaticallySetTextureFilteringForSize(options)) {
    setTextureFilteringForSize(gl, tex, options, width, height, internalFormat, type);
  }

  setTextureParameters(gl, tex, options);
}

function noop() {}
/**
 * Loads an image
 * @param {string} url url to image
 * @param {string} crossOrigin
 * @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null
 *     if there was an error
 * @return {HTMLImageElement} the image being loaded.
 */


function loadImage(url, crossOrigin, callback) {
  callback = callback || noop;
  var img;

  if (_globalObject.default.Image) {
    img = new _globalObject.default.Image();
    crossOrigin = crossOrigin !== undefined ? crossOrigin : defaults.crossOrigin;

    if (crossOrigin !== undefined) {
      img.crossOrigin = crossOrigin;
    }

    var clearEventHandlers = function clearEventHandlers() {
      img.removeEventListener('error', onError); // eslint-disable-line

      img.removeEventListener('load', onLoad); // eslint-disable-line

      img = null;
    };

    var onError = function onError() {
      var msg = "couldn't load image: " + url;
      helper.error(msg);
      callback(msg, img);
      clearEventHandlers();
    };

    var onLoad = function onLoad() {
      callback(null, img);
      clearEventHandlers();
    };

    img.addEventListener('error', onError);
    img.addEventListener('load', onLoad);
    img.src = url;
    return img;
  } else if (_globalObject.default.ImageBitmap) {
    var err;
    var bm;

    var cb = function cb() {
      callback(err, bm);
    };

    var options = {};

    if (crossOrigin) {
      options.mode = 'cors'; // TODO: not sure how to translate image.crossOrigin
    }

    fetch(url, options).then(function (response) {
      if (!response.ok) {
        throw response;
      }

      return response.blob();
    }).then(function (blob) {
      return _globalObject.default.createImageBitmap(blob, {
        premultiplyAlpha: 'none',
        colorSpaceConversion: 'none'
      });
    }).then(function (bitmap) {
      // not sure if this works. We don't want
      // to catch the user's error. So, call
      // the callback in a timeout so we're
      // not in this scope inside the promise.
      bm = bitmap;
      setTimeout(cb);
    }).catch(function (e) {
      err = e;
      setTimeout(cb);
    });
    img = null;
  }

  return img;
}
/**
 * check if object is a TexImageSource
 *
 * @param {Object} obj Object to test
 * @return {boolean} true if object is a TexImageSource
 */


function isTexImageSource(obj) {
  return _globalObject.default.ImageBitmap && obj instanceof _globalObject.default.ImageBitmap || _globalObject.default.ImageData && obj instanceof _globalObject.default.ImageData || _globalObject.default.HTMLElement && obj instanceof _globalObject.default.HTMLElement;
}
/**
 * if obj is an TexImageSource then just
 * uses it otherwise if obj is a string
 * then load it first.
 *
 * @param {string|TexImageSource} obj
 * @param {string} crossOrigin
 * @param {function(err, img)} [callback] a callback that's passed an error and the image. The error will be non-null
 *     if there was an error
 */


function loadAndUseImage(obj, crossOrigin, callback) {
  if (isTexImageSource(obj)) {
    setTimeout(function () {
      callback(null, obj);
    });
    return obj;
  }

  return loadImage(obj, crossOrigin, callback);
}
/**
 * Sets a texture to a 1x1 pixel color. If `options.color === false` is nothing happens. If it's not set
 * the default texture color is used which can be set by calling `setDefaultTextureColor`.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @memberOf module:twgl/textures
 */


function setTextureTo1PixelColor(gl, tex, options) {
  options = options || defaults.textureOptions;
  var target = options.target || gl.TEXTURE_2D;
  gl.bindTexture(target, tex);

  if (options.color === false) {
    return;
  } // Assume it's a URL
  // Put 1x1 pixels in texture. That makes it renderable immediately regardless of filtering.


  var color = make1Pixel(options.color);

  if (target === gl.TEXTURE_CUBE_MAP) {
    for (var ii = 0; ii < 6; ++ii) {
      gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + ii, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, color);
    }
  } else if (target === gl.TEXTURE_3D || target === gl.TEXTURE_2D_ARRAY) {
    gl.texImage3D(target, 0, gl.RGBA, 1, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, color);
  } else {
    gl.texImage2D(target, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, color);
  }
}
/**
 * The src image(s) used to create a texture.
 *
 * When you call {@link module:twgl.createTexture} or {@link module:twgl.createTextures}
 * you can pass in urls for images to load into the textures. If it's a single url
 * then this will be a single HTMLImageElement. If it's an array of urls used for a cubemap
 * this will be a corresponding array of images for the cubemap.
 *
 * @typedef {HTMLImageElement|HTMLImageElement[]} TextureSrc
 * @memberOf module:twgl
 */

/**
 * A callback for when an image finished downloading and been uploaded into a texture
 * @callback TextureReadyCallback
 * @param {*} err If truthy there was an error.
 * @param {WebGLTexture} texture the texture.
 * @param {module:twgl.TextureSrc} souce image(s) used to as the src for the texture
 * @memberOf module:twgl
 */

/**
 * A callback for when all images have finished downloading and been uploaded into their respective textures
 * @callback TexturesReadyCallback
 * @param {*} err If truthy there was an error.
 * @param {Object.<string, WebGLTexture>} textures the created textures by name. Same as returned by {@link module:twgl.createTextures}.
 * @param {Object.<string, module:twgl.TextureSrc>} sources the image(s) used for the texture by name.
 * @memberOf module:twgl
 */

/**
 * A callback for when an image finished downloading and been uploaded into a texture
 * @callback CubemapReadyCallback
 * @param {*} err If truthy there was an error.
 * @param {WebGLTexture} tex the texture.
 * @param {HTMLImageElement[]} imgs the images for each face.
 * @memberOf module:twgl
 */

/**
 * A callback for when an image finished downloading and been uploaded into a texture
 * @callback ThreeDReadyCallback
 * @param {*} err If truthy there was an error.
 * @param {WebGLTexture} tex the texture.
 * @param {HTMLImageElement[]} imgs the images for each slice.
 * @memberOf module:twgl
 */

/**
 * Loads a texture from an image from a Url as specified in `options.src`
 * If `options.color !== false` will set the texture to a 1x1 pixel color so that the texture is
 * immediately useable. It will be updated with the contents of the image once the image has finished
 * downloading. Filtering options will be set as approriate for image unless `options.auto === false`.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 * @param {module:twgl.TextureReadyCallback} [callback] A function to be called when the image has finished loading. err will
 *    be non null if there was an error.
 * @return {HTMLImageElement} the image being downloaded.
 * @memberOf module:twgl/textures
 */


function loadTextureFromUrl(gl, tex, options, callback) {
  callback = callback || noop;
  options = options || defaults.textureOptions;
  setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.

  options = Object.assign({}, options);
  var img = loadAndUseImage(options.src, options.crossOrigin, function (err, img) {
    if (err) {
      callback(err, tex, img);
    } else {
      setTextureFromElement(gl, tex, img, options);
      callback(null, tex, img);
    }
  });
  return img;
}
/**
 * Loads a cubemap from 6 urls or TexImageSources as specified in `options.src`. Will set the cubemap to a 1x1 pixel color
 * so that it is usable immediately unless `option.color === false`.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @param {module:twgl.CubemapReadyCallback} [callback] A function to be called when all the images have finished loading. err will
 *    be non null if there was an error.
 * @memberOf module:twgl/textures
 */


function loadCubemapFromUrls(gl, tex, options, callback) {
  callback = callback || noop;
  var urls = options.src;

  if (urls.length !== 6) {
    throw "there must be 6 urls for a cubemap";
  }

  var level = options.level || 0;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type = options.type || gl.UNSIGNED_BYTE;
  var target = options.target || gl.TEXTURE_2D;

  if (target !== gl.TEXTURE_CUBE_MAP) {
    throw "target must be TEXTURE_CUBE_MAP";
  }

  setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.

  options = Object.assign({}, options);
  var numToLoad = 6;
  var errors = [];
  var faces = getCubeFaceOrder(gl, options);
  var imgs; // eslint-disable-line

  function uploadImg(faceTarget) {
    return function (err, img) {
      --numToLoad;

      if (err) {
        errors.push(err);
      } else {
        if (img.width !== img.height) {
          errors.push("cubemap face img is not a square: " + img.src);
        } else {
          savePackState(gl, options);
          gl.bindTexture(target, tex); // So assuming this is the first image we now have one face that's img sized
          // and 5 faces that are 1x1 pixel so size the other faces

          if (numToLoad === 5) {
            // use the default order
            getCubeFaceOrder(gl).forEach(function (otherTarget) {
              // Should we re-use the same face or a color?
              gl.texImage2D(otherTarget, level, internalFormat, format, type, img);
            });
          } else {
            gl.texImage2D(faceTarget, level, internalFormat, format, type, img);
          }

          restorePackState(gl, options);

          if (shouldAutomaticallySetTextureFilteringForSize(options)) {
            gl.generateMipmap(target);
          }
        }
      }

      if (numToLoad === 0) {
        callback(errors.length ? errors : undefined, tex, imgs);
      }
    };
  }

  imgs = urls.map(function (url, ndx) {
    return loadAndUseImage(url, options.crossOrigin, uploadImg(faces[ndx]));
  });
}
/**
 * Loads a 2d array or 3d texture from urls OR TexImageSources as specified in `options.src`.
 * Will set the texture to a 1x1 pixel color
 * so that it is usable immediately unless `option.color === false`.
 *
 * If the width and height is not specified the width and height of the first
 * image loaded will be used. Note that since images are loaded async
 * which image downloads first is unknown.
 *
 * If an image is not the same size as the width and height it will be scaled
 * to that width and height.
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @param {module:twgl.ThreeDReadyCallback} [callback] A function to be called when all the images have finished loading. err will
 *    be non null if there was an error.
 * @memberOf module:twgl/textures
 */


function loadSlicesFromUrls(gl, tex, options, callback) {
  callback = callback || noop;
  var urls = options.src;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type = options.type || gl.UNSIGNED_BYTE;
  var target = options.target || gl.TEXTURE_2D_ARRAY;

  if (target !== gl.TEXTURE_3D && target !== gl.TEXTURE_2D_ARRAY) {
    throw "target must be TEXTURE_3D or TEXTURE_2D_ARRAY";
  }

  setTextureTo1PixelColor(gl, tex, options); // Because it's async we need to copy the options.

  options = Object.assign({}, options);
  var numToLoad = urls.length;
  var errors = [];
  var imgs; // eslint-disable-line

  var level = options.level || 0;
  var width = options.width;
  var height = options.height;
  var depth = urls.length;
  var firstImage = true;

  function uploadImg(slice) {
    return function (err, img) {
      --numToLoad;

      if (err) {
        errors.push(err);
      } else {
        savePackState(gl, options);
        gl.bindTexture(target, tex);

        if (firstImage) {
          firstImage = false;
          width = options.width || img.width;
          height = options.height || img.height;
          gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, null); // put it in every slice otherwise some slices will be 0,0,0,0

          for (var s = 0; s < depth; ++s) {
            gl.texSubImage3D(target, level, 0, 0, s, width, height, 1, format, type, img);
          }
        } else {
          var src = img;

          if (img.width !== width || img.height !== height) {
            // Size the image to fix
            src = ctx.canvas;
            ctx.canvas.width = width;
            ctx.canvas.height = height;
            ctx.drawImage(img, 0, 0, width, height);
          }

          gl.texSubImage3D(target, level, 0, 0, slice, width, height, 1, format, type, src); // free the canvas memory

          if (src === ctx.canvas) {
            ctx.canvas.width = 0;
            ctx.canvas.height = 0;
          }
        }

        restorePackState(gl, options);

        if (shouldAutomaticallySetTextureFilteringForSize(options)) {
          gl.generateMipmap(target);
        }
      }

      if (numToLoad === 0) {
        callback(errors.length ? errors : undefined, tex, imgs);
      }
    };
  }

  imgs = urls.map(function (url, ndx) {
    return loadAndUseImage(url, options.crossOrigin, uploadImg(ndx));
  });
}
/**
 * Sets a texture from an array or typed array. If the width or height is not provided will attempt to
 * guess the size. See {@link module:twgl.TextureOptions}.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {(number[]|ArrayBufferView)} src An array or typed arry with texture data.
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 *   This is often the same options you passed in when you created the texture.
 * @memberOf module:twgl/textures
 */


function setTextureFromArray(gl, tex, src, options) {
  options = options || defaults.textureOptions;
  var target = options.target || gl.TEXTURE_2D;
  gl.bindTexture(target, tex);
  var width = options.width;
  var height = options.height;
  var depth = options.depth;
  var level = options.level || 0;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type = options.type || getTextureTypeForArrayType(gl, src, formatType.type);

  if (!isArrayBuffer(src)) {
    var Type = typedArrays.getTypedArrayTypeForGLType(type);
    src = new Type(src);
  } else if (src instanceof Uint8ClampedArray) {
    src = new Uint8Array(src.buffer);
  }

  var bytesPerElement = getBytesPerElementForInternalFormat(internalFormat, type);
  var numElements = src.byteLength / bytesPerElement; // TODO: check UNPACK_ALIGNMENT?

  if (numElements % 1) {
    throw "length wrong size for format: " + utils.glEnumToString(gl, format);
  }

  var dimensions;

  if (target === gl.TEXTURE_3D) {
    if (!width && !height && !depth) {
      var size = Math.cbrt(numElements);

      if (size % 1 !== 0) {
        throw "can't guess cube size of array of numElements: " + numElements;
      }

      width = size;
      height = size;
      depth = size;
    } else if (width && (!height || !depth)) {
      dimensions = guessDimensions(gl, target, height, depth, numElements / width);
      height = dimensions.width;
      depth = dimensions.height;
    } else if (height && (!width || !depth)) {
      dimensions = guessDimensions(gl, target, width, depth, numElements / height);
      width = dimensions.width;
      depth = dimensions.height;
    } else {
      dimensions = guessDimensions(gl, target, width, height, numElements / depth);
      width = dimensions.width;
      height = dimensions.height;
    }
  } else {
    dimensions = guessDimensions(gl, target, width, height, numElements);
    width = dimensions.width;
    height = dimensions.height;
  }

  saveSkipState(gl);
  gl.pixelStorei(gl.UNPACK_ALIGNMENT, options.unpackAlignment || 1);
  savePackState(gl, options);

  if (target === gl.TEXTURE_CUBE_MAP) {
    var elementsPerElement = bytesPerElement / src.BYTES_PER_ELEMENT;
    var faceSize = numElements / 6 * elementsPerElement;
    getCubeFacesWithNdx(gl, options).forEach(function (f) {
      var offset = faceSize * f.ndx;
      var data = src.subarray(offset, offset + faceSize);
      gl.texImage2D(f.face, level, internalFormat, width, height, 0, format, type, data);
    });
  } else if (target === gl.TEXTURE_3D) {
    gl.texImage3D(target, level, internalFormat, width, height, depth, 0, format, type, src);
  } else {
    gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, src);
  }

  restorePackState(gl, options);
  restoreSkipState(gl);
  return {
    width: width,
    height: height,
    depth: depth,
    type: type
  };
}
/**
 * Sets a texture with no contents of a certain size. In other words calls `gl.texImage2D` with `null`.
 * You must set `options.width` and `options.height`.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the WebGLTexture to set parameters for
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @memberOf module:twgl/textures
 */


function setEmptyTexture(gl, tex, options) {
  var target = options.target || gl.TEXTURE_2D;
  gl.bindTexture(target, tex);
  var level = options.level || 0;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type = options.type || formatType.type;
  savePackState(gl, options);

  if (target === gl.TEXTURE_CUBE_MAP) {
    for (var ii = 0; ii < 6; ++ii) {
      gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, options.width, options.height, 0, format, type, null);
    }
  } else if (target === gl.TEXTURE_3D) {
    gl.texImage3D(target, level, internalFormat, options.width, options.height, options.depth, 0, format, type, null);
  } else {
    gl.texImage2D(target, level, internalFormat, options.width, options.height, 0, format, type, null);
  }

  restorePackState(gl, options);
}
/**
 * Creates a texture based on the options passed in.
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.TextureOptions} [options] A TextureOptions object with whatever parameters you want set.
 * @param {module:twgl.TextureReadyCallback} [callback] A callback called when an image has been downloaded and uploaded to the texture.
 * @return {WebGLTexture} the created texture.
 * @memberOf module:twgl/textures
 */


function createTexture(gl, options, callback) {
  callback = callback || noop;
  options = options || defaults.textureOptions;
  var tex = gl.createTexture();
  var target = options.target || gl.TEXTURE_2D;
  var width = options.width || 1;
  var height = options.height || 1;
  var internalFormat = options.internalFormat || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var type = options.type || formatType.type;
  gl.bindTexture(target, tex);

  if (target === gl.TEXTURE_CUBE_MAP) {
    // this should have been the default for CUBEMAPS :(
    gl.texParameteri(target, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(target, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
  }

  var src = options.src;

  if (src) {
    if (typeof src === "function") {
      src = src(gl, options);
    }

    if (typeof src === "string") {
      loadTextureFromUrl(gl, tex, options, callback);
    } else if (isArrayBuffer(src) || Array.isArray(src) && (typeof src[0] === 'number' || Array.isArray(src[0]) || isArrayBuffer(src[0]))) {
      var dimensions = setTextureFromArray(gl, tex, src, options);
      width = dimensions.width;
      height = dimensions.height;
      type = dimensions.type;
    } else if (Array.isArray(src) && (typeof src[0] === 'string' || isTexImageSource(src[0]))) {
      if (target === gl.TEXTURE_CUBE_MAP) {
        loadCubemapFromUrls(gl, tex, options, callback);
      } else {
        loadSlicesFromUrls(gl, tex, options, callback);
      }
    } else if (isTexImageSource(src)) {
      setTextureFromElement(gl, tex, src, options);
      width = src.width;
      height = src.height;
    } else {
      throw "unsupported src type";
    }
  } else {
    setEmptyTexture(gl, tex, options);
  }

  if (shouldAutomaticallySetTextureFilteringForSize(options)) {
    setTextureFilteringForSize(gl, tex, options, width, height, internalFormat, type);
  }

  setTextureParameters(gl, tex, options);
  return tex;
}
/**
 * Resizes a texture based on the options passed in.
 *
 * Note: This is not a generic resize anything function.
 * It's mostly used by {@link module:twgl.resizeFramebufferInfo}
 * It will use `options.src` if it exists to try to determine a `type`
 * otherwise it will assume `gl.UNSIGNED_BYTE`. No data is provided
 * for the texture. Texture parameters will be set accordingly
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {WebGLTexture} tex the texture to resize
 * @param {module:twgl.TextureOptions} options A TextureOptions object with whatever parameters you want set.
 * @param {number} [width] the new width. If not passed in will use `options.width`
 * @param {number} [height] the new height. If not passed in will use `options.height`
 * @memberOf module:twgl/textures
 */


function resizeTexture(gl, tex, options, width, height) {
  width = width || options.width;
  height = height || options.height;
  var target = options.target || gl.TEXTURE_2D;
  gl.bindTexture(target, tex);
  var level = options.level || 0;
  var internalFormat = options.internalFormat || options.format || gl.RGBA;
  var formatType = getFormatAndTypeForInternalFormat(internalFormat);
  var format = options.format || formatType.format;
  var type;
  var src = options.src;

  if (!src) {
    type = options.type || formatType.type;
  } else if (isArrayBuffer(src) || Array.isArray(src) && typeof src[0] === 'number') {
    type = options.type || getTextureTypeForArrayType(gl, src, formatType.type);
  } else {
    type = options.type || formatType.type;
  }

  if (target === gl.TEXTURE_CUBE_MAP) {
    for (var ii = 0; ii < 6; ++ii) {
      gl.texImage2D(gl.TEXTURE_CUBE_MAP_POSITIVE_X + ii, level, internalFormat, width, height, 0, format, type, null);
    }
  } else {
    gl.texImage2D(target, level, internalFormat, width, height, 0, format, type, null);
  }
}
/**
 * Check if a src is an async request.
 * if src is a string we're going to download an image
 * if src is an array of strings we're going to download cubemap images
 * @param {*} src The src from a TextureOptions
 * @returns {bool} true if src is async.
 */


function isAsyncSrc(src) {
  return typeof src === 'string' || Array.isArray(src) && typeof src[0] === 'string';
}
/**
 * Creates a bunch of textures based on the passed in options.
 *
 * Example:
 *
 *     const textures = twgl.createTextures(gl, {
 *       // a power of 2 image
 *       hftIcon: { src: "images/hft-icon-16.png", mag: gl.NEAREST },
 *       // a non-power of 2 image
 *       clover: { src: "images/clover.jpg" },
 *       // From a canvas
 *       fromCanvas: { src: ctx.canvas },
 *       // A cubemap from 6 images
 *       yokohama: {
 *         target: gl.TEXTURE_CUBE_MAP,
 *         src: [
 *           'images/yokohama/posx.jpg',
 *           'images/yokohama/negx.jpg',
 *           'images/yokohama/posy.jpg',
 *           'images/yokohama/negy.jpg',
 *           'images/yokohama/posz.jpg',
 *           'images/yokohama/negz.jpg',
 *         ],
 *       },
 *       // A cubemap from 1 image (can be 1x6, 2x3, 3x2, 6x1)
 *       goldengate: {
 *         target: gl.TEXTURE_CUBE_MAP,
 *         src: 'images/goldengate.jpg',
 *       },
 *       // A 2x2 pixel texture from a JavaScript array
 *       checker: {
 *         mag: gl.NEAREST,
 *         min: gl.LINEAR,
 *         src: [
 *           255,255,255,255,
 *           192,192,192,255,
 *           192,192,192,255,
 *           255,255,255,255,
 *         ],
 *       },
 *       // a 1x2 pixel texture from a typed array.
 *       stripe: {
 *         mag: gl.NEAREST,
 *         min: gl.LINEAR,
 *         format: gl.LUMINANCE,
 *         src: new Uint8Array([
 *           255,
 *           128,
 *           255,
 *           128,
 *           255,
 *           128,
 *           255,
 *           128,
 *         ]),
 *         width: 1,
 *       },
 *     });
 *
 * Now
 *
 * *   `textures.hftIcon` will be a 2d texture
 * *   `textures.clover` will be a 2d texture
 * *   `textures.fromCanvas` will be a 2d texture
 * *   `textures.yohohama` will be a cubemap texture
 * *   `textures.goldengate` will be a cubemap texture
 * *   `textures.checker` will be a 2d texture
 * *   `textures.stripe` will be a 2d texture
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {Object.<string,module:twgl.TextureOptions>} options A object of TextureOptions one per texture.
 * @param {module:twgl.TexturesReadyCallback} [callback] A callback called when all textures have been downloaded.
 * @return {Object.<string,WebGLTexture>} the created textures by name
 * @memberOf module:twgl/textures
 */


function createTextures(gl, textureOptions, callback) {
  callback = callback || noop;
  var numDownloading = 0;
  var errors = [];
  var textures = {};
  var images = {};

  function callCallbackIfReady() {
    if (numDownloading === 0) {
      setTimeout(function () {
        callback(errors.length ? errors : undefined, textures, images);
      }, 0);
    }
  }

  Object.keys(textureOptions).forEach(function (name) {
    var options = textureOptions[name];
    var onLoadFn;

    if (isAsyncSrc(options.src)) {
      onLoadFn = function onLoadFn(err, tex, img) {
        images[name] = img;
        --numDownloading;

        if (err) {
          errors.push(err);
        }

        callCallbackIfReady();
      };

      ++numDownloading;
    }

    textures[name] = createTexture(gl, options, onLoadFn);
  }); // queue the callback if there are no images to download.
  // We do this because if your code is structured to wait for
  // images to download but then you comment out all the async
  // images your code would break.

  callCallbackIfReady();
  return textures;
} // Using quotes prevents Uglify from changing the names.
// No speed diff AFAICT.

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

"use strict";


exports.__esModule = true;
var _exportNames = {
  m4: true,
  v3: true,
  primitives: true
};
exports.primitives = exports.v3 = exports.m4 = void 0;

var m4 = _interopRequireWildcard(__webpack_require__(6));

exports.m4 = m4;

var v3 = _interopRequireWildcard(__webpack_require__(3));

exports.v3 = v3;

var primitives = _interopRequireWildcard(__webpack_require__(10));

exports.primitives = primitives;

var _twgl = __webpack_require__(11);

Object.keys(_twgl).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _twgl[key];
});

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

"use strict";


exports.__esModule = true;
exports.create3DFVertices = create3DFVertices;
exports.createAugmentedTypedArray = createAugmentedTypedArray;
exports.createCubeVertices = createCubeVertices;
exports.createPlaneVertices = createPlaneVertices;
exports.createSphereVertices = createSphereVertices;
exports.createTruncatedConeVertices = createTruncatedConeVertices;
exports.createXYQuadVertices = createXYQuadVertices;
exports.createCresentVertices = createCresentVertices;
exports.createCylinderVertices = createCylinderVertices;
exports.createTorusVertices = createTorusVertices;
exports.createDiscVertices = createDiscVertices;
exports.deindexVertices = deindexVertices;
exports.flattenNormals = flattenNormals;
exports.makeRandomVertexColors = makeRandomVertexColors;
exports.reorientDirections = reorientDirections;
exports.reorientNormals = reorientNormals;
exports.reorientPositions = reorientPositions;
exports.reorientVertices = reorientVertices;
exports.concatVertices = concatVertices;
exports.duplicateVertices = duplicateVertices;
exports.createDiscBuffers = exports.createDiscBufferInfo = exports.createTorusBuffers = exports.createTorusBufferInfo = exports.createCylinderBuffers = exports.createCylinderBufferInfo = exports.createCresentBuffers = exports.createCresentBufferInfo = exports.createXYQuadBuffers = exports.createXYQuadBufferInfo = exports.createTruncatedConeBuffers = exports.createTruncatedConeBufferInfo = exports.createSphereBuffers = exports.createSphereBufferInfo = exports.createPlaneBuffers = exports.createPlaneBufferInfo = exports.createCubeBuffers = exports.createCubeBufferInfo = exports.create3DFBuffers = exports.create3DFBufferInfo = void 0;

var attributes = _interopRequireWildcard(__webpack_require__(7));

var helper = _interopRequireWildcard(__webpack_require__(0));

var typedArrays = _interopRequireWildcard(__webpack_require__(1));

var m4 = _interopRequireWildcard(__webpack_require__(6));

var v3 = _interopRequireWildcard(__webpack_require__(3));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Various functions to make simple primitives
 *
 * note: Most primitive functions come in 3 styles
 *
 * *  `createSomeShapeBufferInfo`
 *
 *    These functions are almost always the functions you want to call. They
 *    create vertices then make WebGLBuffers and create {@link module:twgl.AttribInfo}s
 *    returing a {@link module:twgl.BufferInfo} you can pass to {@link module:twgl.setBuffersAndAttributes}
 *    and {@link module:twgl.drawBufferInfo} etc...
 *
 * *  `createSomeShapeBuffers`
 *
 *    These create WebGLBuffers and put your data in them but nothing else.
 *    It's a shortcut to doing it yourself if you don't want to use
 *    the higher level functions.
 *
 * *  `createSomeShapeVertices`
 *
 *    These just create vertices, no buffers. This allows you to manipulate the vertices
 *    or add more data before generating a {@link module:twgl.BufferInfo}. Once you're finished
 *    manipulating the vertices call {@link module:twgl.createBufferInfoFromArrays}.
 *
 *    example:
 *
 *        const arrays = twgl.primitives.createPlaneArrays(1);
 *        twgl.primitives.reorientVertices(arrays, m4.rotationX(Math.PI * 0.5));
 *        const bufferInfo = twgl.createBufferInfoFromArrays(gl, arrays);
 *
 * @module twgl/primitives
 */
var getArray = attributes.getArray_; // eslint-disable-line

var getNumComponents = attributes.getNumComponents_; // eslint-disable-line

/**
 * Add `push` to a typed array. It just keeps a 'cursor'
 * and allows use to `push` values into the array so we
 * don't have to manually compute offsets
 * @param {TypedArray} typedArray TypedArray to augment
 * @param {number} numComponents number of components.
 */

function augmentTypedArray(typedArray, numComponents) {
  var cursor = 0;

  typedArray.push = function () {
    for (var ii = 0; ii < arguments.length; ++ii) {
      var value = arguments[ii];

      if (value instanceof Array || typedArrays.isArrayBuffer(value)) {
        for (var jj = 0; jj < value.length; ++jj) {
          typedArray[cursor++] = value[jj];
        }
      } else {
        typedArray[cursor++] = value;
      }
    }
  };

  typedArray.reset = function (opt_index) {
    cursor = opt_index || 0;
  };

  typedArray.numComponents = numComponents;
  Object.defineProperty(typedArray, 'numElements', {
    get: function get() {
      return this.length / this.numComponents | 0;
    }
  });
  return typedArray;
}
/**
 * creates a typed array with a `push` function attached
 * so that you can easily *push* values.
 *
 * `push` can take multiple arguments. If an argument is an array each element
 * of the array will be added to the typed array.
 *
 * Example:
 *
 *     const array = createAugmentedTypedArray(3, 2);  // creates a Float32Array with 6 values
 *     array.push(1, 2, 3);
 *     array.push([4, 5, 6]);
 *     // array now contains [1, 2, 3, 4, 5, 6]
 *
 * Also has `numComponents` and `numElements` properties.
 *
 * @param {number} numComponents number of components
 * @param {number} numElements number of elements. The total size of the array will be `numComponents * numElements`.
 * @param {constructor} opt_type A constructor for the type. Default = `Float32Array`.
 * @return {ArrayBufferView} A typed array.
 * @memberOf module:twgl/primitives
 */


function createAugmentedTypedArray(numComponents, numElements, opt_type) {
  var Type = opt_type || Float32Array;
  return augmentTypedArray(new Type(numComponents * numElements), numComponents);
}

function allButIndices(name) {
  return name !== "indices";
}
/**
 * Given indexed vertices creates a new set of vertices unindexed by expanding the indexed vertices.
 * @param {Object.<string, TypedArray>} vertices The indexed vertices to deindex
 * @return {Object.<string, TypedArray>} The deindexed vertices
 * @memberOf module:twgl/primitives
 */


function deindexVertices(vertices) {
  var indices = vertices.indices;
  var newVertices = {};
  var numElements = indices.length;

  function expandToUnindexed(channel) {
    var srcBuffer = vertices[channel];
    var numComponents = srcBuffer.numComponents;
    var dstBuffer = createAugmentedTypedArray(numComponents, numElements, srcBuffer.constructor);

    for (var ii = 0; ii < numElements; ++ii) {
      var ndx = indices[ii];
      var offset = ndx * numComponents;

      for (var jj = 0; jj < numComponents; ++jj) {
        dstBuffer.push(srcBuffer[offset + jj]);
      }
    }

    newVertices[channel] = dstBuffer;
  }

  Object.keys(vertices).filter(allButIndices).forEach(expandToUnindexed);
  return newVertices;
}
/**
 * flattens the normals of deindexed vertices in place.
 * @param {Object.<string, TypedArray>} vertices The deindexed vertices who's normals to flatten
 * @return {Object.<string, TypedArray>} The flattened vertices (same as was passed in)
 * @memberOf module:twgl/primitives
 */


function flattenNormals(vertices) {
  if (vertices.indices) {
    throw "can't flatten normals of indexed vertices. deindex them first";
  }

  var normals = vertices.normal;
  var numNormals = normals.length;

  for (var ii = 0; ii < numNormals; ii += 9) {
    // pull out the 3 normals for this triangle
    var nax = normals[ii + 0];
    var nay = normals[ii + 1];
    var naz = normals[ii + 2];
    var nbx = normals[ii + 3];
    var nby = normals[ii + 4];
    var nbz = normals[ii + 5];
    var ncx = normals[ii + 6];
    var ncy = normals[ii + 7];
    var ncz = normals[ii + 8]; // add them

    var nx = nax + nbx + ncx;
    var ny = nay + nby + ncy;
    var nz = naz + nbz + ncz; // normalize them

    var length = Math.sqrt(nx * nx + ny * ny + nz * nz);
    nx /= length;
    ny /= length;
    nz /= length; // copy them back in

    normals[ii + 0] = nx;
    normals[ii + 1] = ny;
    normals[ii + 2] = nz;
    normals[ii + 3] = nx;
    normals[ii + 4] = ny;
    normals[ii + 5] = nz;
    normals[ii + 6] = nx;
    normals[ii + 7] = ny;
    normals[ii + 8] = nz;
  }

  return vertices;
}

function applyFuncToV3Array(array, matrix, fn) {
  var len = array.length;
  var tmp = new Float32Array(3);

  for (var ii = 0; ii < len; ii += 3) {
    fn(matrix, [array[ii], array[ii + 1], array[ii + 2]], tmp);
    array[ii] = tmp[0];
    array[ii + 1] = tmp[1];
    array[ii + 2] = tmp[2];
  }
}

function transformNormal(mi, v, dst) {
  dst = dst || v3.create();
  var v0 = v[0];
  var v1 = v[1];
  var v2 = v[2];
  dst[0] = v0 * mi[0 * 4 + 0] + v1 * mi[0 * 4 + 1] + v2 * mi[0 * 4 + 2];
  dst[1] = v0 * mi[1 * 4 + 0] + v1 * mi[1 * 4 + 1] + v2 * mi[1 * 4 + 2];
  dst[2] = v0 * mi[2 * 4 + 0] + v1 * mi[2 * 4 + 1] + v2 * mi[2 * 4 + 2];
  return dst;
}
/**
 * Reorients directions by the given matrix..
 * @param {number[]|TypedArray} array The array. Assumes value floats per element.
 * @param {Matrix} matrix A matrix to multiply by.
 * @return {number[]|TypedArray} the same array that was passed in
 * @memberOf module:twgl/primitives
 */


function reorientDirections(array, matrix) {
  applyFuncToV3Array(array, matrix, m4.transformDirection);
  return array;
}
/**
 * Reorients normals by the inverse-transpose of the given
 * matrix..
 * @param {number[]|TypedArray} array The array. Assumes value floats per element.
 * @param {Matrix} matrix A matrix to multiply by.
 * @return {number[]|TypedArray} the same array that was passed in
 * @memberOf module:twgl/primitives
 */


function reorientNormals(array, matrix) {
  applyFuncToV3Array(array, m4.inverse(matrix), transformNormal);
  return array;
}
/**
 * Reorients positions by the given matrix. In other words, it
 * multiplies each vertex by the given matrix.
 * @param {number[]|TypedArray} array The array. Assumes value floats per element.
 * @param {Matrix} matrix A matrix to multiply by.
 * @return {number[]|TypedArray} the same array that was passed in
 * @memberOf module:twgl/primitives
 */


function reorientPositions(array, matrix) {
  applyFuncToV3Array(array, matrix, m4.transformPoint);
  return array;
}
/**
 * Reorients arrays by the given matrix. Assumes arrays have
 * names that contains 'pos' could be reoriented as positions,
 * 'binorm' or 'tan' as directions, and 'norm' as normals.
 *
 * @param {Object.<string, (number[]|TypedArray)>} arrays The vertices to reorient
 * @param {Matrix} matrix matrix to reorient by.
 * @return {Object.<string, (number[]|TypedArray)>} same arrays that were passed in.
 * @memberOf module:twgl/primitives
 */


function reorientVertices(arrays, matrix) {
  Object.keys(arrays).forEach(function (name) {
    var array = arrays[name];

    if (name.indexOf("pos") >= 0) {
      reorientPositions(array, matrix);
    } else if (name.indexOf("tan") >= 0 || name.indexOf("binorm") >= 0) {
      reorientDirections(array, matrix);
    } else if (name.indexOf("norm") >= 0) {
      reorientNormals(array, matrix);
    }
  });
  return arrays;
}
/**
 * Creates XY quad BufferInfo
 *
 * The default with no parameters will return a 2x2 quad with values from -1 to +1.
 * If you want a unit quad with that goes from 0 to 1 you'd call it with
 *
 *     twgl.primitives.createXYQuadBufferInfo(gl, 1, 0.5, 0.5);
 *
 * If you want a unit quad centered above 0,0 you'd call it with
 *
 *     twgl.primitives.createXYQuadBufferInfo(gl, 1, 0, 0.5);
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
 * @param {number} [xOffset] the amount to offset the quad in X
 * @param {number} [yOffset] the amount to offset the quad in Y
 * @return {Object.<string, WebGLBuffer>} the created XY Quad BufferInfo
 * @memberOf module:twgl/primitives
 * @function createXYQuadBufferInfo
 */

/**
 * Creates XY quad Buffers
 *
 * The default with no parameters will return a 2x2 quad with values from -1 to +1.
 * If you want a unit quad with that goes from 0 to 1 you'd call it with
 *
 *     twgl.primitives.createXYQuadBufferInfo(gl, 1, 0.5, 0.5);
 *
 * If you want a unit quad centered above 0,0 you'd call it with
 *
 *     twgl.primitives.createXYQuadBufferInfo(gl, 1, 0, 0.5);
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
 * @param {number} [xOffset] the amount to offset the quad in X
 * @param {number} [yOffset] the amount to offset the quad in Y
 * @return {module:twgl.BufferInfo} the created XY Quad buffers
 * @memberOf module:twgl/primitives
 * @function createXYQuadBuffers
 */

/**
 * Creates XY quad vertices
 *
 * The default with no parameters will return a 2x2 quad with values from -1 to +1.
 * If you want a unit quad with that goes from 0 to 1 you'd call it with
 *
 *     twgl.primitives.createXYQuadVertices(1, 0.5, 0.5);
 *
 * If you want a unit quad centered above 0,0 you'd call it with
 *
 *     twgl.primitives.createXYQuadVertices(1, 0, 0.5);
 *
 * @param {number} [size] the size across the quad. Defaults to 2 which means vertices will go from -1 to +1
 * @param {number} [xOffset] the amount to offset the quad in X
 * @param {number} [yOffset] the amount to offset the quad in Y
 * @return {Object.<string, TypedArray> the created XY Quad vertices
 * @memberOf module:twgl/primitives
 */


function createXYQuadVertices(size, xOffset, yOffset) {
  size = size || 2;
  xOffset = xOffset || 0;
  yOffset = yOffset || 0;
  size *= 0.5;
  return {
    position: {
      numComponents: 2,
      data: [xOffset + -1 * size, yOffset + -1 * size, xOffset + 1 * size, yOffset + -1 * size, xOffset + -1 * size, yOffset + 1 * size, xOffset + 1 * size, yOffset + 1 * size]
    },
    normal: [0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1],
    texcoord: [0, 0, 1, 0, 0, 1, 1, 1],
    indices: [0, 1, 2, 2, 1, 3]
  };
}
/**
 * Creates XZ plane BufferInfo.
 *
 * The created plane has position, normal, and texcoord data
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [width] Width of the plane. Default = 1
 * @param {number} [depth] Depth of the plane. Default = 1
 * @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
 * @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
 * @param {Matrix4} [matrix] A matrix by which to multiply all the vertices.
 * @return {@module:twgl.BufferInfo} The created plane BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createPlaneBufferInfo
 */

/**
 * Creates XZ plane buffers.
 *
 * The created plane has position, normal, and texcoord data
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [width] Width of the plane. Default = 1
 * @param {number} [depth] Depth of the plane. Default = 1
 * @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
 * @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
 * @param {Matrix4} [matrix] A matrix by which to multiply all the vertices.
 * @return {Object.<string, WebGLBuffer>} The created plane buffers.
 * @memberOf module:twgl/primitives
 * @function createPlaneBuffers
 */

/**
 * Creates XZ plane vertices.
 *
 * The created plane has position, normal, and texcoord data
 *
 * @param {number} [width] Width of the plane. Default = 1
 * @param {number} [depth] Depth of the plane. Default = 1
 * @param {number} [subdivisionsWidth] Number of steps across the plane. Default = 1
 * @param {number} [subdivisionsDepth] Number of steps down the plane. Default = 1
 * @param {Matrix4} [matrix] A matrix by which to multiply all the vertices.
 * @return {Object.<string, TypedArray>} The created plane vertices.
 * @memberOf module:twgl/primitives
 */


function createPlaneVertices(width, depth, subdivisionsWidth, subdivisionsDepth, matrix) {
  width = width || 1;
  depth = depth || 1;
  subdivisionsWidth = subdivisionsWidth || 1;
  subdivisionsDepth = subdivisionsDepth || 1;
  matrix = matrix || m4.identity();
  var numVertices = (subdivisionsWidth + 1) * (subdivisionsDepth + 1);
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);

  for (var z = 0; z <= subdivisionsDepth; z++) {
    for (var x = 0; x <= subdivisionsWidth; x++) {
      var u = x / subdivisionsWidth;
      var v = z / subdivisionsDepth;
      positions.push(width * u - width * 0.5, 0, depth * v - depth * 0.5);
      normals.push(0, 1, 0);
      texcoords.push(u, v);
    }
  }

  var numVertsAcross = subdivisionsWidth + 1;
  var indices = createAugmentedTypedArray(3, subdivisionsWidth * subdivisionsDepth * 2, Uint16Array);

  for (var _z = 0; _z < subdivisionsDepth; _z++) {
    // eslint-disable-line
    for (var _x = 0; _x < subdivisionsWidth; _x++) {
      // eslint-disable-line
      // Make triangle 1 of quad.
      indices.push((_z + 0) * numVertsAcross + _x, (_z + 1) * numVertsAcross + _x, (_z + 0) * numVertsAcross + _x + 1); // Make triangle 2 of quad.

      indices.push((_z + 1) * numVertsAcross + _x, (_z + 1) * numVertsAcross + _x + 1, (_z + 0) * numVertsAcross + _x + 1);
    }
  }

  var arrays = reorientVertices({
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  }, matrix);
  return arrays;
}
/**
 * Creates sphere BufferInfo.
 *
 * The created sphere has position, normal, and texcoord data
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius radius of the sphere.
 * @param {number} subdivisionsAxis number of steps around the sphere.
 * @param {number} subdivisionsHeight number of vertically on the sphere.
 * @param {number} [opt_startLatitudeInRadians] where to start the
 *     top of the sphere. Default = 0.
 * @param {number} [opt_endLatitudeInRadians] Where to end the
 *     bottom of the sphere. Default = Math.PI.
 * @param {number} [opt_startLongitudeInRadians] where to start
 *     wrapping the sphere. Default = 0.
 * @param {number} [opt_endLongitudeInRadians] where to end
 *     wrapping the sphere. Default = 2 * Math.PI.
 * @return {module:twgl.BufferInfo} The created sphere BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createSphereBufferInfo
 */

/**
 * Creates sphere buffers.
 *
 * The created sphere has position, normal, and texcoord data
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius radius of the sphere.
 * @param {number} subdivisionsAxis number of steps around the sphere.
 * @param {number} subdivisionsHeight number of vertically on the sphere.
 * @param {number} [opt_startLatitudeInRadians] where to start the
 *     top of the sphere. Default = 0.
 * @param {number} [opt_endLatitudeInRadians] Where to end the
 *     bottom of the sphere. Default = Math.PI.
 * @param {number} [opt_startLongitudeInRadians] where to start
 *     wrapping the sphere. Default = 0.
 * @param {number} [opt_endLongitudeInRadians] where to end
 *     wrapping the sphere. Default = 2 * Math.PI.
 * @return {Object.<string, WebGLBuffer>} The created sphere buffers.
 * @memberOf module:twgl/primitives
 * @function createSphereBuffers
 */

/**
 * Creates sphere vertices.
 *
 * The created sphere has position, normal, and texcoord data
 *
 * @param {number} radius radius of the sphere.
 * @param {number} subdivisionsAxis number of steps around the sphere.
 * @param {number} subdivisionsHeight number of vertically on the sphere.
 * @param {number} [opt_startLatitudeInRadians] where to start the
 *     top of the sphere. Default = 0.
 * @param {number} [opt_endLatitudeInRadians] Where to end the
 *     bottom of the sphere. Default = Math.PI.
 * @param {number} [opt_startLongitudeInRadians] where to start
 *     wrapping the sphere. Default = 0.
 * @param {number} [opt_endLongitudeInRadians] where to end
 *     wrapping the sphere. Default = 2 * Math.PI.
 * @return {Object.<string, TypedArray>} The created sphere vertices.
 * @memberOf module:twgl/primitives
 */


function createSphereVertices(radius, subdivisionsAxis, subdivisionsHeight, opt_startLatitudeInRadians, opt_endLatitudeInRadians, opt_startLongitudeInRadians, opt_endLongitudeInRadians) {
  if (subdivisionsAxis <= 0 || subdivisionsHeight <= 0) {
    throw Error('subdivisionAxis and subdivisionHeight must be > 0');
  }

  opt_startLatitudeInRadians = opt_startLatitudeInRadians || 0;
  opt_endLatitudeInRadians = opt_endLatitudeInRadians || Math.PI;
  opt_startLongitudeInRadians = opt_startLongitudeInRadians || 0;
  opt_endLongitudeInRadians = opt_endLongitudeInRadians || Math.PI * 2;
  var latRange = opt_endLatitudeInRadians - opt_startLatitudeInRadians;
  var longRange = opt_endLongitudeInRadians - opt_startLongitudeInRadians; // We are going to generate our sphere by iterating through its
  // spherical coordinates and generating 2 triangles for each quad on a
  // ring of the sphere.

  var numVertices = (subdivisionsAxis + 1) * (subdivisionsHeight + 1);
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices); // Generate the individual vertices in our vertex buffer.

  for (var y = 0; y <= subdivisionsHeight; y++) {
    for (var x = 0; x <= subdivisionsAxis; x++) {
      // Generate a vertex based on its spherical coordinates
      var u = x / subdivisionsAxis;
      var v = y / subdivisionsHeight;
      var theta = longRange * u;
      var phi = latRange * v;
      var sinTheta = Math.sin(theta);
      var cosTheta = Math.cos(theta);
      var sinPhi = Math.sin(phi);
      var cosPhi = Math.cos(phi);
      var ux = cosTheta * sinPhi;
      var uy = cosPhi;
      var uz = sinTheta * sinPhi;
      positions.push(radius * ux, radius * uy, radius * uz);
      normals.push(ux, uy, uz);
      texcoords.push(1 - u, v);
    }
  }

  var numVertsAround = subdivisionsAxis + 1;
  var indices = createAugmentedTypedArray(3, subdivisionsAxis * subdivisionsHeight * 2, Uint16Array);

  for (var _x2 = 0; _x2 < subdivisionsAxis; _x2++) {
    // eslint-disable-line
    for (var _y = 0; _y < subdivisionsHeight; _y++) {
      // eslint-disable-line
      // Make triangle 1 of quad.
      indices.push((_y + 0) * numVertsAround + _x2, (_y + 0) * numVertsAround + _x2 + 1, (_y + 1) * numVertsAround + _x2); // Make triangle 2 of quad.

      indices.push((_y + 1) * numVertsAround + _x2, (_y + 0) * numVertsAround + _x2 + 1, (_y + 1) * numVertsAround + _x2 + 1);
    }
  }

  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * Array of the indices of corners of each face of a cube.
 * @type {Array.<number[]>}
 */


var CUBE_FACE_INDICES = [[3, 7, 5, 1], // right
[6, 2, 0, 4], // left
[6, 7, 3, 2], // ??
[0, 1, 5, 4], // ??
[7, 6, 4, 5], // front
[2, 3, 1, 0]];
/**
 * Creates a BufferInfo for a cube.
 *
 * The cube is created around the origin. (-size / 2, size / 2).
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [size] width, height and depth of the cube.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createCubeBufferInfo
 */

/**
 * Creates the buffers and indices for a cube.
 *
 * The cube is created around the origin. (-size / 2, size / 2).
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} [size] width, height and depth of the cube.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function createCubeBuffers
 */

/**
 * Creates the vertices and indices for a cube.
 *
 * The cube is created around the origin. (-size / 2, size / 2).
 *
 * @param {number} [size] width, height and depth of the cube.
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */

function createCubeVertices(size) {
  size = size || 1;
  var k = size / 2;
  var cornerVertices = [[-k, -k, -k], [+k, -k, -k], [-k, +k, -k], [+k, +k, -k], [-k, -k, +k], [+k, -k, +k], [-k, +k, +k], [+k, +k, +k]];
  var faceNormals = [[+1, +0, +0], [-1, +0, +0], [+0, +1, +0], [+0, -1, +0], [+0, +0, +1], [+0, +0, -1]];
  var uvCoords = [[1, 0], [0, 0], [0, 1], [1, 1]];
  var numVertices = 6 * 4;
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);
  var indices = createAugmentedTypedArray(3, 6 * 2, Uint16Array);

  for (var f = 0; f < 6; ++f) {
    var faceIndices = CUBE_FACE_INDICES[f];

    for (var v = 0; v < 4; ++v) {
      var position = cornerVertices[faceIndices[v]];
      var normal = faceNormals[f];
      var uv = uvCoords[v]; // Each face needs all four vertices because the normals and texture
      // coordinates are not all the same.

      positions.push(position);
      normals.push(normal);
      texcoords.push(uv);
    } // Two triangles make a square face.


    var offset = 4 * f;
    indices.push(offset + 0, offset + 1, offset + 2);
    indices.push(offset + 0, offset + 2, offset + 3);
  }

  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * Creates a BufferInfo for a truncated cone, which is like a cylinder
 * except that it has different top and bottom radii. A truncated cone
 * can also be used to create cylinders and regular cones. The
 * truncated cone will be created centered about the origin, with the
 * y axis as its vertical axis.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} bottomRadius Bottom radius of truncated cone.
 * @param {number} topRadius Top radius of truncated cone.
 * @param {number} height Height of truncated cone.
 * @param {number} radialSubdivisions The number of subdivisions around the
 *     truncated cone.
 * @param {number} verticalSubdivisions The number of subdivisions down the
 *     truncated cone.
 * @param {boolean} [opt_topCap] Create top cap. Default = true.
 * @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
 * @return {module:twgl.BufferInfo} The created cone BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createTruncatedConeBufferInfo
 */

/**
 * Creates buffers for a truncated cone, which is like a cylinder
 * except that it has different top and bottom radii. A truncated cone
 * can also be used to create cylinders and regular cones. The
 * truncated cone will be created centered about the origin, with the
 * y axis as its vertical axis.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} bottomRadius Bottom radius of truncated cone.
 * @param {number} topRadius Top radius of truncated cone.
 * @param {number} height Height of truncated cone.
 * @param {number} radialSubdivisions The number of subdivisions around the
 *     truncated cone.
 * @param {number} verticalSubdivisions The number of subdivisions down the
 *     truncated cone.
 * @param {boolean} [opt_topCap] Create top cap. Default = true.
 * @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
 * @return {Object.<string, WebGLBuffer>} The created cone buffers.
 * @memberOf module:twgl/primitives
 * @function createTruncatedConeBuffers
 */

/**
 * Creates vertices for a truncated cone, which is like a cylinder
 * except that it has different top and bottom radii. A truncated cone
 * can also be used to create cylinders and regular cones. The
 * truncated cone will be created centered about the origin, with the
 * y axis as its vertical axis. .
 *
 * @param {number} bottomRadius Bottom radius of truncated cone.
 * @param {number} topRadius Top radius of truncated cone.
 * @param {number} height Height of truncated cone.
 * @param {number} radialSubdivisions The number of subdivisions around the
 *     truncated cone.
 * @param {number} verticalSubdivisions The number of subdivisions down the
 *     truncated cone.
 * @param {boolean} [opt_topCap] Create top cap. Default = true.
 * @param {boolean} [opt_bottomCap] Create bottom cap. Default = true.
 * @return {Object.<string, TypedArray>} The created cone vertices.
 * @memberOf module:twgl/primitives
 */


function createTruncatedConeVertices(bottomRadius, topRadius, height, radialSubdivisions, verticalSubdivisions, opt_topCap, opt_bottomCap) {
  if (radialSubdivisions < 3) {
    throw Error('radialSubdivisions must be 3 or greater');
  }

  if (verticalSubdivisions < 1) {
    throw Error('verticalSubdivisions must be 1 or greater');
  }

  var topCap = opt_topCap === undefined ? true : opt_topCap;
  var bottomCap = opt_bottomCap === undefined ? true : opt_bottomCap;
  var extra = (topCap ? 2 : 0) + (bottomCap ? 2 : 0);
  var numVertices = (radialSubdivisions + 1) * (verticalSubdivisions + 1 + extra);
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);
  var indices = createAugmentedTypedArray(3, radialSubdivisions * (verticalSubdivisions + extra) * 2, Uint16Array);
  var vertsAroundEdge = radialSubdivisions + 1; // The slant of the cone is constant across its surface

  var slant = Math.atan2(bottomRadius - topRadius, height);
  var cosSlant = Math.cos(slant);
  var sinSlant = Math.sin(slant);
  var start = topCap ? -2 : 0;
  var end = verticalSubdivisions + (bottomCap ? 2 : 0);

  for (var yy = start; yy <= end; ++yy) {
    var v = yy / verticalSubdivisions;
    var y = height * v;
    var ringRadius = void 0;

    if (yy < 0) {
      y = 0;
      v = 1;
      ringRadius = bottomRadius;
    } else if (yy > verticalSubdivisions) {
      y = height;
      v = 1;
      ringRadius = topRadius;
    } else {
      ringRadius = bottomRadius + (topRadius - bottomRadius) * (yy / verticalSubdivisions);
    }

    if (yy === -2 || yy === verticalSubdivisions + 2) {
      ringRadius = 0;
      v = 0;
    }

    y -= height / 2;

    for (var ii = 0; ii < vertsAroundEdge; ++ii) {
      var sin = Math.sin(ii * Math.PI * 2 / radialSubdivisions);
      var cos = Math.cos(ii * Math.PI * 2 / radialSubdivisions);
      positions.push(sin * ringRadius, y, cos * ringRadius);
      normals.push(yy < 0 || yy > verticalSubdivisions ? 0 : sin * cosSlant, yy < 0 ? -1 : yy > verticalSubdivisions ? 1 : sinSlant, yy < 0 || yy > verticalSubdivisions ? 0 : cos * cosSlant);
      texcoords.push(ii / radialSubdivisions, 1 - v);
    }
  }

  for (var _yy = 0; _yy < verticalSubdivisions + extra; ++_yy) {
    // eslint-disable-line
    for (var _ii = 0; _ii < radialSubdivisions; ++_ii) {
      // eslint-disable-line
      indices.push(vertsAroundEdge * (_yy + 0) + 0 + _ii, vertsAroundEdge * (_yy + 0) + 1 + _ii, vertsAroundEdge * (_yy + 1) + 1 + _ii);
      indices.push(vertsAroundEdge * (_yy + 0) + 0 + _ii, vertsAroundEdge * (_yy + 1) + 1 + _ii, vertsAroundEdge * (_yy + 1) + 0 + _ii);
    }
  }

  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * Expands RLE data
 * @param {number[]} rleData data in format of run-length, x, y, z, run-length, x, y, z
 * @param {number[]} [padding] value to add each entry with.
 * @return {number[]} the expanded rleData
 */


function expandRLEData(rleData, padding) {
  padding = padding || [];
  var data = [];

  for (var ii = 0; ii < rleData.length; ii += 4) {
    var runLength = rleData[ii];
    var element = rleData.slice(ii + 1, ii + 4);
    element.push.apply(element, padding);

    for (var jj = 0; jj < runLength; ++jj) {
      data.push.apply(data, element);
    }
  }

  return data;
}
/**
 * Creates 3D 'F' BufferInfo.
 * An 'F' is useful because you can easily tell which way it is oriented.
 * The created 'F' has position, normal, texcoord, and color buffers.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function create3DFBufferInfo
 */

/**
 * Creates 3D 'F' buffers.
 * An 'F' is useful because you can easily tell which way it is oriented.
 * The created 'F' has position, normal, texcoord, and color buffers.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function create3DFBuffers
 */

/**
 * Creates 3D 'F' vertices.
 * An 'F' is useful because you can easily tell which way it is oriented.
 * The created 'F' has position, normal, texcoord, and color arrays.
 *
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */


function create3DFVertices() {
  var positions = [// left column front
  0, 0, 0, 0, 150, 0, 30, 0, 0, 0, 150, 0, 30, 150, 0, 30, 0, 0, // top rung front
  30, 0, 0, 30, 30, 0, 100, 0, 0, 30, 30, 0, 100, 30, 0, 100, 0, 0, // middle rung front
  30, 60, 0, 30, 90, 0, 67, 60, 0, 30, 90, 0, 67, 90, 0, 67, 60, 0, // left column back
  0, 0, 30, 30, 0, 30, 0, 150, 30, 0, 150, 30, 30, 0, 30, 30, 150, 30, // top rung back
  30, 0, 30, 100, 0, 30, 30, 30, 30, 30, 30, 30, 100, 0, 30, 100, 30, 30, // middle rung back
  30, 60, 30, 67, 60, 30, 30, 90, 30, 30, 90, 30, 67, 60, 30, 67, 90, 30, // top
  0, 0, 0, 100, 0, 0, 100, 0, 30, 0, 0, 0, 100, 0, 30, 0, 0, 30, // top rung front
  100, 0, 0, 100, 30, 0, 100, 30, 30, 100, 0, 0, 100, 30, 30, 100, 0, 30, // under top rung
  30, 30, 0, 30, 30, 30, 100, 30, 30, 30, 30, 0, 100, 30, 30, 100, 30, 0, // between top rung and middle
  30, 30, 0, 30, 60, 30, 30, 30, 30, 30, 30, 0, 30, 60, 0, 30, 60, 30, // top of middle rung
  30, 60, 0, 67, 60, 30, 30, 60, 30, 30, 60, 0, 67, 60, 0, 67, 60, 30, // front of middle rung
  67, 60, 0, 67, 90, 30, 67, 60, 30, 67, 60, 0, 67, 90, 0, 67, 90, 30, // bottom of middle rung.
  30, 90, 0, 30, 90, 30, 67, 90, 30, 30, 90, 0, 67, 90, 30, 67, 90, 0, // front of bottom
  30, 90, 0, 30, 150, 30, 30, 90, 30, 30, 90, 0, 30, 150, 0, 30, 150, 30, // bottom
  0, 150, 0, 0, 150, 30, 30, 150, 30, 0, 150, 0, 30, 150, 30, 30, 150, 0, // left side
  0, 0, 0, 0, 0, 30, 0, 150, 30, 0, 0, 0, 0, 150, 30, 0, 150, 0];
  var texcoords = [// left column front
  0.22, 0.19, 0.22, 0.79, 0.34, 0.19, 0.22, 0.79, 0.34, 0.79, 0.34, 0.19, // top rung front
  0.34, 0.19, 0.34, 0.31, 0.62, 0.19, 0.34, 0.31, 0.62, 0.31, 0.62, 0.19, // middle rung front
  0.34, 0.43, 0.34, 0.55, 0.49, 0.43, 0.34, 0.55, 0.49, 0.55, 0.49, 0.43, // left column back
  0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // top rung back
  0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // middle rung back
  0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, // top
  0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, // top rung front
  0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, // under top rung
  0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // between top rung and middle
  0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // top of middle rung
  0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // front of middle rung
  0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // bottom of middle rung.
  0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // front of bottom
  0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, // bottom
  0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, // left side
  0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0];
  var normals = expandRLEData([// left column front
  // top rung front
  // middle rung front
  18, 0, 0, 1, // left column back
  // top rung back
  // middle rung back
  18, 0, 0, -1, // top
  6, 0, 1, 0, // top rung front
  6, 1, 0, 0, // under top rung
  6, 0, -1, 0, // between top rung and middle
  6, 1, 0, 0, // top of middle rung
  6, 0, 1, 0, // front of middle rung
  6, 1, 0, 0, // bottom of middle rung.
  6, 0, -1, 0, // front of bottom
  6, 1, 0, 0, // bottom
  6, 0, -1, 0, // left side
  6, -1, 0, 0]);
  var colors = expandRLEData([// left column front
  // top rung front
  // middle rung front
  18, 200, 70, 120, // left column back
  // top rung back
  // middle rung back
  18, 80, 70, 200, // top
  6, 70, 200, 210, // top rung front
  6, 200, 200, 70, // under top rung
  6, 210, 100, 70, // between top rung and middle
  6, 210, 160, 70, // top of middle rung
  6, 70, 180, 210, // front of middle rung
  6, 100, 70, 210, // bottom of middle rung.
  6, 76, 210, 100, // front of bottom
  6, 140, 210, 80, // bottom
  6, 90, 130, 110, // left side
  6, 160, 160, 220], [255]);
  var numVerts = positions.length / 3;
  var arrays = {
    position: createAugmentedTypedArray(3, numVerts),
    texcoord: createAugmentedTypedArray(2, numVerts),
    normal: createAugmentedTypedArray(3, numVerts),
    color: createAugmentedTypedArray(4, numVerts, Uint8Array),
    indices: createAugmentedTypedArray(3, numVerts / 3, Uint16Array)
  };
  arrays.position.push(positions);
  arrays.texcoord.push(texcoords);
  arrays.normal.push(normals);
  arrays.color.push(colors);

  for (var ii = 0; ii < numVerts; ++ii) {
    arrays.indices.push(ii);
  }

  return arrays;
}
/**
 * Creates cresent BufferInfo.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} verticalRadius The vertical radius of the cresent.
 * @param {number} outerRadius The outer radius of the cresent.
 * @param {number} innerRadius The inner radius of the cresent.
 * @param {number} thickness The thickness of the cresent.
 * @param {number} subdivisionsDown number of steps around the cresent.
 * @param {number} subdivisionsThick number of vertically on the cresent.
 * @param {number} [startOffset] Where to start arc. Default 0.
 * @param {number} [endOffset] Where to end arg. Default 1.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createCresentBufferInfo
 */

/**
 * Creates cresent buffers.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} verticalRadius The vertical radius of the cresent.
 * @param {number} outerRadius The outer radius of the cresent.
 * @param {number} innerRadius The inner radius of the cresent.
 * @param {number} thickness The thickness of the cresent.
 * @param {number} subdivisionsDown number of steps around the cresent.
 * @param {number} subdivisionsThick number of vertically on the cresent.
 * @param {number} [startOffset] Where to start arc. Default 0.
 * @param {number} [endOffset] Where to end arg. Default 1.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function createCresentBuffers
 */

/**
 * Creates cresent vertices.
 *
 * @param {number} verticalRadius The vertical radius of the cresent.
 * @param {number} outerRadius The outer radius of the cresent.
 * @param {number} innerRadius The inner radius of the cresent.
 * @param {number} thickness The thickness of the cresent.
 * @param {number} subdivisionsDown number of steps around the cresent.
 * @param {number} subdivisionsThick number of vertically on the cresent.
 * @param {number} [startOffset] Where to start arc. Default 0.
 * @param {number} [endOffset] Where to end arg. Default 1.
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */


function createCresentVertices(verticalRadius, outerRadius, innerRadius, thickness, subdivisionsDown, startOffset, endOffset) {
  if (subdivisionsDown <= 0) {
    throw Error('subdivisionDown must be > 0');
  }

  startOffset = startOffset || 0;
  endOffset = endOffset || 1;
  var subdivisionsThick = 2;
  var offsetRange = endOffset - startOffset;
  var numVertices = (subdivisionsDown + 1) * 2 * (2 + subdivisionsThick);
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);

  function lerp(a, b, s) {
    return a + (b - a) * s;
  }

  function createArc(arcRadius, x, normalMult, normalAdd, uMult, uAdd) {
    for (var z = 0; z <= subdivisionsDown; z++) {
      var uBack = x / (subdivisionsThick - 1);
      var v = z / subdivisionsDown;
      var xBack = (uBack - 0.5) * 2;
      var angle = (startOffset + v * offsetRange) * Math.PI;
      var s = Math.sin(angle);
      var c = Math.cos(angle);
      var radius = lerp(verticalRadius, arcRadius, s);
      var px = xBack * thickness;
      var py = c * verticalRadius;
      var pz = s * radius;
      positions.push(px, py, pz);
      var n = v3.add(v3.multiply([0, s, c], normalMult), normalAdd);
      normals.push(n);
      texcoords.push(uBack * uMult + uAdd, v);
    }
  } // Generate the individual vertices in our vertex buffer.


  for (var x = 0; x < subdivisionsThick; x++) {
    var uBack = (x / (subdivisionsThick - 1) - 0.5) * 2;
    createArc(outerRadius, x, [1, 1, 1], [0, 0, 0], 1, 0);
    createArc(outerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 0);
    createArc(innerRadius, x, [1, 1, 1], [0, 0, 0], 1, 0);
    createArc(innerRadius, x, [0, 0, 0], [uBack, 0, 0], 0, 1);
  } // Do outer surface.


  var indices = createAugmentedTypedArray(3, subdivisionsDown * 2 * (2 + subdivisionsThick), Uint16Array);

  function createSurface(leftArcOffset, rightArcOffset) {
    for (var z = 0; z < subdivisionsDown; ++z) {
      // Make triangle 1 of quad.
      indices.push(leftArcOffset + z + 0, leftArcOffset + z + 1, rightArcOffset + z + 0); // Make triangle 2 of quad.

      indices.push(leftArcOffset + z + 1, rightArcOffset + z + 1, rightArcOffset + z + 0);
    }
  }

  var numVerticesDown = subdivisionsDown + 1; // front

  createSurface(numVerticesDown * 0, numVerticesDown * 4); // right

  createSurface(numVerticesDown * 5, numVerticesDown * 7); // back

  createSurface(numVerticesDown * 6, numVerticesDown * 2); // left

  createSurface(numVerticesDown * 3, numVerticesDown * 1);
  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * Creates cylinder BufferInfo. The cylinder will be created around the origin
 * along the y-axis.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius Radius of cylinder.
 * @param {number} height Height of cylinder.
 * @param {number} radialSubdivisions The number of subdivisions around the cylinder.
 * @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
 * @param {boolean} [topCap] Create top cap. Default = true.
 * @param {boolean} [bottomCap] Create bottom cap. Default = true.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createCylinderBufferInfo
 */

/**
 * Creates cylinder buffers. The cylinder will be created around the origin
 * along the y-axis.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius Radius of cylinder.
 * @param {number} height Height of cylinder.
 * @param {number} radialSubdivisions The number of subdivisions around the cylinder.
 * @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
 * @param {boolean} [topCap] Create top cap. Default = true.
 * @param {boolean} [bottomCap] Create bottom cap. Default = true.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function createCylinderBuffers
 */

/**
 * Creates cylinder vertices. The cylinder will be created around the origin
 * along the y-axis.
 *
 * @param {number} radius Radius of cylinder.
 * @param {number} height Height of cylinder.
 * @param {number} radialSubdivisions The number of subdivisions around the cylinder.
 * @param {number} verticalSubdivisions The number of subdivisions down the cylinder.
 * @param {boolean} [topCap] Create top cap. Default = true.
 * @param {boolean} [bottomCap] Create bottom cap. Default = true.
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */


function createCylinderVertices(radius, height, radialSubdivisions, verticalSubdivisions, topCap, bottomCap) {
  return createTruncatedConeVertices(radius, radius, height, radialSubdivisions, verticalSubdivisions, topCap, bottomCap);
}
/**
 * Creates BufferInfo for a torus
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius radius of center of torus circle.
 * @param {number} thickness radius of torus ring.
 * @param {number} radialSubdivisions The number of subdivisions around the torus.
 * @param {number} bodySubdivisions The number of subdivisions around the body torus.
 * @param {boolean} [startAngle] start angle in radians. Default = 0.
 * @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createTorusBufferInfo
 */

/**
 * Creates buffers for a torus
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius radius of center of torus circle.
 * @param {number} thickness radius of torus ring.
 * @param {number} radialSubdivisions The number of subdivisions around the torus.
 * @param {number} bodySubdivisions The number of subdivisions around the body torus.
 * @param {boolean} [startAngle] start angle in radians. Default = 0.
 * @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function createTorusBuffers
 */

/**
 * Creates vertices for a torus
 *
 * @param {number} radius radius of center of torus circle.
 * @param {number} thickness radius of torus ring.
 * @param {number} radialSubdivisions The number of subdivisions around the torus.
 * @param {number} bodySubdivisions The number of subdivisions around the body torus.
 * @param {boolean} [startAngle] start angle in radians. Default = 0.
 * @param {boolean} [endAngle] end angle in radians. Default = Math.PI * 2.
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */


function createTorusVertices(radius, thickness, radialSubdivisions, bodySubdivisions, startAngle, endAngle) {
  if (radialSubdivisions < 3) {
    throw Error('radialSubdivisions must be 3 or greater');
  }

  if (bodySubdivisions < 3) {
    throw Error('verticalSubdivisions must be 3 or greater');
  }

  startAngle = startAngle || 0;
  endAngle = endAngle || Math.PI * 2;
  var range = endAngle - startAngle;
  var radialParts = radialSubdivisions + 1;
  var bodyParts = bodySubdivisions + 1;
  var numVertices = radialParts * bodyParts;
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);
  var indices = createAugmentedTypedArray(3, radialSubdivisions * bodySubdivisions * 2, Uint16Array);

  for (var slice = 0; slice < bodyParts; ++slice) {
    var v = slice / bodySubdivisions;
    var sliceAngle = v * Math.PI * 2;
    var sliceSin = Math.sin(sliceAngle);
    var ringRadius = radius + sliceSin * thickness;
    var ny = Math.cos(sliceAngle);
    var y = ny * thickness;

    for (var ring = 0; ring < radialParts; ++ring) {
      var u = ring / radialSubdivisions;
      var ringAngle = startAngle + u * range;
      var xSin = Math.sin(ringAngle);
      var zCos = Math.cos(ringAngle);
      var x = xSin * ringRadius;
      var z = zCos * ringRadius;
      var nx = xSin * sliceSin;
      var nz = zCos * sliceSin;
      positions.push(x, y, z);
      normals.push(nx, ny, nz);
      texcoords.push(u, 1 - v);
    }
  }

  for (var _slice = 0; _slice < bodySubdivisions; ++_slice) {
    // eslint-disable-line
    for (var _ring = 0; _ring < radialSubdivisions; ++_ring) {
      // eslint-disable-line
      var nextRingIndex = 1 + _ring;
      var nextSliceIndex = 1 + _slice;
      indices.push(radialParts * _slice + _ring, radialParts * nextSliceIndex + _ring, radialParts * _slice + nextRingIndex);
      indices.push(radialParts * nextSliceIndex + _ring, radialParts * nextSliceIndex + nextRingIndex, radialParts * _slice + nextRingIndex);
    }
  }

  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * Creates a disc BufferInfo. The disc will be in the xz plane, centered at
 * the origin. When creating, at least 3 divisions, or pie
 * pieces, need to be specified, otherwise the triangles making
 * up the disc will be degenerate. You can also specify the
 * number of radial pieces `stacks`. A value of 1 for
 * stacks will give you a simple disc of pie pieces.  If you
 * want to create an annulus you can set `innerRadius` to a
 * value > 0. Finally, `stackPower` allows you to have the widths
 * increase or decrease as you move away from the center. This
 * is particularly useful when using the disc as a ground plane
 * with a fixed camera such that you don't need the resolution
 * of small triangles near the perimeter. For example, a value
 * of 2 will produce stacks whose ouside radius increases with
 * the square of the stack index. A value of 1 will give uniform
 * stacks.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius Radius of the ground plane.
 * @param {number} divisions Number of triangles in the ground plane (at least 3).
 * @param {number} [stacks] Number of radial divisions (default=1).
 * @param {number} [innerRadius] Default 0.
 * @param {number} [stackPower] Power to raise stack size to for decreasing width.
 * @return {module:twgl.BufferInfo} The created BufferInfo.
 * @memberOf module:twgl/primitives
 * @function createDiscBufferInfo
 */

/**
 * Creates disc buffers. The disc will be in the xz plane, centered at
 * the origin. When creating, at least 3 divisions, or pie
 * pieces, need to be specified, otherwise the triangles making
 * up the disc will be degenerate. You can also specify the
 * number of radial pieces `stacks`. A value of 1 for
 * stacks will give you a simple disc of pie pieces.  If you
 * want to create an annulus you can set `innerRadius` to a
 * value > 0. Finally, `stackPower` allows you to have the widths
 * increase or decrease as you move away from the center. This
 * is particularly useful when using the disc as a ground plane
 * with a fixed camera such that you don't need the resolution
 * of small triangles near the perimeter. For example, a value
 * of 2 will produce stacks whose ouside radius increases with
 * the square of the stack index. A value of 1 will give uniform
 * stacks.
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext.
 * @param {number} radius Radius of the ground plane.
 * @param {number} divisions Number of triangles in the ground plane (at least 3).
 * @param {number} [stacks] Number of radial divisions (default=1).
 * @param {number} [innerRadius] Default 0.
 * @param {number} [stackPower] Power to raise stack size to for decreasing width.
 * @return {Object.<string, WebGLBuffer>} The created buffers.
 * @memberOf module:twgl/primitives
 * @function createDiscBuffers
 */

/**
 * Creates disc vertices. The disc will be in the xz plane, centered at
 * the origin. When creating, at least 3 divisions, or pie
 * pieces, need to be specified, otherwise the triangles making
 * up the disc will be degenerate. You can also specify the
 * number of radial pieces `stacks`. A value of 1 for
 * stacks will give you a simple disc of pie pieces.  If you
 * want to create an annulus you can set `innerRadius` to a
 * value > 0. Finally, `stackPower` allows you to have the widths
 * increase or decrease as you move away from the center. This
 * is particularly useful when using the disc as a ground plane
 * with a fixed camera such that you don't need the resolution
 * of small triangles near the perimeter. For example, a value
 * of 2 will produce stacks whose ouside radius increases with
 * the square of the stack index. A value of 1 will give uniform
 * stacks.
 *
 * @param {number} radius Radius of the ground plane.
 * @param {number} divisions Number of triangles in the ground plane (at least 3).
 * @param {number} [stacks] Number of radial divisions (default=1).
 * @param {number} [innerRadius] Default 0.
 * @param {number} [stackPower] Power to raise stack size to for decreasing width.
 * @return {Object.<string, TypedArray>} The created vertices.
 * @memberOf module:twgl/primitives
 */


function createDiscVertices(radius, divisions, stacks, innerRadius, stackPower) {
  if (divisions < 3) {
    throw Error('divisions must be at least 3');
  }

  stacks = stacks ? stacks : 1;
  stackPower = stackPower ? stackPower : 1;
  innerRadius = innerRadius ? innerRadius : 0; // Note: We don't share the center vertex because that would
  // mess up texture coordinates.

  var numVertices = (divisions + 1) * (stacks + 1);
  var positions = createAugmentedTypedArray(3, numVertices);
  var normals = createAugmentedTypedArray(3, numVertices);
  var texcoords = createAugmentedTypedArray(2, numVertices);
  var indices = createAugmentedTypedArray(3, stacks * divisions * 2, Uint16Array);
  var firstIndex = 0;
  var radiusSpan = radius - innerRadius;
  var pointsPerStack = divisions + 1; // Build the disk one stack at a time.

  for (var stack = 0; stack <= stacks; ++stack) {
    var stackRadius = innerRadius + radiusSpan * Math.pow(stack / stacks, stackPower);

    for (var i = 0; i <= divisions; ++i) {
      var theta = 2.0 * Math.PI * i / divisions;
      var x = stackRadius * Math.cos(theta);
      var z = stackRadius * Math.sin(theta);
      positions.push(x, 0, z);
      normals.push(0, 1, 0);
      texcoords.push(1 - i / divisions, stack / stacks);

      if (stack > 0 && i !== divisions) {
        // a, b, c and d are the indices of the vertices of a quad.  unless
        // the current stack is the one closest to the center, in which case
        // the vertices a and b connect to the center vertex.
        var a = firstIndex + (i + 1);
        var b = firstIndex + i;
        var c = firstIndex + i - pointsPerStack;
        var d = firstIndex + (i + 1) - pointsPerStack; // Make a quad of the vertices a, b, c, d.

        indices.push(a, b, c);
        indices.push(a, c, d);
      }
    }

    firstIndex += divisions + 1;
  }

  return {
    position: positions,
    normal: normals,
    texcoord: texcoords,
    indices: indices
  };
}
/**
 * creates a random integer between 0 and range - 1 inclusive.
 * @param {number} range
 * @return {number} random value between 0 and range - 1 inclusive.
 */


function randInt(range) {
  return Math.random() * range | 0;
}
/**
 * Used to supply random colors
 * @callback RandomColorFunc
 * @param {number} ndx index of triangle/quad if unindexed or index of vertex if indexed
 * @param {number} channel 0 = red, 1 = green, 2 = blue, 3 = alpha
 * @return {number} a number from 0 to 255
 * @memberOf module:twgl/primitives
 */

/**
 * @typedef {Object} RandomVerticesOptions
 * @property {number} [vertsPerColor] Defaults to 3 for non-indexed vertices
 * @property {module:twgl/primitives.RandomColorFunc} [rand] A function to generate random numbers
 * @memberOf module:twgl/primitives
 */

/**
 * Creates an augmentedTypedArray of random vertex colors.
 * If the vertices are indexed (have an indices array) then will
 * just make random colors. Otherwise assumes they are triangles
 * and makes one random color for every 3 vertices.
 * @param {Object.<string, augmentedTypedArray>} vertices Vertices as returned from one of the createXXXVertices functions.
 * @param {module:twgl/primitives.RandomVerticesOptions} [options] options.
 * @return {Object.<string, augmentedTypedArray>} same vertices as passed in with `color` added.
 * @memberOf module:twgl/primitives
 */


function makeRandomVertexColors(vertices, options) {
  options = options || {};
  var numElements = vertices.position.numElements;
  var vcolors = createAugmentedTypedArray(4, numElements, Uint8Array);

  var rand = options.rand || function (ndx, channel) {
    return channel < 3 ? randInt(256) : 255;
  };

  vertices.color = vcolors;

  if (vertices.indices) {
    // just make random colors if index
    for (var ii = 0; ii < numElements; ++ii) {
      vcolors.push(rand(ii, 0), rand(ii, 1), rand(ii, 2), rand(ii, 3));
    }
  } else {
    // make random colors per triangle
    var numVertsPerColor = options.vertsPerColor || 3;
    var numSets = numElements / numVertsPerColor;

    for (var _ii2 = 0; _ii2 < numSets; ++_ii2) {
      // eslint-disable-line
      var color = [rand(_ii2, 0), rand(_ii2, 1), rand(_ii2, 2), rand(_ii2, 3)];

      for (var jj = 0; jj < numVertsPerColor; ++jj) {
        vcolors.push(color);
      }
    }
  }

  return vertices;
}
/**
 * creates a function that calls fn to create vertices and then
 * creates a buffers for them
 */


function createBufferFunc(fn) {
  return function (gl) {
    var arrays = fn.apply(this, Array.prototype.slice.call(arguments, 1));
    return attributes.createBuffersFromArrays(gl, arrays);
  };
}
/**
 * creates a function that calls fn to create vertices and then
 * creates a bufferInfo object for them
 */


function createBufferInfoFunc(fn) {
  return function (gl) {
    var arrays = fn.apply(null, Array.prototype.slice.call(arguments, 1));
    return attributes.createBufferInfoFromArrays(gl, arrays);
  };
}

var arraySpecPropertyNames = ["numComponents", "size", "type", "normalize", "stride", "offset", "attrib", "name", "attribName"];
/**
 * Copy elements from one array to another
 *
 * @param {Array|TypedArray} src source array
 * @param {Array|TypedArray} dst dest array
 * @param {number} dstNdx index in dest to copy src
 * @param {number} [offset] offset to add to copied values
 */

function copyElements(src, dst, dstNdx, offset) {
  offset = offset || 0;
  var length = src.length;

  for (var ii = 0; ii < length; ++ii) {
    dst[dstNdx + ii] = src[ii] + offset;
  }
}
/**
 * Creates an array of the same time
 *
 * @param {(number[]|ArrayBufferView|module:twgl.FullArraySpec)} srcArray array who's type to copy
 * @param {number} length size of new array
 * @return {(number[]|ArrayBufferView|module:twgl.FullArraySpec)} array with same type as srcArray
 */


function createArrayOfSameType(srcArray, length) {
  var arraySrc = getArray(srcArray);
  var newArray = new arraySrc.constructor(length);
  var newArraySpec = newArray; // If it appears to have been augmented make new one augemented

  if (arraySrc.numComponents && arraySrc.numElements) {
    augmentTypedArray(newArray, arraySrc.numComponents);
  } // If it was a fullspec make new one a fullspec


  if (srcArray.data) {
    newArraySpec = {
      data: newArray
    };
    helper.copyNamedProperties(arraySpecPropertyNames, srcArray, newArraySpec);
  }

  return newArraySpec;
}
/**
 * Concatinates sets of vertices
 *
 * Assumes the vertices match in composition. For example
 * if one set of vertices has positions, normals, and indices
 * all sets of vertices must have positions, normals, and indices
 * and of the same type.
 *
 * Example:
 *
 *      const cubeVertices = twgl.primtiives.createCubeVertices(2);
 *      const sphereVertices = twgl.primitives.createSphereVertices(1, 10, 10);
 *      // move the sphere 2 units up
 *      twgl.primitives.reorientVertices(
 *          sphereVertices, twgl.m4.translation([0, 2, 0]));
 *      // merge the sphere with the cube
 *      const cubeSphereVertices = twgl.primitives.concatVertices(
 *          [cubeVertices, sphereVertices]);
 *      // turn them into WebGL buffers and attrib data
 *      const bufferInfo = twgl.createBufferInfoFromArrays(gl, cubeSphereVertices);
 *
 * @param {module:twgl.Arrays[]} arrays Array of arrays of vertices
 * @return {module:twgl.Arrays} The concatinated vertices.
 * @memberOf module:twgl/primitives
 */


function concatVertices(arrayOfArrays) {
  var names = {};
  var baseName; // get names of all arrays.
  // and numElements for each set of vertices

  var _loop = function _loop(ii) {
    var arrays = arrayOfArrays[ii];
    Object.keys(arrays).forEach(function (name) {
      // eslint-disable-line
      if (!names[name]) {
        names[name] = [];
      }

      if (!baseName && name !== 'indices') {
        baseName = name;
      }

      var arrayInfo = arrays[name];
      var numComponents = getNumComponents(arrayInfo, name);
      var array = getArray(arrayInfo);
      var numElements = array.length / numComponents;
      names[name].push(numElements);
    });
  };

  for (var ii = 0; ii < arrayOfArrays.length; ++ii) {
    _loop(ii);
  } // compute length of combined array
  // and return one for reference


  function getLengthOfCombinedArrays(name) {
    var length = 0;
    var arraySpec;

    for (var ii = 0; ii < arrayOfArrays.length; ++ii) {
      var arrays = arrayOfArrays[ii];
      var arrayInfo = arrays[name];
      var array = getArray(arrayInfo);
      length += array.length;

      if (!arraySpec || arrayInfo.data) {
        arraySpec = arrayInfo;
      }
    }

    return {
      length: length,
      spec: arraySpec
    };
  }

  function copyArraysToNewArray(name, base, newArray) {
    var baseIndex = 0;
    var offset = 0;

    for (var ii = 0; ii < arrayOfArrays.length; ++ii) {
      var arrays = arrayOfArrays[ii];
      var arrayInfo = arrays[name];
      var array = getArray(arrayInfo);

      if (name === 'indices') {
        copyElements(array, newArray, offset, baseIndex);
        baseIndex += base[ii];
      } else {
        copyElements(array, newArray, offset);
      }

      offset += array.length;
    }
  }

  var base = names[baseName];
  var newArrays = {};
  Object.keys(names).forEach(function (name) {
    var info = getLengthOfCombinedArrays(name);
    var newArraySpec = createArrayOfSameType(info.spec, info.length);
    copyArraysToNewArray(name, base, getArray(newArraySpec));
    newArrays[name] = newArraySpec;
  });
  return newArrays;
}
/**
 * Creates a duplicate set of vertices
 *
 * This is useful for calling reorientVertices when you
 * also want to keep the original available
 *
 * @param {module:twgl.Arrays} arrays of vertices
 * @return {module:twgl.Arrays} The dupilicated vertices.
 * @memberOf module:twgl/primitives
 */


function duplicateVertices(arrays) {
  var newArrays = {};
  Object.keys(arrays).forEach(function (name) {
    var arraySpec = arrays[name];
    var srcArray = getArray(arraySpec);
    var newArraySpec = createArrayOfSameType(arraySpec, srcArray.length);
    copyElements(srcArray, getArray(newArraySpec), 0);
    newArrays[name] = newArraySpec;
  });
  return newArrays;
}

var create3DFBufferInfo = createBufferInfoFunc(create3DFVertices);
exports.create3DFBufferInfo = create3DFBufferInfo;
var create3DFBuffers = createBufferFunc(create3DFVertices);
exports.create3DFBuffers = create3DFBuffers;
var createCubeBufferInfo = createBufferInfoFunc(createCubeVertices);
exports.createCubeBufferInfo = createCubeBufferInfo;
var createCubeBuffers = createBufferFunc(createCubeVertices);
exports.createCubeBuffers = createCubeBuffers;
var createPlaneBufferInfo = createBufferInfoFunc(createPlaneVertices);
exports.createPlaneBufferInfo = createPlaneBufferInfo;
var createPlaneBuffers = createBufferFunc(createPlaneVertices);
exports.createPlaneBuffers = createPlaneBuffers;
var createSphereBufferInfo = createBufferInfoFunc(createSphereVertices);
exports.createSphereBufferInfo = createSphereBufferInfo;
var createSphereBuffers = createBufferFunc(createSphereVertices);
exports.createSphereBuffers = createSphereBuffers;
var createTruncatedConeBufferInfo = createBufferInfoFunc(createTruncatedConeVertices);
exports.createTruncatedConeBufferInfo = createTruncatedConeBufferInfo;
var createTruncatedConeBuffers = createBufferFunc(createTruncatedConeVertices);
exports.createTruncatedConeBuffers = createTruncatedConeBuffers;
var createXYQuadBufferInfo = createBufferInfoFunc(createXYQuadVertices);
exports.createXYQuadBufferInfo = createXYQuadBufferInfo;
var createXYQuadBuffers = createBufferFunc(createXYQuadVertices);
exports.createXYQuadBuffers = createXYQuadBuffers;
var createCresentBufferInfo = createBufferInfoFunc(createCresentVertices);
exports.createCresentBufferInfo = createCresentBufferInfo;
var createCresentBuffers = createBufferFunc(createCresentVertices);
exports.createCresentBuffers = createCresentBuffers;
var createCylinderBufferInfo = createBufferInfoFunc(createCylinderVertices);
exports.createCylinderBufferInfo = createCylinderBufferInfo;
var createCylinderBuffers = createBufferFunc(createCylinderVertices);
exports.createCylinderBuffers = createCylinderBuffers;
var createTorusBufferInfo = createBufferInfoFunc(createTorusVertices);
exports.createTorusBufferInfo = createTorusBufferInfo;
var createTorusBuffers = createBufferFunc(createTorusVertices);
exports.createTorusBuffers = createTorusBuffers;
var createDiscBufferInfo = createBufferInfoFunc(createDiscVertices);
exports.createDiscBufferInfo = createDiscBufferInfo;
var createDiscBuffers = createBufferFunc(createDiscVertices);
exports.createDiscBuffers = createDiscBuffers;

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

"use strict";


exports.__esModule = true;
var _exportNames = {
  addExtensionsToContext: true,
  getContext: true,
  getWebGLContext: true,
  resizeCanvasToDisplaySize: true,
  setDefaults: true
};
exports.addExtensionsToContext = addExtensionsToContext;
exports.getContext = getContext;
exports.getWebGLContext = getWebGLContext;
exports.resizeCanvasToDisplaySize = resizeCanvasToDisplaySize;
exports.setDefaults = setDefaults;

var attributes = _interopRequireWildcard(__webpack_require__(7));

Object.keys(attributes).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = attributes[key];
});

var textures = _interopRequireWildcard(__webpack_require__(8));

Object.keys(textures).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = textures[key];
});

var helper = _interopRequireWildcard(__webpack_require__(0));

var utils = _interopRequireWildcard(__webpack_require__(4));

Object.keys(utils).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = utils[key];
});

var _draw = __webpack_require__(12);

Object.keys(_draw).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _draw[key];
});

var _framebuffers = __webpack_require__(13);

Object.keys(_framebuffers).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _framebuffers[key];
});

var _programs = __webpack_require__(5);

Object.keys(_programs).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _programs[key];
});

var _typedarrays = __webpack_require__(1);

Object.keys(_typedarrays).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _typedarrays[key];
});

var _vertexArrays = __webpack_require__(14);

Object.keys(_vertexArrays).forEach(function (key) {
  if (key === "default" || key === "__esModule") return;
  if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
  exports[key] = _vertexArrays[key];
});

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * The main TWGL module.
 *
 * For most use cases you shouldn't need anything outside this module.
 * Exceptions between the stuff added to twgl-full (v3, m4, primitives)
 *
 * @module twgl
 * @borrows module:twgl/attributes.setAttribInfoBufferFromArray as setAttribInfoBufferFromArray
 * @borrows module:twgl/attributes.createBufferInfoFromArrays as createBufferInfoFromArrays
 * @borrows module:twgl/attributes.createVertexArrayInfo as createVertexArrayInfo
 * @borrows module:twgl/draw.drawBufferInfo as drawBufferInfo
 * @borrows module:twgl/draw.drawObjectList as drawObjectList
 * @borrows module:twgl/framebuffers.createFramebufferInfo as createFramebufferInfo
 * @borrows module:twgl/framebuffers.resizeFramebufferInfo as resizeFramebufferInfo
 * @borrows module:twgl/framebuffers.bindFramebufferInfo as bindFramebufferInfo
 * @borrows module:twgl/programs.createProgramInfo as createProgramInfo
 * @borrows module:twgl/programs.createUniformBlockInfo as createUniformBlockInfo
 * @borrows module:twgl/programs.bindUniformBlock as bindUniformBlock
 * @borrows module:twgl/programs.setUniformBlock as setUniformBlock
 * @borrows module:twgl/programs.setBlockUniforms as setBlockUniforms
 * @borrows module:twgl/programs.setUniforms as setUniforms
 * @borrows module:twgl/programs.setBuffersAndAttributes as setBuffersAndAttributes
 * @borrows module:twgl/textures.setTextureFromArray as setTextureFromArray
 * @borrows module:twgl/textures.createTexture as createTexture
 * @borrows module:twgl/textures.resizeTexture as resizeTexture
 * @borrows module:twgl/textures.createTextures as createTextures
 */
// make sure we don't see a global gl
var gl = undefined; // eslint-disable-line

var defaults = {
  addExtensionsToContext: true
};
/**
 * Various default settings for twgl.
 *
 * Note: You can call this any number of times. Example:
 *
 *     twgl.setDefaults({ textureColor: [1, 0, 0, 1] });
 *     twgl.setDefaults({ attribPrefix: 'a_' });
 *
 * is equivalent to
 *
 *     twgl.setDefaults({
 *       textureColor: [1, 0, 0, 1],
 *       attribPrefix: 'a_',
 *     });
 *
 * @typedef {Object} Defaults
 * @property {string} attribPrefix The prefix to stick on attributes
 *
 *   When writing shaders I prefer to name attributes with `a_`, uniforms with `u_` and varyings with `v_`
 *   as it makes it clear where they came from. But, when building geometry I prefer using unprefixed names.
 *
 *   In otherwords I'll create arrays of geometry like this
 *
 *       const arrays = {
 *         position: ...
 *         normal: ...
 *         texcoord: ...
 *       };
 *
 *   But need those mapped to attributes and my attributes start with `a_`.
 *
 *   Default: `""`
 *
 * @property {number[]} textureColor Array of 4 values in the range 0 to 1
 *
 *   The default texture color is used when loading textures from
 *   urls. Because the URL will be loaded async we'd like to be
 *   able to use the texture immediately. By putting a 1x1 pixel
 *   color in the texture we can start using the texture before
 *   the URL has loaded.
 *
 *   Default: `[0.5, 0.75, 1, 1]`
 *
 * @property {string} crossOrigin
 *
 *   If not undefined sets the crossOrigin attribute on images
 *   that twgl creates when downloading images for textures.
 *
 *   Also see {@link module:twgl.TextureOptions}.
 *
 * @property {bool} addExtensionsToContext
 *
 *   If true, then, when twgl will try to add any supported WebGL extensions
 *   directly to the context under their normal GL names. For example
 *   if ANGLE_instances_arrays exists then twgl would enable it,
 *   add the functions `vertexAttribDivisor`, `drawArraysInstanced`,
 *   `drawElementsInstanced`, and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR`
 *   to the `WebGLRenderingContext`.
 *
 * @memberOf module:twgl
 */

/**
 * Sets various defaults for twgl.
 *
 * In the interest of terseness which is kind of the point
 * of twgl I've integrated a few of the older functions here
 *
 * @param {module:twgl.Defaults} newDefaults The default settings.
 * @memberOf module:twgl
 */

function setDefaults(newDefaults) {
  helper.copyExistingProperties(newDefaults, defaults);
  attributes.setAttributeDefaults_(newDefaults); // eslint-disable-line

  textures.setTextureDefaults_(newDefaults); // eslint-disable-line
}

var prefixRE = /^(.*?)_/;

function addExtensionToContext(gl, extensionName) {
  utils.glEnumToString(gl, 0);
  var ext = gl.getExtension(extensionName);

  if (ext) {
    var enums = {};
    var fnSuffix = prefixRE.exec(extensionName)[1];
    var enumSuffix = '_' + fnSuffix;

    for (var key in ext) {
      var value = ext[key];
      var isFunc = typeof value === 'function';
      var suffix = isFunc ? fnSuffix : enumSuffix;
      var name = key; // examples of where this is not true are WEBGL_compressed_texture_s3tc
      // and WEBGL_compressed_texture_pvrtc

      if (key.endsWith(suffix)) {
        name = key.substring(0, key.length - suffix.length);
      }

      if (gl[name] !== undefined) {
        if (!isFunc && gl[name] !== value) {
          helper.warn(name, gl[name], value, key);
        }
      } else {
        if (isFunc) {
          gl[name] = function (origFn) {
            return function () {
              return origFn.apply(ext, arguments);
            };
          }(value);
        } else {
          gl[name] = value;
          enums[name] = value;
        }
      }
    } // pass the modified enums to glEnumToString


    enums.constructor = {
      name: ext.constructor.name
    };
    utils.glEnumToString(enums, 0);
  }

  return ext;
}
/*
 * If you're wondering why the code doesn't just iterate
 * over all extensions using `gl.getExtensions` is that it's possible
 * some future extension is incompatible with this code. Rather than
 * have thing suddenly break it seems better to manually add to this
 * list.
 *
 */


var supportedExtensions = ['ANGLE_instanced_arrays', 'EXT_blend_minmax', 'EXT_color_buffer_float', 'EXT_color_buffer_half_float', 'EXT_disjoint_timer_query', 'EXT_disjoint_timer_query_webgl2', 'EXT_frag_depth', 'EXT_sRGB', 'EXT_shader_texture_lod', 'EXT_texture_filter_anisotropic', 'OES_element_index_uint', 'OES_standard_derivatives', 'OES_texture_float', 'OES_texture_float_linear', 'OES_texture_half_float', 'OES_texture_half_float_linear', 'OES_vertex_array_object', 'WEBGL_color_buffer_float', 'WEBGL_compressed_texture_atc', 'WEBGL_compressed_texture_etc1', 'WEBGL_compressed_texture_pvrtc', 'WEBGL_compressed_texture_s3tc', 'WEBGL_compressed_texture_s3tc_srgb', 'WEBGL_depth_texture', 'WEBGL_draw_buffers'];
/**
 * Attempts to enable all of the following extensions
 * and add their functions and constants to the
 * `WebGLRenderingContext` using their normal non-extension like names.
 *
 *      ANGLE_instanced_arrays
 *      EXT_blend_minmax
 *      EXT_color_buffer_float
 *      EXT_color_buffer_half_float
 *      EXT_disjoint_timer_query
 *      EXT_disjoint_timer_query_webgl2
 *      EXT_frag_depth
 *      EXT_sRGB
 *      EXT_shader_texture_lod
 *      EXT_texture_filter_anisotropic
 *      OES_element_index_uint
 *      OES_standard_derivatives
 *      OES_texture_float
 *      OES_texture_float_linear
 *      OES_texture_half_float
 *      OES_texture_half_float_linear
 *      OES_vertex_array_object
 *      WEBGL_color_buffer_float
 *      WEBGL_compressed_texture_atc
 *      WEBGL_compressed_texture_etc1
 *      WEBGL_compressed_texture_pvrtc
 *      WEBGL_compressed_texture_s3tc
 *      WEBGL_compressed_texture_s3tc_srgb
 *      WEBGL_depth_texture
 *      WEBGL_draw_buffers
 *
 * For example if `ANGLE_instanced_arrays` exists then the functions
 * `drawArraysInstanced`, `drawElementsInstanced`, `vertexAttribDivisor`
 * and the constant `VERTEX_ATTRIB_ARRAY_DIVISOR` are added to the
 * `WebGLRenderingContext`.
 *
 * Note that if you want to know if the extension exists you should
 * probably call `gl.getExtension` for each extension. Alternatively
 * you can check for the existance of the functions or constants that
 * are expected to be added. For example
 *
 *    if (gl.drawBuffers) {
 *      // Either WEBGL_draw_buffers was enabled OR you're running in WebGL2
 *      ....
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @memberOf module:twgl
 */

function addExtensionsToContext(gl) {
  for (var ii = 0; ii < supportedExtensions.length; ++ii) {
    addExtensionToContext(gl, supportedExtensions[ii]);
  }
}
/**
 * Creates a webgl context.
 * @param {HTMLCanvasElement} canvas The canvas tag to get
 *     context from. If one is not passed in one will be
 *     created.
 * @return {WebGLRenderingContext} The created context.
 */


function create3DContext(canvas, opt_attribs) {
  var names = ["webgl", "experimental-webgl"];
  var context = null;

  for (var ii = 0; ii < names.length; ++ii) {
    context = canvas.getContext(names[ii], opt_attribs);

    if (context) {
      if (defaults.addExtensionsToContext) {
        addExtensionsToContext(context);
      }

      break;
    }
  }

  return context;
}
/**
 * Gets a WebGL1 context.
 *
 * Note: Will attempt to enable Vertex Array Objects
 * and add WebGL2 entry points. (unless you first set defaults with
 * `twgl.setDefaults({enableVertexArrayObjects: false})`;
 *
 * @param {HTMLCanvasElement} canvas a canvas element.
 * @param {WebGLContextCreationAttirbutes} [opt_attribs] optional webgl context creation attributes
 * @memberOf module:twgl
 */


function getWebGLContext(canvas, opt_attribs) {
  var gl = create3DContext(canvas, opt_attribs);
  return gl;
}
/**
 * Creates a webgl context.
 *
 * Will return a WebGL2 context if possible.
 *
 * You can check if it's WebGL2 with
 *
 *     twgl.isWebGL2(gl);
 *
 * @param {HTMLCanvasElement} canvas The canvas tag to get
 *     context from. If one is not passed in one will be
 *     created.
 * @return {WebGLRenderingContext} The created context.
 */


function createContext(canvas, opt_attribs) {
  var names = ["webgl2", "webgl", "experimental-webgl"];
  var context = null;

  for (var ii = 0; ii < names.length; ++ii) {
    context = canvas.getContext(names[ii], opt_attribs);

    if (context) {
      if (defaults.addExtensionsToContext) {
        addExtensionsToContext(context);
      }

      break;
    }
  }

  return context;
}
/**
 * Gets a WebGL context.  Will create a WebGL2 context if possible.
 *
 * You can check if it's WebGL2 with
 *
 *    function isWebGL2(gl) {
 *      return gl.getParameter(gl.VERSION).indexOf("WebGL 2.0 ") == 0;
 *    }
 *
 * Note: For a WebGL1 context will attempt to enable Vertex Array Objects
 * and add WebGL2 entry points. (unless you first set defaults with
 * `twgl.setDefaults({enableVertexArrayObjects: false})`;
 *
 * @param {HTMLCanvasElement} canvas a canvas element.
 * @param {WebGLContextCreationAttirbutes} [opt_attribs] optional webgl context creation attributes
 * @return {WebGLRenderingContext} The created context.
 * @memberOf module:twgl
 */


function getContext(canvas, opt_attribs) {
  var gl = createContext(canvas, opt_attribs);
  return gl;
}
/**
 * Resize a canvas to match the size it's displayed.
 * @param {HTMLCanvasElement} canvas The canvas to resize.
 * @param {number} [multiplier] So you can pass in `window.devicePixelRatio` or other scale value if you want to.
 * @return {boolean} true if the canvas was resized.
 * @memberOf module:twgl
 */


function resizeCanvasToDisplaySize(canvas, multiplier) {
  multiplier = multiplier || 1;
  multiplier = Math.max(0, multiplier);
  var width = canvas.clientWidth * multiplier | 0;
  var height = canvas.clientHeight * multiplier | 0;

  if (canvas.width !== width || canvas.height !== height) {
    canvas.width = width;
    canvas.height = height;
    return true;
  }

  return false;
}

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

"use strict";


exports.__esModule = true;
exports.drawBufferInfo = drawBufferInfo;
exports.drawObjectList = drawObjectList;

var programs = _interopRequireWildcard(__webpack_require__(5));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Drawing related functions
 *
 * For backward compatibily they are available at both `twgl.draw` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/draw
 */

/**
 * Calls `gl.drawElements` or `gl.drawArrays`, whichever is appropriate
 *
 * normally you'd call `gl.drawElements` or `gl.drawArrays` yourself
 * but calling this means if you switch from indexed data to non-indexed
 * data you don't have to remember to update your draw call.
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @param {(module:twgl.BufferInfo|module:twgl.VertexArrayInfo)} bufferInfo A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays} or
 *   a VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo}
 * @param {enum} [type] eg (gl.TRIANGLES, gl.LINES, gl.POINTS, gl.TRIANGLE_STRIP, ...). Defaults to `gl.TRIANGLES`
 * @param {number} [count] An optional count. Defaults to bufferInfo.numElements
 * @param {number} [offset] An optional offset. Defaults to 0.
 * @param {number} [instanceCount] An optional instanceCount. if set then `drawArraysInstanced` or `drawElementsInstanced` will be called
 * @memberOf module:twgl/draw
 */
function drawBufferInfo(gl, bufferInfo, type, count, offset, instanceCount) {
  type = type === undefined ? gl.TRIANGLES : type;
  var indices = bufferInfo.indices;
  var elementType = bufferInfo.elementType;
  var numElements = count === undefined ? bufferInfo.numElements : count;
  offset = offset === undefined ? 0 : offset;

  if (elementType || indices) {
    if (instanceCount !== undefined) {
      gl.drawElementsInstanced(type, numElements, elementType === undefined ? gl.UNSIGNED_SHORT : bufferInfo.elementType, offset, instanceCount);
    } else {
      gl.drawElements(type, numElements, elementType === undefined ? gl.UNSIGNED_SHORT : bufferInfo.elementType, offset);
    }
  } else {
    if (instanceCount !== undefined) {
      gl.drawArraysInstanced(type, offset, numElements, instanceCount);
    } else {
      gl.drawArrays(type, offset, numElements);
    }
  }
}
/**
 * A DrawObject is useful for putting objects in to an array and passing them to {@link module:twgl.drawObjectList}.
 *
 * You need either a `BufferInfo` or a `VertexArrayInfo`.
 *
 * @typedef {Object} DrawObject
 * @property {boolean} [active] whether or not to draw. Default = `true` (must be `false` to be not true). In otherwords `undefined` = `true`
 * @property {number} [type] type to draw eg. `gl.TRIANGLES`, `gl.LINES`, etc...
 * @property {module:twgl.ProgramInfo} programInfo A ProgramInfo as returned from {@link module:twgl.createProgramInfo}
 * @property {module:twgl.BufferInfo} [bufferInfo] A BufferInfo as returned from {@link module:twgl.createBufferInfoFromArrays}
 * @property {module:twgl.VertexArrayInfo} [vertexArrayInfo] A VertexArrayInfo as returned from {@link module:twgl.createVertexArrayInfo}
 * @property {Object<string, ?>} uniforms The values for the uniforms.
 *   You can pass multiple objects by putting them in an array. For example
 *
 *     var sharedUniforms = {
 *       u_fogNear: 10,
 *       u_projection: ...
 *       ...
 *     };
 *
 *     var localUniforms = {
 *       u_world: ...
 *       u_diffuseColor: ...
 *     };
 *
 *     var drawObj = {
 *       ...
 *       uniforms: [sharedUniforms, localUniforms],
 *     };
 *
 * @property {number} [offset] the offset to pass to `gl.drawArrays` or `gl.drawElements`. Defaults to 0.
 * @property {number} [count] the count to pass to `gl.drawArrays` or `gl.drawElemnts`. Defaults to bufferInfo.numElements.
 * @property {number} [instanceCount] the number of instances. Defaults to undefined.
 * @memberOf module:twgl
 */

/**
 * Draws a list of objects
 * @param {DrawObject[]} objectsToDraw an array of objects to draw.
 * @memberOf module:twgl/draw
 */


function drawObjectList(gl, objectsToDraw) {
  var lastUsedProgramInfo = null;
  var lastUsedBufferInfo = null;
  objectsToDraw.forEach(function (object) {
    if (object.active === false) {
      return;
    }

    var programInfo = object.programInfo;
    var bufferInfo = object.vertexArrayInfo || object.bufferInfo;
    var bindBuffers = false;
    var type = object.type === undefined ? gl.TRIANGLES : object.type;

    if (programInfo !== lastUsedProgramInfo) {
      lastUsedProgramInfo = programInfo;
      gl.useProgram(programInfo.program); // We have to rebind buffers when changing programs because we
      // only bind buffers the program uses. So if 2 programs use the same
      // bufferInfo but the 1st one uses only positions the when the
      // we switch to the 2nd one some of the attributes will not be on.

      bindBuffers = true;
    } // Setup all the needed attributes.


    if (bindBuffers || bufferInfo !== lastUsedBufferInfo) {
      if (lastUsedBufferInfo && lastUsedBufferInfo.vertexArrayObject && !bufferInfo.vertexArrayObject) {
        gl.bindVertexArray(null);
      }

      lastUsedBufferInfo = bufferInfo;
      programs.setBuffersAndAttributes(gl, programInfo, bufferInfo);
    } // Set the uniforms.


    programs.setUniforms(programInfo, object.uniforms); // Draw

    drawBufferInfo(gl, bufferInfo, type, object.count, object.offset, object.instanceCount);
  });

  if (lastUsedBufferInfo.vertexArrayObject) {
    gl.bindVertexArray(null);
  }
}

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

"use strict";


exports.__esModule = true;
exports.bindFramebufferInfo = bindFramebufferInfo;
exports.createFramebufferInfo = createFramebufferInfo;
exports.resizeFramebufferInfo = resizeFramebufferInfo;

var textures = _interopRequireWildcard(__webpack_require__(8));

var helper = _interopRequireWildcard(__webpack_require__(0));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * Framebuffer related functions
 *
 * For backward compatibily they are available at both `twgl.framebuffer` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/framebuffers
 */
// make sure we don't see a global gl
var gl = undefined; // eslint-disable-line

var UNSIGNED_BYTE = 0x1401;
/* PixelFormat */

var DEPTH_COMPONENT = 0x1902;
var RGBA = 0x1908;
/* Framebuffer Object. */

var RGBA4 = 0x8056;
var RGB5_A1 = 0x8057;
var RGB565 = 0x8D62;
var DEPTH_COMPONENT16 = 0x81A5;
var STENCIL_INDEX = 0x1901;
var STENCIL_INDEX8 = 0x8D48;
var DEPTH_STENCIL = 0x84F9;
var COLOR_ATTACHMENT0 = 0x8CE0;
var DEPTH_ATTACHMENT = 0x8D00;
var STENCIL_ATTACHMENT = 0x8D20;
var DEPTH_STENCIL_ATTACHMENT = 0x821A;
/* TextureWrapMode */

var REPEAT = 0x2901; // eslint-disable-line

var CLAMP_TO_EDGE = 0x812F;
var MIRRORED_REPEAT = 0x8370; // eslint-disable-line

/* TextureMagFilter */

var NEAREST = 0x2600; // eslint-disable-line

var LINEAR = 0x2601;
/* TextureMinFilter */

var NEAREST_MIPMAP_NEAREST = 0x2700; // eslint-disable-line

var LINEAR_MIPMAP_NEAREST = 0x2701; // eslint-disable-line

var NEAREST_MIPMAP_LINEAR = 0x2702; // eslint-disable-line

var LINEAR_MIPMAP_LINEAR = 0x2703; // eslint-disable-line

/**
 * The options for a framebuffer attachment.
 *
 * Note: For a `format` that is a texture include all the texture
 * options from {@link module:twgl.TextureOptions} for example
 * `min`, `mag`, `clamp`, etc... Note that unlike {@link module:twgl.TextureOptions}
 * `auto` defaults to `false` for attachment textures but `min` and `mag` default
 * to `gl.LINEAR` and `wrap` defaults to `CLAMP_TO_EDGE`
 *
 * @typedef {Object} AttachmentOptions
 * @property {number} [attach] The attachment point. Defaults
 *   to `gl.COLOR_ATTACTMENT0 + ndx` unless type is a depth or stencil type
 *   then it's gl.DEPTH_ATTACHMENT or `gl.DEPTH_STENCIL_ATTACHMENT` depending
 *   on the format or attachment type.
 * @property {number} [format] The format. If one of `gl.RGBA4`,
 *   `gl.RGB565`, `gl.RGB5_A1`, `gl.DEPTH_COMPONENT16`,
 *   `gl.STENCIL_INDEX8` or `gl.DEPTH_STENCIL` then will create a
 *   renderbuffer. Otherwise will create a texture. Default = `gl.RGBA`
 * @property {number} [type] The type. Used for texture. Default = `gl.UNSIGNED_BYTE`.
 * @property {number} [target] The texture target for `gl.framebufferTexture2D`.
 *   Defaults to `gl.TEXTURE_2D`. Set to appropriate face for cube maps.
 * @property {number} [level] level for `gl.framebufferTexture2D`. Defaults to 0.
 * @property {WebGLObject} [attachment] An existing renderbuffer or texture.
 *    If provided will attach this Object. This allows you to share
 *    attachemnts across framebuffers.
 * @memberOf module:twgl
 */

var defaultAttachments = [{
  format: RGBA,
  type: UNSIGNED_BYTE,
  min: LINEAR,
  wrap: CLAMP_TO_EDGE
}, {
  format: DEPTH_STENCIL
}];
var attachmentsByFormat = {};
attachmentsByFormat[DEPTH_STENCIL] = DEPTH_STENCIL_ATTACHMENT;
attachmentsByFormat[STENCIL_INDEX] = STENCIL_ATTACHMENT;
attachmentsByFormat[STENCIL_INDEX8] = STENCIL_ATTACHMENT;
attachmentsByFormat[DEPTH_COMPONENT] = DEPTH_ATTACHMENT;
attachmentsByFormat[DEPTH_COMPONENT16] = DEPTH_ATTACHMENT;

function getAttachmentPointForFormat(format) {
  return attachmentsByFormat[format];
}

var renderbufferFormats = {};
renderbufferFormats[RGBA4] = true;
renderbufferFormats[RGB5_A1] = true;
renderbufferFormats[RGB565] = true;
renderbufferFormats[DEPTH_STENCIL] = true;
renderbufferFormats[DEPTH_COMPONENT16] = true;
renderbufferFormats[STENCIL_INDEX] = true;
renderbufferFormats[STENCIL_INDEX8] = true;

function isRenderbufferFormat(format) {
  return renderbufferFormats[format];
}
/**
 * @typedef {Object} FramebufferInfo
 * @property {WebGLFramebuffer} framebuffer The WebGLFramebuffer for this framebufferInfo
 * @property {WebGLObject[]} attachments The created attachments in the same order as passed in to {@link module:twgl.createFramebufferInfo}.
 * @memberOf module:twgl
 */

/**
 * Creates a framebuffer and attachments.
 *
 * This returns a {@link module:twgl.FramebufferInfo} because it needs to return the attachments as well as the framebuffer.
 *
 * The simplest usage
 *
 *     // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer
 *     const fbi = twgl.createFramebufferInfo(gl);
 *
 * More complex usage
 *
 *     // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer
 *     const attachments = [
 *       { format: RGB565, mag: NEAREST },
 *       { format: STENCIL_INDEX8 },
 *     ]
 *     const fbi = twgl.createFramebufferInfo(gl, attachments);
 *
 * Passing in a specific size
 *
 *     const width = 256;
 *     const height = 256;
 *     const fbi = twgl.createFramebufferInfo(gl, attachments, width, height);
 *
 * **Note!!** It is up to you to check if the framebuffer is renderable by calling `gl.checkFramebufferStatus`.
 * [WebGL only guarantees 3 combinations of attachments work](https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6).
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.AttachmentOptions[]} [attachments] which attachments to create. If not provided the default is a framebuffer with an
 *    `RGBA`, `UNSIGNED_BYTE` texture `COLOR_ATTACHMENT0` and a `DEPTH_STENCIL` renderbuffer `DEPTH_STENCIL_ATTACHMENT`.
 * @param {number} [width] the width for the attachments. Default = size of drawingBuffer
 * @param {number} [height] the height for the attachments. Defautt = size of drawingBuffer
 * @return {module:twgl.FramebufferInfo} the framebuffer and attachments.
 * @memberOf module:twgl/framebuffers
 */


function createFramebufferInfo(gl, attachments, width, height) {
  var target = gl.FRAMEBUFFER;
  var fb = gl.createFramebuffer();
  gl.bindFramebuffer(target, fb);
  width = width || gl.drawingBufferWidth;
  height = height || gl.drawingBufferHeight;
  attachments = attachments || defaultAttachments;
  var colorAttachmentCount = 0;
  var framebufferInfo = {
    framebuffer: fb,
    attachments: [],
    width: width,
    height: height
  };
  attachments.forEach(function (attachmentOptions) {
    var attachment = attachmentOptions.attachment;
    var format = attachmentOptions.format;
    var attachmentPoint = getAttachmentPointForFormat(format);

    if (!attachmentPoint) {
      attachmentPoint = COLOR_ATTACHMENT0 + colorAttachmentCount++;
    }

    if (!attachment) {
      if (isRenderbufferFormat(format)) {
        attachment = gl.createRenderbuffer();
        gl.bindRenderbuffer(gl.RENDERBUFFER, attachment);
        gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height);
      } else {
        var textureOptions = Object.assign({}, attachmentOptions);
        textureOptions.width = width;
        textureOptions.height = height;

        if (textureOptions.auto === undefined) {
          textureOptions.auto = false;
          textureOptions.min = textureOptions.min || textureOptions.minMag || gl.LINEAR;
          textureOptions.mag = textureOptions.mag || textureOptions.minMag || gl.LINEAR;
          textureOptions.wrapS = textureOptions.wrapS || textureOptions.wrap || gl.CLAMP_TO_EDGE;
          textureOptions.wrapT = textureOptions.wrapT || textureOptions.wrap || gl.CLAMP_TO_EDGE;
        }

        attachment = textures.createTexture(gl, textureOptions);
      }
    }

    if (helper.isRenderbuffer(gl, attachment)) {
      gl.framebufferRenderbuffer(target, attachmentPoint, gl.RENDERBUFFER, attachment);
    } else if (helper.isTexture(gl, attachment)) {
      gl.framebufferTexture2D(target, attachmentPoint, attachmentOptions.texTarget || gl.TEXTURE_2D, attachment, attachmentOptions.level || 0);
    } else {
      throw "unknown attachment type";
    }

    framebufferInfo.attachments.push(attachment);
  });
  return framebufferInfo;
}
/**
 * Resizes the attachments of a framebuffer.
 *
 * You need to pass in the same `attachments` as you passed in {@link module:twgl.createFramebufferInfo}
 * because TWGL has no idea the format/type of each attachment.
 *
 * The simplest usage
 *
 *     // create an RGBA/UNSIGNED_BYTE texture and DEPTH_STENCIL renderbuffer
 *     const fbi = twgl.createFramebufferInfo(gl);
 *
 *     ...
 *
 *     function render() {
 *       if (twgl.resizeCanvasToDisplaySize(gl.canvas)) {
 *         // resize the attachments
 *         twgl.resizeFramebufferInfo(gl, fbi);
 *       }
 *
 * More complex usage
 *
 *     // create an RGB565 renderbuffer and a STENCIL_INDEX8 renderbuffer
 *     const attachments = [
 *       { format: RGB565, mag: NEAREST },
 *       { format: STENCIL_INDEX8 },
 *     ]
 *     const fbi = twgl.createFramebufferInfo(gl, attachments);
 *
 *     ...
 *
 *     function render() {
 *       if (twgl.resizeCanvasToDisplaySize(gl.canvas)) {
 *         // resize the attachments to match
 *         twgl.resizeFramebufferInfo(gl, fbi, attachments);
 *       }
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.FramebufferInfo} framebufferInfo a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}.
 * @param {module:twgl.AttachmentOptions[]} [attachments] the same attachments options as passed to {@link module:twgl.createFramebufferInfo}.
 * @param {number} [width] the width for the attachments. Default = size of drawingBuffer
 * @param {number} [height] the height for the attachments. Defautt = size of drawingBuffer
 * @memberOf module:twgl/framebuffers
 */


function resizeFramebufferInfo(gl, framebufferInfo, attachments, width, height) {
  width = width || gl.drawingBufferWidth;
  height = height || gl.drawingBufferHeight;
  framebufferInfo.width = width;
  framebufferInfo.height = height;
  attachments = attachments || defaultAttachments;
  attachments.forEach(function (attachmentOptions, ndx) {
    var attachment = framebufferInfo.attachments[ndx];
    var format = attachmentOptions.format;

    if (helper.isRenderbuffer(gl, attachment)) {
      gl.bindRenderbuffer(gl.RENDERBUFFER, attachment);
      gl.renderbufferStorage(gl.RENDERBUFFER, format, width, height);
    } else if (helper.isTexture(gl, attachment)) {
      textures.resizeTexture(gl, attachment, attachmentOptions, width, height);
    } else {
      throw "unknown attachment type";
    }
  });
}
/**
 * Binds a framebuffer
 *
 * This function pretty much soley exists because I spent hours
 * trying to figure out why something I wrote wasn't working only
 * to realize I forget to set the viewport dimensions.
 * My hope is this function will fix that.
 *
 * It is effectively the same as
 *
 *     gl.bindFramebuffer(gl.FRAMEBUFFER, someFramebufferInfo.framebuffer);
 *     gl.viewport(0, 0, someFramebufferInfo.width, someFramebufferInfo.height);
 *
 * @param {WebGLRenderingContext} gl the WebGLRenderingContext
 * @param {module:twgl.FramebufferInfo} [framebufferInfo] a framebufferInfo as returned from {@link module:twgl.createFramebufferInfo}.
 *   If not passed will bind the canvas.
 * @param {number} [target] The target. If not passed `gl.FRAMEBUFFER` will be used.
 * @memberOf module:twgl/framebuffers
 */


function bindFramebufferInfo(gl, framebufferInfo, target) {
  target = target || gl.FRAMEBUFFER;

  if (framebufferInfo) {
    gl.bindFramebuffer(target, framebufferInfo.framebuffer);
    gl.viewport(0, 0, framebufferInfo.width, framebufferInfo.height);
  } else {
    gl.bindFramebuffer(target, null);
    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  }
}

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

"use strict";


exports.__esModule = true;
exports.createVertexArrayInfo = createVertexArrayInfo;
exports.createVAOAndSetAttributes = createVAOAndSetAttributes;
exports.createVAOFromBufferInfo = createVAOFromBufferInfo;

var programs = _interopRequireWildcard(__webpack_require__(5));

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } }

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

/**
 * vertex array object related functions
 *
 * You should generally not need to use these functions. They are provided
 * for those cases where you're doing something out of the ordinary
 * and you need lower level access.
 *
 * For backward compatibily they are available at both `twgl.attributes` and `twgl`
 * itself
 *
 * See {@link module:twgl} for core functions
 *
 * @module twgl/vertexArrays
 */

/**
 * @typedef {Object} VertexArrayInfo
 * @property {number} numElements The number of elements to pass to `gl.drawArrays` or `gl.drawElements`.
 * @property {number} [elementType] The type of indices `UNSIGNED_BYTE`, `UNSIGNED_SHORT` etc..
 * @property {WebGLVertexArrayObject} [vertexArrayObject] a vertex array object
 * @memberOf module:twgl
 */

/**
 * Creates a VertexArrayInfo from a BufferInfo and one or more ProgramInfos
 *
 * This can be passed to {@link module:twgl.setBuffersAndAttributes} and to
 * {@link module:twgl:drawBufferInfo}.
 *
 * > **IMPORTANT:** Vertex Array Objects are **not** a direct analog for a BufferInfo. Vertex Array Objects
 *   assign buffers to specific attributes at creation time. That means they can only be used with programs
 *   who's attributes use the same attribute locations for the same purposes.
 *
 * > Bind your attribute locations by passing an array of attribute names to {@link module:twgl.createProgramInfo}
 *   or use WebGL 2's GLSL ES 3's `layout(location = <num>)` to make sure locations match.
 *
 * also
 *
 * > **IMPORTANT:** After calling twgl.setBuffersAndAttribute with a BufferInfo that uses a Vertex Array Object
 *   that Vertex Array Object will be bound. That means **ANY MANIPULATION OF ELEMENT_ARRAY_BUFFER or ATTRIBUTES**
 *   will affect the Vertex Array Object state.
 *
 * > Call `gl.bindVertexArray(null)` to get back manipulating the global attributes and ELEMENT_ARRAY_BUFFER.
 *
 * @param {WebGLRenderingContext} gl A WebGLRenderingContext
 * @param {module:twgl.ProgramInfo|module:twgl.ProgramInfo[]} programInfo a programInfo or array of programInfos
 * @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
 *
 *    You need to make sure every attribute that will be used is bound. So for example assume shader 1
 *    uses attributes A, B, C and shader 2 uses attributes A, B, D. If you only pass in the programInfo
 *    for shader 1 then only attributes A, B, and C will have their attributes set because TWGL doesn't
 *    now attribute D's location.
 *
 *    So, you can pass in both shader 1 and shader 2's programInfo
 *
 * @return {module:twgl.VertexArrayInfo} The created VertexArrayInfo
 *
 * @memberOf module:twgl/vertexArrays
 */
function createVertexArrayInfo(gl, programInfos, bufferInfo) {
  var vao = gl.createVertexArray();
  gl.bindVertexArray(vao);

  if (!programInfos.length) {
    programInfos = [programInfos];
  }

  programInfos.forEach(function (programInfo) {
    programs.setBuffersAndAttributes(gl, programInfo, bufferInfo);
  });
  gl.bindVertexArray(null);
  return {
    numElements: bufferInfo.numElements,
    elementType: bufferInfo.elementType,
    vertexArrayObject: vao
  };
}
/**
 * Creates a vertex array object and then sets the attributes on it
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext to use.
 * @param {Object.<string, function>} setters Attribute setters as returned from createAttributeSetters
 * @param {Object.<string, module:twgl.AttribInfo>} attribs AttribInfos mapped by attribute name.
 * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
 * @memberOf module:twgl/vertexArrays
 */


function createVAOAndSetAttributes(gl, setters, attribs, indices) {
  var vao = gl.createVertexArray();
  gl.bindVertexArray(vao);
  programs.setAttributes(setters, attribs);

  if (indices) {
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indices);
  } // We unbind this because otherwise any change to ELEMENT_ARRAY_BUFFER
  // like when creating buffers for other stuff will mess up this VAO's binding


  gl.bindVertexArray(null);
  return vao;
}
/**
 * Creates a vertex array object and then sets the attributes
 * on it
 *
 * @param {WebGLRenderingContext} gl The WebGLRenderingContext
 *        to use.
 * @param {Object.<string, function>| module:twgl.ProgramInfo} programInfo as returned from createProgramInfo or Attribute setters as returned from createAttributeSetters
 * @param {module:twgl.BufferInfo} bufferInfo BufferInfo as returned from createBufferInfoFromArrays etc...
 * @param {WebGLBuffer} [indices] an optional ELEMENT_ARRAY_BUFFER of indices
 * @memberOf module:twgl/vertexArrays
 */


function createVAOFromBufferInfo(gl, programInfo, bufferInfo) {
  return createVAOAndSetAttributes(gl, programInfo.attribSetters || programInfo, bufferInfo.attribs, bufferInfo.indices);
}

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

/***/ }),

/***/ "./node_modules/unicode-trie/index.js":
/*!********************************************!*\
  !*** ./node_modules/unicode-trie/index.js ***!
  \********************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

// Generated by CoffeeScript 1.7.1
var UnicodeTrie, inflate;

inflate = __webpack_require__(/*! tiny-inflate */ "./node_modules/tiny-inflate/index.js");

UnicodeTrie = (function() {
  var DATA_BLOCK_LENGTH, DATA_GRANULARITY, DATA_MASK, INDEX_1_OFFSET, INDEX_2_BLOCK_LENGTH, INDEX_2_BMP_LENGTH, INDEX_2_MASK, INDEX_SHIFT, LSCP_INDEX_2_LENGTH, LSCP_INDEX_2_OFFSET, OMITTED_BMP_INDEX_1_LENGTH, SHIFT_1, SHIFT_1_2, SHIFT_2, UTF8_2B_INDEX_2_LENGTH, UTF8_2B_INDEX_2_OFFSET;

  SHIFT_1 = 6 + 5;

  SHIFT_2 = 5;

  SHIFT_1_2 = SHIFT_1 - SHIFT_2;

  OMITTED_BMP_INDEX_1_LENGTH = 0x10000 >> SHIFT_1;

  INDEX_2_BLOCK_LENGTH = 1 << SHIFT_1_2;

  INDEX_2_MASK = INDEX_2_BLOCK_LENGTH - 1;

  INDEX_SHIFT = 2;

  DATA_BLOCK_LENGTH = 1 << SHIFT_2;

  DATA_MASK = DATA_BLOCK_LENGTH - 1;

  LSCP_INDEX_2_OFFSET = 0x10000 >> SHIFT_2;

  LSCP_INDEX_2_LENGTH = 0x400 >> SHIFT_2;

  INDEX_2_BMP_LENGTH = LSCP_INDEX_2_OFFSET + LSCP_INDEX_2_LENGTH;

  UTF8_2B_INDEX_2_OFFSET = INDEX_2_BMP_LENGTH;

  UTF8_2B_INDEX_2_LENGTH = 0x800 >> 6;

  INDEX_1_OFFSET = UTF8_2B_INDEX_2_OFFSET + UTF8_2B_INDEX_2_LENGTH;

  DATA_GRANULARITY = 1 << INDEX_SHIFT;

  function UnicodeTrie(data) {
    var isBuffer, uncompressedLength, view;
    isBuffer = typeof data.readUInt32BE === 'function' && typeof data.slice === 'function';
    if (isBuffer || data instanceof Uint8Array) {
      if (isBuffer) {
        this.highStart = data.readUInt32BE(0);
        this.errorValue = data.readUInt32BE(4);
        uncompressedLength = data.readUInt32BE(8);
        data = data.slice(12);
      } else {
        view = new DataView(data.buffer);
        this.highStart = view.getUint32(0);
        this.errorValue = view.getUint32(4);
        uncompressedLength = view.getUint32(8);
        data = data.subarray(12);
      }
      data = inflate(data, new Uint8Array(uncompressedLength));
      data = inflate(data, new Uint8Array(uncompressedLength));
      this.data = new Uint32Array(data.buffer);
    } else {
      this.data = data.data, this.highStart = data.highStart, this.errorValue = data.errorValue;
    }
  }

  UnicodeTrie.prototype.get = function(codePoint) {
    var index;
    if (codePoint < 0 || codePoint > 0x10ffff) {
      return this.errorValue;
    }
    if (codePoint < 0xd800 || (codePoint > 0xdbff && codePoint <= 0xffff)) {
      index = (this.data[codePoint >> SHIFT_2] << INDEX_SHIFT) + (codePoint & DATA_MASK);
      return this.data[index];
    }
    if (codePoint <= 0xffff) {
      index = (this.data[LSCP_INDEX_2_OFFSET + ((codePoint - 0xd800) >> SHIFT_2)] << INDEX_SHIFT) + (codePoint & DATA_MASK);
      return this.data[index];
    }
    if (codePoint < this.highStart) {
      index = this.data[(INDEX_1_OFFSET - OMITTED_BMP_INDEX_1_LENGTH) + (codePoint >> SHIFT_1)];
      index = this.data[index + ((codePoint >> SHIFT_2) & INDEX_2_MASK)];
      index = (index << INDEX_SHIFT) + (codePoint & DATA_MASK);
      return this.data[index];
    }
    return this.data[this.data.length - DATA_GRANULARITY];
  };

  return UnicodeTrie;

})();

module.exports = UnicodeTrie;


/***/ }),

/***/ "./node_modules/webpack/buildin/amd-options.js":
/*!****************************************!*\
  !*** (webpack)/buildin/amd-options.js ***!
  \****************************************/
/*! no static exports found */
/***/ (function(module, exports) {

/* WEBPACK VAR INJECTION */(function(__webpack_amd_options__) {/* globals __webpack_amd_options__ */
module.exports = __webpack_amd_options__;

/* WEBPACK VAR INJECTION */}.call(this, {}))

/***/ }),

/***/ "./node_modules/webpack/buildin/global.js":
/*!***********************************!*\
  !*** (webpack)/buildin/global.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

var g;

// This works in non-strict mode
g = (function() {
	return this;
})();

try {
	// This works if eval is allowed (see CSP)
	g = g || new Function("return this")();
} catch (e) {
	// This works if the window reference is available
	if (typeof window === "object") g = window;
}

// g can still be undefined, but nothing to do about it...
// We return undefined, instead of nothing here, so it's
// easier to handle this case. if(!global) { ...}

module.exports = g;


/***/ }),

/***/ "./node_modules/webpack/buildin/module.js":
/*!***********************************!*\
  !*** (webpack)/buildin/module.js ***!
  \***********************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = function(module) {
	if (!module.webpackPolyfill) {
		module.deprecate = function() {};
		module.paths = [];
		// module.parent = undefined by default
		if (!module.children) module.children = [];
		Object.defineProperty(module, "loaded", {
			enumerable: true,
			get: function() {
				return module.l;
			}
		});
		Object.defineProperty(module, "id", {
			enumerable: true,
			get: function() {
				return module.i;
			}
		});
		module.webpackPolyfill = 1;
	}
	return module;
};


/***/ }),

/***/ "./node_modules/worker-loader/dist/workers/InlineWorker.js":
/*!*****************************************************************!*\
  !*** ./node_modules/worker-loader/dist/workers/InlineWorker.js ***!
  \*****************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


// http://stackoverflow.com/questions/10343913/how-to-create-a-web-worker-from-a-string

var URL = window.URL || window.webkitURL;

module.exports = function (content, url) {
  try {
    try {
      var blob;

      try {
        // BlobBuilder = Deprecated, but widely implemented
        var BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder;

        blob = new BlobBuilder();

        blob.append(content);

        blob = blob.getBlob();
      } catch (e) {
        // The proposed API
        blob = new Blob([content]);
      }

      return new Worker(URL.createObjectURL(blob));
    } catch (e) {
      return new Worker('data:application/javascript,' + encodeURIComponent(content));
    }
  } catch (e) {
    if (!url) {
      throw Error('Inline worker is not supported');
    }

    return new Worker(url);
  }
};

/***/ }),

/***/ "./src/build/inline-worker-loader/worker-loader.js?name=js/extension-worker/extension-worker.[hash].js!./node_modules/scratch-vm/src/extension-support/extension-worker.js":
/*!*********************************************************************************************************************************************************************************!*\
  !*** ./src/build/inline-worker-loader/worker-loader.js?name=js/extension-worker/extension-worker.[hash].js!./node_modules/scratch-vm/src/extension-support/extension-worker.js ***!
  \*********************************************************************************************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = function() {
  return __webpack_require__(/*! !./node_modules/worker-loader/dist/workers/InlineWorker.js */ "./node_modules/worker-loader/dist/workers/InlineWorker.js")("/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-vm/src/extension-support/extension-worker.js\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-vm/src/extension-support/extension-worker.js\":\n/*!*******************************************************************************************************************!*\\\n  !*** ./node_modules/babel-loader/lib??ref--4!./node_modules/scratch-vm/src/extension-support/extension-worker.js ***!\n  \\*******************************************************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* WEBPACK VAR INJECTION */(function(global) {/* eslint-env worker */\nconst ScratchCommon = __webpack_require__(/*! ./tw-extension-api-common */ \"./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js\");\n\nconst dispatch = __webpack_require__(/*! ../dispatch/worker-dispatch */ \"./node_modules/scratch-vm/src/dispatch/worker-dispatch.js\");\n\nconst log = __webpack_require__(/*! ../util/log */ \"./node_modules/scratch-vm/src/util/log.js\");\n\nconst {\n  isWorker\n} = __webpack_require__(/*! ./tw-extension-worker-context */ \"./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js\");\n\nconst loadScripts = url => {\n  if (isWorker) {\n    importScripts(url);\n  } else {\n    return new Promise((resolve, reject) => {\n      const script = document.createElement('script');\n\n      script.onload = () => resolve();\n\n      script.onerror = () => {\n        reject(new Error(\"Error in sandboxed script: \".concat(url, \". Check the console for more information.\")));\n      };\n\n      script.src = url;\n      document.body.appendChild(script);\n    });\n  }\n};\n\nclass ExtensionWorker {\n  constructor() {\n    this.nextExtensionId = 0;\n    this.initialRegistrations = [];\n    this.firstRegistrationPromise = new Promise(resolve => {\n      this.firstRegistrationCallback = resolve;\n    });\n    dispatch.waitForConnection.then(() => {\n      dispatch.call('extensions', 'allocateWorker').then(async x => {\n        const [id, extension] = x;\n        this.workerId = id;\n\n        try {\n          await loadScripts(extension);\n          await this.firstRegistrationPromise;\n          const initialRegistrations = this.initialRegistrations;\n          this.initialRegistrations = null;\n          Promise.all(initialRegistrations).then(() => dispatch.call('extensions', 'onWorkerInit', id));\n        } catch (e) {\n          log.error(e);\n          dispatch.call('extensions', 'onWorkerInit', id, \"\".concat(e));\n        }\n      });\n    });\n    this.extensions = [];\n  }\n\n  register(extensionObject) {\n    const extensionId = this.nextExtensionId++;\n    this.extensions.push(extensionObject);\n    const serviceName = \"extension.\".concat(this.workerId, \".\").concat(extensionId);\n    const promise = dispatch.setService(serviceName, extensionObject).then(() => dispatch.call('extensions', 'registerExtensionService', serviceName));\n\n    if (this.initialRegistrations) {\n      this.firstRegistrationCallback();\n      this.initialRegistrations.push(promise);\n    }\n\n    return promise;\n  }\n\n}\n\nglobal.Scratch = global.Scratch || {};\nObject.assign(global.Scratch, ScratchCommon);\n/**\n * Expose only specific parts of the worker to extensions.\n */\n\nconst extensionWorker = new ExtensionWorker();\nglobal.Scratch.extensions = {\n  register: extensionWorker.register.bind(extensionWorker)\n};\nglobal.ScratchExtensions = __webpack_require__(/*! ./tw-scratchx-compatibility-layer */ \"./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js\");\n/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ \"./node_modules/webpack/buildin/global.js\")))\n\n/***/ }),\n\n/***/ \"./node_modules/microee/index.js\":\n/*!***************************************!*\\\n  !*** ./node_modules/microee/index.js ***!\n  \\***************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nfunction M() { this._events = {}; }\nM.prototype = {\n  on: function(ev, cb) {\n    this._events || (this._events = {});\n    var e = this._events;\n    (e[ev] || (e[ev] = [])).push(cb);\n    return this;\n  },\n  removeListener: function(ev, cb) {\n    var e = this._events[ev] || [], i;\n    for(i = e.length-1; i >= 0 && e[i]; i--){\n      if(e[i] === cb || e[i].cb === cb) { e.splice(i, 1); }\n    }\n  },\n  removeAllListeners: function(ev) {\n    if(!ev) { this._events = {}; }\n    else { this._events[ev] && (this._events[ev] = []); }\n  },\n  listeners: function(ev) {\n    return (this._events ? this._events[ev] || [] : []);\n  },\n  emit: function(ev) {\n    this._events || (this._events = {});\n    var args = Array.prototype.slice.call(arguments, 1), i, e = this._events[ev] || [];\n    for(i = e.length-1; i >= 0 && e[i]; i--){\n      e[i].apply(this, args);\n    }\n    return this;\n  },\n  when: function(ev, cb) {\n    return this.once(ev, cb, true);\n  },\n  once: function(ev, cb, when) {\n    if(!cb) return this;\n    function c() {\n      if(!when) this.removeListener(ev, c);\n      if(cb.apply(this, arguments) && when) this.removeListener(ev, c);\n    }\n    c.cb = cb;\n    this.on(ev, c);\n    return this;\n  }\n};\nM.mixin = function(dest) {\n  var o = M.prototype, k;\n  for (k in o) {\n    o.hasOwnProperty(k) && (dest.prototype[k] = o[k]);\n  }\n};\nmodule.exports = M;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/common/filter.js\":\n/*!***************************************************!*\\\n  !*** ./node_modules/minilog/lib/common/filter.js ***!\n  \\***************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n// default filter\nvar Transform = __webpack_require__(/*! ./transform.js */ \"./node_modules/minilog/lib/common/transform.js\");\n\nvar levelMap = { debug: 1, info: 2, warn: 3, error: 4 };\n\nfunction Filter() {\n  this.enabled = true;\n  this.defaultResult = true;\n  this.clear();\n}\n\nTransform.mixin(Filter);\n\n// allow all matching, with level >= given level\nFilter.prototype.allow = function(name, level) {\n  this._white.push({ n: name, l: levelMap[level] });\n  return this;\n};\n\n// deny all matching, with level <= given level\nFilter.prototype.deny = function(name, level) {\n  this._black.push({ n: name, l: levelMap[level] });\n  return this;\n};\n\nFilter.prototype.clear = function() {\n  this._white = [];\n  this._black = [];\n  return this;\n};\n\nfunction test(rule, name) {\n  // use .test for RegExps\n  return (rule.n.test ? rule.n.test(name) : rule.n == name);\n};\n\nFilter.prototype.test = function(name, level) {\n  var i, len = Math.max(this._white.length, this._black.length);\n  for(i = 0; i < len; i++) {\n    if(this._white[i] && test(this._white[i], name) && levelMap[level] >= this._white[i].l) {\n      return true;\n    }\n    if(this._black[i] && test(this._black[i], name) && levelMap[level] <= this._black[i].l) {\n      return false;\n    }\n  }\n  return this.defaultResult;\n};\n\nFilter.prototype.write = function(name, level, args) {\n  if(!this.enabled || this.test(name, level)) {\n    return this.emit('item', name, level, args);\n  }\n};\n\nmodule.exports = Filter;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/common/minilog.js\":\n/*!****************************************************!*\\\n  !*** ./node_modules/minilog/lib/common/minilog.js ***!\n  \\****************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ./transform.js */ \"./node_modules/minilog/lib/common/transform.js\"),\n    Filter = __webpack_require__(/*! ./filter.js */ \"./node_modules/minilog/lib/common/filter.js\");\n\nvar log = new Transform(),\n    slice = Array.prototype.slice;\n\nexports = module.exports = function create(name) {\n  var o   = function() { log.write(name, undefined, slice.call(arguments)); return o; };\n  o.debug = function() { log.write(name, 'debug', slice.call(arguments)); return o; };\n  o.info  = function() { log.write(name, 'info',  slice.call(arguments)); return o; };\n  o.warn  = function() { log.write(name, 'warn',  slice.call(arguments)); return o; };\n  o.error = function() { log.write(name, 'error', slice.call(arguments)); return o; };\n  o.log   = o.debug; // for interface compliance with Node and browser consoles\n  o.suggest = exports.suggest;\n  o.format = log.format;\n  return o;\n};\n\n// filled in separately\nexports.defaultBackend = exports.defaultFormatter = null;\n\nexports.pipe = function(dest) {\n  return log.pipe(dest);\n};\n\nexports.end = exports.unpipe = exports.disable = function(from) {\n  return log.unpipe(from);\n};\n\nexports.Transform = Transform;\nexports.Filter = Filter;\n// this is the default filter that's applied when .enable() is called normally\n// you can bypass it completely and set up your own pipes\nexports.suggest = new Filter();\n\nexports.enable = function() {\n  if(exports.defaultFormatter) {\n    return log.pipe(exports.suggest) // filter\n              .pipe(exports.defaultFormatter) // formatter\n              .pipe(exports.defaultBackend); // backend\n  }\n  return log.pipe(exports.suggest) // filter\n            .pipe(exports.defaultBackend); // formatter\n};\n\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/common/transform.js\":\n/*!******************************************************!*\\\n  !*** ./node_modules/minilog/lib/common/transform.js ***!\n  \\******************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar microee = __webpack_require__(/*! microee */ \"./node_modules/microee/index.js\");\n\n// Implements a subset of Node's stream.Transform - in a cross-platform manner.\nfunction Transform() {}\n\nmicroee.mixin(Transform);\n\n// The write() signature is different from Node's\n// --> makes it much easier to work with objects in logs.\n// One of the lessons from v1 was that it's better to target\n// a good browser rather than the lowest common denominator\n// internally.\n// If you want to use external streams, pipe() to ./stringify.js first.\nTransform.prototype.write = function(name, level, args) {\n  this.emit('item', name, level, args);\n};\n\nTransform.prototype.end = function() {\n  this.emit('end');\n  this.removeAllListeners();\n};\n\nTransform.prototype.pipe = function(dest) {\n  var s = this;\n  // prevent double piping\n  s.emit('unpipe', dest);\n  // tell the dest that it's being piped to\n  dest.emit('pipe', s);\n\n  function onItem() {\n    dest.write.apply(dest, Array.prototype.slice.call(arguments));\n  }\n  function onEnd() { !dest._isStdio && dest.end(); }\n\n  s.on('item', onItem);\n  s.on('end', onEnd);\n\n  s.when('unpipe', function(from) {\n    var match = (from === dest) || typeof from == 'undefined';\n    if(match) {\n      s.removeListener('item', onItem);\n      s.removeListener('end', onEnd);\n      dest.emit('unpipe');\n    }\n    return match;\n  });\n\n  return dest;\n};\n\nTransform.prototype.unpipe = function(from) {\n  this.emit('unpipe', from);\n  return this;\n};\n\nTransform.prototype.format = function(dest) {\n  throw new Error([\n    'Warning: .format() is deprecated in Minilog v2! Use .pipe() instead. For example:',\n    'var Minilog = require(\\'minilog\\');',\n    'Minilog',\n    '  .pipe(Minilog.backends.console.formatClean)',\n    '  .pipe(Minilog.backends.console);'].join('\\n'));\n};\n\nTransform.mixin = function(dest) {\n  var o = Transform.prototype, k;\n  for (k in o) {\n    o.hasOwnProperty(k) && (dest.prototype[k] = o[k]);\n  }\n};\n\nmodule.exports = Transform;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/array.js\":\n/*!***********************************************!*\\\n  !*** ./node_modules/minilog/lib/web/array.js ***!\n  \\***********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\"),\n    cache = [ ];\n\nvar logger = new Transform();\n\nlogger.write = function(name, level, args) {\n  cache.push([ name, level, args ]);\n};\n\n// utility functions\nlogger.get = function() { return cache; };\nlogger.empty = function() { cache = []; };\n\nmodule.exports = logger;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/console.js\":\n/*!*************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/console.js ***!\n  \\*************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\");\n\nvar newlines = /\\n+$/,\n    logger = new Transform();\n\nlogger.write = function(name, level, args) {\n  var i = args.length-1;\n  if (typeof console === 'undefined' || !console.log) {\n    return;\n  }\n  if(console.log.apply) {\n    return console.log.apply(console, [name, level].concat(args));\n  } else if(JSON && JSON.stringify) {\n    // console.log.apply is undefined in IE8 and IE9\n    // for IE8/9: make console.log at least a bit less awful\n    if(args[i] && typeof args[i] == 'string') {\n      args[i] = args[i].replace(newlines, '');\n    }\n    try {\n      for(i = 0; i < args.length; i++) {\n        args[i] = JSON.stringify(args[i]);\n      }\n    } catch(e) {}\n    console.log(args.join(' '));\n  }\n};\n\nlogger.formatters = ['color', 'minilog'];\nlogger.color = __webpack_require__(/*! ./formatters/color.js */ \"./node_modules/minilog/lib/web/formatters/color.js\");\nlogger.minilog = __webpack_require__(/*! ./formatters/minilog.js */ \"./node_modules/minilog/lib/web/formatters/minilog.js\");\n\nmodule.exports = logger;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/formatters/color.js\":\n/*!**********************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/formatters/color.js ***!\n  \\**********************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\"),\n    color = __webpack_require__(/*! ./util.js */ \"./node_modules/minilog/lib/web/formatters/util.js\");\n\nvar colors = { debug: ['cyan'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] },\n    logger = new Transform();\n\nlogger.write = function(name, level, args) {\n  var fn = console.log;\n  if(console[level] && console[level].apply) {\n    fn = console[level];\n    fn.apply(console, [ '%c'+name+' %c'+level, color('gray'), color.apply(color, colors[level])].concat(args));\n  }\n};\n\n// NOP, because piping the formatted logs can only cause trouble.\nlogger.pipe = function() { };\n\nmodule.exports = logger;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/formatters/minilog.js\":\n/*!************************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/formatters/minilog.js ***!\n  \\************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\"),\n    color = __webpack_require__(/*! ./util.js */ \"./node_modules/minilog/lib/web/formatters/util.js\"),\n    colors = { debug: ['gray'], info: ['purple' ], warn: [ 'yellow', true ], error: [ 'red', true ] },\n    logger = new Transform();\n\nlogger.write = function(name, level, args) {\n  var fn = console.log;\n  if(level != 'debug' && console[level]) {\n    fn = console[level];\n  }\n\n  var subset = [], i = 0;\n  if(level != 'info') {\n    for(; i < args.length; i++) {\n      if(typeof args[i] != 'string') break;\n    }\n    fn.apply(console, [ '%c'+name +' '+ args.slice(0, i).join(' '), color.apply(color, colors[level]) ].concat(args.slice(i)));\n  } else {\n    fn.apply(console, [ '%c'+name, color.apply(color, colors[level]) ].concat(args));\n  }\n};\n\n// NOP, because piping the formatted logs can only cause trouble.\nlogger.pipe = function() { };\n\nmodule.exports = logger;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/formatters/util.js\":\n/*!*********************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/formatters/util.js ***!\n  \\*********************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nvar hex = {\n  black: '#000',\n  red: '#c23621',\n  green: '#25bc26',\n  yellow: '#bbbb00',\n  blue:  '#492ee1',\n  magenta: '#d338d3',\n  cyan: '#33bbc8',\n  gray: '#808080',\n  purple: '#708'\n};\nfunction color(fg, isInverse) {\n  if(isInverse) {\n    return 'color: #fff; background: '+hex[fg]+';';\n  } else {\n    return 'color: '+hex[fg]+';';\n  }\n}\n\nmodule.exports = color;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/index.js\":\n/*!***********************************************!*\\\n  !*** ./node_modules/minilog/lib/web/index.js ***!\n  \\***********************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Minilog = __webpack_require__(/*! ../common/minilog.js */ \"./node_modules/minilog/lib/common/minilog.js\");\n\nvar oldEnable = Minilog.enable,\n    oldDisable = Minilog.disable,\n    isChrome = (typeof navigator != 'undefined' && /chrome/i.test(navigator.userAgent)),\n    console = __webpack_require__(/*! ./console.js */ \"./node_modules/minilog/lib/web/console.js\");\n\n// Use a more capable logging backend if on Chrome\nMinilog.defaultBackend = (isChrome ? console.minilog : console);\n\n// apply enable inputs from localStorage and from the URL\nif(typeof window != 'undefined') {\n  try {\n    Minilog.enable(JSON.parse(window.localStorage['minilogSettings']));\n  } catch(e) {}\n  if(window.location && window.location.search) {\n    var match = RegExp('[?&]minilog=([^&]*)').exec(window.location.search);\n    match && Minilog.enable(decodeURIComponent(match[1]));\n  }\n}\n\n// Make enable also add to localStorage\nMinilog.enable = function() {\n  oldEnable.call(Minilog, true);\n  try { window.localStorage['minilogSettings'] = JSON.stringify(true); } catch(e) {}\n  return this;\n};\n\nMinilog.disable = function() {\n  oldDisable.call(Minilog);\n  try { delete window.localStorage.minilogSettings; } catch(e) {}\n  return this;\n};\n\nexports = module.exports = Minilog;\n\nexports.backends = {\n  array: __webpack_require__(/*! ./array.js */ \"./node_modules/minilog/lib/web/array.js\"),\n  browser: Minilog.defaultBackend,\n  localStorage: __webpack_require__(/*! ./localstorage.js */ \"./node_modules/minilog/lib/web/localstorage.js\"),\n  jQuery: __webpack_require__(/*! ./jquery_simple.js */ \"./node_modules/minilog/lib/web/jquery_simple.js\")\n};\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/jquery_simple.js\":\n/*!*******************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/jquery_simple.js ***!\n  \\*******************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\");\n\nvar cid = new Date().valueOf().toString(36);\n\nfunction AjaxLogger(options) {\n  this.url = options.url || '';\n  this.cache = [];\n  this.timer = null;\n  this.interval = options.interval || 30*1000;\n  this.enabled = true;\n  this.jQuery = window.jQuery;\n  this.extras = {};\n}\n\nTransform.mixin(AjaxLogger);\n\nAjaxLogger.prototype.write = function(name, level, args) {\n  if(!this.timer) { this.init(); }\n  this.cache.push([name, level].concat(args));\n};\n\nAjaxLogger.prototype.init = function() {\n  if(!this.enabled || !this.jQuery) return;\n  var self = this;\n  this.timer = setTimeout(function() {\n    var i, logs = [], ajaxData, url = self.url;\n    if(self.cache.length == 0) return self.init();\n    // Test each log line and only log the ones that are valid (e.g. don't have circular references).\n    // Slight performance hit but benefit is we log all valid lines.\n    for(i = 0; i < self.cache.length; i++) {\n      try {\n        JSON.stringify(self.cache[i]);\n        logs.push(self.cache[i]);\n      } catch(e) { }\n    }\n    if(self.jQuery.isEmptyObject(self.extras)) {\n        ajaxData = JSON.stringify({ logs: logs });\n        url = self.url + '?client_id=' + cid;\n    } else {\n        ajaxData = JSON.stringify(self.jQuery.extend({logs: logs}, self.extras));\n    }\n\n    self.jQuery.ajax(url, {\n      type: 'POST',\n      cache: false,\n      processData: false,\n      data: ajaxData,\n      contentType: 'application/json',\n      timeout: 10000\n    }).success(function(data, status, jqxhr) {\n      if(data.interval) {\n        self.interval = Math.max(1000, data.interval);\n      }\n    }).error(function() {\n      self.interval = 30000;\n    }).always(function() {\n      self.init();\n    });\n    self.cache = [];\n  }, this.interval);\n};\n\nAjaxLogger.prototype.end = function() {};\n\n// wait until jQuery is defined. Useful if you don't control the load order.\nAjaxLogger.jQueryWait = function(onDone) {\n  if(typeof window !== 'undefined' && (window.jQuery || window.$)) {\n    return onDone(window.jQuery || window.$);\n  } else if (typeof window !== 'undefined') {\n    setTimeout(function() { AjaxLogger.jQueryWait(onDone); }, 200);\n  }\n};\n\nmodule.exports = AjaxLogger;\n\n\n/***/ }),\n\n/***/ \"./node_modules/minilog/lib/web/localstorage.js\":\n/*!******************************************************!*\\\n  !*** ./node_modules/minilog/lib/web/localstorage.js ***!\n  \\******************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nvar Transform = __webpack_require__(/*! ../common/transform.js */ \"./node_modules/minilog/lib/common/transform.js\"),\n    cache = false;\n\nvar logger = new Transform();\n\nlogger.write = function(name, level, args) {\n  if(typeof window == 'undefined' || typeof JSON == 'undefined' || !JSON.stringify || !JSON.parse) return;\n  try {\n    if(!cache) { cache = (window.localStorage.minilog ? JSON.parse(window.localStorage.minilog) : []); }\n    cache.push([ new Date().toString(), name, level, args ]);\n    window.localStorage.minilog = JSON.stringify(cache);\n  } catch(e) {}\n};\n\nmodule.exports = logger;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/dispatch/shared-dispatch.js\":\n/*!*****************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/dispatch/shared-dispatch.js ***!\n  \\*****************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nconst log = __webpack_require__(/*! ../util/log */ \"./node_modules/scratch-vm/src/util/log.js\");\n/**\n * @typedef {object} DispatchCallMessage - a message to the dispatch system representing a service method call\n * @property {*} responseId - send a response message with this response ID. See {@link DispatchResponseMessage}\n * @property {string} service - the name of the service to be called\n * @property {string} method - the name of the method to be called\n * @property {Array|undefined} args - the arguments to be passed to the method\n */\n\n/**\n * @typedef {object} DispatchResponseMessage - a message to the dispatch system representing the results of a call\n * @property {*} responseId - a copy of the response ID from the call which generated this response\n * @property {*|undefined} error - if this is truthy, then it contains results from a failed call (such as an exception)\n * @property {*|undefined} result - if error is not truthy, then this contains the return value of the call (if any)\n */\n\n/**\n * @typedef {DispatchCallMessage|DispatchResponseMessage} DispatchMessage\n * Any message to the dispatch system.\n */\n\n/**\n * The SharedDispatch class is responsible for dispatch features shared by\n * {@link CentralDispatch} and {@link WorkerDispatch}.\n */\n\n\nclass SharedDispatch {\n  constructor() {\n    /**\n     * List of callback registrations for promises waiting for a response from a call to a service on another\n     * worker. A callback registration is an array of [resolve,reject] Promise functions.\n     * Calls to local services don't enter this list.\n     * @type {Array.<Function[]>}\n     */\n    this.callbacks = [];\n    /**\n     * The next response ID to be used.\n     * @type {int}\n     */\n\n    this.nextResponseId = 0;\n  }\n  /**\n   * Call a particular method on a particular service, regardless of whether that service is provided locally or on\n   * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone\n   * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be\n   * transferred to the worker, and they should not be used after this call.\n   * @example\n   *      dispatcher.call('vm', 'setData', 'cat', 42);\n   *      // this finds the worker for the 'vm' service, then on that worker calls:\n   *      vm.setData('cat', 42);\n   * @param {string} service - the name of the service.\n   * @param {string} method - the name of the method.\n   * @param {*} [args] - the arguments to be copied to the method, if any.\n   * @returns {Promise} - a promise for the return value of the service method.\n   */\n\n\n  call(service, method) {\n    for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {\n      args[_key - 2] = arguments[_key];\n    }\n\n    return this.transferCall(service, method, null, ...args);\n  }\n  /**\n   * Call a particular method on a particular service, regardless of whether that service is provided locally or on\n   * a worker. If the service is provided by a worker, the `args` will be copied using the Structured Clone\n   * algorithm, except for any items which are also in the `transfer` list. Ownership of those items will be\n   * transferred to the worker, and they should not be used after this call.\n   * @example\n   *      dispatcher.transferCall('vm', 'setData', [myArrayBuffer], 'cat', myArrayBuffer);\n   *      // this finds the worker for the 'vm' service, transfers `myArrayBuffer` to it, then on that worker calls:\n   *      vm.setData('cat', myArrayBuffer);\n   * @param {string} service - the name of the service.\n   * @param {string} method - the name of the method.\n   * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.\n   * @param {*} [args] - the arguments to be copied to the method, if any.\n   * @returns {Promise} - a promise for the return value of the service method.\n   */\n\n\n  transferCall(service, method, transfer) {\n    try {\n      const {\n        provider,\n        isRemote\n      } = this._getServiceProvider(service);\n\n      if (provider) {\n        for (var _len2 = arguments.length, args = new Array(_len2 > 3 ? _len2 - 3 : 0), _key2 = 3; _key2 < _len2; _key2++) {\n          args[_key2 - 3] = arguments[_key2];\n        }\n\n        if (isRemote) {\n          return this._remoteTransferCall(provider, service, method, transfer, ...args);\n        }\n\n        const result = provider[method].apply(provider, args);\n        return Promise.resolve(result);\n      }\n\n      return Promise.reject(new Error(\"Service not found: \".concat(service)));\n    } catch (e) {\n      return Promise.reject(e);\n    }\n  }\n  /**\n   * Check if a particular service lives on another worker.\n   * @param {string} service - the service to check.\n   * @returns {boolean} - true if the service is remote (calls must cross a Worker boundary), false otherwise.\n   * @private\n   */\n\n\n  _isRemoteService(service) {\n    return this._getServiceProvider(service).isRemote;\n  }\n  /**\n   * Like {@link call}, but force the call to be posted through a particular communication channel.\n   * @param {object} provider - send the call through this object's `postMessage` function.\n   * @param {string} service - the name of the service.\n   * @param {string} method - the name of the method.\n   * @param {*} [args] - the arguments to be copied to the method, if any.\n   * @returns {Promise} - a promise for the return value of the service method.\n   */\n\n\n  _remoteCall(provider, service, method) {\n    for (var _len3 = arguments.length, args = new Array(_len3 > 3 ? _len3 - 3 : 0), _key3 = 3; _key3 < _len3; _key3++) {\n      args[_key3 - 3] = arguments[_key3];\n    }\n\n    return this._remoteTransferCall(provider, service, method, null, ...args);\n  }\n  /**\n   * Like {@link transferCall}, but force the call to be posted through a particular communication channel.\n   * @param {object} provider - send the call through this object's `postMessage` function.\n   * @param {string} service - the name of the service.\n   * @param {string} method - the name of the method.\n   * @param {Array} [transfer] - objects to be transferred instead of copied. Must be present in `args` to be useful.\n   * @param {*} [args] - the arguments to be copied to the method, if any.\n   * @returns {Promise} - a promise for the return value of the service method.\n   */\n\n\n  _remoteTransferCall(provider, service, method, transfer) {\n    for (var _len4 = arguments.length, args = new Array(_len4 > 4 ? _len4 - 4 : 0), _key4 = 4; _key4 < _len4; _key4++) {\n      args[_key4 - 4] = arguments[_key4];\n    }\n\n    return new Promise((resolve, reject) => {\n      const responseId = this._storeCallbacks(resolve, reject);\n      /** @TODO: remove this hack! this is just here so we don't try to send `util` to a worker */\n      // tw: upstream's logic is broken\n      // Args is actually a 3 length list of [args, util, real block info]\n      // We only want to send args. The others will throw errors when they try to be cloned\n\n\n      if (args.length > 0 && typeof args[args.length - 1].func === 'function') {\n        args.pop();\n        args.pop();\n      }\n\n      if (transfer) {\n        provider.postMessage({\n          service,\n          method,\n          responseId,\n          args\n        }, transfer);\n      } else {\n        provider.postMessage({\n          service,\n          method,\n          responseId,\n          args\n        });\n      }\n    });\n  }\n  /**\n   * Store callback functions pending a response message.\n   * @param {Function} resolve - function to call if the service method returns.\n   * @param {Function} reject - function to call if the service method throws.\n   * @returns {*} - a unique response ID for this set of callbacks. See {@link _deliverResponse}.\n   * @protected\n   */\n\n\n  _storeCallbacks(resolve, reject) {\n    const responseId = this.nextResponseId++;\n    this.callbacks[responseId] = [resolve, reject];\n    return responseId;\n  }\n  /**\n   * Deliver call response from a worker. This should only be called as the result of a message from a worker.\n   * @param {int} responseId - the response ID of the callback set to call.\n   * @param {DispatchResponseMessage} message - the message containing the response value(s).\n   * @protected\n   */\n\n\n  _deliverResponse(responseId, message) {\n    try {\n      const [resolve, reject] = this.callbacks[responseId];\n      delete this.callbacks[responseId];\n\n      if (message.error) {\n        reject(message.error);\n      } else {\n        resolve(message.result);\n      }\n    } catch (e) {\n      log.error(\"Dispatch callback failed: \".concat(e));\n    }\n  }\n  /**\n   * Handle a message event received from a connected worker.\n   * @param {Worker} worker - the worker which sent the message, or the global object if running in a worker.\n   * @param {MessageEvent} event - the message event to be handled.\n   * @protected\n   */\n\n\n  _onMessage(worker, event) {\n    /** @type {DispatchMessage} */\n    const message = event.data;\n    message.args = message.args || [];\n    let promise;\n\n    if (message.service) {\n      if (message.service === 'dispatch') {\n        promise = this._onDispatchMessage(worker, message);\n      } else {\n        promise = this.call(message.service, message.method, ...message.args);\n      }\n    } else if (typeof message.responseId === 'undefined') {\n      log.error(\"Dispatch caught malformed message from a worker: \".concat(JSON.stringify(event)));\n    } else {\n      this._deliverResponse(message.responseId, message);\n    }\n\n    if (promise) {\n      if (typeof message.responseId === 'undefined') {\n        log.error(\"Dispatch message missing required response ID: \".concat(JSON.stringify(event)));\n      } else {\n        promise.then(result => worker.postMessage({\n          responseId: message.responseId,\n          result\n        }), error => worker.postMessage({\n          responseId: message.responseId,\n          error: \"\".concat(error)\n        }));\n      }\n    }\n  }\n  /**\n   * Fetch the service provider object for a particular service name.\n   * @abstract\n   * @param {string} service - the name of the service to look up\n   * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found\n   * @protected\n   */\n\n\n  _getServiceProvider(service) {\n    throw new Error(\"Could not get provider for \".concat(service, \": _getServiceProvider not implemented\"));\n  }\n  /**\n   * Handle a call message sent to the dispatch service itself\n   * @abstract\n   * @param {Worker} worker - the worker which sent the message.\n   * @param {DispatchCallMessage} message - the message to be handled.\n   * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate\n   * @private\n   */\n\n\n  _onDispatchMessage(worker, message) {\n    throw new Error(\"Unimplemented dispatch message handler cannot handle \".concat(message.method, \" method\"));\n  }\n\n}\n\nmodule.exports = SharedDispatch;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/dispatch/worker-dispatch.js\":\n/*!*****************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/dispatch/worker-dispatch.js ***!\n  \\*****************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nconst SharedDispatch = __webpack_require__(/*! ./shared-dispatch */ \"./node_modules/scratch-vm/src/dispatch/shared-dispatch.js\");\n\nconst log = __webpack_require__(/*! ../util/log */ \"./node_modules/scratch-vm/src/util/log.js\");\n\nconst {\n  centralDispatchService\n} = __webpack_require__(/*! ../extension-support/tw-extension-worker-context */ \"./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js\");\n/**\n * This class provides a Worker with the means to participate in the message dispatch system managed by CentralDispatch.\n * From any context in the messaging system, the dispatcher's \"call\" method can call any method on any \"service\"\n * provided in any participating context. The dispatch system will forward function arguments and return values across\n * worker boundaries as needed.\n * @see {CentralDispatch}\n */\n\n\nclass WorkerDispatch extends SharedDispatch {\n  constructor() {\n    super();\n    /**\n     * This promise will be resolved when we have successfully connected to central dispatch.\n     * @type {Promise}\n     * @see {waitForConnection}\n     * @private\n     */\n\n    this._connectionPromise = new Promise(resolve => {\n      this._onConnect = resolve;\n    });\n    /**\n     * Map of service name to local service provider.\n     * If a service is not listed here, it is assumed to be provided by another context (another Worker or the main\n     * thread).\n     * @see {setService}\n     * @type {object}\n     */\n\n    this.services = {};\n    this._onMessage = this._onMessage.bind(this, centralDispatchService);\n\n    if (typeof self !== 'undefined') {\n      self.onmessage = this._onMessage;\n    }\n  }\n  /**\n   * @returns {Promise} a promise which will resolve upon connection to central dispatch. If you need to make a call\n   * immediately on \"startup\" you can attach a 'then' to this promise.\n   * @example\n   *      dispatch.waitForConnection.then(() => {\n   *          dispatch.call('myService', 'hello');\n   *      })\n   */\n\n\n  get waitForConnection() {\n    return this._connectionPromise;\n  }\n  /**\n   * Set a local object as the global provider of the specified service.\n   * WARNING: Any method on the provider can be called from any worker within the dispatch system.\n   * @param {string} service - a globally unique string identifying this service. Examples: 'vm', 'gui', 'extension9'.\n   * @param {object} provider - a local object which provides this service.\n   * @returns {Promise} - a promise which will resolve once the service is registered.\n   */\n\n\n  setService(service, provider) {\n    if (this.services.hasOwnProperty(service)) {\n      log.warn(\"Worker dispatch replacing existing service provider for \".concat(service));\n    }\n\n    this.services[service] = provider;\n    return this.waitForConnection.then(() => this._remoteCall(centralDispatchService, 'dispatch', 'setService', service));\n  }\n  /**\n   * Fetch the service provider object for a particular service name.\n   * @override\n   * @param {string} service - the name of the service to look up\n   * @returns {{provider:(object|Worker), isRemote:boolean}} - the means to contact the service, if found\n   * @protected\n   */\n\n\n  _getServiceProvider(service) {\n    // if we don't have a local service by this name, contact central dispatch by calling `postMessage` on self\n    const provider = this.services[service];\n    return {\n      provider: provider || centralDispatchService,\n      isRemote: !provider\n    };\n  }\n  /**\n   * Handle a call message sent to the dispatch service itself\n   * @override\n   * @param {Worker} worker - the worker which sent the message.\n   * @param {DispatchCallMessage} message - the message to be handled.\n   * @returns {Promise|undefined} - a promise for the results of this operation, if appropriate\n   * @protected\n   */\n\n\n  _onDispatchMessage(worker, message) {\n    let promise;\n\n    switch (message.method) {\n      case 'handshake':\n        promise = this._onConnect();\n        break;\n\n      case 'terminate':\n        // Don't close until next tick, after sending confirmation back\n        setTimeout(() => self.close(), 0);\n        promise = Promise.resolve();\n        break;\n\n      default:\n        log.error(\"Worker dispatch received message for unknown method: \".concat(message.method));\n    }\n\n    return promise;\n  }\n\n}\n\nmodule.exports = new WorkerDispatch();\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/argument-type.js\":\n/*!************************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/argument-type.js ***!\n  \\************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n/**\n * Block argument types\n * @enum {string}\n */\nconst ArgumentType = {\n  /**\n   * Numeric value with angle picker\n   */\n  ANGLE: 'angle',\n\n  /**\n   * Boolean value with hexagonal placeholder\n   */\n  BOOLEAN: 'Boolean',\n\n  /**\n   * Numeric value with color picker\n   */\n  COLOR: 'color',\n\n  /**\n   * Numeric value with text field\n   */\n  NUMBER: 'number',\n\n  /**\n   * String value with text field\n   */\n  STRING: 'string',\n\n  /**\n   * String value with matrix field\n   */\n  MATRIX: 'matrix',\n\n  /**\n   * MIDI note number with note picker (piano) field\n   */\n  NOTE: 'note',\n\n  /**\n   * Inline image on block (as part of the label)\n   */\n  IMAGE: 'image'\n};\nmodule.exports = ArgumentType;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/block-type.js\":\n/*!*********************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/block-type.js ***!\n  \\*********************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n/**\n * Types of block\n * @enum {string}\n */\nconst BlockType = {\n  /**\n   * Boolean reporter with hexagonal shape\n   */\n  BOOLEAN: 'Boolean',\n\n  /**\n   * A button (not an actual block) for some special action, like making a variable\n   */\n  BUTTON: 'button',\n\n  /**\n   * Command block\n   */\n  COMMAND: 'command',\n\n  /**\n   * Specialized command block which may or may not run a child branch\n   * The thread continues with the next block whether or not a child branch ran.\n   */\n  CONDITIONAL: 'conditional',\n\n  /**\n   * Specialized hat block with no implementation function\n   * This stack only runs if the corresponding event is emitted by other code.\n   */\n  EVENT: 'event',\n\n  /**\n   * Hat block which conditionally starts a block stack\n   */\n  HAT: 'hat',\n\n  /**\n   * Specialized command block which may or may not run a child branch\n   * If a child branch runs, the thread evaluates the loop block again.\n   */\n  LOOP: 'loop',\n\n  /**\n   * General reporter with numeric or string value\n   */\n  REPORTER: 'reporter'\n};\nmodule.exports = BlockType;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/target-type.js\":\n/*!**********************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/target-type.js ***!\n  \\**********************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n/**\n * Default types of Target supported by the VM\n * @enum {string}\n */\nconst TargetType = {\n  /**\n   * Rendered target which can move, change costumes, etc.\n   */\n  SPRITE: 'sprite',\n\n  /**\n   * Rendered target which cannot move but can change backdrops\n   */\n  STAGE: 'stage'\n};\nmodule.exports = TargetType;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js\":\n/*!**********************************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/tw-extension-api-common.js ***!\n  \\**********************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nconst ArgumentType = __webpack_require__(/*! ./argument-type */ \"./node_modules/scratch-vm/src/extension-support/argument-type.js\");\n\nconst BlockType = __webpack_require__(/*! ./block-type */ \"./node_modules/scratch-vm/src/extension-support/block-type.js\");\n\nconst TargetType = __webpack_require__(/*! ./target-type */ \"./node_modules/scratch-vm/src/extension-support/target-type.js\");\n\nconst Cast = __webpack_require__(/*! ../util/cast */ \"./node_modules/scratch-vm/src/util/cast.js\");\n\nconst Scratch = {\n  ArgumentType,\n  BlockType,\n  TargetType,\n  Cast\n};\nmodule.exports = Scratch;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js\":\n/*!**************************************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js ***!\n  \\**************************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nmodule.exports = {\n  isWorker: true,\n  // centralDispatchService is the object to call postMessage() on to send a message to parent.\n  centralDispatchService: self\n};\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js\":\n/*!******************************************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js ***!\n  \\******************************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n// ScratchX API Documentation: https://github.com/LLK/scratchx/wiki/\n// Global Scratch API from extension-worker.js\n\n/* globals Scratch */\nconst ArgumentType = __webpack_require__(/*! ./argument-type */ \"./node_modules/scratch-vm/src/extension-support/argument-type.js\");\n\nconst BlockType = __webpack_require__(/*! ./block-type */ \"./node_modules/scratch-vm/src/extension-support/block-type.js\");\n\nconst {\n  argumentIndexToId,\n  generateExtensionId\n} = __webpack_require__(/*! ./tw-scratchx-utilities */ \"./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js\");\n/**\n * @typedef ScratchXDescriptor\n * @property {unknown[][]} blocks\n * @property {Record<string, unknown[]>} [menus]\n * @property {string} [url]\n * @property {string} [displayName]\n */\n\n/**\n * @typedef ScratchXStatus\n * @property {0|1|2} status 0 is red/error, 1 is yellow/not ready, 2 is green/ready\n * @property {string} msg\n */\n\n\nconst parseScratchXBlockType = type => {\n  if (type === '' || type === ' ' || type === 'w') {\n    return {\n      type: BlockType.COMMAND,\n      async: type === 'w'\n    };\n  }\n\n  if (type === 'r' || type === 'R') {\n    return {\n      type: BlockType.REPORTER,\n      async: type === 'R'\n    };\n  }\n\n  if (type === 'b') {\n    return {\n      type: BlockType.BOOLEAN,\n      // ScratchX docs don't seem to mention boolean reporters that wait\n      async: false\n    };\n  }\n\n  if (type === 'h') {\n    return {\n      type: BlockType.HAT,\n      async: false\n    };\n  }\n\n  throw new Error(\"Unknown ScratchX block type: \".concat(type));\n};\n\nconst isScratchCompatibleValue = v => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean';\n/**\n * @param {string} argument ScratchX argument with leading % removed.\n * @param {unknown} defaultValue Default value, if any\n */\n\n\nconst parseScratchXArgument = (argument, defaultValue) => {\n  const result = {};\n  const hasDefaultValue = isScratchCompatibleValue(defaultValue);\n\n  if (hasDefaultValue) {\n    result.defaultValue = defaultValue;\n  } // TODO: ScratchX docs don't mention support for boolean arguments?\n\n\n  if (argument === 's') {\n    result.type = ArgumentType.STRING;\n\n    if (!hasDefaultValue) {\n      result.defaultValue = '';\n    }\n  } else if (argument === 'n') {\n    result.type = ArgumentType.NUMBER;\n\n    if (!hasDefaultValue) {\n      result.defaultValue = 0;\n    }\n  } else if (argument[0] === 'm') {\n    result.type = ArgumentType.STRING;\n    const split = argument.split(/\\.|:/);\n    const menuName = split[1];\n    result.menu = menuName;\n  } else {\n    throw new Error(\"Unknown ScratchX argument type: \".concat(argument));\n  }\n\n  return result;\n};\n\nconst wrapScratchXFunction = (originalFunction, argumentCount, async) => args => {\n  // Convert Scratch 3's argument object to an argument list expected by ScratchX\n  const argumentList = [];\n\n  for (let i = 0; i < argumentCount; i++) {\n    argumentList.push(args[argumentIndexToId(i)]);\n  }\n\n  if (async) {\n    return new Promise(resolve => {\n      originalFunction(...argumentList, resolve);\n    });\n  }\n\n  return originalFunction(...argumentList);\n};\n/**\n * @param {string} name\n * @param {ScratchXDescriptor} descriptor\n * @param {Record<string, () => unknown>} functions\n */\n\n\nconst convert = (name, descriptor, functions) => {\n  const extensionId = generateExtensionId(name);\n  const info = {\n    id: extensionId,\n    name: descriptor.displayName || name,\n    blocks: [],\n    color1: '#4a4a5e',\n    color2: '#31323f',\n    color3: '#191a21'\n  };\n  const scratch3Extension = {\n    getInfo: () => info,\n    _getStatus: functions._getStatus\n  };\n\n  if (descriptor.url) {\n    info.docsURI = descriptor.url;\n  }\n\n  for (const blockDescriptor of descriptor.blocks) {\n    if (blockDescriptor.length === 1) {\n      // Separator\n      info.blocks.push('---');\n      continue;\n    }\n\n    const scratchXBlockType = blockDescriptor[0];\n    const blockText = blockDescriptor[1];\n    const functionName = blockDescriptor[2];\n    const defaultArgumentValues = blockDescriptor.slice(3);\n    let scratchText = '';\n    const argumentInfo = [];\n    const blockTextParts = blockText.split(/%([\\w.:]+)/g);\n\n    for (let i = 0; i < blockTextParts.length; i++) {\n      const part = blockTextParts[i];\n      const isArgument = i % 2 === 1;\n\n      if (isArgument) {\n        parseScratchXArgument(part);\n        const argumentIndex = Math.floor(i / 2).toString();\n        const argumentDefaultValue = defaultArgumentValues[argumentIndex];\n        const argumentId = argumentIndexToId(argumentIndex);\n        argumentInfo[argumentId] = parseScratchXArgument(part, argumentDefaultValue);\n        scratchText += \"[\".concat(argumentId, \"]\");\n      } else {\n        scratchText += part;\n      }\n    }\n\n    const scratch3BlockType = parseScratchXBlockType(scratchXBlockType);\n    const blockInfo = {\n      opcode: functionName,\n      blockType: scratch3BlockType.type,\n      text: scratchText,\n      arguments: argumentInfo\n    };\n    info.blocks.push(blockInfo);\n    const originalFunction = functions[functionName];\n    const argumentCount = argumentInfo.length;\n    scratch3Extension[functionName] = wrapScratchXFunction(originalFunction, argumentCount, scratch3BlockType.async);\n  }\n\n  const menus = descriptor.menus;\n\n  if (menus) {\n    const scratch3Menus = {};\n\n    for (const menuName of Object.keys(menus) || {}) {\n      const menuItems = menus[menuName];\n      const menuInfo = {\n        items: menuItems\n      };\n      scratch3Menus[menuName] = menuInfo;\n    }\n\n    info.menus = scratch3Menus;\n  }\n\n  return scratch3Extension;\n};\n\nconst extensionNameToExtension = new Map();\n\nconst register = (name, descriptor, functions) => {\n  const scratch3Extension = convert(name, descriptor, functions);\n  extensionNameToExtension.set(name, scratch3Extension);\n  Scratch.extensions.register(scratch3Extension);\n};\n/**\n * @param {string} extensionName\n * @returns {ScratchXStatus}\n */\n\n\nconst getStatus = extensionName => {\n  const extension = extensionNameToExtension.get(extensionName);\n\n  if (extension) {\n    return extension._getStatus();\n  }\n\n  return {\n    status: 0,\n    msg: 'does not exist'\n  };\n};\n\nmodule.exports = {\n  register,\n  getStatus,\n  // For tests\n  convert\n};\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js\":\n/*!********************************************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/extension-support/tw-scratchx-utilities.js ***!\n  \\********************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n/**\n * @fileoverview\n * General ScratchX-related utilities used in multiple places.\n * Changing these functions may break projects.\n */\n\n/**\n * @param {string} scratchXName\n * @returns {string}\n */\nconst generateExtensionId = scratchXName => {\n  const sanitizedName = scratchXName.replace(/[^a-z0-9]/gi, '').toLowerCase();\n  return \"sbx\".concat(sanitizedName);\n};\n/**\n * @param {number} i 0-indexed index of argument in list\n * @returns {string} Scratch 3 argument name\n */\n\n\nconst argumentIndexToId = i => i.toString();\n\nmodule.exports = {\n  generateExtensionId,\n  argumentIndexToId\n};\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/util/cast.js\":\n/*!**************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/util/cast.js ***!\n  \\**************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nconst Color = __webpack_require__(/*! ../util/color */ \"./node_modules/scratch-vm/src/util/color.js\");\n/**\n * @fileoverview\n * Utilities for casting and comparing Scratch data-types.\n * Scratch behaves slightly differently from JavaScript in many respects,\n * and these differences should be encapsulated below.\n * For example, in Scratch, add(1, join(\"hello\", world\")) -> 1.\n * This is because \"hello world\" is cast to 0.\n * In JavaScript, 1 + Number(\"hello\" + \"world\") would give you NaN.\n * Use when coercing a value before computation.\n */\n\n/**\n * Used internally by compare()\n * @param {*} val A value that evaluates to 0 in JS string-to-number conversation such as empty string, 0, or tab.\n * @returns {boolean} True if the value should not be treated as the number zero.\n */\n\n\nconst isNotActuallyZero = val => {\n  if (typeof val !== 'string') return false;\n\n  for (let i = 0; i < val.length; i++) {\n    const code = val.charCodeAt(i); // '0'.charCodeAt(0) === 48\n    // '\\t'.charCodeAt(0) === 9\n    // We include tab for compatibility with scratch-www's broken trim() polyfill.\n    // https://github.com/TurboWarp/scratch-vm/issues/115\n    // https://scratch.mit.edu/projects/788261699/\n\n    if (code === 48 || code === 9) {\n      return false;\n    }\n  }\n\n  return true;\n};\n\nclass Cast {\n  /**\n   * Scratch cast to number.\n   * Treats NaN as 0.\n   * In Scratch 2.0, this is captured by `interp.numArg.`\n   * @param {*} value Value to cast to number.\n   * @return {number} The Scratch-casted number value.\n   */\n  static toNumber(value) {\n    // If value is already a number we don't need to coerce it with\n    // Number().\n    if (typeof value === 'number') {\n      // Scratch treats NaN as 0, when needed as a number.\n      // E.g., 0 + NaN -> 0.\n      if (Number.isNaN(value)) {\n        return 0;\n      }\n\n      return value;\n    }\n\n    const n = Number(value);\n\n    if (Number.isNaN(n)) {\n      // Scratch treats NaN as 0, when needed as a number.\n      // E.g., 0 + NaN -> 0.\n      return 0;\n    }\n\n    return n;\n  }\n  /**\n   * Scratch cast to boolean.\n   * In Scratch 2.0, this is captured by `interp.boolArg.`\n   * Treats some string values differently from JavaScript.\n   * @param {*} value Value to cast to boolean.\n   * @return {boolean} The Scratch-casted boolean value.\n   */\n\n\n  static toBoolean(value) {\n    // Already a boolean?\n    if (typeof value === 'boolean') {\n      return value;\n    }\n\n    if (typeof value === 'string') {\n      // These specific strings are treated as false in Scratch.\n      if (value === '' || value === '0' || value.toLowerCase() === 'false') {\n        return false;\n      } // All other strings treated as true.\n\n\n      return true;\n    } // Coerce other values and numbers.\n\n\n    return Boolean(value);\n  }\n  /**\n   * Scratch cast to string.\n   * @param {*} value Value to cast to string.\n   * @return {string} The Scratch-casted string value.\n   */\n\n\n  static toString(value) {\n    return String(value);\n  }\n  /**\n   * Cast any Scratch argument to an RGB color array to be used for the renderer.\n   * @param {*} value Value to convert to RGB color array.\n   * @return {Array.<number>} [r,g,b], values between 0-255.\n   */\n\n\n  static toRgbColorList(value) {\n    const color = Cast.toRgbColorObject(value);\n    return [color.r, color.g, color.b];\n  }\n  /**\n   * Cast any Scratch argument to an RGB color object to be used for the renderer.\n   * @param {*} value Value to convert to RGB color object.\n   * @return {RGBOject} [r,g,b], values between 0-255.\n   */\n\n\n  static toRgbColorObject(value) {\n    let color;\n\n    if (typeof value === 'string' && value.substring(0, 1) === '#') {\n      color = Color.hexToRgb(value); // If the color wasn't *actually* a hex color, cast to black\n\n      if (!color) color = {\n        r: 0,\n        g: 0,\n        b: 0,\n        a: 255\n      };\n    } else {\n      color = Color.decimalToRgb(Cast.toNumber(value));\n    }\n\n    return color;\n  }\n  /**\n   * Determine if a Scratch argument is a white space string (or null / empty).\n   * @param {*} val value to check.\n   * @return {boolean} True if the argument is all white spaces or null / empty.\n   */\n\n\n  static isWhiteSpace(val) {\n    return val === null || typeof val === 'string' && val.trim().length === 0;\n  }\n  /**\n   * Compare two values, using Scratch cast, case-insensitive string compare, etc.\n   * In Scratch 2.0, this is captured by `interp.compare.`\n   * @param {*} v1 First value to compare.\n   * @param {*} v2 Second value to compare.\n   * @returns {number} Negative number if v1 < v2; 0 if equal; positive otherwise.\n   */\n\n\n  static compare(v1, v2) {\n    let n1 = Number(v1);\n    let n2 = Number(v2);\n\n    if (n1 === 0 && isNotActuallyZero(v1)) {\n      n1 = NaN;\n    } else if (n2 === 0 && isNotActuallyZero(v2)) {\n      n2 = NaN;\n    }\n\n    if (isNaN(n1) || isNaN(n2)) {\n      // At least one argument can't be converted to a number.\n      // Scratch compares strings as case insensitive.\n      const s1 = String(v1).toLowerCase();\n      const s2 = String(v2).toLowerCase();\n\n      if (s1 < s2) {\n        return -1;\n      } else if (s1 > s2) {\n        return 1;\n      }\n\n      return 0;\n    } // Handle the special case of Infinity\n\n\n    if (n1 === Infinity && n2 === Infinity || n1 === -Infinity && n2 === -Infinity) {\n      return 0;\n    } // Compare as numbers.\n\n\n    return n1 - n2;\n  }\n  /**\n   * Determine if a Scratch argument number represents a round integer.\n   * @param {*} val Value to check.\n   * @return {boolean} True if number looks like an integer.\n   */\n\n\n  static isInt(val) {\n    // Values that are already numbers.\n    if (typeof val === 'number') {\n      if (isNaN(val)) {\n        // NaN is considered an integer.\n        return true;\n      } // True if it's \"round\" (e.g., 2.0 and 2).\n\n\n      return val === Math.floor(val);\n    } else if (typeof val === 'boolean') {\n      // `True` and `false` always represent integer after Scratch cast.\n      return true;\n    } else if (typeof val === 'string') {\n      // If it contains a decimal point, don't consider it an int.\n      return val.indexOf('.') < 0;\n    }\n\n    return false;\n  }\n\n  static get LIST_INVALID() {\n    return 'INVALID';\n  }\n\n  static get LIST_ALL() {\n    return 'ALL';\n  }\n  /**\n   * Compute a 1-based index into a list, based on a Scratch argument.\n   * Two special cases may be returned:\n   * LIST_ALL: if the block is referring to all of the items in the list.\n   * LIST_INVALID: if the index was invalid in any way.\n   * @param {*} index Scratch arg, including 1-based numbers or special cases.\n   * @param {number} length Length of the list.\n   * @param {boolean} acceptAll Whether it should accept \"all\" or not.\n   * @return {(number|string)} 1-based index for list, LIST_ALL, or LIST_INVALID.\n   */\n\n\n  static toListIndex(index, length, acceptAll) {\n    if (typeof index !== 'number') {\n      if (index === 'all') {\n        return acceptAll ? Cast.LIST_ALL : Cast.LIST_INVALID;\n      }\n\n      if (index === 'last') {\n        if (length > 0) {\n          return length;\n        }\n\n        return Cast.LIST_INVALID;\n      } else if (index === 'random' || index === 'any') {\n        if (length > 0) {\n          return 1 + Math.floor(Math.random() * length);\n        }\n\n        return Cast.LIST_INVALID;\n      }\n    }\n\n    index = Math.floor(Cast.toNumber(index));\n\n    if (index < 1 || index > length) {\n      return Cast.LIST_INVALID;\n    }\n\n    return index;\n  }\n\n}\n\nmodule.exports = Cast;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/util/color.js\":\n/*!***************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/util/color.js ***!\n  \\***************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nclass Color {\n  /**\n   * @typedef {object} RGBObject - An object representing a color in RGB format.\n   * @property {number} r - the red component, in the range [0, 255].\n   * @property {number} g - the green component, in the range [0, 255].\n   * @property {number} b - the blue component, in the range [0, 255].\n   */\n\n  /**\n   * @typedef {object} HSVObject - An object representing a color in HSV format.\n   * @property {number} h - hue, in the range [0-359).\n   * @property {number} s - saturation, in the range [0,1].\n   * @property {number} v - value, in the range [0,1].\n   */\n\n  /** @type {RGBObject} */\n  static get RGB_BLACK() {\n    return {\n      r: 0,\n      g: 0,\n      b: 0\n    };\n  }\n  /** @type {RGBObject} */\n\n\n  static get RGB_WHITE() {\n    return {\n      r: 255,\n      g: 255,\n      b: 255\n    };\n  }\n  /**\n   * Convert a Scratch decimal color to a hex string, #RRGGBB.\n   * @param {number} decimal RGB color as a decimal.\n   * @return {string} RGB color as #RRGGBB hex string.\n   */\n\n\n  static decimalToHex(decimal) {\n    if (decimal < 0) {\n      decimal += 0xFFFFFF + 1;\n    }\n\n    let hex = Number(decimal).toString(16);\n    hex = \"#\".concat('000000'.substr(0, 6 - hex.length)).concat(hex);\n    return hex;\n  }\n  /**\n   * Convert a Scratch decimal color to an RGB color object.\n   * @param {number} decimal RGB color as decimal.\n   * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   */\n\n\n  static decimalToRgb(decimal) {\n    const a = decimal >> 24 & 0xFF;\n    const r = decimal >> 16 & 0xFF;\n    const g = decimal >> 8 & 0xFF;\n    const b = decimal & 0xFF;\n    return {\n      r: r,\n      g: g,\n      b: b,\n      a: a > 0 ? a : 255\n    };\n  }\n  /**\n   * Convert a hex color (e.g., F00, #03F, #0033FF) to an RGB color object.\n   * @param {!string} hex Hex representation of the color.\n   * @return {RGBObject} null on failure, or rgb: {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   */\n\n\n  static hexToRgb(hex) {\n    if (hex.startsWith('#')) {\n      hex = hex.substring(1);\n    }\n\n    const parsed = parseInt(hex, 16);\n\n    if (isNaN(parsed)) {\n      return null;\n    }\n\n    if (hex.length === 6) {\n      return {\n        r: parsed >> 16 & 0xff,\n        g: parsed >> 8 & 0xff,\n        b: parsed & 0xff\n      };\n    } else if (hex.length === 3) {\n      const r = parsed >> 8 & 0xf;\n      const g = parsed >> 4 & 0xf;\n      const b = parsed & 0xf;\n      return {\n        r: r << 4 | r,\n        g: g << 4 | g,\n        b: b << 4 | b\n      };\n    }\n\n    return null;\n  }\n  /**\n   * Convert an RGB color object to a hex color.\n   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   * @return {!string} Hex representation of the color.\n   */\n\n\n  static rgbToHex(rgb) {\n    return Color.decimalToHex(Color.rgbToDecimal(rgb));\n  }\n  /**\n   * Convert an RGB color object to a Scratch decimal color.\n   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   * @return {!number} Number representing the color.\n   */\n\n\n  static rgbToDecimal(rgb) {\n    return (rgb.r << 16) + (rgb.g << 8) + rgb.b;\n  }\n  /**\n  * Convert a hex color (e.g., F00, #03F, #0033FF) to a decimal color number.\n  * @param {!string} hex Hex representation of the color.\n  * @return {!number} Number representing the color.\n  */\n\n\n  static hexToDecimal(hex) {\n    return Color.rgbToDecimal(Color.hexToRgb(hex));\n  }\n  /**\n   * Convert an HSV color to RGB format.\n   * @param {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]}\n   * @return {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   */\n\n\n  static hsvToRgb(hsv) {\n    let h = hsv.h % 360;\n    if (h < 0) h += 360;\n    const s = Math.max(0, Math.min(hsv.s, 1));\n    const v = Math.max(0, Math.min(hsv.v, 1));\n    const i = Math.floor(h / 60);\n    const f = h / 60 - i;\n    const p = v * (1 - s);\n    const q = v * (1 - s * f);\n    const t = v * (1 - s * (1 - f));\n    let r;\n    let g;\n    let b;\n\n    switch (i) {\n      default:\n      case 0:\n        r = v;\n        g = t;\n        b = p;\n        break;\n\n      case 1:\n        r = q;\n        g = v;\n        b = p;\n        break;\n\n      case 2:\n        r = p;\n        g = v;\n        b = t;\n        break;\n\n      case 3:\n        r = p;\n        g = q;\n        b = v;\n        break;\n\n      case 4:\n        r = t;\n        g = p;\n        b = v;\n        break;\n\n      case 5:\n        r = v;\n        g = p;\n        b = q;\n        break;\n    }\n\n    return {\n      r: Math.floor(r * 255),\n      g: Math.floor(g * 255),\n      b: Math.floor(b * 255)\n    };\n  }\n  /**\n   * Convert an RGB color to HSV format.\n   * @param {RGBObject} rgb - {r: red [0,255], g: green [0,255], b: blue [0,255]}.\n   * @return {HSVObject} hsv - {h: hue [0,360), s: saturation [0,1], v: value [0,1]}\n   */\n\n\n  static rgbToHsv(rgb) {\n    const r = rgb.r / 255;\n    const g = rgb.g / 255;\n    const b = rgb.b / 255;\n    const x = Math.min(Math.min(r, g), b);\n    const v = Math.max(Math.max(r, g), b); // For grays, hue will be arbitrarily reported as zero. Otherwise, calculate\n\n    let h = 0;\n    let s = 0;\n\n    if (x !== v) {\n      const f = r === x ? g - b : g === x ? b - r : r - g;\n      const i = r === x ? 3 : g === x ? 5 : 1;\n      h = (i - f / (v - x)) * 60 % 360;\n      s = (v - x) / v;\n    }\n\n    return {\n      h: h,\n      s: s,\n      v: v\n    };\n  }\n  /**\n   * Linear interpolation between rgb0 and rgb1.\n   * @param {RGBObject} rgb0 - the color corresponding to fraction1 <= 0.\n   * @param {RGBObject} rgb1 - the color corresponding to fraction1 >= 1.\n   * @param {number} fraction1 - the interpolation parameter. If this is 0.5, for example, mix the two colors equally.\n   * @return {RGBObject} the interpolated color.\n   */\n\n\n  static mixRgb(rgb0, rgb1, fraction1) {\n    if (fraction1 <= 0) return rgb0;\n    if (fraction1 >= 1) return rgb1;\n    const fraction0 = 1 - fraction1;\n    return {\n      r: fraction0 * rgb0.r + fraction1 * rgb1.r,\n      g: fraction0 * rgb0.g + fraction1 * rgb1.g,\n      b: fraction0 * rgb0.b + fraction1 * rgb1.b\n    };\n  }\n\n}\n\nmodule.exports = Color;\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-vm/src/util/log.js\":\n/*!*************************************************!*\\\n  !*** ./node_modules/scratch-vm/src/util/log.js ***!\n  \\*************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\nconst minilog = __webpack_require__(/*! minilog */ \"./node_modules/minilog/lib/web/index.js\");\n\nminilog.enable();\nmodule.exports = minilog('vm');\n\n/***/ }),\n\n/***/ \"./node_modules/webpack/buildin/global.js\":\n/*!***********************************!*\\\n  !*** (webpack)/buildin/global.js ***!\n  \\***********************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\nvar g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n\n\n/***/ })\n\n/******/ });\n//# sourceMappingURL=cf823a98d0fb1be07e5e.worker.js.map", null);
};

/***/ }),

/***/ "./src/build/inline-worker-loader/worker-loader.js?{\"inline\":true,\"fallback\":true}!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js":
/*!******************************************************************************************************************************************************!*\
  !*** ./src/build/inline-worker-loader/worker-loader.js?{"inline":true,"fallback":true}!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js ***!
  \******************************************************************************************************************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

module.exports = function() {
  return __webpack_require__(/*! !./node_modules/worker-loader/dist/workers/InlineWorker.js */ "./node_modules/worker-loader/dist/workers/InlineWorker.js")("/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId]) {\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/ \t\t}\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\ti: moduleId,\n/******/ \t\t\tl: false,\n/******/ \t\t\texports: {}\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.l = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// define getter function for harmony exports\n/******/ \t__webpack_require__.d = function(exports, name, getter) {\n/******/ \t\tif(!__webpack_require__.o(exports, name)) {\n/******/ \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n/******/ \t\t}\n/******/ \t};\n/******/\n/******/ \t// define __esModule on exports\n/******/ \t__webpack_require__.r = function(exports) {\n/******/ \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n/******/ \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n/******/ \t\t}\n/******/ \t\tObject.defineProperty(exports, '__esModule', { value: true });\n/******/ \t};\n/******/\n/******/ \t// create a fake namespace object\n/******/ \t// mode & 1: value is a module id, require it\n/******/ \t// mode & 2: merge all properties of value into the ns\n/******/ \t// mode & 4: return value when already ns object\n/******/ \t// mode & 8|1: behave like require\n/******/ \t__webpack_require__.t = function(value, mode) {\n/******/ \t\tif(mode & 1) value = __webpack_require__(value);\n/******/ \t\tif(mode & 8) return value;\n/******/ \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n/******/ \t\tvar ns = Object.create(null);\n/******/ \t\t__webpack_require__.r(ns);\n/******/ \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n/******/ \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n/******/ \t\treturn ns;\n/******/ \t};\n/******/\n/******/ \t// getDefaultExport function for compatibility with non-harmony modules\n/******/ \t__webpack_require__.n = function(module) {\n/******/ \t\tvar getter = module && module.__esModule ?\n/******/ \t\t\tfunction getDefault() { return module['default']; } :\n/******/ \t\t\tfunction getModuleExports() { return module; };\n/******/ \t\t__webpack_require__.d(getter, 'a', getter);\n/******/ \t\treturn getter;\n/******/ \t};\n/******/\n/******/ \t// Object.prototype.hasOwnProperty.call\n/******/ \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(__webpack_require__.s = \"./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js\");\n/******/ })\n/************************************************************************/\n/******/ ({\n\n/***/ \"./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js\":\n/*!************************************************************************************************************!*\\\n  !*** ./node_modules/babel-loader/lib??ref--4!./node_modules/scratch-storage/src/FetchWorkerTool.worker.js ***!\n  \\************************************************************************************************************/\n/*! no static exports found */\n/***/ (function(module, exports, __webpack_require__) {\n\n/* eslint-env worker */\nconst saferFetchAsArrayBuffer = __webpack_require__(/*! ./safer-fetch */ \"./node_modules/scratch-storage/src/safer-fetch.js\");\n\nconst complete = [];\nlet timeoutId = null;\n\nconst checkCompleted = () => {\n  if (timeoutId) return;\n  timeoutId = setTimeout(() => {\n    timeoutId = null;\n\n    if (complete.length) {\n      // Send our chunk of completed requests and instruct postMessage to\n      // transfer the buffers instead of copying them.\n      postMessage(complete.slice(), // Instruct postMessage that these buffers in the sent message\n      // should use their Transferable trait. After the postMessage\n      // call the \"buffers\" will still be in complete if you looked,\n      // but they will all be length 0 as the data they reference has\n      // been sent to the window. This lets us send a lot of data\n      // without the normal postMessage behaviour of making a copy of\n      // all of the data for the window.\n      complete.map(response => response.buffer).filter(Boolean));\n      complete.length = 0;\n    }\n  });\n};\n/**\n * Receive a job from the parent and fetch the requested data.\n * @param {object} options.job A job id, url, and options descriptor to perform.\n */\n\n\nconst onMessage = _ref => {\n  let {\n    data: job\n  } = _ref;\n  saferFetchAsArrayBuffer(job.url, job.options).then(buffer => complete.push({\n    id: job.id,\n    buffer\n  })).catch(error => complete.push({\n    id: job.id,\n    error: error && error.message || \"Failed request: \".concat(job.url)\n  })).then(checkCompleted);\n};\n\nif (self.fetch) {\n  postMessage({\n    support: {\n      fetch: true\n    }\n  });\n  self.addEventListener('message', onMessage);\n} else {\n  postMessage({\n    support: {\n      fetch: false\n    }\n  });\n  self.addEventListener('message', _ref2 => {\n    let {\n      data: job\n    } = _ref2;\n    postMessage([{\n      id: job.id,\n      error: 'fetch is unavailable'\n    }]);\n  });\n}\n\n/***/ }),\n\n/***/ \"./node_modules/scratch-storage/src/safer-fetch.js\":\n/*!*********************************************************!*\\\n  !*** ./node_modules/scratch-storage/src/safer-fetch.js ***!\n  \\*********************************************************/\n/*! no static exports found */\n/***/ (function(module, exports) {\n\n/* eslint-env browser */\n\n/* eslint-disable no-use-before-define */\n// This throttles and retries fetch() to mitigate the effect of random network errors and\n// random browser errors (especially in Chrome)\nlet currentFetches = 0;\nconst queue = [];\n\nconst startNextFetch = _ref => {\n  let [resolve, url, options] = _ref;\n  let firstError;\n  let failedAttempts = 0;\n\n  const attemptToFetch = () => fetch(url, options).then(result => {\n    // In a macOS WKWebView, requests from file: URLs to other file: URLs always have status: 0 and ok: false\n    // even though the requests were successful. If the requested file doesn't exist, fetch() rejects instead.\n    // We aren't aware of any other cases where fetch() can resolve with status 0, so this should be safe.\n    if (result.ok || result.status === 0) return result.arrayBuffer();\n    if (result.status === 404) return null;\n    return Promise.reject(result.status);\n  }).then(buffer => {\n    currentFetches--;\n    checkStartNextFetch();\n    return buffer;\n  }).catch(error => {\n    if (error === 403) {\n      // Retrying this request will not help, so return an error now.\n      throw error;\n    }\n\n    console.warn(\"Attempt to fetch \".concat(url, \" failed\"), error);\n\n    if (!firstError) {\n      firstError = error;\n    }\n\n    if (failedAttempts < 2) {\n      failedAttempts++;\n      return new Promise(cb => setTimeout(cb, (failedAttempts + Math.random() - 1) * 5000)).then(attemptToFetch);\n    }\n\n    currentFetches--;\n    checkStartNextFetch();\n    throw firstError;\n  });\n\n  return resolve(attemptToFetch());\n};\n\nconst checkStartNextFetch = () => {\n  if (currentFetches < 100 && queue.length > 0) {\n    currentFetches++;\n    startNextFetch(queue.shift());\n  }\n};\n\nconst saferFetchAsArrayBuffer = (url, options) => new Promise(resolve => {\n  queue.push([resolve, url, options]);\n  checkStartNextFetch();\n});\n\nmodule.exports = saferFetchAsArrayBuffer;\n\n/***/ })\n\n/******/ });\n//# sourceMappingURL=23993d036323baba2c1e.worker.js.map", null);
};

/***/ }),

/***/ "./src/common/event-target.js":
/*!************************************!*\
  !*** ./src/common/event-target.js ***!
  \************************************/
/*! exports provided: EventTarget, CustomEvent */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "EventTarget", function() { return EventTargetShim; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CustomEvent", function() { return CustomEventShim; });
// Browser support for EventTarget constructor is surprisingly poor, so we always polyfill it
// We also need to polyfill CustomEvent for Node.js
class EventTargetShim {
  constructor() {
    this._events = {};
  }

  addEventListener(event, handler) {
    if (!this._events[event]) {
      this._events[event] = [];
    }

    this._events[event].push(handler);
  }

  removeEventListener(event, handler) {
    const handlers = this._events[event];

    if (handlers) {
      this._events[event] = handlers.filter(i => i !== handler);
    }
  }

  dispatchEvent(event) {
    const handlers = this._events[event.type];

    if (handlers) {
      for (const fn of handlers) {
        fn(event);
      }
    }
  }

}

class CustomEventShim {
  constructor(type, options) {
    this.type = type;
    this.detail = options ? options.detail : {};
  }

}



/***/ }),

/***/ "./src/common/readers.js":
/*!*******************************!*\
  !*** ./src/common/readers.js ***!
  \*******************************/
/*! exports provided: readAsArrayBuffer, readAsURL, readAsText */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readAsArrayBuffer", function() { return readAsArrayBuffer; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readAsURL", function() { return readAsURL; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "readAsText", function() { return readAsText; });
/**
 * @param {Blob} o
 * @returns {Promise<ArrayBuffer>}
 */
const readAsArrayBuffer = o => new Promise((resolve, reject) => {
  const fr = new FileReader();

  fr.onload = () => resolve(fr.result);

  fr.onerror = () => reject(new Error("Cannot read as array buffer: ".concat(fr.error)));

  fr.readAsArrayBuffer(o);
});
/**
 * @param {Blob} o
 * @returns {Promise<string>}
 */

const readAsURL = o => new Promise((resolve, reject) => {
  const fr = new FileReader();

  fr.onload = () => resolve(fr.result);

  fr.onerror = () => reject(new Error("Cannot read as URL: ".concat(fr.error)));

  fr.readAsDataURL(o);
});
/**
 * @param {Blob} o
 * @returns {Promise<string>}
 */

const readAsText = o => new Promise((resolve, reject) => {
  const fr = new FileReader();

  fr.onload = () => resolve(fr.result);

  fr.onerror = () => reject(new Error("Cannot read as text: ".concat(fr.error)));

  fr.readAsText(o);
});

/***/ }),

/***/ "./src/scaffolding/check.svg":
/*!***********************************!*\
  !*** ./src/scaffolding/check.svg ***!
  \***********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjQgMjQiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZD0iTTIwLjc3MzU5NSA1LjcyODA1MTlhMS4zMDc3Nzc0IDEuMzA3Nzc3NCAwIDAgMC0xLjg1NzA0MyAwTDkuMTczNjEwNSAxNS40ODQwNzFsLTQuMDkzMzQzMi00LjEwNjQyYTEuMzM2NDQ2OCAxLjMzNjQ0NjggMCAxIDAtMS44NTcwNDM5IDEuOTIyNDMybDUuMDIxODY1MSA1LjAyMTg2NmExLjMwNzc3NzQgMS4zMDc3Nzc0IDAgMCAwIDEuODU3MDQ0NSAwTDIwLjc3MzU5NSA3LjY1MDQ4NDdhMS4zMDc3Nzc0IDEuMzA3Nzc3NCAwIDAgMCAwLTEuOTIyNDMyOHoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=");

/***/ }),

/***/ "./src/scaffolding/cloud.js":
/*!**********************************!*\
  !*** ./src/scaffolding/cloud.js ***!
  \**********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
class CloudManager {
  constructor(parent) {
    this.parent = parent;
    this.providers = [];
    this.overrides = new Map();
  }

  hasCloudData() {
    return this.parent.vm.runtime.hasCloudData();
  }

  projectReady() {
    if (this.hasCloudData()) {
      for (const provider of this.providers) {
        provider.enable();
      }
    }
  }

  setVariable(provider, name, value) {
    if (this.overrides.has(name) && this.overrides.get(name) !== provider) {
      return;
    }

    this.parent.vm.postIOData('cloud', {
      varUpdate: {
        name,
        value
      }
    });
  }

  getUsername() {
    return this.parent._username;
  }

  addProvider(provider) {
    provider.manager = this;

    if (this.hasCloudData()) {
      provider.enable();
    }

    this.providers.push(provider);
  }

  requestCloseConnection() {// no-op
  }

  createVariable(name, value) {// no-op
  }

  renameVariable(oldName, newName) {// no-op
  }

  deleteVariable(name) {// no-op
  }

  addProviderOverride(name, provider) {
    if (provider && !this.providers.includes(provider)) {
      throw new Error('Manager is not aware of this provider');
    }

    this.overrides.set(name, provider);
  }

  updateVariable(name, value) {
    if (this.overrides.has(name)) {
      const provider = this.overrides.get(name);

      if (provider) {
        provider.handleUpdateVariable(name, value);
      }

      return;
    }

    for (const provider of this.providers) {
      provider.handleUpdateVariable(name, value);
    }
  }

}

class WebSocketProvider {
  /**
   * @param {string[]|string} cloudHost URLs of servers to connect to. Must start with ws: or wss:
   * If cloudHost is an array, the server will consecutively try each server until one connects.
   * @param {string} projectId The ID of the project
   */
  constructor(cloudHost, projectId) {
    this.cloudHosts = Array.isArray(cloudHost) ? cloudHost : [cloudHost];
    this.projectId = projectId;
    this.attemptedConnections = 0;
    this.bufferedMessages = [];
    this.scheduledBufferedSend = null;
    this.reconnectTimeout = null;
    this.openConnection = this.openConnection.bind(this);
    this._scheduledSendBufferedMessages = this._scheduledSendBufferedMessages.bind(this);
  }

  enable() {
    this.openConnection();
  }

  setProjectId(id) {
    this.projectId = id;
    this.closeAndReconnect();
  }

  openConnection() {
    this.currentCloudHost = this.cloudHosts[this.attemptedConnections % this.cloudHosts.length];
    this.attemptedConnections++;
    console.log("Connecting to ".concat(this.currentCloudHost, " with ID ").concat(this.projectId, ", username ").concat(this.manager.getUsername()));

    try {
      // Don't try to validate the cloud host ourselves. Let the browser do it.
      // Edge cases like ws://localhost being considered secure are too complex to handle correctly.
      this.ws = new WebSocket(this.currentCloudHost);
    } catch (e) {
      console.error(e); // The error message from the browser (especially Firefox) is sometimes very generic and not helpful.

      throw new Error("Cloud host ".concat(this.currentCloudHost, " is invalid: ").concat(e));
    }

    this.ws.onerror = this.onerror.bind(this);
    this.ws.onmessage = this.onmessage.bind(this);
    this.ws.onopen = this.onopen.bind(this);
    this.ws.onclose = this.onclose.bind(this);
  }

  onerror(event) {
    console.error('WebSocket error', event);
  }

  onmessage(event) {
    for (const line of event.data.split('\n')) {
      if (line) {
        const parsed = JSON.parse(line);

        if (parsed.method === 'set') {
          this.manager.setVariable(this, parsed.name, parsed.value);
        }
      }
    }
  }

  onopen() {
    this.attemptedConnections = 0;
    this.writeToServer({
      method: 'handshake'
    });
    this.sendBufferedMessages();
    console.log('WebSocket connected');
  }

  onclose(e) {
    // https://github.com/TurboWarp/cloud-server/blob/master/doc/protocol.md#status-codes
    if (e && e.code === 4002) {
      console.log('Username is invalid; not reconnecting.');
      return;
    }

    if (e && e.code === 4004) {
      console.log('Project is blocked; not reconnecting.');
      return;
    }

    const timeout = Math.random() * (Math.pow(2, Math.min(this.attemptedConnections + 1, 5)) - 1) * 1000;
    console.log("Connection lost; reconnecting in ".concat(Math.round(timeout), "ms"));
    this.reconnectTimeout = setTimeout(this.openConnection, timeout);
  }

  closeAndReconnect() {
    console.log('Closing connection and reconnecting.');

    if (this.ws) {
      this.ws.onclose = null;
      this.ws.onerror = null;
      this.ws.close();
    }

    clearTimeout(this.reconnectTimeout); // There should be a slight delay so that repeated project ID changes won't trigger too many connections.

    const delay = 1000 / 30;
    this.reconnectTimeout = setTimeout(this.openConnection, delay);
  }

  canWriteToServer() {
    return this.ws && this.ws.readyState === WebSocket.OPEN;
  }

  scheduleBufferedSend() {
    if (!this.scheduledBufferedSend) {
      this.scheduledBufferedSend = true;
      Promise.resolve().then(this._scheduledSendBufferedMessages);
    }
  }

  _scheduledSendBufferedMessages() {
    this.scheduledBufferedSend = false;

    if (this.canWriteToServer()) {
      this.sendBufferedMessages();
    }
  }

  sendBufferedMessages() {
    for (const message of this.bufferedMessages) {
      this.writeToServer(message);
    }

    this.bufferedMessages.length = 0;
  }

  bufferedWriteToServer(message) {
    this.bufferedMessages.push(message);
    this.scheduleBufferedSend();
  }

  writeToServer(message) {
    message.project_id = this.projectId;
    message.user = this.manager.getUsername();
    this.ws.send(JSON.stringify(message));
  }

  handleUpdateVariable(name, value) {
    // If this variable already has an update queued, we'll replace its value instead of adding another update.
    for (const i of this.bufferedMessages) {
      if (i.name === name) {
        i.value = value;
        return;
      }
    }

    this.bufferedWriteToServer({
      method: 'set',
      name,
      value
    });
  }

}

class LocalStorageProvider {
  constructor() {
    let key = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'p4:cloudvariables';
    this.key = key;
    this.variables = {};
    this.handleStorageEvent = this.handleStorageEvent.bind(this);
  }

  readFromLocalStorage() {
    let parsed;

    try {
      parsed = JSON.parse(localStorage.getItem(this.key));

      if (!parsed || typeof parsed !== 'object') {
        return;
      }
    } catch (e) {
      return;
    }

    this.variables = parsed;

    for (const key of Object.keys(this.variables)) {
      this.manager.setVariable(this, key, this.variables[key]);
    }
  }

  storeToLocalStorage() {
    try {
      localStorage.setItem(this.key, JSON.stringify(this.variables));
    } catch (e) {// ignore
    }
  }

  handleStorageEvent(e) {
    if (e.key === this.key && e.storageArea === localStorage) {
      this.readFromLocalStorage();
    }
  }

  enable() {
    this.readFromLocalStorage();
    window.addEventListener('storage', this.handleStorageEvent);
  }

  handleUpdateVariable(name, value) {
    this.variables[name] = value;
    this.storeToLocalStorage();
  }

}

/* harmony default export */ __webpack_exports__["default"] = ({
  CloudManager,
  WebSocketProvider,
  LocalStorageProvider
});

/***/ }),

/***/ "./src/scaffolding/context-menu.js":
/*!*****************************************!*\
  !*** ./src/scaffolding/context-menu.js ***!
  \*****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");


class ContextMenu {
  constructor(parent) {
    this.parent = parent;
    this.root = document.createElement('div');
    this.root.className = _style_css__WEBPACK_IMPORTED_MODULE_0__["default"].contextMenu;
    this._onmousedown = this._onmousedown.bind(this);
    this._onresize = this._onresize.bind(this);
    this._onblur = this._onblur.bind(this);
  }

  _onmousedown(e) {
    if (!this.root.contains(e.target)) {
      this.destroy();
    }
  }

  _onresize() {
    this.destroy();
  }

  _onblur() {
    this.destroy();
  }

  add(option) {
    const item = document.createElement('button');
    item.className = _style_css__WEBPACK_IMPORTED_MODULE_0__["default"].contextMenuItem;
    item.textContent = option.text;
    item.addEventListener('click', () => {
      this.destroy();
      option.callback();
    });
    item.addEventListener('contextmenu', e => {
      e.preventDefault();
    });
    this.root.appendChild(item);
  }

  show(mouseEvent) {
    document.addEventListener('mousedown', this._onmousedown);
    window.addEventListener('resize', this._onresize);
    window.addEventListener('blur', this._onblur);

    this.parent._addLayer(this.root);

    const layersRect = this.parent.layersRect;
    const menuRect = this.root.getBoundingClientRect();
    let x = mouseEvent.clientX - layersRect.left;
    let y = mouseEvent.clientY - layersRect.top;

    if (x + menuRect.width > layersRect.width) {
      x -= menuRect.width;
    }

    if (y + menuRect.height > layersRect.height) {
      y -= menuRect.height;
    }

    this.root.style.transform = "translate(".concat(x, "px, ").concat(y, "px)");
    getComputedStyle(this.root).opacity;
    this.root.style.opacity = '1';
  }

  destroy() {
    document.removeEventListener('mousedown', this._onmousedown);
    window.removeEventListener('resize', this._onresize);
    window.removeEventListener('blur', this._onblur);
    this.root.style.opacity = '0';
    this.root.style.pointerEvents = 'none';
    setTimeout(() => {
      this.root.remove();
    }, 200);
  }

}

/* harmony default export */ __webpack_exports__["default"] = (ContextMenu);

/***/ }),

/***/ "./src/scaffolding/control-bar.js":
/*!****************************************!*\
  !*** ./src/scaffolding/control-bar.js ***!
  \****************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");


class ControlBar {
  constructor() {
    this.hasItem = false;
    this.root = document.createElement('div');
    this.root.className = _style_css__WEBPACK_IMPORTED_MODULE_0__["default"].controlsBar;
    this.start = document.createElement('div');
    this.end = document.createElement('div');
    this.root.appendChild(this.start);
    this.root.appendChild(this.end);
  }

  addToStart(el) {
    this.hasItem = true;
    this.start.appendChild(el);
  }

  addToEnd(el) {
    this.hasItem = true;
    this.end.appendChild(el);
  }

  computeHeight() {
    if (!this.hasItem) {
      return 0;
    }

    return this.root.getBoundingClientRect().height;
  }

}

/* harmony default export */ __webpack_exports__["default"] = (ControlBar);

/***/ }),

/***/ "./src/scaffolding/download.js":
/*!*************************************!*\
  !*** ./src/scaffolding/download.js ***!
  \*************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
// Based on https://github.com/LLK/scratch-gui/blob/develop/src/lib/download-blob.js
const downloadBlob = (filename, blob) => {
  if (typeof ExternalDownloadHelper !== 'undefined') {
    ExternalDownloadHelper.download(filename, blob);
    return;
  }

  const downloadLink = document.createElement('a');
  document.body.appendChild(downloadLink); // Use special ms version if available to get it working on Edge.

  if (navigator.msSaveOrOpenBlob) {
    navigator.msSaveOrOpenBlob(blob, filename);
    return;
  }

  if ('download' in HTMLAnchorElement.prototype) {
    const url = window.URL.createObjectURL(blob);
    downloadLink.href = url;
    downloadLink.download = filename;
    downloadLink.type = blob.type;
    downloadLink.click(); // remove the link after a timeout to prevent a crash on iOS 13 Safari

    window.setTimeout(() => {
      document.body.removeChild(downloadLink);
      window.URL.revokeObjectURL(url);
    }, 1000);
  } else {
    // iOS 12 Safari, open a new page and set href to data-uri
    let popup = window.open('', '_blank');
    const reader = new FileReader();

    reader.onloadend = function () {
      popup.location.href = reader.result;
      popup = null;
    };

    reader.readAsDataURL(blob);
  }
};

/* harmony default export */ __webpack_exports__["default"] = (downloadBlob);

/***/ }),

/***/ "./src/scaffolding/drop-area.js":
/*!**************************************!*\
  !*** ./src/scaffolding/drop-area.js ***!
  \**************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");
/* harmony import */ var _common_readers__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../common/readers */ "./src/common/readers.js");



class DropArea {
  constructor(el, callback) {
    this.el = el;
    this.callback = callback;
    this.el.addEventListener('dragover', this.ondragover.bind(this));
    this.el.addEventListener('dragleave', this.ondragleave.bind(this));
    this.el.addEventListener('drop', this.ondrop.bind(this));
  }

  ondragover(e) {
    if (e.dataTransfer.types.includes('Files')) {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'copy';
      this.el.classList.add(_style_css__WEBPACK_IMPORTED_MODULE_0__["default"].dropping);
    }
  }

  ondragleave(e) {
    e.preventDefault();
    this.el.classList.remove(_style_css__WEBPACK_IMPORTED_MODULE_0__["default"].dropping);
  }

  ondrop(e) {
    e.preventDefault();
    this.el.classList.remove(_style_css__WEBPACK_IMPORTED_MODULE_0__["default"].dropping);

    if (e.dataTransfer.types.includes('Files') && e.dataTransfer.files.length > 0) {
      Promise.all(Array.from(e.dataTransfer.files).map(_common_readers__WEBPACK_IMPORTED_MODULE_1__["readAsText"])).then(texts => {
        this.callback(texts);
      });
    }
  }

}

/* harmony default export */ __webpack_exports__["default"] = (DropArea);

/***/ }),

/***/ "./src/scaffolding/export.js":
/*!***********************************!*\
  !*** ./src/scaffolding/export.js ***!
  \***********************************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _scaffolding__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./scaffolding */ "./src/scaffolding/scaffolding.js");


if (window.Scaffolding) {
  throw new Error('Scaffolding already exists on this page');
}

if (true) {
  console.log('This is not a production build. Set NODE_ENV to production or use `npm run build-prod` for improved file size and performance (This message will go away).');
}

window.Scaffolding = _scaffolding__WEBPACK_IMPORTED_MODULE_0__;

/***/ }),

/***/ "./src/scaffolding/htmlparser2/index.js":
/*!**********************************************!*\
  !*** ./src/scaffolding/htmlparser2/index.js ***!
  \**********************************************/
/*! no static exports found */
/***/ (function(module, exports) {

// no-op
module.exports = {};

/***/ }),

/***/ "./src/scaffolding/messages.json":
/*!***************************************!*\
  !*** ./src/scaffolding/messages.json ***!
  \***************************************/
/*! exports provided: var-x, var-y, var-direction, var-username, var-costume-number, var-costume-name, var-backdrop-number, var-backdrop-name, var-size, var-answer, var-mousedown, var-mousex, var-mousey, var-loudness, var-timer, var-dayssince2000, var-volume, var-year, var-month, var-date, var-day-of-week, var-hour, var-minute, var-second, list-empty, list-length, list-import, list-export, default */
/***/ (function(module) {

module.exports = JSON.parse("{\"var-x\":\"x position\",\"var-y\":\"y position\",\"var-direction\":\"direction\",\"var-username\":\"username\",\"var-costume-number\":\"costume number\",\"var-costume-name\":\"costume name\",\"var-backdrop-number\":\"backdrop number\",\"var-backdrop-name\":\"backdrop name\",\"var-size\":\"size\",\"var-answer\":\"answer\",\"var-mousedown\":\"mouse down?\",\"var-mousex\":\"mouse x\",\"var-mousey\":\"mouse y\",\"var-loudness\":\"loudness\",\"var-timer\":\"timer\",\"var-dayssince2000\":\"days since 2000\",\"var-volume\":\"volume\",\"var-year\":\"year\",\"var-month\":\"month\",\"var-date\":\"date\",\"var-day-of-week\":\"day of week\",\"var-hour\":\"hour\",\"var-minute\":\"minute\",\"var-second\":\"second\",\"list-empty\":\"(empty)\",\"list-length\":\"length {n}\",\"list-import\":\"import\",\"list-export\":\"export\"}");

/***/ }),

/***/ "./src/scaffolding/monitor.js":
/*!************************************!*\
  !*** ./src/scaffolding/monitor.js ***!
  \************************************/
/*! exports provided: VariableMonitor, ListMonitor */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VariableMonitor", function() { return VariableMonitor; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ListMonitor", function() { return ListMonitor; });
/* harmony import */ var _context_menu__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./context-menu */ "./src/scaffolding/context-menu.js");
/* harmony import */ var _drop_area__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./drop-area */ "./src/scaffolding/drop-area.js");
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");
/* harmony import */ var _common_readers__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../common/readers */ "./src/common/readers.js");
/* harmony import */ var _download__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./download */ "./src/scaffolding/download.js");






class Monitor {
  constructor(parent, monitor) {
    this.parent = parent;
    this.id = monitor.get('id');
    this.spriteName = monitor.get('spriteName');
    this.targetId = monitor.get('targetId');
    this.opcode = monitor.get('opcode');
    this.params = monitor.get('params');
    this.root = document.createElement('div');
    this.root.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRoot;
    this.root.setAttribute('opcode', this.opcode);

    this.parent._monitorOverlay.appendChild(this.root);
  }

  getLabel() {
    let label;

    if (this.opcode === 'data_variable') {
      label = this.params.VARIABLE;
    } else if (this.opcode === 'data_listcontents') {
      label = this.params.LIST;
    } else if (this.opcode === 'motion_xposition') {
      label = this.parent.getMessage('var-x');
    } else if (this.opcode === 'motion_yposition') {
      label = this.parent.getMessage('var-y');
    } else if (this.opcode === 'motion_direction') {
      label = this.parent.getMessage('var-direction');
    } else if (this.opcode === 'sensing_username') {
      label = this.parent.getMessage('var-username');
    } else if (this.opcode === 'looks_costumenumbername') {
      if (this.params.NUMBER_NAME === 'number') {
        label = this.parent.getMessage('var-costume-number');
      } else {
        label = this.parent.getMessage('var-costume-name');
      }
    } else if (this.opcode === 'looks_backdropnumbername') {
      if (this.params.NUMBER_NAME === 'number') {
        label = this.parent.getMessage('var-backdrop-number');
      } else {
        label = this.parent.getMessage('var-backdrop-name');
      }
    } else if (this.opcode === 'looks_size') {
      label = this.parent.getMessage('var-size');
    } else if (this.opcode === 'sensing_answer') {
      label = this.parent.getMessage('var-answer');
    } else if (this.opcode === 'sensing_mousedown') {
      label = this.parent.getMessage('var-mousedown');
    } else if (this.opcode === 'sensing_mousex') {
      label = this.parent.getMessage('var-mousex');
    } else if (this.opcode === 'sensing_mousey') {
      label = this.parent.getMessage('var-mousey');
    } else if (this.opcode === 'sensing_loudness') {
      label = this.parent.getMessage('var-loudness');
    } else if (this.opcode === 'sensing_timer') {
      label = this.parent.getMessage('var-timer');
    } else if (this.opcode === 'sensing_dayssince2000') {
      label = this.parent.getMessage('var-dayssince2000');
    } else if (this.opcode === 'sound_volume') {
      label = this.parent.getMessage('var-volume');
    } else if (this.opcode === 'sensing_current') {
      const menu = this.params.CURRENTMENU.toLowerCase();

      if (menu === 'year') {
        label = this.parent.getMessage('var-year');
      } else if (menu === 'month') {
        label = this.parent.getMessage('var-month');
      } else if (menu === 'date') {
        label = this.parent.getMessage('var-date');
      } else if (menu === 'dayofweek') {
        label = this.parent.getMessage('var-day-of-week');
      } else if (menu === 'hour') {
        label = this.parent.getMessage('var-hour');
      } else if (menu === 'minute') {
        label = this.parent.getMessage('var-minute');
      } else if (menu === 'second') {
        label = this.parent.getMessage('var-second');
      }
    } else {
      const vmLabel = this.parent.vm.runtime.getLabelForOpcode(this.opcode);

      if (vmLabel) {
        label = vmLabel.label;
      } else {
        label = this.opcode;
      }
    }

    if (this.spriteName) {
      return "".concat(this.spriteName, ": ").concat(label);
    }

    return label;
  }

  getTarget() {
    if (this.targetId) {
      return this.parent.vm.runtime.getTargetById(this.targetId);
    }

    return this.parent.vm.runtime.getTargetForStage();
  }

  getVmVariable() {
    const target = this.getTarget();
    return target.variables[this.id];
  }

  update(monitor) {
    this.x = monitor.get('x');
    this.y = monitor.get('y');
    this.visible = monitor.get('visible');
    this.root.style.transform = "translate(".concat(Math.round(this.x), "px, ").concat(Math.round(this.y), "px)");
    this.root.style.display = this.visible ? '' : 'none';
  }

}

class VariableMonitor extends Monitor {
  constructor(parent, monitor) {
    super(parent, monitor);
    this.mode = monitor.get('mode');

    if (this.mode === 'large') {
      this.valueElement = document.createElement('div');
      this.valueElement.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorLargeValue + ' ' + _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorValueColor;
      this.root.appendChild(this.valueElement);
    } else {
      this.inner = document.createElement('div');
      this.inner.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorInner;
      this.valueRow = document.createElement('div');
      this.valueRow.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRow;
      this.label = document.createElement('div');
      this.label.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorLabel;
      this.label.textContent = this.getLabel();
      this.valueElement = document.createElement('div');
      this.valueElement.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorValue + ' ' + _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorValueColor;
      this.valueRow.appendChild(this.label);
      this.valueRow.appendChild(this.valueElement);
      this.inner.appendChild(this.valueRow);

      if (this.mode === 'slider') {
        this.sliderRow = document.createElement('div');
        this.sliderRow.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRow;
        this.slider = document.createElement('input');
        this.slider.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorSlider;
        this.slider.type = 'range';
        this.slider.min = monitor.get('sliderMin');
        this.slider.max = monitor.get('sliderMax');
        this.slider.step = monitor.get('isDiscrete') ? 1 : 0.01;
        this.slider.addEventListener('input', this.onsliderchange.bind(this));
        this.sliderRow.appendChild(this.slider);
        this.inner.appendChild(this.sliderRow);
      }

      this.root.appendChild(this.inner);
    }

    this.parent._monitorOverlay.appendChild(this.root);

    this._value = '';
  }

  setVariableValue(value) {
    const variable = this.getVmVariable();
    variable.value = value;

    if (variable.isCloud) {
      const runtime = this.parent.vm.runtime;
      runtime.ioDevices.cloud.requestUpdateVariable(variable.name, variable.value);
    }

    this._value = value;
    this.valueElement.textContent = value;
  }

  onsliderchange(e) {
    this.setVariableValue(+e.target.value);
  }

  update(monitor) {
    super.update(monitor);

    if (!this.visible) {
      return;
    }

    let value = monitor.get('value');

    if (typeof value === 'number') {
      value = Number(value.toFixed(6));
    }

    if (this._value !== value) {
      this._value = value;
      this.valueElement.textContent = value;

      if (this.slider) {
        this.slider.value = value;
      }
    }
  }

}

const ROW_HEIGHT = 24;

class Row {
  constructor(monitor) {
    this.monitor = monitor;
    this.index = -1;
    this.value = '';
    this.locked = false;
    this.root = document.createElement('label');
    this.root.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowRoot;
    this.indexEl = document.createElement('div');
    this.indexEl.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowIndex;
    this.valueOuter = document.createElement('div');
    this.valueOuter.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowValueOuter;
    this.editable = this.monitor.editable;

    if (this.editable) {
      this.valueInner = document.createElement('input');
      this.valueInner.tabIndex = -1;
      this.valueInner.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowValueInner;
      this.valueInner.readOnly = true;
      this.valueInner.addEventListener('click', this._onclickinput.bind(this));
      this.valueInner.addEventListener('blur', this._onblurinput.bind(this));
      this.valueInner.addEventListener('keypress', this._onkeypressinput.bind(this));
      this.valueInner.addEventListener('keydown', this._onkeypressdown.bind(this));
      this.valueInner.addEventListener('contextmenu', this._oncontextmenu.bind(this));
      this.valueInner.addEventListener('input', this._oninput.bind(this));
      this.valueOuter.appendChild(this.valueInner);
      this.deleteButton = document.createElement('button');
      this.deleteButton.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowDelete;
      this.deleteButton.textContent = '×';
      this.deleteButton.addEventListener('mousedown', this._onclickdelete.bind(this));
      this.valueOuter.appendChild(this.deleteButton);
    } else {
      this.valueInner = document.createElement('div');
      this.valueInner.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowValueInner;
      this.valueOuter.appendChild(this.valueInner);
      this.valueInner.addEventListener('contextmenu', this._oncontextmenuuneditable.bind(this));
    }

    this.root.appendChild(this.indexEl);
    this.root.appendChild(this.valueOuter);
  }

  _onclickinput() {
    this.valueInner.focus();

    if (this.locked) {
      return;
    }

    this.valueInner.select();
    this.valueInner.readOnly = false;
    this.locked = true;
    this.root.classList.add(_style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowValueEditing);
    this.addNewValue = false;
    this.deleteValue = false;
    this.valueWasChanged = false;
  }

  _onblurinput() {
    if (!this.locked) {
      return;
    }

    this.unfocus();

    if (this.deleteValue) {
      const value = [...this.monitor.value];
      value.splice(this.index, 1);
      this.monitor.setValue(value);
      this.monitor.tryToFocusRow(Math.min(value.length - 1, this.index));
    } else if (this.valueWasChanged || this.addNewValue) {
      const value = [...this.monitor.value];
      value[this.index] = this.valueInner.value;

      if (this.addNewValue) {
        value.splice(this.index + 1, 0, '');
      }

      this.monitor.setValue(value);

      if (this.addNewValue) {
        this.monitor.tryToFocusRow(this.index + 1);
      }
    }
  }

  _oninput() {
    this.valueWasChanged = true;
  }

  _onkeypressinput(e) {
    if (e.key === 'Enter') {
      this.addNewValue = true;
      this.valueInner.blur();
    }
  }

  _onkeypressdown(e) {
    if (e.key === 'Escape') {
      this.valueInner.blur();
    } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown' || e.key === 'Tab') {
      e.preventDefault();
      let index = this.index;

      if (e.key === 'ArrowUp' || e.key === 'Tab' && e.shiftKey) {
        index--;
        if (index < 0) index = this.monitor.value.length - 1;
      } else {
        index++;
        if (index >= this.monitor.value.length) index = 0;
      }

      this.monitor.tryToFocusRow(index);
    }
  }

  _onclickdelete(e) {
    e.preventDefault();
    this.deleteValue = true;
    this.valueInner.blur();
  }

  _oncontextmenu(e) {
    if (this.locked) {
      // Open native context menu instead of custom list one when editing
      e.stopPropagation();
    } else {
      // Right clicking should not focus and highlight input
      e.preventDefault();
    }
  }

  _oncontextmenuuneditable(e) {
    // When row has been highlighted, eg. by triple click, open native context menu instead of custom
    const selection = getSelection();

    if (this.valueInner.contains(selection.anchorNode) && !selection.isCollapsed) {
      e.stopPropagation();
    }
  }

  setIndex(index) {
    if (this.index !== index) {
      this.index = index;
      this.root.dataset.index = index;
      this.root.style.transform = "translateY(".concat(index * ROW_HEIGHT, "px)");
      this.indexEl.textContent = index + 1;
    }
  }

  setValue(value) {
    if (this.value !== value && !this.locked) {
      this.value = value;

      if (this.editable) {
        this.valueInner.value = value;
      } else {
        this.valueInner.textContent = value;
      }
    }
  }

  focus() {
    this.valueInner.click();

    if (document.activeElement !== this.valueInner) {
      setTimeout(() => this.valueInner.click());
    }
  }

  unfocus() {
    if (this.locked) {
      this.locked = false;
      this.valueInner.readOnly = true;
      this.root.classList.remove(_style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowValueEditing);
    }
  }

}

class ListMonitor extends Monitor {
  constructor(parent, monitor) {
    super(parent, monitor);
    this.editable = parent.editableLists;
    this.rows = new Map();
    this.cachedRows = [];
    this.scrollTop = 0;
    this.oldLength = -1;
    this.label = document.createElement('div');
    this.label.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorListLabel;
    this.label.textContent = this.getLabel();
    this.footer = document.createElement('div');
    this.footer.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorListFooter;
    this.footerText = document.createElement('div');
    this.footerText.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorListFooterText;
    this.rowsOuter = document.createElement('div');
    this.rowsOuter.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowsOuter;
    this.rowsInner = document.createElement('div');
    this.rowsInner.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowsInner;
    this.rowsInner.addEventListener('scroll', this._onscroll.bind(this), {
      passive: true
    });
    this.endPoint = document.createElement('div');
    this.endPoint.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorRowsEndpoint;
    this.emptyLabel = document.createElement('div');
    this.emptyLabel.textContent = parent.getMessage('list-empty');
    this.emptyLabel.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorEmpty;

    if (this.editable) {
      this.addButton = document.createElement('button');
      this.addButton.className = _style_css__WEBPACK_IMPORTED_MODULE_2__["default"].monitorListAdd;
      this.addButton.textContent = '+';
      this.addButton.addEventListener('click', this._onclickaddbutton.bind(this));
      this.footer.appendChild(this.addButton);
    }

    this.rowsInner.appendChild(this.endPoint);
    this.rowsInner.appendChild(this.emptyLabel);
    this.rowsOuter.appendChild(this.rowsInner);
    this.footer.appendChild(this.footerText);
    this.root.appendChild(this.label);
    this.root.appendChild(this.rowsOuter);
    this.root.appendChild(this.footer);
    this.dropper = new _drop_area__WEBPACK_IMPORTED_MODULE_1__["default"](this.rowsOuter, this.dropperCallback.bind(this));
    this.handleImport = this.handleImport.bind(this);
    this.handleExport = this.handleExport.bind(this);
    this.root.addEventListener('contextmenu', this._oncontextmenu.bind(this));
  }

  _onclickaddbutton(e) {
    this.setValue([...this.value, '']);
    this.tryToFocusRow(this.value.length - 1);
  }

  unfocusAllRows() {
    for (const row of this.rows.values()) {
      row.unfocus();
    }
  }

  tryToFocusRow(index) {
    if (index >= 0 && index < this.value.length) {
      this.unfocusAllRows();
      let row = this.rows.get(index);

      if (!row) {
        row = this.createRow(index);
      }

      row.focus();
    }
  }

  _onscroll(e) {
    this.scrollTop = e.target.scrollTop;
    this.updateValue(this.value);
  }

  _oncontextmenu(e) {
    e.preventDefault();
    const menu = new _context_menu__WEBPACK_IMPORTED_MODULE_0__["default"](this.parent);
    menu.add({
      text: this.parent.getMessage('list-import'),
      callback: this.handleImport
    });
    menu.add({
      text: this.parent.getMessage('list-export'),
      callback: this.handleExport
    });
    menu.show(e);
  }

  handleImport() {
    const fileSelector = document.createElement('input');
    fileSelector.type = 'file';
    fileSelector.accept = '.txt,.csv,.tsv';
    fileSelector.style.display = 'none';
    document.body.appendChild(fileSelector);
    fileSelector.addEventListener('change', e => {
      const files = e.target.files;
      if (files.length === 0) return;
      const file = files[0];
      Object(_common_readers__WEBPACK_IMPORTED_MODULE_3__["readAsText"])(file).then(text => this.import(text));
    });
    fileSelector.click();
  }

  import(text) {
    // TODO: Scratch uses a CSV parser
    const lines = text.split(/\r?\n/);
    this.setValue(lines);
  }

  handleExport() {
    const value = this.getValue();
    const exported = value.join('\n');
    const blob = new Blob([exported], {
      type: 'text/plain'
    });
    Object(_download__WEBPACK_IMPORTED_MODULE_4__["default"])("".concat(this.getLabel(), ".txt"), blob);
  }

  dropperCallback(texts) {
    this.import(texts.join('\n'));
  }

  getValue() {
    return this.getVmVariable().value;
  }

  setValue(value) {
    const variable = this.getVmVariable();
    variable.value = value;
    this.updateValue(value);
  }

  update(monitor) {
    super.update(monitor);

    if (!this.visible) {
      return;
    }

    this.width = monitor.get('width') || 100;
    this.height = monitor.get('height') || 200;
    this.root.style.width = "".concat(this.width, "px");
    this.root.style.height = "".concat(this.height, "px");
    this.updateValue(monitor.get('value'));
  }

  createRow(index) {
    const row = this.cachedRows.pop() || new Row(this);
    row.setIndex(index);
    row.setValue(this.value[index]);
    this.rows.set(index, row);
    let foundPlaceInDOM = false;

    for (const root of this.rowsInner.children) {
      const otherIndexString = root.dataset.index;

      if (!otherIndexString) {
        continue;
      }

      const otherIndexNumber = +otherIndexString;

      if (otherIndexNumber > index) {
        this.rowsInner.insertBefore(row.root, root);
        foundPlaceInDOM = true;
        break;
      }
    }

    if (!foundPlaceInDOM) {
      this.rowsInner.appendChild(row.root);
    }

    return row;
  }

  updateValue(value) {
    this.value = value;

    if (value.length !== this.oldLength) {
      this.oldLength = value.length;
      this.footerText.textContent = this.parent.getMessage('list-length').replace('{n}', value.length);
      this.endPoint.style.transform = "translateY(".concat(value.length * ROW_HEIGHT, "px)");
      this.emptyLabel.style.display = value.length ? 'none' : '';
    }

    let startIndex = Math.floor(this.scrollTop / ROW_HEIGHT) - 5;
    if (startIndex < 0) startIndex = 0;
    let endIndex = Math.ceil((this.scrollTop + this.height) / ROW_HEIGHT) + 3;
    if (endIndex > value.length - 1) endIndex = value.length - 1;

    for (const index of this.rows.keys()) {
      if (index < startIndex || index > endIndex) {
        const row = this.rows.get(index);

        if (!row.locked || index >= value.length) {
          row.unfocus();
          row.root.remove();
          this.rows.delete(index);

          if (this.cachedRows.length < 10) {
            this.cachedRows.push(row);
          }
        }
      }
    }

    for (let i = startIndex; i <= endIndex; i++) {
      const row = this.rows.get(i);

      if (row) {
        row.setValue(value[i]);
      } else {
        this.createRow(i);
      }
    }
  }

}



/***/ }),

/***/ "./src/scaffolding/question.js":
/*!*************************************!*\
  !*** ./src/scaffolding/question.js ***!
  \*************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _drop_area__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./drop-area */ "./src/scaffolding/drop-area.js");
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");



class Question {
  constructor(parent, text) {
    this.parent = parent;
    this.text = text;
    this.root = document.createElement('div');
    this.root.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionRoot;
    this.inner = document.createElement('div');
    this.inner.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionInner;

    if (text) {
      this.textElement = document.createElement('div');
      this.textElement.textContent = text;
      this.textElement.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionText;
    }

    this.inputContainer = document.createElement('div');
    this.inputContainer.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionInputOuter;
    this.input = document.createElement('input');
    this.input.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionInput;
    this.input.addEventListener('keypress', this.onkeypress.bind(this));
    this.dropper = new _drop_area__WEBPACK_IMPORTED_MODULE_0__["default"](this.input, this.dropperCallback.bind(this));
    this.submitButton = document.createElement('button');
    this.submitButton.className = _style_css__WEBPACK_IMPORTED_MODULE_1__["default"].questionSubmitButton;
    this.submitButton.addEventListener('click', this.onsubmitpressclick.bind(this));
    this.inputContainer.appendChild(this.input);
    this.inputContainer.appendChild(this.submitButton);

    if (this.textElement) {
      this.inner.appendChild(this.textElement);
    }

    this.inner.appendChild(this.inputContainer);
    this.root.appendChild(this.inner);

    this.parent._addLayer(this.root);

    this.input.focus();
    this.answerCallback = new Promise(resolve => {
      this.callback = resolve;
    });
  }

  answer() {
    return this.answerCallback;
  }

  submit() {
    this.callback(this.input.value);
    this.destroy();
  }

  onkeypress(e) {
    if (e.key === 'Enter') {
      this.submit();
    }
  }

  dropperCallback(texts) {
    const text = texts.join('').replace(/\r?\n/g, ' ');
    this.input.value = text;
  }

  onsubmitpressclick() {
    this.submit();
  }

  destroy() {
    this.root.remove();
    this.parent.question = null;
  }

}

/* harmony default export */ __webpack_exports__["default"] = (Question);

/***/ }),

/***/ "./src/scaffolding/scaffolding.js":
/*!****************************************!*\
  !*** ./src/scaffolding/scaffolding.js ***!
  \****************************************/
/*! exports provided: Scaffolding, Cloud, VM, Renderer, Storage, AudioEngine, JSZip */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Scaffolding", function() { return Scaffolding; });
/* harmony import */ var scratch_vm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! scratch-vm */ "./node_modules/scratch-vm/src/index.js");
/* harmony import */ var scratch_vm__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(scratch_vm__WEBPACK_IMPORTED_MODULE_0__);
/* harmony reexport (default from non-harmony) */ __webpack_require__.d(__webpack_exports__, "VM", function() { return scratch_vm__WEBPACK_IMPORTED_MODULE_0___default.a; });
/* harmony import */ var scratch_render__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! scratch-render */ "./node_modules/scratch-render/src/index.js");
/* harmony import */ var scratch_render__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(scratch_render__WEBPACK_IMPORTED_MODULE_1__);
/* harmony reexport (default from non-harmony) */ __webpack_require__.d(__webpack_exports__, "Renderer", function() { return scratch_render__WEBPACK_IMPORTED_MODULE_1___default.a; });
/* harmony import */ var _storage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./storage */ "./src/scaffolding/storage.js");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Storage", function() { return _storage__WEBPACK_IMPORTED_MODULE_2__["default"]; });

/* harmony import */ var scratch_audio__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! scratch-audio */ "./node_modules/scratch-audio/src/index.js");
/* harmony import */ var scratch_audio__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(scratch_audio__WEBPACK_IMPORTED_MODULE_3__);
/* harmony reexport (default from non-harmony) */ __webpack_require__.d(__webpack_exports__, "AudioEngine", function() { return scratch_audio__WEBPACK_IMPORTED_MODULE_3___default.a; });
/* harmony import */ var scratch_svg_renderer__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! scratch-svg-renderer */ "./node_modules/scratch-svg-renderer/src/index.js");
/* harmony import */ var scratch_svg_renderer__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(scratch_svg_renderer__WEBPACK_IMPORTED_MODULE_4__);
/* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
/* harmony import */ var jszip__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(jszip__WEBPACK_IMPORTED_MODULE_5__);
/* harmony reexport (default from non-harmony) */ __webpack_require__.d(__webpack_exports__, "JSZip", function() { return jszip__WEBPACK_IMPORTED_MODULE_5___default.a; });
/* harmony import */ var _common_event_target__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../common/event-target */ "./src/common/event-target.js");
/* harmony import */ var _video__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./video */ "./src/scaffolding/video.js");
/* harmony import */ var _cloud__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./cloud */ "./src/scaffolding/cloud.js");
/* harmony reexport (safe) */ __webpack_require__.d(__webpack_exports__, "Cloud", function() { return _cloud__WEBPACK_IMPORTED_MODULE_8__["default"]; });

/* harmony import */ var _question__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./question */ "./src/scaffolding/question.js");
/* harmony import */ var _monitor__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./monitor */ "./src/scaffolding/monitor.js");
/* harmony import */ var _control_bar__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(/*! ./control-bar */ "./src/scaffolding/control-bar.js");
/* harmony import */ var _verify_value__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(/*! ./verify-value */ "./src/scaffolding/verify-value.js");
/* harmony import */ var _messages_json__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(/*! ./messages.json */ "./src/scaffolding/messages.json");
var _messages_json__WEBPACK_IMPORTED_MODULE_13___namespace = /*#__PURE__*/__webpack_require__.t(/*! ./messages.json */ "./src/scaffolding/messages.json", 1);
/* harmony import */ var _style_css__WEBPACK_IMPORTED_MODULE_14__ = __webpack_require__(/*! ./style.css */ "./src/scaffolding/style.css");
















const getEventXY = e => {
  if (e.touches && e.touches[0]) {
    return {
      x: e.touches[0].clientX,
      y: e.touches[0].clientY
    };
  } else if (e.changedTouches && e.changedTouches[0]) {
    return {
      x: e.changedTouches[0].clientX,
      y: e.changedTouches[0].clientY
    };
  }

  return {
    x: e.clientX,
    y: e.clientY
  };
};

const wrapAsFunctionIfNotFunction = value => {
  if (typeof value === 'function') {
    return value;
  }

  return () => value;
};

class Scaffolding extends _common_event_target__WEBPACK_IMPORTED_MODULE_6__["EventTarget"] {
  constructor() {
    super();
    this.width = 480;
    this.height = 360;
    this.resizeMode = 'preserve-ratio';
    this.editableLists = false;
    this.shouldConnectPeripherals = true;
    this.usePackagedRuntime = false;
    this.messages = _messages_json__WEBPACK_IMPORTED_MODULE_13__;
    this._monitors = new Map();
    this._mousedownPosition = null;
    this._draggingId = null;
    this._draggingStartMousePosition = null;
    this._draggingStartSpritePosition = null;
    this._offsetFromTop = 0;
    this._offsetFromBottom = 0;
    this._offsetFromLeft = 0;
    this._offsetFromRight = 0;
    this._root = document.createElement('div');
    this._root.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].root;
    this._layers = document.createElement('div');
    this._layers.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].layers;

    this._root.appendChild(this._layers);

    this._canvas = document.createElement('canvas');
    this._canvas.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].canvas;

    this._addLayer(this._canvas);

    this._overlays = document.createElement('div');
    this._overlays.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].scaledOverlaysInner;
    this._overlaysOuter = document.createElement('div');
    this._overlaysOuter.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].scaledOverlaysOuter;

    this._overlaysOuter.appendChild(this._overlays);

    this._addLayer(this._overlaysOuter);

    this._monitorOverlay = document.createElement('div');
    this._monitorOverlay.className = _style_css__WEBPACK_IMPORTED_MODULE_14__["default"].monitorOverlay;

    this._overlays.appendChild(this._monitorOverlay);

    this._topControls = new _control_bar__WEBPACK_IMPORTED_MODULE_11__["default"]();

    this._layers.appendChild(this._topControls.root);

    document.addEventListener('mousemove', this._onmousemove.bind(this));

    this._canvas.addEventListener('mousedown', this._onmousedown.bind(this));

    document.addEventListener('mouseup', this._onmouseup.bind(this));

    this._canvas.addEventListener('touchstart', this._ontouchstart.bind(this));

    document.addEventListener('touchmove', this._ontouchmove.bind(this));
    document.addEventListener('touchend', this._ontouchend.bind(this));

    this._canvas.addEventListener('contextmenu', this._oncontextmenu.bind(this));

    this._canvas.addEventListener('wheel', this._onwheel.bind(this));

    document.addEventListener('keydown', this._onkeydown.bind(this));
    document.addEventListener('keyup', this._onkeyup.bind(this));
    window.addEventListener('resize', this._onresize.bind(this));
  }

  _addLayer(el) {
    this._layers.appendChild(el);
  }

  _scratchCoordinates(x, y) {
    return {
      x: this.width / this.layersRect.width * (x - this.layersRect.width / 2),
      y: -(this.height / this.layersRect.height) * (y - this.layersRect.height / 2)
    };
  }

  _onmousemove(e) {
    const {
      x,
      y
    } = getEventXY(e);
    const data = {
      x: x - this.layersRect.left,
      y: y - this.layersRect.top,
      canvasWidth: this.layersRect.width,
      canvasHeight: this.layersRect.height
    };

    if (this._mousedownPosition && !this._draggingId) {
      const distance = Math.sqrt(Math.pow(data.x - this._mousedownPosition.x, 2) + Math.pow(data.y - this._mousedownPosition.y, 2));

      if (distance > 3) {
        this._startDragging(data.x, data.y);

        this._cancelDragTimeout();
      }
    } else if (this._draggingId) {
      const position = this._scratchCoordinates(data.x, data.y);

      this.vm.postSpriteInfo({
        x: position.x - this._draggingStartMousePosition.x + this._draggingStartSpritePosition.x,
        y: position.y - this._draggingStartMousePosition.y + this._draggingStartSpritePosition.y,
        force: true
      });
    }

    this.vm.postIOData('mouse', data);
  }

  _startDragging(x, y) {
    if (this._draggingId) return;
    const drawableId = this.renderer.pick(x, y);
    if (drawableId === null) return;
    const targetId = this.vm.getTargetIdForDrawableId(drawableId);
    if (targetId === null) return;
    const target = this.vm.runtime.getTargetById(targetId);
    if (!target.draggable) return;
    target.goToFront();
    this._draggingId = targetId;
    this._draggingStartMousePosition = this._scratchCoordinates(x, y);
    this._draggingStartSpritePosition = {
      x: target.x,
      y: target.y
    };
    this.vm.startDrag(targetId);
  }

  _cancelDragTimeout() {
    clearTimeout(this._dragTimeout);
    this._dragTimeout = null;
  }

  _onmousedown(e) {
    const {
      x,
      y
    } = getEventXY(e);
    const data = {
      x: x - this.layersRect.left,
      y: y - this.layersRect.top,
      button: e.button,
      canvasWidth: this.layersRect.width,
      canvasHeight: this.layersRect.height,
      isDown: true
    };
    const isTouchEvent = typeof TouchEvent !== 'undefined' && e instanceof TouchEvent;

    if (e.button === 0 || isTouchEvent) {
      this._dragTimeout = setTimeout(this._startDragging.bind(this, data.x, data.y), 400);
    }

    if (isTouchEvent) {
      e.preventDefault();

      if (document.activeElement && document.activeElement.blur) {
        document.activeElement.blur();
      }
    }

    this._mousedownPosition = {
      x: data.x,
      y: data.y
    };
    this.vm.postIOData('mouse', data);
  }

  _onmouseup(e) {
    this._cancelDragTimeout();

    const {
      x,
      y
    } = getEventXY(e);
    const data = {
      x: x - this.layersRect.left,
      y: y - this.layersRect.top,
      button: e.button,
      canvasWidth: this.layersRect.width,
      canvasHeight: this.layersRect.height,
      isDown: false,
      wasDragged: this._draggingId !== null
    };
    this._mousedownPosition = null;
    this.vm.postIOData('mouse', data);

    if (this._draggingId) {
      this.vm.stopDrag(this._draggingId);
      this._draggingStartMousePosition = null;
      this._draggingStartSpritePosition = null;
      this._draggingId = null;
    }
  }

  _ontouchstart(e) {
    this._onmousedown(e);
  }

  _ontouchmove(e) {
    this._onmousemove(e);
  }

  _ontouchend(e) {
    this._onmouseup(e);
  }

  _oncontextmenu(e) {
    e.preventDefault();
  }

  _onwheel(e) {
    const data = {
      deltaX: e.deltaX,
      deltaY: e.deltaY
    };
    this.vm.postIOData('mouseWheel', data);
  }

  _onkeydown(e) {
    if (e.target !== document && e.target !== document.body) {
      return;
    }

    const data = {
      key: e.key,
      keyCode: e.keyCode,
      isDown: true
    };
    this.vm.postIOData('keyboard', data);

    if (e.keyCode === 32 || e.keyCode >= 37 && e.keyCode <= 40 || e.keyCode === 8 || e.keyCode === 222 || e.keyCode === 191) {
      e.preventDefault();
    }
  }

  _onkeyup(e) {
    const data = {
      key: e.key,
      keyCode: e.keyCode,
      isDown: false
    };
    this.vm.postIOData('keyboard', data);

    if (e.target !== document && e.target !== document.body) {
      e.preventDefault();
    }
  }

  _onresize() {
    this.relayout();
  }

  relayout() {
    const totalWidth = Math.max(1, this._root.offsetWidth);
    const totalHeight = Math.max(1, this._root.offsetHeight);

    const offsetFromTop = this._offsetFromTop + this._topControls.computeHeight();

    const offsetFromBottom = this._offsetFromBottom;
    const offsetFromLeft = this._offsetFromLeft;
    const offsetFromRight = this._offsetFromRight;
    const projectAreaWidth = Math.max(1, totalWidth - offsetFromLeft - offsetFromRight);
    const projectAreaHeight = Math.max(1, totalHeight - offsetFromTop - offsetFromBottom);

    if (this.resizeMode === 'dynamic-resize') {
      // setStageSize is a TurboWarp-specific method
      if (this.vm.setStageSize) {
        this.width = projectAreaWidth;
        this.height = projectAreaHeight;
        this.vm.setStageSize(this.width, this.height);
      } else {
        console.warn('dynamic-resize not supported: vm does not implement setStageSize');
      }
    }

    let width = projectAreaWidth;
    let height = projectAreaHeight;

    if (this.resizeMode !== 'stretch') {
      width = height / this.height * this.width;

      if (width > projectAreaWidth) {
        height = projectAreaWidth / this.width * this.height;
        width = projectAreaWidth;
      }
    }

    const distanceFromTop = totalHeight - height;
    const distanceFromLeft = totalWidth - width;
    const translateY = (distanceFromLeft - offsetFromLeft - offsetFromRight) / 2 + offsetFromLeft - distanceFromLeft / 2;
    const translateX = (distanceFromTop - offsetFromTop - offsetFromBottom) / 2 + offsetFromTop - distanceFromTop / 2;
    this._layers.style.transform = "translate(".concat(translateY, "px, ").concat(translateX, "px)");
    this._layers.style.width = "".concat(width, "px");
    this._layers.style.height = "".concat(height, "px");
    this._overlays.style.transform = "scale(".concat(width / this.width, ", ").concat(height / this.height, ")");
    this.renderer.resize(width, height);
    this.layersRect = this._layers.getBoundingClientRect();
  }

  appendTo(element) {
    element.appendChild(this._root);
    this.relayout();
  }

  setup() {
    this.vm = new scratch_vm__WEBPACK_IMPORTED_MODULE_0___default.a();
    this.vm.setCompatibilityMode(true);
    this.vm.setLocale(navigator.language);
    this.vm.on('MONITORS_UPDATE', this._onmonitorsupdate.bind(this));
    this.vm.runtime.on('QUESTION', this._onquestion.bind(this));
    this.vm.on('PROJECT_RUN_START', () => this.dispatchEvent(new Event('PROJECT_RUN_START')));
    this.vm.on('PROJECT_RUN_STOP', () => this.dispatchEvent(new Event('PROJECT_RUN_STOP'))); // TurboWarp-specific VM extensions

    if (this.usePackagedRuntime && this.vm.convertToPackagedRuntime) {
      this.vm.convertToPackagedRuntime();
    }

    if (this.vm.setStageSize) {
      this.vm.setStageSize(this.width, this.height);
    }

    if (this.vm.runtime.cloudOptions) {
      this.vm.runtime.cloudOptions.limit = Infinity;
    } // TODO: remove when https://github.com/TurboWarp/packager/issues/213 is fixed


    this.vm.on('STAGE_SIZE_CHANGED', (width, height) => {
      if (this.width !== width || this.height !== height) {
        this.width = width;
        this.height = height;
        this.relayout();
      }
    });
    this.cloudManager = new _cloud__WEBPACK_IMPORTED_MODULE_8__["default"].CloudManager(this);
    this.renderer = new scratch_render__WEBPACK_IMPORTED_MODULE_1___default.a(this._canvas, -this.width / 2, this.width / 2, -this.height / 2, this.height / 2);
    this.vm.attachRenderer(this.renderer);
    this.storage = new _storage__WEBPACK_IMPORTED_MODULE_2__["default"]();
    this.vm.attachStorage(this.storage);

    if (typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined') {
      this.audioEngine = new scratch_audio__WEBPACK_IMPORTED_MODULE_3___default.a();
      this.vm.attachAudioEngine(this.audioEngine);
    } else {
      console.warn('AudioContext not supported. Sound will not work.');
    }

    this.bitmapAdapter = new scratch_svg_renderer__WEBPACK_IMPORTED_MODULE_4__["BitmapAdapter"]();
    this.vm.attachV2BitmapAdapter(this.bitmapAdapter);
    this.videoProvider = new _video__WEBPACK_IMPORTED_MODULE_7__["default"]();
    this.vm.setVideoProvider(this.videoProvider);
  }

  async _connectPeripherals() {
    const scanExtension = extensionId => new Promise(resolve => {
      const onListUpdate = peripherals => {
        const peripheralArray = Object.keys(peripherals).map(id => peripherals[id]);

        if (peripheralArray.length > 0) {
          const peripheral = peripheralArray[0];
          console.log('Connecting to peripheral', peripheral);
          this.vm.connectPeripheral(extensionId, peripheral.peripheralId);
        } else {
          console.error('No peripherals found for', extensionId);
        }

        done();
      };

      const onScanTimeout = () => {
        console.error('Peripheral scan timed out for', extensionId);
        done();
      };

      const done = () => {
        this.vm.removeListener('PERIPHERAL_LIST_UPDATE', onListUpdate);
        this.vm.removeListener('PERIPHERAL_SCAN_TIMEOUT', onScanTimeout);
        resolve();
      };

      this.vm.on('PERIPHERAL_LIST_UPDATE', onListUpdate);
      this.vm.on('PERIPHERAL_SCAN_TIMEOUT', onScanTimeout);
      this.vm.scanForPeripheral(extensionId);
    });

    for (const extensionId of Object.keys(this.vm.runtime.peripheralExtensions)) {
      await scanExtension(extensionId);
    }
  }

  _onmonitorsupdate(monitors) {
    for (const monitorData of monitors.valueSeq()) {
      const id = monitorData.get('id');

      if (!this._monitors.has(id)) {
        const visible = monitorData.get('visible');

        if (!visible) {
          // Would be a waste to make it now
          continue;
        } // TODO: add to DOM in same order as appears in list


        const mode = monitorData.get('mode');

        if (mode === 'list') {
          this._monitors.set(id, new _monitor__WEBPACK_IMPORTED_MODULE_10__["ListMonitor"](this, monitorData));
        } else {
          this._monitors.set(id, new _monitor__WEBPACK_IMPORTED_MODULE_10__["VariableMonitor"](this, monitorData));
        }
      }

      const monitorObject = this._monitors.get(id);

      monitorObject.update(monitorData);
    }
  }

  ask(text) {
    this._question = new _question__WEBPACK_IMPORTED_MODULE_9__["default"](this, text);
    return this._question.answer();
  }

  _onquestion(question) {
    if (this._question) {
      this._question.destroy();
    }

    if (question !== null) {
      this.ask(question).then(answer => {
        this.vm.runtime.emit('ANSWER', answer);
      });
    }
  }

  loadProject(data) {
    return this.vm.loadProject(data).then(() => {
      this.vm.setCloudProvider(this.cloudManager);
      this.cloudManager.projectReady();
      this.renderer.draw(); // Render again after a short delay because some costumes are loaded async

      setTimeout(() => {
        this.renderer.draw();
      });

      if (this.shouldConnectPeripherals) {
        this._connectPeripherals();
      }
    });
  }

  setUsername(username) {
    this._username = username;
    this.vm.postIOData('userData', {
      username
    });
  }

  addCloudProvider(provider) {
    this.cloudManager.addProvider(provider);
  }

  addCloudProviderOverride(name, provider) {
    this.cloudManager.addProviderOverride(name, provider);
  }

  addControlButton(_ref) {
    let {
      element,
      where
    } = _ref;

    if (where === 'top-left') {
      this._topControls.addToStart(element);
    } else if (where === 'top-right') {
      this._topControls.addToEnd(element);
    } else {
      throw new Error("Unknown 'where': ".concat(where));
    }

    this.relayout();
  }

  getMessage(id) {
    return this.messages[id] || id;
  }
  /**
   * Change primary accent color.
   * @param {string} color Color in the format #abcdef
   */


  setAccentColor(color) {
    this._root.style.setProperty('--sc-accent-color', color);

    this._root.style.setProperty('--sc-accent-color-transparent', "".concat(color, "59"));
  }

  start() {
    this.vm.start();
    this.vm.greenFlag();
  }

  greenFlag() {
    this.start();
  }

  stopAll() {
    this.vm.stopAll();
  }

  _lookupVariable(name, type) {
    const variable = this.vm.runtime.getTargetForStage().lookupVariableByNameAndType(name, type);
    if (!variable) throw new Error("Global ".concat(type || 'variable', " does not exist: ").concat(name));
    return variable;
  }

  setExtensionSecurityManager(_ref2) {
    let {
      getSandboxMode,
      canLoadExtensionFromProject
    } = _ref2;
    const securityManager = this.vm.extensionManager.securityManager;

    if (!securityManager) {
      console.warn('setExtensionSecurityManager not supported: there is no security manager');
      return;
    }

    if (typeof getSandboxMode !== 'undefined') {
      securityManager.getSandboxMode = wrapAsFunctionIfNotFunction(getSandboxMode);
    }

    if (typeof canLoadExtensionFromProject !== 'undefined') {
      securityManager.canLoadExtensionFromProject = wrapAsFunctionIfNotFunction(canLoadExtensionFromProject);
    }
  }

  getVariable(name) {
    return this._lookupVariable(name, '').value;
  }

  setVariable(name, value) {
    if (!Object(_verify_value__WEBPACK_IMPORTED_MODULE_12__["isValidVariableValue"])(value)) {
      throw new Error('Invalid variable value');
    }

    this._lookupVariable(name, '').value = value;
  }

  getList(name) {
    return this._lookupVariable(name, 'list').value;
  }

  setList(name, value) {
    if (!Object(_verify_value__WEBPACK_IMPORTED_MODULE_12__["isValidListValue"])(value)) {
      throw new Error('Invalid list value');
    }

    this._lookupVariable(name, 'list').value = value;
  }

}



/***/ }),

/***/ "./src/scaffolding/scratch-parser/index.js":
/*!*************************************************!*\
  !*** ./src/scaffolding/scratch-parser/index.js ***!
  \*************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const promisify = functionWithCallback => function () {
  for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
    args[_key] = arguments[_key];
  }

  return new Promise((resolve, reject) => {
    functionWithCallback(...args, (err, result) => {
      if (err) {
        if (typeof err === 'string') {
          // This will at least give a partial error stack.
          reject(new Error(err));
        } else {
          reject(err);
        }
      } else {
        resolve(result);
      }
    });
  });
};

var unpack = promisify(__webpack_require__(/*! ./lib/unpack */ "./src/scaffolding/scratch-parser/lib/unpack.js"));
var parse = promisify(__webpack_require__(/*! ./lib/parse */ "./src/scaffolding/scratch-parser/lib/parse.js"));
var validate = promisify(__webpack_require__(/*! ./lib/validate */ "./src/scaffolding/scratch-parser/lib/validate.js"));

module.exports = function (input, isSprite, callback) {
  unpack(input, isSprite).then(function (unpackedProject) {
    return parse(unpackedProject[0]).then(validate.bind(null, isSprite)).then(function (validatedProject) {
      return [validatedProject, unpackedProject[1]];
    });
  }).then(callback.bind(null, null), callback);
};

/***/ }),

/***/ "./src/scaffolding/scratch-parser/lib/parse.js":
/*!*****************************************************!*\
  !*** ./src/scaffolding/scratch-parser/lib/parse.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

const ExtendedJSON = __webpack_require__(/*! @turbowarp/json */ "./node_modules/@turbowarp/json/src/index.js");
/**
 * Converts string from unpack method into a project object. Note: this method
 * will be expanded greatly in the future in order to support the Scratch 1.4
 * file format. For now, this is nothing but an (awkward) async wrapper around
 * the `JSON.parse` function.
 * @param {string}   input    Stringified JSON object
 * @param {Function} callback Returns error or parsed JSON object
 * @return {void}
 */


module.exports = function (input, callback) {
  var result;

  try {
    // The input is a JSON string, which may contain control characters
    // that should be removed. See LLK/scratch-vm#1077
    // So far we've only encountered the backspace control character,
    // so remove that specific one before continuing.
    // SB2 JSONs and SB3 JSONs have different versions of the
    // character serialized (e.g. \u0008 and \b), strip out both versions
    result = ExtendedJSON.parse(input.replace(/(\\+)(b|u0008)/g, (match, backslash, code) => {
      // If the number is odd, there is an actual backspace.
      if (backslash.length % 2) {
        // The match contains an actual backspace, instead of backslashes followed by b.
        // Remove backspace and keep backslashes that are not part of
        // the control character representation.
        return match.replace('\\' + code, '');
      } // They are just backslashes followed by b or u0008. (e.g. "\\b")
      // Don't replace in this case. (LLK/scratch-parser#56)


      return match;
    }));
  } catch (e) {
    return callback(e.toString());
  }

  return callback(null, result);
};

/***/ }),

/***/ "./src/scaffolding/scratch-parser/lib/unpack.js":
/*!******************************************************!*\
  !*** ./src/scaffolding/scratch-parser/lib/unpack.js ***!
  \******************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

/* WEBPACK VAR INJECTION */(function(Buffer) {var unzip = __webpack_require__(/*! ./unzip */ "./src/scaffolding/scratch-parser/lib/unzip.js");
/**
 * If input a buffer, transforms buffer into a UTF-8 string.
 * If input is encoded in zip format, the input will be extracted and decoded.
 * If input is a string, passes that string along to the given callback.
 * @param {Buffer | string} input    Project data
 * @param {boolean}         isSprite Whether the input should be treated as
 * a sprite (true) or a whole project (false)
 * @param {Function}        callback Error or stringified project data
 * @return {void}
 */


module.exports = function (input, isSprite, callback) {
  if (typeof input === 'string') {
    // Pass string to callback
    return callback(null, [input, null]);
  } // Validate input type


  var typeError = 'Input must be a Buffer or a string.';

  if (!Buffer.isBuffer(input)) {
    try {
      input = new Buffer(input);
    } catch (e) {
      return callback(typeError);
    }
  } // Determine format
  // We don't use the file suffix as this is unreliable and mine-type
  // information is unavailable from Scratch's project CDN. Instead, we look
  // at the first few bytes from the provided buffer (byte signature).
  // https://en.wikipedia.org/wiki/List_of_file_signatures


  var signature = input.slice(0, 3).join(' ');
  var isLegacy = false;
  var isZip = false;
  if (signature.indexOf('83 99 114') === 0) isLegacy = true;
  if (signature.indexOf('80 75') === 0) isZip = true; // If not legacy or zip, convert buffer to UTF-8 string and return

  if (!isZip && !isLegacy) {
    return callback(null, [input.toString('utf-8'), null]);
  } // Return error if legacy encoding detected


  if (isLegacy) return callback('Parser only supports Scratch 2.X and above');
  unzip(input, isSprite, callback);
};
/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../../node_modules/buffer/index.js */ "./node_modules/buffer/index.js").Buffer))

/***/ }),

/***/ "./src/scaffolding/scratch-parser/lib/unzip.js":
/*!*****************************************************!*\
  !*** ./src/scaffolding/scratch-parser/lib/unzip.js ***!
  \*****************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

var JSZip = __webpack_require__(/*! jszip */ "./node_modules/jszip/dist/jszip.min.js");
/**
 * Unpacks a zip file.
 * @param {string}  input    Zip file provided as a string
 * @param {boolean} isSprite Whether the input should be treated as
 * a sprite (true) or whole project (false)
 * @param {array}   callback Array including both the project and zip archive
 * @return {void}
 */


module.exports = function (input, isSprite, callback) {
  var msg = 'Failed to unzip and extract project.json, with error: ';
  return JSZip.loadAsync(input).then(function (zip) {
    // look for json in the list of files, or in a subdirectory
    // assumes there is only one sprite or project json in the zipfile
    const file = isSprite ? zip.file(/^([^/]*\/)?sprite\.json$/)[0] : zip.file(/^([^/]*\/)?project\.json$/)[0];

    if (file) {
      return file.async('string').then(function (project) {
        return callback(null, [project, zip]);
      });
    }

    return callback(msg + 'missing project or sprite json');
  }).catch(function (err) {
    return callback(msg + err);
  });
};

/***/ }),

/***/ "./src/scaffolding/scratch-parser/lib/validate.js":
/*!********************************************************!*\
  !*** ./src/scaffolding/scratch-parser/lib/validate.js ***!
  \********************************************************/
/*! no static exports found */
/***/ (function(module, exports) {

module.exports = function (isSprite, input, callback) {
  if ('objName' in input) {
    input.projectVersion = 2;
    return callback(null, input);
  }

  if (isSprite) {
    if ('name' in input) {
      input.projectVersion = 3;
      return callback(null, input);
    }
  } else {
    if ('targets' in input) {
      input.projectVersion = 3;
      return callback(null, input);
    }
  }

  callback(new Error('Could not parse as a valid SB2 or SB3 project.'));
};

/***/ }),

/***/ "./src/scaffolding/scratch-translate-extension-languages/languages.json":
/*!******************************************************************************!*\
  !*** ./src/scaffolding/scratch-translate-extension-languages/languages.json ***!
  \******************************************************************************/
/*! exports provided: menuMap, nameMap, scratchToGoogleMap, previouslySupported, default */
/***/ (function(module) {

module.exports = JSON.parse("{\"menuMap\":{\"cs\":[{\"code\":\"cs\",\"name\":\"čeština\"}],\"da\":[{\"code\":\"da\",\"name\":\"Dansk\"}],\"gl\":[{\"code\":\"gl\",\"name\":\"galego\"}],\"sl\":[{\"code\":\"sl\",\"name\":\"slovenščina\"}],\"fr\":[{\"code\":\"fr\",\"name\":\"Français\"}],\"hu\":[{\"code\":\"hu\",\"name\":\"magyar\"}],\"uk\":[{\"code\":\"uk\",\"name\":\"українська\"}],\"zh-tw\":[{\"code\":\"zh-tw\",\"name\":\"中文(繁體)\"}],\"pt\":[{\"code\":\"pt\",\"name\":\"Portuguese\"}],\"pt-br\":[{\"code\":\"pt\",\"name\":\"Português\"}],\"tr\":[{\"code\":\"tr\",\"name\":\"Türkçe\"}],\"ga\":[{\"code\":\"ga\",\"name\":\"Gaeilge\"}],\"th\":[{\"code\":\"th\",\"name\":\"ภาษาไทย\"}],\"ja\":[{\"code\":\"ja\",\"name\":\"日本語\"}],\"ja-hira\":[{\"code\":\"ja\",\"name\":\"日本語\"}],\"nl\":[{\"code\":\"nl\",\"name\":\"Nederlands\"}],\"gd\":[{\"code\":\"gd\",\"name\":\"Gàidhlig\"}],\"nb\":[{\"code\":\"nb\",\"name\":\"norsk\"}],\"de\":[{\"code\":\"de\",\"name\":\"Deutsch\"}],\"he\":[{\"code\":\"he\",\"name\":\"עברית\"}],\"sk\":[{\"code\":\"sk\",\"name\":\"slovenčina\"}],\"zu\":[{\"code\":\"zu\",\"name\":\"isiZulu\"}],\"ru\":[{\"code\":\"ru\",\"name\":\"русский\"}],\"pl\":[{\"code\":\"pl\",\"name\":\"polski\"}],\"am\":[{\"code\":\"am\",\"name\":\"አማርኛ\"}],\"az\":[{\"code\":\"az\",\"name\":\"Azərbaycan dili\"}],\"hr\":[{\"code\":\"hr\",\"name\":\"hrvatski\"}],\"fa\":[{\"code\":\"fa\",\"name\":\"فارسی\"}],\"mi\":[{\"code\":\"mi\",\"name\":\"Māori\"}],\"el\":[{\"code\":\"el\",\"name\":\"Ελληνικά\"}],\"is\":[{\"code\":\"is\",\"name\":\"íslenska\"}],\"fi\":[{\"code\":\"fi\",\"name\":\"suomi\"}],\"eu\":[{\"code\":\"eu\",\"name\":\"euskara\"}],\"ca\":[{\"code\":\"ca\",\"name\":\"català\"}],\"lv\":[{\"code\":\"lv\",\"name\":\"latviešu\"}],\"id\":[{\"code\":\"id\",\"name\":\"Indonesia\"}],\"es\":[{\"code\":\"es\",\"name\":\"español\"}],\"es-419\":[{\"code\":\"es\",\"name\":\"español\"}],\"et\":[{\"code\":\"et\",\"name\":\"eesti\"}],\"cy\":[{\"code\":\"cy\",\"name\":\"Cymraeg\"}],\"zh-cn\":[{\"code\":\"zh-cn\",\"name\":\"中文(简体)\"}],\"bg\":[{\"code\":\"bg\",\"name\":\"български\"}],\"ar\":[{\"code\":\"ar\",\"name\":\"العربية\"}],\"en\":[{\"code\":\"en\",\"name\":\"English\"}],\"sr\":[{\"code\":\"sr\",\"name\":\"српски\"}],\"sv\":[{\"code\":\"sv\",\"name\":\"svenska\"}],\"ro\":[{\"code\":\"ro\",\"name\":\"Română\"}],\"lt\":[{\"code\":\"lt\",\"name\":\"lietuvių\"}],\"ko\":[{\"code\":\"ko\",\"name\":\"한국어\"}],\"it\":[{\"code\":\"it\",\"name\":\"Italiano\"}],\"vi\":[{\"code\":\"vi\",\"name\":\"Tiếng Việt\"}]},\"nameMap\":{\"albánština\":\"sq\",\"amharština\":\"am\",\"angličtina\":\"en\",\"arabština\":\"ar\",\"arménština\":\"hy\",\"ázerbájdžánština\":\"az\",\"barmština\":\"my\",\"baskičtina\":\"eu\",\"běloruština\":\"be\",\"bulharština\":\"bg\",\"čeština\":\"cs\",\"čínština (tradiční)\":\"zh-tw\",\"čínština (zjednodušená)\":\"zh-cn\",\"dánština\":\"da\",\"esperanto\":\"eo\",\"estonština\":\"et\",\"finština\":\"fi\",\"francouzština\":\"fr\",\"galicijština\":\"gl\",\"haitská kreolština\":\"ht\",\"hebrejština\":\"he\",\"hindština\":\"hi\",\"holandština\":\"nl\",\"chorvatština\":\"hr\",\"indonéština\":\"id\",\"irština\":\"ga\",\"islandština\":\"is\",\"italština\":\"it\",\"japonština\":\"ja\",\"kannadština\":\"kn\",\"katalánština\":\"ca\",\"korejština\":\"ko\",\"kurdština\":\"ku\",\"latina\":\"la\",\"litevština\":\"lt\",\"lotyština\":\"lv\",\"maďarština\":\"hu\",\"makedonština\":\"mk\",\"malajálamština\":\"ml\",\"malajština\":\"ms\",\"maltština\":\"mt\",\"maorština\":\"mi\",\"marátština\":\"mr\",\"mongolština\":\"mn\",\"němčina\":\"de\",\"norština\":\"nb\",\"perština\":\"fa\",\"polština\":\"pl\",\"portugalština\":\"pt\",\"rumunština\":\"ro\",\"ruština\":\"ru\",\"řečtina\":\"el\",\"skotská gaelština\":\"gd\",\"slovenština\":\"sk\",\"slovinština\":\"sl\",\"srbština\":\"sr\",\"španělština\":\"es\",\"švédština\":\"sv\",\"telužština\":\"te\",\"thajština\":\"th\",\"turečtina\":\"tr\",\"ukrajinština\":\"uk\",\"uzbečtina\":\"uz\",\"velština\":\"cy\",\"vietnamština\":\"vi\",\"zulu\":\"zu\",\"albansk\":\"sq\",\"amharisk\":\"am\",\"arabisk\":\"ar\",\"armensk\":\"hy\",\"aserbajdsjansk\":\"az\",\"baskisk\":\"eu\",\"bulgarsk\":\"bg\",\"burmesisk\":\"my\",\"dansk\":\"da\",\"engelsk\":\"en\",\"estisk\":\"et\",\"finsk\":\"fi\",\"fransk\":\"fr\",\"galicisk\":\"gl\",\"græsk\":\"el\",\"haitisk kreolsk\":\"ht\",\"hebraisk\":\"he\",\"hindi\":\"hi\",\"hollandsk\":\"nl\",\"hviderussisk\":\"be\",\"indonesisk\":\"id\",\"irsk\":\"ga\",\"islandsk\":\"is\",\"italiensk\":\"it\",\"japansk\":\"ja\",\"kannada\":\"kn\",\"katalansk\":\"ca\",\"kinesisk (forenklet)\":\"zh-cn\",\"kinesisk (traditionelt)\":\"zh-tw\",\"koreansk\":\"ko\",\"kroatisk\":\"hr\",\"kurdisk\":\"ku\",\"latin\":\"la\",\"lettisk\":\"lv\",\"litauisk\":\"lt\",\"makedonsk\":\"mk\",\"malajisk\":\"ms\",\"malayalam\":\"ml\",\"maltesisk\":\"mt\",\"maori\":\"mi\",\"marathi\":\"mr\",\"mongolsk\":\"mn\",\"norsk\":\"nb\",\"persisk\":\"fa\",\"polsk\":\"pl\",\"portugisisk\":\"pt\",\"rumænsk\":\"ro\",\"russisk\":\"ru\",\"serbisk\":\"sr\",\"skotsk gælisk\":\"gd\",\"slovakisk\":\"sk\",\"slovensk\":\"sl\",\"spansk\":\"es\",\"svensk\":\"sv\",\"telugu\":\"te\",\"thailandsk\":\"th\",\"tjekkisk\":\"cs\",\"tyrkisk\":\"tr\",\"tysk\":\"de\",\"ukrainsk\":\"uk\",\"ungarsk\":\"hu\",\"usbekisk\":\"uz\",\"vietnamesisk\":\"vi\",\"walisisk\":\"cy\",\"acerbaixano\":\"az\",\"albanés\":\"sq\",\"alemán\":\"de\",\"amárico\":\"am\",\"árabe\":\"ar\",\"armenio\":\"hy\",\"bielorruso\":\"be\",\"birmano\":\"my\",\"búlgaro\":\"bg\",\"canarés\":\"kn\",\"catalán\":\"ca\",\"checo\":\"cs\",\"chinés (simplificado)\":\"zh-cn\",\"chinés (tradicional)\":\"zh-tw\",\"coreano\":\"ko\",\"crioulo haitiano\":\"ht\",\"croata\":\"hr\",\"curdo\":\"ku\",\"dinamarqués\":\"da\",\"eslovaco\":\"sk\",\"esloveno\":\"sl\",\"español\":\"es\",\"estoniano\":\"et\",\"éuscaro\":\"eu\",\"finés\":\"fi\",\"francés\":\"fr\",\"gaélico escocés\":\"gd\",\"galego\":\"gl\",\"galés\":\"cy\",\"grego\":\"el\",\"hebreo\":\"he\",\"húngaro\":\"hu\",\"indonesio\":\"id\",\"inglés\":\"en\",\"irlandés\":\"ga\",\"islandés\":\"is\",\"italiano\":\"it\",\"latín\":\"la\",\"letón\":\"lv\",\"lituano\":\"lt\",\"macedonio\":\"mk\",\"malabar\":\"ml\",\"malaio\":\"ms\",\"maltés\":\"mt\",\"maorí\":\"mi\",\"mongol\":\"mn\",\"neerlandés\":\"nl\",\"noruegués\":\"nb\",\"persa\":\"fa\",\"polaco\":\"pl\",\"portugués\":\"pt\",\"romanés\":\"ro\",\"ruso\":\"ru\",\"serbio\":\"sr\",\"sueco\":\"sv\",\"tailandés\":\"th\",\"telugú\":\"te\",\"turco\":\"tr\",\"ucraíno\":\"uk\",\"usbeco\":\"uz\",\"vietnamita\":\"vi\",\"xaponés\":\"ja\",\"zulú\":\"zu\",\"albanščina\":\"sq\",\"amharščina\":\"am\",\"angleščina\":\"en\",\"arabščina\":\"ar\",\"armenščina\":\"hy\",\"azerbajdžanščina\":\"az\",\"baskovščina\":\"eu\",\"beloruščina\":\"be\",\"bolgarščina\":\"bg\",\"burmanščina\":\"my\",\"češčina\":\"cs\",\"danščina\":\"da\",\"estonščina\":\"et\",\"finščina\":\"fi\",\"francoščina\":\"fr\",\"galicijščina\":\"gl\",\"grščina\":\"el\",\"haitijska kreolščina\":\"ht\",\"hebrejščina\":\"he\",\"hindijščina\":\"hi\",\"hrvaščina\":\"hr\",\"indonezijščina\":\"id\",\"irščina\":\"ga\",\"islandščina\":\"is\",\"italijanščina\":\"it\",\"japonščina\":\"ja\",\"kanareščina\":\"kn\",\"katalonščina\":\"ca\",\"kitajščina (poenostavljena)\":\"zh-cn\",\"kitajščina (tradicionalna)\":\"zh-tw\",\"korejščina\":\"ko\",\"kurdščina\":\"ku\",\"latinščina\":\"la\",\"latvijščina\":\"lv\",\"litovščina\":\"lt\",\"madžarščina\":\"hu\",\"makedonščina\":\"mk\",\"malajalščina\":\"ml\",\"malajščina\":\"ms\",\"malteščina\":\"mt\",\"maorščina\":\"mi\",\"maratščina\":\"mr\",\"mongolščina\":\"mn\",\"nemščina\":\"de\",\"nizozemščina\":\"nl\",\"norveščina\":\"nb\",\"perzijščina\":\"fa\",\"poljščina\":\"pl\",\"portugalščina\":\"pt\",\"romunščina\":\"ro\",\"ruščina\":\"ru\",\"slovaščina\":\"sk\",\"slovenščina\":\"sl\",\"srbščina\":\"sr\",\"škotska gelščina\":\"gd\",\"španščina\":\"es\",\"švedščina\":\"sv\",\"tajščina\":\"th\",\"teluščina\":\"te\",\"turščina\":\"tr\",\"ukrajinščina\":\"uk\",\"uzbeščina\":\"uz\",\"valižanščina\":\"cy\",\"vietnamščina\":\"vi\",\"zulujščina\":\"zu\",\"albanais\":\"sq\",\"allemand\":\"de\",\"amharique\":\"am\",\"anglais\":\"en\",\"arabe\":\"ar\",\"arménien\":\"hy\",\"azéri\":\"az\",\"basque\":\"eu\",\"biélorusse\":\"be\",\"birman\":\"my\",\"bulgare\":\"bg\",\"catalan\":\"ca\",\"chinois (simplifié)\":\"zh-cn\",\"chinois (traditionnel)\":\"zh-tw\",\"coréen\":\"ko\",\"créole haïtien\":\"ht\",\"croate\":\"hr\",\"danois\":\"da\",\"espagnol\":\"es\",\"espéranto\":\"eo\",\"estonien\":\"et\",\"finnois\":\"fi\",\"français\":\"fr\",\"gaélique (écosse)\":\"gd\",\"galicien\":\"gl\",\"gallois\":\"cy\",\"grec\":\"el\",\"hébreu\":\"he\",\"hongrois\":\"hu\",\"indonésien\":\"id\",\"irlandais\":\"ga\",\"islandais\":\"is\",\"italien\":\"it\",\"japonais\":\"ja\",\"kurde\":\"ku\",\"letton\":\"lv\",\"lituanien\":\"lt\",\"macédonien\":\"mk\",\"malaisien\":\"ms\",\"maltais\":\"mt\",\"néerlandais\":\"nl\",\"norvégien\":\"nb\",\"ouzbek\":\"uz\",\"persan\":\"fa\",\"polonais\":\"pl\",\"portugais\":\"pt\",\"roumain\":\"ro\",\"russe\":\"ru\",\"serbe\":\"sr\",\"slovaque\":\"sk\",\"slovène\":\"sl\",\"suédois\":\"sv\",\"tchèque\":\"cs\",\"thaï\":\"th\",\"turc\":\"tr\",\"ukrainien\":\"uk\",\"vietnamien\":\"vi\",\"zoulou\":\"zu\",\"albán\":\"sq\",\"amhara\":\"am\",\"angol\":\"en\",\"arab\":\"ar\",\"azeri\":\"az\",\"baszk\":\"eu\",\"belorusz\":\"be\",\"bolgár\":\"bg\",\"burmai\":\"my\",\"cseh\":\"cs\",\"dán\":\"da\",\"eszperantó\":\"eo\",\"észt\":\"et\",\"finn\":\"fi\",\"francia\":\"fr\",\"galíciai\":\"gl\",\"görög\":\"el\",\"haiti kreol\":\"ht\",\"héber\":\"he\",\"holland\":\"nl\",\"horvát\":\"hr\",\"indonéz\":\"id\",\"ír\":\"ga\",\"izlandi\":\"is\",\"japán\":\"ja\",\"katalán\":\"ca\",\"kínai (egyszerűsített)\":\"zh-cn\",\"kínai (hagyományos)\":\"zh-tw\",\"koreai\":\"ko\",\"kurd\":\"ku\",\"lengyel\":\"pl\",\"lett\":\"lv\",\"litván\":\"lt\",\"macedón\":\"mk\",\"magyar\":\"hu\",\"maláj\":\"ms\",\"malajálam\":\"ml\",\"máltai\":\"mt\",\"maráthi\":\"mr\",\"német\":\"de\",\"norvég\":\"nb\",\"olasz\":\"it\",\"orosz\":\"ru\",\"örmény\":\"hy\",\"perzsa\":\"fa\",\"portugál\":\"pt\",\"román\":\"ro\",\"skót-gael\":\"gd\",\"spanyol\":\"es\",\"svéd\":\"sv\",\"szerb\":\"sr\",\"szlovák\":\"sk\",\"szlovén\":\"sl\",\"thai\":\"th\",\"török\":\"tr\",\"ukrán\":\"uk\",\"üzbég\":\"uz\",\"vietnami\":\"vi\",\"walesi\":\"cy\",\"азербайджанська\":\"az\",\"албанська\":\"sq\",\"амхарська\":\"am\",\"англійська\":\"en\",\"арабська\":\"ar\",\"баскська\":\"eu\",\"білоруська\":\"be\",\"бірманська\":\"my\",\"болгарська\":\"bg\",\"в’єтнамська\":\"vi\",\"валлійська\":\"cy\",\"вірменська\":\"hy\",\"гаїтянська креольська\":\"ht\",\"гінді\":\"hi\",\"грецька\":\"el\",\"ґалісійська\":\"gl\",\"данська\":\"da\",\"есперанто\":\"eo\",\"естонська\":\"et\",\"зулу\":\"zu\",\"іврит\":\"he\",\"індонезійська\":\"id\",\"ірландська\":\"ga\",\"ісландська\":\"is\",\"іспанська\":\"es\",\"італійська\":\"it\",\"каннада\":\"kn\",\"каталанська\":\"ca\",\"китайська (спрощена)\":\"zh-cn\",\"китайська (традиційна)\":\"zh-tw\",\"корейська\":\"ko\",\"курдська\":\"ku\",\"латинська\":\"la\",\"латиська\":\"lv\",\"литовська\":\"lt\",\"македонська\":\"mk\",\"малайська\":\"ms\",\"малаялам\":\"ml\",\"мальтійська\":\"mt\",\"маорі\":\"mi\",\"маратхі\":\"mr\",\"монгольська\":\"mn\",\"нідерландська\":\"nl\",\"німецька\":\"de\",\"норвезька\":\"nb\",\"перська\":\"fa\",\"польська\":\"pl\",\"португальська\":\"pt\",\"російська\":\"ru\",\"румунська\":\"ro\",\"сербська\":\"sr\",\"словацька\":\"sk\",\"словенська\":\"sl\",\"тайська\":\"th\",\"телуґу\":\"te\",\"турецька\":\"tr\",\"угорська\":\"hu\",\"узбецька\":\"uz\",\"українська\":\"uk\",\"фінська\":\"fi\",\"французька\":\"fr\",\"хорватська\":\"hr\",\"чеська\":\"cs\",\"шведська\":\"sv\",\"шотландська (ґельська)\":\"gd\",\"японська\":\"ja\",\"土耳其文\":\"tr\",\"中文(繁體)\":\"zh-tw\",\"中文(簡體)\":\"zh-cn\",\"丹麥文\":\"da\",\"巴斯克文\":\"eu\",\"日文\":\"ja\",\"毛利文\":\"mi\",\"世界語\":\"eo\",\"加里西亞文\":\"gl\",\"加泰羅尼亞文\":\"ca\",\"卡納達文\":\"kn\",\"白俄羅斯文\":\"be\",\"立陶宛文\":\"lt\",\"冰島文\":\"is\",\"匈牙利文\":\"hu\",\"印尼文\":\"id\",\"印度文\":\"hi\",\"西班牙文\":\"es\",\"克羅埃西亞文\":\"hr\",\"希伯來文\":\"he\",\"希臘文\":\"el\",\"亞美尼亞文\":\"hy\",\"亞塞拜然文\":\"az\",\"拉丁文\":\"la\",\"拉脫維亞文\":\"lv\",\"法文\":\"fr\",\"波斯文\":\"fa\",\"波蘭文\":\"pl\",\"芬蘭文\":\"fi\",\"阿姆哈拉文\":\"am\",\"阿拉伯文\":\"ar\",\"阿爾巴尼亞文\":\"sq\",\"俄文\":\"ru\",\"保加利亞文\":\"bg\",\"南非祖魯文\":\"zu\",\"威爾斯文\":\"cy\",\"英文\":\"en\",\"庫德文\":\"ku\",\"挪威文\":\"nb\",\"泰文\":\"th\",\"泰盧固文\":\"te\",\"海地克里奧文\":\"ht\",\"烏克蘭文\":\"uk\",\"烏茲別克文\":\"uz\",\"馬耳他文\":\"mt\",\"馬來文\":\"ms\",\"馬其頓文\":\"mk\",\"馬拉地文\":\"mr\",\"馬拉雅拉姆文\":\"ml\",\"捷克文\":\"cs\",\"荷蘭文\":\"nl\",\"斯洛伐克文\":\"sk\",\"斯洛維尼亞文\":\"sl\",\"越南文\":\"vi\",\"塞爾維亞文\":\"sr\",\"愛沙尼亞文\":\"et\",\"愛爾蘭文\":\"ga\",\"瑞典文\":\"sv\",\"義大利文\":\"it\",\"葡萄牙文\":\"pt\",\"蒙古文\":\"mn\",\"德文\":\"de\",\"緬甸文\":\"my\",\"韓文\":\"ko\",\"羅馬尼亞文\":\"ro\",\"蘇格蘭的蓋爾文\":\"gd\",\"अझरबैजानी\":\"az\",\"अम्हारिक\":\"am\",\"अरबी\":\"ar\",\"अर्मेनियन\":\"hy\",\"अल्बानियन\":\"sq\",\"आइसलँडिक\":\"is\",\"आयरिश\":\"ga\",\"इंग्रजी\":\"en\",\"इंडोनेशियन\":\"id\",\"इटालियन\":\"it\",\"उझ्बेक\":\"uz\",\"एस्टोनियन\":\"et\",\"एस्परँटो\":\"eo\",\"कन्नड\":\"kn\",\"कुर्दिश (कुर्मांजी)\":\"ku\",\"कॅटलान\":\"ca\",\"कोरियन\":\"ko\",\"क्रोएशियन\":\"hr\",\"गॅलिशियन\":\"gl\",\"ग्रीक\":\"el\",\"चीनी (पारंपारिक)\":\"zh-tw\",\"चीनी (सरलीकृत)\":\"zh-cn\",\"जपानी\":\"ja\",\"जर्मन\":\"de\",\"झुलु\":\"zu\",\"झेक\":\"cs\",\"डच\":\"nl\",\"डॅनिश\":\"da\",\"तुर्की\":\"tr\",\"तेलगू\":\"te\",\"थाई\":\"th\",\"नॉर्वेजियन\":\"nb\",\"पोर्तुगीज\":\"pt\",\"पोलिश\":\"pl\",\"फारसी\":\"fa\",\"फिन्निश\":\"fi\",\"फ्रेंच\":\"fr\",\"बल्गेरियन\":\"bg\",\"बास्क\":\"eu\",\"बेलारुशियन\":\"be\",\"मंगोलियन\":\"mn\",\"मराठी\":\"mr\",\"मलय\":\"ms\",\"मल्याळम\":\"ml\",\"माओरी\":\"mi\",\"माल्टीज\":\"mt\",\"मॅसेडोनियन\":\"mk\",\"म्यानमार (बर्मीज)\":\"my\",\"युक्रेनियन\":\"uk\",\"रशियन\":\"ru\",\"रोमानियन\":\"ro\",\"लाट्वियन\":\"lv\",\"लिथुआनियन\":\"lt\",\"लॅटिन\":\"la\",\"वेल्श\":\"cy\",\"व्हिएतनामी\":\"vi\",\"सर्बियन\":\"sr\",\"स्कॉट्स गेलिक\":\"gd\",\"स्पॅनिश\":\"es\",\"स्लोव्हाक\":\"sk\",\"स्लोव्हेनियन\":\"sl\",\"स्वीडिश\":\"sv\",\"हंगेरियन\":\"hu\",\"हिन्दी\":\"hi\",\"हिब्रू\":\"he\",\"हैतीयन क्रेओल\":\"ht\",\"albanian\":\"sq\",\"amharic\":\"am\",\"arabic\":\"ar\",\"armenian\":\"hy\",\"azerbaijani\":\"az\",\"belarusian\":\"be\",\"bulgarian\":\"bg\",\"chinese (simplified)\":\"zh-cn\",\"chinese (traditional)\":\"zh-tw\",\"croatian\":\"hr\",\"czech\":\"cs\",\"danish\":\"da\",\"dutch\":\"nl\",\"english\":\"en\",\"estonian\":\"et\",\"finnish\":\"fi\",\"french\":\"fr\",\"galician\":\"gl\",\"german\":\"de\",\"greek\":\"el\",\"haitian creole\":\"ht\",\"hebrew\":\"he\",\"hungarian\":\"hu\",\"icelandic\":\"is\",\"indonesian\":\"id\",\"irish\":\"ga\",\"italian\":\"it\",\"japanese\":\"ja\",\"korean\":\"ko\",\"kurdish (kurmanji)\":\"ku\",\"latvian\":\"lv\",\"lithuanian\":\"lt\",\"macedonian\":\"mk\",\"malay\":\"ms\",\"maltese\":\"mt\",\"mongolian\":\"mn\",\"myanmar (burmese)\":\"my\",\"norwegian\":\"nb\",\"persian\":\"fa\",\"polish\":\"pl\",\"portuguese\":\"pt\",\"romanian\":\"ro\",\"russian\":\"ru\",\"scots gaelic\":\"gd\",\"serbian\":\"sr\",\"slovak\":\"sk\",\"slovenian\":\"sl\",\"spanish\":\"es\",\"swedish\":\"sv\",\"turkish\":\"tr\",\"ukrainian\":\"uk\",\"uzbek\":\"uz\",\"vietnamese\":\"vi\",\"welsh\":\"cy\",\"albanês\":\"sq\",\"alemão\":\"de\",\"armênio\":\"hy\",\"azerbaijano\":\"az\",\"basco\":\"eu\",\"bielo-russo\":\"be\",\"birmanês\":\"my\",\"canarês\":\"kn\",\"catalão\":\"ca\",\"chinês (simplificado)\":\"zh-cn\",\"chinês (tradicional)\":\"zh-tw\",\"dinamarquês\":\"da\",\"espanhol\":\"es\",\"finlandês\":\"fi\",\"francês\":\"fr\",\"gaélico escocês\":\"gd\",\"galês\":\"cy\",\"hebraico\":\"he\",\"holandês\":\"nl\",\"indonésio\":\"id\",\"inglês\":\"en\",\"irlandês\":\"ga\",\"islandês\":\"is\",\"japonês\":\"ja\",\"latim\":\"la\",\"letão\":\"lv\",\"macedônio\":\"mk\",\"malaiala\":\"ml\",\"maltês\":\"mt\",\"marata\":\"mr\",\"norueguês\":\"nb\",\"polonês\":\"pl\",\"português\":\"pt\",\"romeno\":\"ro\",\"russo\":\"ru\",\"sérvio\":\"sr\",\"tailandês\":\"th\",\"tcheco\":\"cs\",\"telugo\":\"te\",\"ucraniano\":\"uk\",\"uzbeque\":\"uz\",\"almanca\":\"de\",\"arapça\":\"ar\",\"arnavutça\":\"sq\",\"azerice\":\"az\",\"baskça\":\"eu\",\"belarusça\":\"be\",\"bulgarca\":\"bg\",\"burmaca\":\"my\",\"çekçe\":\"cs\",\"çince (basitleştirilmiş)\":\"zh-cn\",\"çince (geleneksel)\":\"zh-tw\",\"danca\":\"da\",\"endonezya dili\":\"id\",\"ermenice\":\"hy\",\"esperantoca\":\"eo\",\"estonyaca\":\"et\",\"farsça\":\"fa\",\"felemenkçe\":\"nl\",\"fince\":\"fi\",\"fransızca\":\"fr\",\"galce\":\"cy\",\"galiçyaca\":\"gl\",\"habeşçe\":\"am\",\"haiti creole dili\":\"ht\",\"hırvatça\":\"hr\",\"hintçe\":\"hi\",\"i̇branice\":\"he\",\"i̇ngilizce\":\"en\",\"i̇rlandaca\":\"ga\",\"i̇spanyolca\":\"es\",\"i̇sveççe\":\"sv\",\"i̇talyanca\":\"it\",\"i̇zlandaca\":\"is\",\"japonca\":\"ja\",\"katalanca\":\"ca\",\"korece\":\"ko\",\"kuzey i̇skoç dili\":\"gd\",\"kürtçe\":\"ku\",\"latince\":\"la\",\"lehçe\":\"pl\",\"letonca\":\"lv\",\"litvanca\":\"lt\",\"macarca\":\"hu\",\"makedonca\":\"mk\",\"malezya dili\":\"ms\",\"malta dili\":\"mt\",\"maori dili\":\"mi\",\"moğolca\":\"mn\",\"norveççe\":\"nb\",\"özbekçe\":\"uz\",\"portekizce\":\"pt\",\"romence\":\"ro\",\"rusça\":\"ru\",\"sırpça\":\"sr\",\"slovakça\":\"sk\",\"slovence\":\"sl\",\"tay dili\":\"th\",\"telugu dili\":\"te\",\"türkçe\":\"tr\",\"ukraynaca\":\"uk\",\"vietnamca\":\"vi\",\"yunanca\":\"el\",\"airméinis\":\"hy\",\"albáinis\":\"sq\",\"amárais\":\"am\",\"araibis\":\"ar\",\"asarbaiseáinis\":\"az\",\"bascais\":\"eu\",\"bealarúisis\":\"be\",\"béarla\":\"en\",\"breatnais\":\"cy\",\"bulgáiris\":\"bg\",\"cannadais\":\"kn\",\"catalóinis\":\"ca\",\"coirdis (curmainsis)\":\"ku\",\"cóiréis\":\"ko\",\"criól háítí\":\"ht\",\"cróitis\":\"hr\",\"danmhairgis\":\"da\",\"eabhrais\":\"he\",\"eastóinis\":\"et\",\"fionlainnis\":\"fi\",\"fraincis\":\"fr\",\"gaeilge\":\"ga\",\"gaeilge na halban\":\"gd\",\"gailísis\":\"gl\",\"gearmáinis\":\"de\",\"gréigis\":\"el\",\"hiondúis\":\"hi\",\"indinéisis\":\"id\",\"iodáilis\":\"it\",\"ioruais\":\"nb\",\"íoslainnis\":\"is\",\"laidin\":\"la\",\"laitvis\":\"lv\",\"liotuáinis\":\"lt\",\"macadóinis\":\"mk\",\"maenmar (burmais)\":\"my\",\"mailéalaimis\":\"ml\",\"malaeis\":\"ms\",\"máltais\":\"mt\",\"maorais\":\"mi\",\"maraitis\":\"mr\",\"mongóilis\":\"mn\",\"ollainnis\":\"nl\",\"peirsis\":\"fa\",\"polainnis\":\"pl\",\"portaingéilis\":\"pt\",\"rómáinis\":\"ro\",\"rúisis\":\"ru\",\"seapáinis\":\"ja\",\"seicis\":\"cs\",\"seirbis\":\"sr\",\"sínis (simplithe)\":\"zh-cn\",\"sínis (traidisiúnta)\":\"zh-tw\",\"slóivéinis\":\"sl\",\"slóvaicis\":\"sk\",\"spáinnis\":\"es\",\"sualainnis\":\"sv\",\"súlúis\":\"zu\",\"téalainnis\":\"th\",\"teileagúis\":\"te\",\"tuircis\":\"tr\",\"úcráinis\":\"uk\",\"úisbéiceastáinis\":\"uz\",\"ungáiris\":\"hu\",\"vítneaimis\":\"vi\",\"ภาษากรีก\":\"el\",\"ภาษากันนาดา\":\"kn\",\"ภาษากาลิเชียน\":\"gl\",\"ภาษาเกลิกในสก็อต\":\"gd\",\"ภาษาเกาหลี\":\"ko\",\"ภาษาคาตาลัน\":\"ca\",\"ภาษาเคิร์ด\":\"ku\",\"ภาษาโครเอเชีย\":\"hr\",\"ภาษาจีน (ดั้งเดิม)\":\"zh-tw\",\"ภาษาจีน (แบบย่อ)\":\"zh-cn\",\"ภาษาเช็ก\":\"cs\",\"ภาษาซูลู\":\"zu\",\"ภาษาเซอร์เบียน\":\"sr\",\"ภาษาญี่ปุ่น\":\"ja\",\"ภาษาดัตช์\":\"nl\",\"ภาษาเดนมาร์ก\":\"da\",\"ภาษาตุรกี\":\"tr\",\"ภาษาเตลูกู\":\"te\",\"ภาษาไทย\":\"th\",\"ภาษานอร์เวย์\":\"nb\",\"ภาษาบัลกาเรีย\":\"bg\",\"ภาษาบาสก์\":\"eu\",\"ภาษาเบลารูเชียน\":\"be\",\"ภาษาเปอร์เซีย\":\"fa\",\"ภาษาโปรตุเกส\":\"pt\",\"ภาษาโปแลนด์\":\"pl\",\"ภาษาฝรั่งเศส\":\"fr\",\"ภาษาฟินแลนด์\":\"fi\",\"ภาษามองโกเลีย\":\"mn\",\"ภาษามัลทีส\":\"mt\",\"ภาษามาซีโดเนีย\":\"mk\",\"ภาษามาราฐี\":\"mr\",\"ภาษามาลายาลัม\":\"ml\",\"ภาษามาเลย์\":\"ms\",\"ภาษาเมารี\":\"mi\",\"ภาษาเมียนมา (พม่า)\":\"my\",\"ภาษายูเครน\":\"uk\",\"ภาษาเยอรมัน\":\"de\",\"ภาษารัสเซีย\":\"ru\",\"ภาษาโรมาเนีย\":\"ro\",\"ภาษาละติน\":\"la\",\"ภาษาลัทเวีย\":\"lv\",\"ภาษาลิทัวเนีย\":\"lt\",\"ภาษาเวลส์\":\"cy\",\"ภาษาเวียดนาม\":\"vi\",\"ภาษาสเปน\":\"es\",\"ภาษาสโลวัก\":\"sk\",\"ภาษาสโลเวเนีย\":\"sl\",\"ภาษาสวีเดน\":\"sv\",\"ภาษาอังกฤษ\":\"en\",\"ภาษาอัมฮาริก\":\"am\",\"ภาษาอัลบาเนีย\":\"sq\",\"ภาษาอาร์เซอร์ไบจัน\":\"az\",\"ภาษาอาร์เมเนีย\":\"hy\",\"ภาษาอาหรับ\":\"ar\",\"ภาษาอิตาลี\":\"it\",\"ภาษาอินโดนีเซีย\":\"id\",\"ภาษาอุสเบกิสถาน\":\"uz\",\"ภาษาเอสโทเนีย\":\"et\",\"ภาษาเอสเปอแรนโต\":\"eo\",\"ภาษาไอซ์แลนดิก\":\"is\",\"ภาษาไอริช\":\"ga\",\"ภาษาฮังการี\":\"hu\",\"ภาษาฮินดี\":\"hi\",\"ภาษาฮิบรู\":\"he\",\"ภาษาเฮติครีโอล\":\"ht\",\"アイスランド語\":\"is\",\"アイルランド語\":\"ga\",\"アゼルバイジャン語\":\"az\",\"アムハラ語\":\"am\",\"アラビア語\":\"ar\",\"アルバニア語\":\"sq\",\"アルメニア語\":\"hy\",\"イタリア語\":\"it\",\"インドネシア語\":\"id\",\"ウェールズ語\":\"cy\",\"ウクライナ語\":\"uk\",\"ウズベク語\":\"uz\",\"エストニア語\":\"et\",\"エスペラント語\":\"eo\",\"オランダ語\":\"nl\",\"カタルーニャ語\":\"ca\",\"ガリシア語\":\"gl\",\"カンナダ語\":\"kn\",\"ギリシャ語\":\"el\",\"クルド語\":\"ku\",\"クロアチア語\":\"hr\",\"スウェーデン語\":\"sv\",\"ズールー語\":\"zu\",\"スコットランド ゲール語\":\"gd\",\"スペイン語\":\"es\",\"スロバキア語\":\"sk\",\"スロベニア語\":\"sl\",\"セルビア語\":\"sr\",\"タイ語\":\"th\",\"チェコ語\":\"cs\",\"テルグ語\":\"te\",\"デンマーク語\":\"da\",\"ドイツ語\":\"de\",\"トルコ語\":\"tr\",\"ノルウェー語\":\"nb\",\"ハイチ語\":\"ht\",\"バスク語\":\"eu\",\"ハンガリー語\":\"hu\",\"ヒンディー語\":\"hi\",\"フィンランド語\":\"fi\",\"フランス語\":\"fr\",\"ブルガリア語\":\"bg\",\"ベトナム語\":\"vi\",\"ヘブライ語\":\"he\",\"ベラルーシ語\":\"be\",\"ペルシャ語\":\"fa\",\"ポーランド語\":\"pl\",\"ポルトガル語\":\"pt\",\"マオリ語\":\"mi\",\"マケドニア語\":\"mk\",\"マラーティー語\":\"mr\",\"マラヤーラム語\":\"ml\",\"マルタ語\":\"mt\",\"マレー語\":\"ms\",\"ミャンマー語\":\"my\",\"モンゴル語\":\"mn\",\"ラテン語\":\"la\",\"ラトビア語\":\"lv\",\"リトアニア語\":\"lt\",\"ルーマニア語\":\"ro\",\"ロシア語\":\"ru\",\"英語\":\"en\",\"韓国語\":\"ko\",\"中国語(簡体)\":\"zh-cn\",\"中国語(繁体)\":\"zh-tw\",\"日本語\":\"ja\",\"albanees\":\"sq\",\"amharisch\":\"am\",\"arabisch\":\"ar\",\"armeens\":\"hy\",\"azerbeidzjaans\":\"az\",\"baskisch\":\"eu\",\"birmaans\":\"my\",\"bulgaars\":\"bg\",\"catalaans\":\"ca\",\"chinees (traditioneel)\":\"zh-tw\",\"chinees (vereenvoudigd)\":\"zh-cn\",\"deens\":\"da\",\"duits\":\"de\",\"engels\":\"en\",\"ests\":\"et\",\"fins\":\"fi\",\"frans\":\"fr\",\"galicisch\":\"gl\",\"grieks\":\"el\",\"haïtiaans creools\":\"ht\",\"hebreeuws\":\"he\",\"hongaars\":\"hu\",\"iers\":\"ga\",\"ijslands\":\"is\",\"indonesisch\":\"id\",\"italiaans\":\"it\",\"japans\":\"ja\",\"koerdisch\":\"ku\",\"koreaans\":\"ko\",\"kroatisch\":\"hr\",\"latijn\":\"la\",\"lets\":\"lv\",\"litouws\":\"lt\",\"macedonisch\":\"mk\",\"maleis\":\"ms\",\"maltees\":\"mt\",\"mongools\":\"mn\",\"nederlands\":\"nl\",\"noors\":\"nb\",\"oekraïens\":\"uk\",\"oezbeeks\":\"uz\",\"perzisch\":\"fa\",\"pools\":\"pl\",\"portugees\":\"pt\",\"roemeens\":\"ro\",\"russisch\":\"ru\",\"schots keltisch\":\"gd\",\"servisch\":\"sr\",\"slovaaks\":\"sk\",\"sloveens\":\"sl\",\"spaans\":\"es\",\"tsjechisch\":\"cs\",\"turks\":\"tr\",\"vietnamees\":\"vi\",\"wels\":\"cy\",\"wit-russisch\":\"be\",\"zoeloe\":\"zu\",\"zweeds\":\"sv\",\"airmeinis\":\"hy\",\"albàinis\":\"sq\",\"amtharais\":\"am\",\"arabais\":\"ar\",\"asarbaideànais\":\"az\",\"basgais\":\"eu\",\"bealaruisis\":\"be\",\"beurla\":\"en\",\"bhiet-namais\":\"vi\",\"bulgarais\":\"bg\",\"cànan nan tàidh\":\"th\",\"catalanais\":\"ca\",\"coirèanais\":\"ko\",\"crìtheol haidhti\":\"ht\",\"cròthaisis\":\"hr\",\"cuimris\":\"cy\",\"cùrdais (kurmanji)\":\"ku\",\"duitsis\":\"nl\",\"eabhra\":\"he\",\"eadailtis\":\"it\",\"eastoinis\":\"et\",\"fionnlannais\":\"fi\",\"fraingis\":\"fr\",\"gàidhlig\":\"gd\",\"gailìsis\":\"gl\",\"gearmailtis\":\"de\",\"grèigis\":\"el\",\"hindis\":\"hi\",\"innd-innsis\":\"id\",\"innis-tìlis\":\"is\",\"laideann\":\"la\",\"laitbheis\":\"lv\",\"liotuainis\":\"lt\",\"malaidhis\":\"ms\",\"māori\":\"mi\",\"masadonais\":\"mk\",\"miànmar (burmais)\":\"my\",\"mongolais\":\"mn\",\"nirribhis\":\"nb\",\"pòlainnis\":\"pl\",\"portagailis\":\"pt\",\"romàinis\":\"ro\",\"ruisis\":\"ru\",\"seacais\":\"cs\",\"seapanais\":\"ja\",\"sèirbis\":\"sr\",\"sìonais (seann-nòsach)\":\"zh-tw\",\"sìonais (sìmplichte)\":\"zh-cn\",\"slòbhacais\":\"sk\",\"slòbhainis\":\"sl\",\"spàinntis\":\"es\",\"suainis\":\"sv\",\"turcais\":\"tr\",\"ucràinis\":\"uk\",\"ungairis\":\"hu\",\"usbagais\":\"uz\",\"farsi\":\"fa\",\"galisisk\":\"gl\",\"gresk\":\"el\",\"hviterussisk\":\"be\",\"kinesisk (tradisjonell)\":\"zh-tw\",\"kreol (haiti)\":\"ht\",\"latvisk\":\"lv\",\"malayisk\":\"ms\",\"nederlandsk\":\"nl\",\"rumensk\":\"ro\",\"tsjekkisk\":\"cs\",\"albanisch\":\"sq\",\"armenisch\":\"hy\",\"aserbaidschanisch\":\"az\",\"birmanisch\":\"my\",\"bulgarisch\":\"bg\",\"chinesisch (traditionell)\":\"zh-tw\",\"chinesisch (vereinfacht)\":\"zh-cn\",\"dänisch\":\"da\",\"deutsch\":\"de\",\"englisch\":\"en\",\"estnisch\":\"et\",\"finnisch\":\"fi\",\"französisch\":\"fr\",\"galizisch\":\"gl\",\"griechisch\":\"el\",\"haitianisch\":\"ht\",\"hebräisch\":\"he\",\"irisch\":\"ga\",\"isländisch\":\"is\",\"italienisch\":\"it\",\"japanisch\":\"ja\",\"katalanisch\":\"ca\",\"koreanisch\":\"ko\",\"kurdisch (kurmandschi)\":\"ku\",\"lateinisch\":\"la\",\"lettisch\":\"lv\",\"litauisch\":\"lt\",\"malaysisch\":\"ms\",\"maltesisch\":\"mt\",\"mazedonisch\":\"mk\",\"mongolisch\":\"mn\",\"niederländisch\":\"nl\",\"norwegisch\":\"nb\",\"persisch\":\"fa\",\"polnisch\":\"pl\",\"portugiesisch\":\"pt\",\"rumänisch\":\"ro\",\"schottisch-gälisch\":\"gd\",\"schwedisch\":\"sv\",\"serbisch\":\"sr\",\"slowakisch\":\"sk\",\"slowenisch\":\"sl\",\"spanisch\":\"es\",\"thailändisch\":\"th\",\"tschechisch\":\"cs\",\"türkisch\":\"tr\",\"ukrainisch\":\"uk\",\"ungarisch\":\"hu\",\"usbekisch\":\"uz\",\"vietnamesisch\":\"vi\",\"walisisch\":\"cy\",\"weißrussisch\":\"be\",\"ကနာဒါ\":\"kn\",\"ကာ့ဒ် (ကာမန်ဂျီ)\":\"ku\",\"ကိုရီးယား\":\"ko\",\"ကက်တလန်\":\"ca\",\"ခရိုအေးရှား\":\"hr\",\"ချက်\":\"cs\",\"ဂရိ\":\"el\",\"ဂယ်လိရှ\":\"gl\",\"ဂျပန်\":\"ja\",\"ဂျာမန်\":\"de\",\"စကော့ ဂေးလစ်\":\"gd\",\"စပိန်\":\"es\",\"ဆလိုဗေးနီးယား\":\"sl\",\"ဆလိုဗက်\":\"sk\",\"ဆားဘီးယား\":\"sr\",\"ဆွီဒင်\":\"sv\",\"ဇူးလူး\":\"zu\",\"တရုတ် (ရိုးရာ)\":\"zh-tw\",\"တရုတ် (အလွယ်)\":\"zh-cn\",\"တူ​ရ​ကီ\":\"tr\",\"တယ်လူဂူ\":\"te\",\"ထိုင်း\":\"th\",\"ဒတ်ချ်\":\"nl\",\"ဒိန်းမတ်\":\"da\",\"နော်ဝေ\":\"nb\",\"ပါရှန်\":\"fa\",\"ပေါ်တူဂီ\":\"pt\",\"ပိုလန်\":\"pl\",\"ပြင်သစ်\":\"fr\",\"ဖင်လန်\":\"fi\",\"ဗီယက်နမ်\":\"vi\",\"ဘာ့စ်\":\"eu\",\"ဘူဂေးရီးယား\":\"bg\",\"ဘယ်လာရုစ်\":\"be\",\"မလေယာလမ်\":\"ml\",\"မလေး\":\"ms\",\"မာရာသီ\":\"mr\",\"မော်ရီ\":\"mi\",\"မော်လတာ\":\"mt\",\"မက်ဆီဒိုးနီးယား\":\"mk\",\"မြန်မာ (မြန်မာ)\":\"my\",\"မွန်ဂိုလီးယား\":\"mn\",\"ယူ​က​ရိန်း​\":\"uk\",\"ရုရှား\":\"ru\",\"ရိုမေးနီးယား\":\"ro\",\"လက်တင်\":\"la\",\"လစ်သူယေးနီးယား\":\"lt\",\"လတ်ဗီယာ\":\"lv\",\"ဝေလ\":\"cy\",\"ဟီဘရူး\":\"he\",\"ဟေတီ ခရီအိုး\":\"ht\",\"ဟင်ဒီ\":\"hi\",\"ဟန်ဂေရီ\":\"hu\",\"အဇာဘိုင်ဂျန်\":\"az\",\"အာမေးနီးယား\":\"hy\",\"အာရေဗျ\":\"ar\",\"အီတလီ\":\"it\",\"ဥဇဘက်\":\"uz\",\"အက်စတိုးနီးယား\":\"et\",\"အက်စ်ပဲရန်တို\":\"eo\",\"အိုက်စလန်\":\"is\",\"အင်္ဂလိပ်\":\"en\",\"အင်ဒိုနီးရှား\":\"id\",\"အိုင်းရစ်ရှ်\":\"ga\",\"အမ်ဟဲရစ်ခ်\":\"am\",\"အယ်လ်ဘေးနီးယား\":\"sq\",\"אוזבקית\":\"uz\",\"אוקראינית\":\"uk\",\"אזרית\":\"az\",\"איטלקית\":\"it\",\"אינדונזית\":\"id\",\"איסלנדית\":\"is\",\"אירית\":\"ga\",\"אלבנית\":\"sq\",\"אמהרית\":\"am\",\"אנגלית\":\"en\",\"אסטונית\":\"et\",\"אספרנטו\":\"eo\",\"ארמנית\":\"hy\",\"באסקית\":\"eu\",\"בולגרית\":\"bg\",\"בורמזית\":\"my\",\"בלארוסית\":\"be\",\"גליציאנית\":\"gl\",\"גרמנית\":\"de\",\"דנית\":\"da\",\"הולנדית\":\"nl\",\"הונגרית\":\"hu\",\"הינדי\":\"hi\",\"וולשית\":\"cy\",\"וייטנאמית\":\"vi\",\"זולו\":\"zu\",\"טורקית\":\"tr\",\"טלוגו\":\"te\",\"יוונית\":\"el\",\"יפנית\":\"ja\",\"כורדית\":\"ku\",\"לטווית\":\"lv\",\"לטינית\":\"la\",\"ליטאית\":\"lt\",\"מאורית\":\"mi\",\"מאלאיאלם\":\"ml\",\"מאלזית\":\"ms\",\"מאראתי\":\"mr\",\"מונגולית\":\"mn\",\"מלטית\":\"mt\",\"מקדונית\":\"mk\",\"נורווגית\":\"nb\",\"סינית (מסורתית)\":\"zh-tw\",\"סינית (פשוטה)\":\"zh-cn\",\"סלובנית\":\"sl\",\"סלובקית\":\"sk\",\"ספרדית\":\"es\",\"סקוטית גאלית\":\"gd\",\"סרבית\":\"sr\",\"עברית\":\"he\",\"ערבית\":\"ar\",\"פולנית\":\"pl\",\"פורטוגזית\":\"pt\",\"פינית\":\"fi\",\"פרסית\":\"fa\",\"צ'כית\":\"cs\",\"צרפתית\":\"fr\",\"קאנאדה\":\"kn\",\"קוריאנית\":\"ko\",\"קטלאנית\":\"ca\",\"קרואטית\":\"hr\",\"קריאולית האיטית\":\"ht\",\"רומנית\":\"ro\",\"רוסית\":\"ru\",\"שוודית\":\"sv\",\"תאילנדית\":\"th\",\"азербејџански\":\"az\",\"албански\":\"sq\",\"амхарски\":\"am\",\"англиски\":\"en\",\"арапски\":\"ar\",\"баскиски\":\"eu\",\"белоруски\":\"be\",\"бугарски\":\"bg\",\"бурмански\":\"my\",\"велшки\":\"cy\",\"виетнамски\":\"vi\",\"галициски\":\"gl\",\"германски\":\"de\",\"грчки\":\"el\",\"дански\":\"da\",\"ерменски\":\"hy\",\"естонски\":\"et\",\"индонезиски\":\"id\",\"ирски\":\"ga\",\"исландски\":\"is\",\"италијански\":\"it\",\"јапонски\":\"ja\",\"канада\":\"kn\",\"каталонски\":\"ca\",\"кинески (поедноставен)\":\"zh-cn\",\"кинески (традиционален)\":\"zh-tw\",\"корејски\":\"ko\",\"курдски\":\"ku\",\"латвиски\":\"lv\",\"латински\":\"la\",\"литвански\":\"lt\",\"македонски\":\"mk\",\"малајалам\":\"ml\",\"малајски\":\"ms\",\"малтешки\":\"mt\",\"маорски\":\"mi\",\"маратхи\":\"mr\",\"монголски\":\"mn\",\"норвешки\":\"nb\",\"персиски\":\"fa\",\"полски\":\"pl\",\"португалски\":\"pt\",\"романски\":\"ro\",\"руски\":\"ru\",\"словачки\":\"sk\",\"словенечки\":\"sl\",\"српски\":\"sr\",\"тајландски\":\"th\",\"телугу\":\"te\",\"турски\":\"tr\",\"узбечки\":\"uz\",\"украински\":\"uk\",\"унгарски\":\"hu\",\"фински\":\"fi\",\"француски\":\"fr\",\"хаитски креолски\":\"ht\",\"хебрејски\":\"he\",\"хиндиски\":\"hi\",\"холандски\":\"nl\",\"хрватски\":\"hr\",\"чешки\":\"cs\",\"шведски\":\"sv\",\"шкотски галски\":\"gd\",\"шпански\":\"es\",\"ಅಜರ್ಬೈಜಾನಿ\":\"az\",\"ಅಮಹಾರಿಕ್\":\"am\",\"ಅರಬ್ಬಿ\":\"ar\",\"ಆರ್ಮೇನಿಯನ್\":\"hy\",\"ಆಲ್ಬೇನಿಯನ್\":\"sq\",\"ಇಂಗ್ಲಿಷ್‌‌\":\"en\",\"ಇಂಡೋನೇಷಿಯನ್\":\"id\",\"ಇಟಾಲಿಯನ್\":\"it\",\"ಉಜ್ಬೆಕ್\":\"uz\",\"ಎಸ್ಟೋನಿಯನ್\":\"et\",\"ಎಸ್ಪೆರಾಂಟೋ\":\"eo\",\"ಐರಿಷ್\":\"ga\",\"ಐಸ್‌ಲ್ಯಾಂಡಿಕ್‌\":\"is\",\"ಕನ್ನಡ\":\"kn\",\"ಕುರ್ದಿಶ್\":\"ku\",\"ಕೊರಿಯನ್\":\"ko\",\"ಕ್ಯಾಟಲನ್\":\"ca\",\"ಕ್ರೊಯೇಷಿಯನ್\":\"hr\",\"ಗ್ಯಾಲೀಷಿಯನ್\":\"gl\",\"ಗ್ರೀಕ್\":\"el\",\"ಚೀನಿ (ಸರಳೀಕೃತ)\":\"zh-cn\",\"ಚೀನಿ (ಸಾಂಪ್ರದಾಯಿಕ)\":\"zh-tw\",\"ಜಪಾನಿ\":\"ja\",\"ಜರ್ಮನ್\":\"de\",\"ಜುಲು\":\"zu\",\"ಝೆಕ್‌\":\"cs\",\"ಟರ್ಕಿಷ್\":\"tr\",\"ಡಚ್\":\"nl\",\"ಡ್ಯಾನಿಷ್\":\"da\",\"ತೆಲುಗು\":\"te\",\"ಥಾಯ್\":\"th\",\"ನಾರ್ವೇಜಿಯನ್‌\":\"nb\",\"ಪೋರ್ಚುಗೀಸ್\":\"pt\",\"ಪೋಲಿಷ್\":\"pl\",\"ಫಾರ್ಸಿ\":\"fa\",\"ಫಿನ್ನಿಷ್\":\"fi\",\"ಫ್ರೆಂಚ್\":\"fr\",\"ಬರ್ಮೀಸ್\":\"my\",\"ಬಲ್ಗೇರಿಯನ್\":\"bg\",\"ಬಾಸ್ಕ್\":\"eu\",\"ಬೆಲರೂಸಿಯನ್\":\"be\",\"ಮಂಗೋಲಿಯನ್\":\"mn\",\"ಮರಾಠಿ\":\"mr\",\"ಮಲಯ\":\"ms\",\"ಮಲಯಾಳಂ\":\"ml\",\"ಮಾಲ್ಟೀಸ್\":\"mt\",\"ಮಾವೋರಿ\":\"mi\",\"ಮ್ಯಾಸೆಡೋನಿಯನ್\":\"mk\",\"ಯುಕ್ರೇನಿಯನ್\":\"uk\",\"ರಷಿಯನ್\":\"ru\",\"ರೊಮೇನಿಯನ್\":\"ro\",\"ಲಿಥುವೇನಿಯನ್\":\"lt\",\"ಲ್ಯಾಟಿನ್\":\"la\",\"ಲ್ಯಾಟ್ವಿಯನ್‌\":\"lv\",\"ವಿಯೆಟ್ನಾಮಿ\":\"vi\",\"ವೆಲ್ಶ್\":\"cy\",\"ಸರ್ಬಿಯನ್\":\"sr\",\"ಸ್ಕಾಟ್ಸ್ ಗ್ಯಾಲಿಕ್\":\"gd\",\"ಸ್ಪ್ಯಾನಿಷ್\":\"es\",\"ಸ್ಲೊವಾಕ್\":\"sk\",\"ಸ್ಲೊವೆನಿಯನ್\":\"sl\",\"ಸ್ವೀಡಿಷ್\":\"sv\",\"ಹಂಗೇರಿಯನ್\":\"hu\",\"ಹಯಥಿಯನ್‌ ಕ್ರಿಯೋಲ್‌\":\"ht\",\"ಹಿಂದಿ\":\"hi\",\"ಹೀಬ್ರೂ\":\"he\",\"അമാറിക്\":\"am\",\"അർമേനിയൻ\":\"hy\",\"അൽബേനിയൻ\":\"sq\",\"അസർബൈജാനി\":\"az\",\"അറബിക്\":\"ar\",\"ഇന്തോനേഷ്യൻ\":\"id\",\"ഇംഗ്ലീഷ്\":\"en\",\"ഇറ്റാലിയൻ\":\"it\",\"ഉക്രേനിയൻ\":\"uk\",\"ഉസ്ബെക്ക്\":\"uz\",\"എസ്‌പെരന്തോ\":\"eo\",\"എസ്റ്റോണിയൻ\":\"et\",\"ഐസ്‌ലാൻഡിക്\":\"is\",\"ഐറിഷ്\":\"ga\",\"കന്നട\":\"kn\",\"കാറ്റലൻ\":\"ca\",\"കുർദ്ദിഷ്\":\"ku\",\"കൊറിയൻ\":\"ko\",\"ക്രൊയേഷ്യൻ\":\"hr\",\"ഗലീഷ്യൻ\":\"gl\",\"ഗ്രീക്ക്\":\"el\",\"ചെക്ക്\":\"cs\",\"ചൈനീസ് (പരമ്പരാഗതം)\":\"zh-tw\",\"ചൈനീസ് (ലഘൂകരിച്ചത്)\":\"zh-cn\",\"ജർമ്മൻ\":\"de\",\"ജാപ്പനീസ്‌\":\"ja\",\"ടർക്കിഷ്\":\"tr\",\"ഡച്ച്\":\"nl\",\"ഡാനിഷ്\":\"da\",\"തായ്\":\"th\",\"തെലുങ്ക്\":\"te\",\"നോർവീജിയൻ\":\"nb\",\"പേർഷ്യൻ\":\"fa\",\"പോർച്ചുഗീസ്\":\"pt\",\"പോളിഷ്\":\"pl\",\"ഫിന്നിഷ്\":\"fi\",\"ഫ്രെഞ്ച്\":\"fr\",\"ബർമീസ്\":\"my\",\"ബൾഗേറിയൻ\":\"bg\",\"ബാസ്ക്\":\"eu\",\"ബെലാറുഷ്യൻ\":\"be\",\"മംഗോളിയൻ\":\"mn\",\"മലയാളം\":\"ml\",\"മലയ്\":\"ms\",\"മറാഠി\":\"mr\",\"മാസഡോണിയൻ\":\"mk\",\"മാൾട്ടീസ്\":\"mt\",\"മൗറി\":\"mi\",\"ലാറ്റിൻ\":\"la\",\"ലാറ്റ്‌വിയൻ\":\"lv\",\"ലിത്വേനിയൻ\":\"lt\",\"വിയറ്റ്നാമീസ്\":\"vi\",\"വെൽഷ്\":\"cy\",\"സുളു\":\"zu\",\"സെർബിയൻ\":\"sr\",\"സ്കോട്ട്സ് ഗ്യാലിക്\":\"gd\",\"സ്പാനിഷ്\":\"es\",\"സ്ലോവാക്\":\"sk\",\"സ്ലോവേനിയൻ\":\"sl\",\"സ്വീഡിഷ്\":\"sv\",\"ഹംഗേറിയൻ\":\"hu\",\"ഹിന്ദി\":\"hi\",\"ഹീബ്രു\":\"he\",\"ഹെയ്തിയൻ ക്രയോൾ\":\"ht\",\"റഷ്യൻ\":\"ru\",\"റൊമേനിയൻ\":\"ro\",\"albánčina\":\"sq\",\"amharčina\":\"am\",\"arabčina\":\"ar\",\"arménčina\":\"hy\",\"azerbajdžančina\":\"az\",\"barmčina\":\"my\",\"bieloruština\":\"be\",\"bulharčina\":\"bg\",\"čínština (tradičná)\":\"zh-tw\",\"dánčina\":\"da\",\"estónčina\":\"et\",\"fínčina\":\"fi\",\"francúzština\":\"fr\",\"galícijčina\":\"gl\",\"gréčtina\":\"el\",\"haitská kreolčina\":\"ht\",\"hebrejčina\":\"he\",\"hindčina\":\"hi\",\"holandčina\":\"nl\",\"chorvátčina\":\"hr\",\"indonézština\":\"id\",\"írčina\":\"ga\",\"islandčina\":\"is\",\"japončina\":\"ja\",\"kannadčina\":\"kn\",\"katalánčina\":\"ca\",\"kórejčina\":\"ko\",\"kurdčina\":\"ku\",\"latinčina\":\"la\",\"litovčina\":\"lt\",\"macedónčina\":\"mk\",\"maďarčina\":\"hu\",\"malajámčina\":\"ml\",\"malajčina\":\"ms\",\"maltčina\":\"mt\",\"maorijčina\":\"mi\",\"maratčina\":\"mr\",\"mongolčina\":\"mn\",\"nemčina\":\"de\",\"nórčina\":\"nb\",\"perzština\":\"fa\",\"poľština\":\"pl\",\"portugalčina\":\"pt\",\"rumunčina\":\"ro\",\"slovenčina\":\"sk\",\"slovinčina\":\"sl\",\"srbčina\":\"sr\",\"škótska gaelčina\":\"gd\",\"španielčina\":\"es\",\"švédčina\":\"sv\",\"taliančina\":\"it\",\"telugčina\":\"te\",\"thajčina\":\"th\",\"ukrajinčina\":\"uk\",\"vietnamčina\":\"vi\",\"waleština\":\"cy\",\"zuluština\":\"zu\",\"isi-albania\":\"sq\",\"isi-amharic\":\"am\",\"isi-arabic\":\"ar\",\"isi-armenian\":\"hy\",\"isi-azerbaijani\":\"az\",\"isi-basque\":\"eu\",\"isi-belarusian\":\"be\",\"isi-bulgarian\":\"bg\",\"isi-burmese\":\"my\",\"isi-catalan\":\"ca\",\"isi-chinese (simplified)\":\"zh-cn\",\"isi-chinese (traditional)\":\"zh-tw\",\"isi-croatian\":\"hr\",\"isi-czech\":\"cs\",\"isi-danish\":\"da\",\"isi-dutch\":\"nl\",\"isi-english\":\"en\",\"isi-esperanto\":\"eo\",\"isi-estonian\":\"et\",\"isi-finnish\":\"fi\",\"isi-french\":\"fr\",\"isi-galician\":\"gl\",\"isi-german\":\"de\",\"isi-greek\":\"el\",\"isi-haitian creole\":\"ht\",\"isi-hangarian\":\"hu\",\"isi-hebrew\":\"he\",\"isi-hindi\":\"hi\",\"isi-icelandic\":\"is\",\"isi-indonesian\":\"id\",\"isi-irish\":\"ga\",\"isi-italian\":\"it\",\"isi-japanese\":\"ja\",\"isi-kannada\":\"kn\",\"isi-korean\":\"ko\",\"isi-kurdish\":\"ku\",\"isi-latin\":\"la\",\"isi-latvian\":\"lv\",\"isi-lithuanian\":\"lt\",\"isi-macedonian\":\"mk\",\"isi-malay\":\"ms\",\"isi-malayalam\":\"ml\",\"isi-maltese\":\"mt\",\"isi-maori\":\"mi\",\"isi-marathi\":\"mr\",\"isi-mongolian\":\"mn\",\"isi-norwegian\":\"nb\",\"isi-persian\":\"fa\",\"isi-polish\":\"pl\",\"isi-portuguese\":\"pt\",\"isi-romanian\":\"ro\",\"isi-russian\":\"ru\",\"isi-scots gaelic\":\"gd\",\"isi-serbian\":\"sr\",\"isi-slovak\":\"sk\",\"isi-slovenian\":\"sl\",\"isi-spanish\":\"es\",\"isi-swedish\":\"sv\",\"isi-telugu\":\"te\",\"isi-thai\":\"th\",\"isi-turkish\":\"tr\",\"isi-ukrainian\":\"uk\",\"isi-uzbek\":\"uz\",\"isi-vietnamese\":\"vi\",\"isi-welsh\":\"cy\",\"isizulu\":\"zu\",\"albania\":\"sq\",\"armenia\":\"hy\",\"azerbaijan\":\"az\",\"belanda\":\"nl\",\"belarus\":\"be\",\"bulgaria\":\"bg\",\"cina (mudah)\":\"zh-cn\",\"cina (tradisional)\":\"zh-tw\",\"croatia\":\"hr\",\"denmark\":\"da\",\"estonia\":\"et\",\"finland\":\"fi\",\"gaelic scotland\":\"gd\",\"galicia\":\"gl\",\"hungary\":\"hu\",\"ibrani\":\"he\",\"iceland\":\"is\",\"indonesia\":\"id\",\"inggeris\":\"en\",\"ireland\":\"ga\",\"itali\":\"it\",\"jepun\":\"ja\",\"jerman\":\"de\",\"korea\":\"ko\",\"kreol haiti\":\"ht\",\"kurdistan\":\"ku\",\"latvia\":\"lv\",\"lithuania\":\"lt\",\"macedonia\":\"mk\",\"malta\":\"mt\",\"melayu\":\"ms\",\"mongolia\":\"mn\",\"myanmar\":\"my\",\"norway\":\"nb\",\"parsi\":\"fa\",\"perancis\":\"fr\",\"poland\":\"pl\",\"portugis\":\"pt\",\"romania\":\"ro\",\"rusia\":\"ru\",\"sepanyol\":\"es\",\"serbia\":\"sr\",\"slovenia\":\"sl\",\"sweden\":\"sv\",\"turki\":\"tr\",\"ukraine\":\"uk\",\"vietnam\":\"vi\",\"wales\":\"cy\",\"азербайджанский\":\"az\",\"албанский\":\"sq\",\"амхарский\":\"am\",\"английский\":\"en\",\"арабский\":\"ar\",\"армянский\":\"hy\",\"баскский\":\"eu\",\"белорусский\":\"be\",\"бирманский\":\"my\",\"болгарский\":\"bg\",\"валлийский\":\"cy\",\"венгерский\":\"hu\",\"вьетнамский\":\"vi\",\"галисийский\":\"gl\",\"греческий\":\"el\",\"датский\":\"da\",\"иврит\":\"he\",\"индонезийский\":\"id\",\"ирландский\":\"ga\",\"исландский\":\"is\",\"испанский\":\"es\",\"итальянский\":\"it\",\"каталанский\":\"ca\",\"китайский (традиционный)\":\"zh-tw\",\"китайский (упрощенный)\":\"zh-cn\",\"корейский\":\"ko\",\"креольский (гаити)\":\"ht\",\"курманджи\":\"ku\",\"латинский\":\"la\",\"латышский\":\"lv\",\"литовский\":\"lt\",\"македонский\":\"mk\",\"малайский\":\"ms\",\"мальтийский\":\"mt\",\"маори\":\"mi\",\"монгольский\":\"mn\",\"немецкий\":\"de\",\"нидерландский\":\"nl\",\"норвежский\":\"nb\",\"персидский\":\"fa\",\"польский\":\"pl\",\"португальский\":\"pt\",\"румынский\":\"ro\",\"русский\":\"ru\",\"сербский\":\"sr\",\"словацкий\":\"sk\",\"словенский\":\"sl\",\"тайский\":\"th\",\"турецкий\":\"tr\",\"узбекский\":\"uz\",\"украинский\":\"uk\",\"финский\":\"fi\",\"французский\":\"fr\",\"хинди\":\"hi\",\"хорватский\":\"hr\",\"чешский\":\"cs\",\"шведский\":\"sv\",\"шотландский (гэльский)\":\"gd\",\"эсперанто\":\"eo\",\"эстонский\":\"et\",\"японский\":\"ja\",\"albański\":\"sq\",\"amharski\":\"am\",\"angielski\":\"en\",\"arabski\":\"ar\",\"azerski\":\"az\",\"baskijski\":\"eu\",\"białoruski\":\"be\",\"birmański\":\"my\",\"bułgarski\":\"bg\",\"chiński (tradycyjny)\":\"zh-tw\",\"chiński (uproszczony)\":\"zh-cn\",\"chorwacki\":\"hr\",\"czeski\":\"cs\",\"duński\":\"da\",\"estoński\":\"et\",\"fiński\":\"fi\",\"francuski\":\"fr\",\"galicyjski\":\"gl\",\"grecki\":\"el\",\"hebrajski\":\"he\",\"hiszpański\":\"es\",\"indonezyjski\":\"id\",\"irlandzki\":\"ga\",\"islandzki\":\"is\",\"japoński\":\"ja\",\"kataloński\":\"ca\",\"koreański\":\"ko\",\"kreolski (haiti)\":\"ht\",\"kurdyjski\":\"ku\",\"litewski\":\"lt\",\"łaciński\":\"la\",\"łotewski\":\"lv\",\"macedoński\":\"mk\",\"malajalam\":\"ml\",\"malajski\":\"ms\",\"maltański\":\"mt\",\"mongolski\":\"mn\",\"niderlandzki\":\"nl\",\"niemiecki\":\"de\",\"norweski\":\"nb\",\"ormiański\":\"hy\",\"perski\":\"fa\",\"polski\":\"pl\",\"portugalski\":\"pt\",\"rosyjski\":\"ru\",\"rumuński\":\"ro\",\"serbski\":\"sr\",\"słowacki\":\"sk\",\"słoweński\":\"sl\",\"szkocki gaelicki\":\"gd\",\"szwedzki\":\"sv\",\"tajski\":\"th\",\"turecki\":\"tr\",\"ukraiński\":\"uk\",\"uzbecki\":\"uz\",\"walijski\":\"cy\",\"węgierski\":\"hu\",\"wietnamski\":\"vi\",\"włoski\":\"it\",\"азербайджанская\":\"az\",\"албанская\":\"sq\",\"амхарская\":\"am\",\"англійская\":\"en\",\"арабская\":\"ar\",\"армянская\":\"hy\",\"балгарская\":\"bg\",\"баскская\":\"eu\",\"беларуская\":\"be\",\"в'етнамская\":\"vi\",\"валійская\":\"cy\",\"венгерская\":\"hu\",\"гаіцянская крэольская\":\"ht\",\"галандская\":\"nl\",\"галісійская\":\"gl\",\"грэчаская\":\"el\",\"дацкая\":\"da\",\"інданезійская\":\"id\",\"ірландская\":\"ga\",\"ісландская\":\"is\",\"іспанская\":\"es\",\"італьянская\":\"it\",\"іўрыт\":\"he\",\"карэйская\":\"ko\",\"каталонская\":\"ca\",\"кітайская (спрошчаная)\":\"zh-cn\",\"кітайская (традыцыйная)\":\"zh-tw\",\"курдская (курманджы)\":\"ku\",\"латышская\":\"lv\",\"лацінская\":\"la\",\"літоўская\":\"lt\",\"м'янманская (бірманская)\":\"my\",\"маары\":\"mi\",\"македонская\":\"mk\",\"малайская\":\"ms\",\"мальтыйская\":\"mt\",\"мангольская\":\"mn\",\"нарвежская\":\"nb\",\"нямецкая\":\"de\",\"партугальская\":\"pt\",\"персідская\":\"fa\",\"польская\":\"pl\",\"румынская\":\"ro\",\"руская\":\"ru\",\"сербская\":\"sr\",\"славацкая\":\"sk\",\"славенская\":\"sl\",\"тайская\":\"th\",\"турэцкая\":\"tr\",\"тэлугу\":\"te\",\"узбекская\":\"uz\",\"украінская\":\"uk\",\"фінская\":\"fi\",\"французская\":\"fr\",\"харвацкая\":\"hr\",\"хіндзі\":\"hi\",\"чэшская\":\"cs\",\"шатландская гэльская\":\"gd\",\"шведская\":\"sv\",\"эсперанта\":\"eo\",\"эстонская\":\"et\",\"японская\":\"ja\",\"amarikisht\":\"am\",\"anglisht\":\"en\",\"arabisht\":\"ar\",\"armenisht\":\"hy\",\"azerisht\":\"az\",\"baskisht\":\"eu\",\"birmanisht\":\"my\",\"bjellorusisht\":\"be\",\"bullgarisht\":\"bg\",\"çekisht\":\"cs\",\"danisht\":\"da\",\"estonisht\":\"et\",\"finlandisht\":\"fi\",\"frëngjisht\":\"fr\",\"galicianisht\":\"gl\",\"galishte skoceze\":\"gd\",\"greqisht\":\"el\",\"gjermanisht\":\"de\",\"hebraisht\":\"he\",\"hindisht\":\"hi\",\"holandisht\":\"nl\",\"hungarisht\":\"hu\",\"indonezisht\":\"id\",\"irlandisht\":\"ga\",\"islandisht\":\"is\",\"italisht\":\"it\",\"japonisht\":\"ja\",\"kanada\":\"kn\",\"katalonisht\":\"ca\",\"kinezisht (e thjeshtuar)\":\"zh-cn\",\"kinezisht (tradicionale)\":\"zh-tw\",\"koreanisht\":\"ko\",\"kreolishte haitiane\":\"ht\",\"kroatisht\":\"hr\",\"latinisht\":\"la\",\"letonisht\":\"lv\",\"lituanisht\":\"lt\",\"malajalamisht\":\"ml\",\"malajzisht\":\"ms\",\"malteze\":\"mt\",\"maorisht\":\"mi\",\"maqedonisht\":\"mk\",\"maratisht\":\"mr\",\"mongolisht\":\"mn\",\"norvegjisht\":\"nb\",\"persisht\":\"fa\",\"polonisht\":\"pl\",\"portugalisht\":\"pt\",\"rumanisht\":\"ro\",\"rusisht\":\"ru\",\"serbisht\":\"sr\",\"sllovakisht\":\"sk\",\"sllovenisht\":\"sl\",\"spanjisht\":\"es\",\"suedisht\":\"sv\",\"shqip\":\"sq\",\"tajlandisht\":\"th\",\"telugisht\":\"te\",\"turqisht\":\"tr\",\"uellsisht\":\"cy\",\"ukrainisht\":\"uk\",\"uzbekisht\":\"uz\",\"vietnamisht\":\"vi\",\"ሀንጋሪኛ\":\"hu\",\"ህንድኛ\":\"hi\",\"ሊትዌንኛ\":\"lt\",\"ላቲንኛ\":\"la\",\"ላትቪያኛ\":\"lv\",\"ማላያላምኛ\":\"ml\",\"ማላይኛ\":\"ms\",\"ማልቲስኛ\":\"mt\",\"ማራቲኛ\":\"mr\",\"ማዮሪኛ\":\"mi\",\"ሜቄዶኒያኛ\":\"mk\",\"ሞንጎሊያኛ\":\"mn\",\"ራሽያኛ\":\"ru\",\"ሮማኒያንኛ\":\"ro\",\"ሰርቢያኛ\":\"sr\",\"ስሎቫክኛ\":\"sk\",\"ስሎቬንያኛ\":\"sl\",\"ስዊድንኛ\":\"sv\",\"ስፓኒሽኛ\":\"es\",\"በርማኛ\":\"my\",\"ቡልጋሪያኛ\":\"bg\",\"ባስክኛ\":\"eu\",\"ቤላሩስኛ\":\"be\",\"ቪትናምኛ\":\"vi\",\"ቱርክኛ\":\"tr\",\"ታይኛ\":\"th\",\"ቴሉጉኛ\":\"te\",\"ቻይንኛ   (ቀላሉ)\":\"zh-cn\",\"ቻይንኛ  (ባሕላዊው)\":\"zh-tw\",\"ቼክኛ\":\"cs\",\"ኖርዌጅያንኛ\":\"nb\",\"አልባንያኛ\":\"sq\",\"አማርኛ\":\"am\",\"አርመኒያኛ\":\"hy\",\"አዜርባይጃንኛ\":\"az\",\"አይሪሽ\":\"ga\",\"አይስላንድኛ\":\"is\",\"ኡዝቤክኛ\":\"uz\",\"ኤስቶኒያኛ\":\"et\",\"ኤስፐራንቶ\":\"eo\",\"እንዶኔዢያኛ\":\"id\",\"እንግሊዝኛ\":\"en\",\"ኩርድሽኛ\":\"ku\",\"ካታላንኛ\":\"ca\",\"ካናዳኛ\":\"kn\",\"ክሮኤሽያኛ\":\"hr\",\"ኮሪያኛ\":\"ko\",\"ዌልሽ\":\"cy\",\"ዐረብኛ\":\"ar\",\"ዕብራይስጥ\":\"he\",\"ዙሉኛ\":\"zu\",\"የሃይቲ ክረኦሌኛ\":\"ht\",\"የስኮት ጌልክኛ\":\"gd\",\"ዩክሬንኛ\":\"uk\",\"ደችኛ\":\"nl\",\"ዴንሽኛ\":\"da\",\"ጀርመንኛ\":\"de\",\"ጃፓንኛ\":\"ja\",\"ጋሊሺያኛ\":\"gl\",\"ግሪክኛ\":\"el\",\"ጣሊያንኛ\":\"it\",\"ፈረንሳይኛ\":\"fr\",\"ፊኒሽኛ\":\"fi\",\"ፐርሺያኛ\":\"fa\",\"ፖሊሽኛ\":\"pl\",\"ፖርቱጋሊኛ\":\"pt\",\"alban\":\"sq\",\"alman\":\"de\",\"amarikcə\":\"am\",\"azərbaycan dili\":\"az\",\"bask\":\"eu\",\"belarusca\":\"be\",\"bolqar\":\"bg\",\"çex\":\"cs\",\"çin (ən'ənəvi)\":\"zh-tw\",\"çin (sadələşdirilmiş)\":\"zh-cn\",\"danimarka\":\"da\",\"erməni\":\"hy\",\"eston\":\"et\",\"ərəb\":\"ar\",\"fars dili\":\"fa\",\"fin\":\"fi\",\"fransız\":\"fr\",\"haiti kreol dili\":\"ht\",\"hind\":\"hi\",\"xorvat\":\"hr\",\"ispan\":\"es\",\"i̇ndoneziya\":\"id\",\"i̇ngilis\":\"en\",\"i̇rland\":\"ga\",\"i̇sland\":\"is\",\"i̇sveç\":\"sv\",\"i̇talyan\":\"it\",\"i̇vrit\":\"he\",\"katalan\":\"ca\",\"koreya\":\"ko\",\"kürd dili (kurmanci)\":\"ku\",\"qalisian\":\"gl\",\"latın\":\"la\",\"latış\":\"lv\",\"litva\":\"lt\",\"macar\":\"hu\",\"makedoniya\":\"mk\",\"maoricə\":\"mi\",\"monqolca\":\"mn\",\"myanma (birma) dili\":\"my\",\"norveç\":\"nb\",\"özbək\":\"uz\",\"polyak\":\"pl\",\"portuqal\":\"pt\",\"rumın\":\"ro\",\"rus\":\"ru\",\"serb\":\"sr\",\"sloven\":\"sl\",\"şotland (kelt)\":\"gd\",\"tayca\":\"th\",\"teluqu\":\"te\",\"türk\":\"tr\",\"uels\":\"cy\",\"ukrayna\":\"uk\",\"vyetnam\":\"vi\",\"yapon\":\"ja\",\"yunan\":\"el\",\"zulu dili\":\"zu\",\"albaniż\":\"sq\",\"amħari\":\"am\",\"armen\":\"hy\",\"ażerbajġani\":\"az\",\"belarussu\":\"be\",\"bulgaru\":\"bg\",\"ċek\":\"cs\",\"ċiniż (semplifikat)\":\"zh-cn\",\"ċiniż (tradizzjonali)\":\"zh-tw\",\"creole haiti\":\"ht\",\"daniż\":\"da\",\"ebrajk\":\"he\",\"estonjan\":\"et\",\"finlandiż\":\"fi\",\"franċiż\":\"fr\",\"ġappuniż\":\"ja\",\"ġermaniż\":\"de\",\"gaelic tal-iskoċċiżi\":\"gd\",\"galizjan\":\"gl\",\"grieg\":\"el\",\"għarbi\":\"ar\",\"ħindi\":\"hi\",\"indoneżjan\":\"id\",\"ingliż\":\"en\",\"irlandiż\":\"ga\",\"islandiż\":\"is\",\"kroat\":\"hr\",\"kurd (kurmanji)\":\"ku\",\"latvjan\":\"lv\",\"litwen\":\"lt\",\"maċedonjan\":\"mk\",\"malasjan\":\"ms\",\"malti\":\"mt\",\"marati\":\"mr\",\"mjanmar (burma)\":\"my\",\"mongoljan\":\"mn\",\"norveġiż\":\"nb\",\"olandiż\":\"nl\",\"persjan\":\"fa\",\"pollakk\":\"pl\",\"portugiż\":\"pt\",\"rumen\":\"ro\",\"russu\":\"ru\",\"slovakk\":\"sk\",\"spanjol\":\"es\",\"svediż\":\"sv\",\"tajlandiż\":\"th\",\"taljan\":\"it\",\"tork\":\"tr\",\"ukren\":\"uk\",\"ungeriż\":\"hu\",\"użbek\":\"uz\",\"vjetnamiż\":\"vi\",\"żulu\":\"zu\",\"albanski\":\"sq\",\"amharik\":\"am\",\"arapski\":\"ar\",\"armenijski\":\"hy\",\"azerbajdžanski\":\"az\",\"bjeloruski\":\"be\",\"bugarski\":\"bg\",\"burmanski\":\"my\",\"češki\":\"cs\",\"danski\":\"da\",\"engleski\":\"en\",\"estonski\":\"et\",\"finski\":\"fi\",\"galski\":\"gl\",\"grčki\":\"el\",\"haićansko-kreolski\":\"ht\",\"hebrejski\":\"he\",\"hindu\":\"hi\",\"hrvatski\":\"hr\",\"indonezijski\":\"id\",\"irski\":\"ga\",\"islandski\":\"is\",\"japanski\":\"ja\",\"katalonski\":\"ca\",\"kineski (pojednost.)\":\"zh-cn\",\"kineski (tradicionalni)\":\"zh-tw\",\"korejski\":\"ko\",\"kurdski\":\"ku\",\"latinski\":\"la\",\"latvijski/letonski\":\"lv\",\"litvanski\":\"lt\",\"mađarski\":\"hu\",\"makedonski\":\"mk\",\"malezijski\":\"ms\",\"malteški\":\"mt\",\"nizozemski\":\"nl\",\"norveški\":\"nb\",\"njemački\":\"de\",\"perzijski\":\"fa\",\"poljski\":\"pl\",\"rumunjski\":\"ro\",\"ruski\":\"ru\",\"slovački\":\"sk\",\"slovenski\":\"sl\",\"srpski\":\"sr\",\"škotski keltski\":\"gd\",\"španjolski\":\"es\",\"švedski\":\"sv\",\"tajlandski\":\"th\",\"talijanski\":\"it\",\"turski\":\"tr\",\"ukrajinski\":\"uk\",\"uzbekistanski\":\"uz\",\"velški\":\"cy\",\"vijetnamski\":\"vi\",\"آذرباﻳﺠﺎﻧﻰ\":\"az\",\"آلبانیایی\":\"sq\",\"آلمانی\":\"de\",\"ارمنی\":\"hy\",\"ازبکی\":\"uz\",\"اسپانیایی\":\"es\",\"اسپرانتو\":\"eo\",\"استونيايی\":\"et\",\"اسلواکی\":\"sk\",\"اسلونیایی\":\"sl\",\"اکراينی\":\"uk\",\"امهری\":\"am\",\"اندونزيايی\":\"id\",\"انگلیسی\":\"en\",\"ایتالیایی\":\"it\",\"ایرلندی\":\"ga\",\"ايسلندی\":\"is\",\"باسکی\":\"eu\",\"برمه‌ای\":\"my\",\"بلاروسی\":\"be\",\"بلغاری\":\"bg\",\"پرتغالی\":\"pt\",\"تايلندی\":\"th\",\"ترکی استانبولی\":\"tr\",\"تلوگو\":\"te\",\"چک\":\"cs\",\"چینی (ساده‌شده)\":\"zh-cn\",\"چینی (سنتی)\":\"zh-tw\",\"دانمارکی\":\"da\",\"روسی\":\"ru\",\"رومانيايی\":\"ro\",\"زولو\":\"zu\",\"ژاپنی\":\"ja\",\"سوئدی\":\"sv\",\"صربی\":\"sr\",\"عبری\":\"he\",\"عربی\":\"ar\",\"فارسی\":\"fa\",\"فرانسوی\":\"fr\",\"فنلاندی\":\"fi\",\"کاتالان\":\"ca\",\"کانارا\":\"kn\",\"کرئول هائیتی\":\"ht\",\"کردی\":\"ku\",\"کرواتی\":\"hr\",\"کره‌ای\":\"ko\",\"گالیسی\":\"gl\",\"گاليک اسکاتلندی\":\"gd\",\"لاتين\":\"la\",\"لتونيايی\":\"lv\",\"لهستانی\":\"pl\",\"ليتوانيايی\":\"lt\",\"مائوری\":\"mi\",\"مالایالمی\":\"ml\",\"مالايی\":\"ms\",\"مالتی\":\"mt\",\"مجاری\":\"hu\",\"مراتی\":\"mr\",\"مغولی\":\"mn\",\"مقدونيه‌ای\":\"mk\",\"نروژی\":\"nb\",\"ولزی\":\"cy\",\"ويتنامی\":\"vi\",\"هلندی\":\"nl\",\"هندی\":\"hi\",\"يونانی\":\"el\",\"ahepaitani\":\"az\",\"airihi\":\"ga\",\"amariki\":\"am\",\"amēniana\":\"hy\",\"arapeinia\":\"sq\",\"arapi\":\"ar\",\"eperānato\":\"eo\",\"etōnia\":\"et\",\"haina (onamata)\":\"zh-tw\",\"hainamana (kua whakamāmātia)\":\"zh-cn\",\"hanekeria\":\"hu\",\"hapanihi\":\"ja\",\"herepia\":\"sr\",\"hinerangi\":\"fi\",\"hīni\":\"hi\",\"hiperu\":\"he\",\"horowākia\":\"sk\",\"horowinia\":\"sl\",\"huitene\":\"sv\",\"huru\":\"zu\",\"ingarihi\":\"en\",\"initonīhia\":\"id\",\"itāriana\":\"it\",\"kanata\":\"kn\",\"karihia\":\"gl\",\"katarāna\":\"ca\",\"kereore haiti\":\"ht\",\"kiriki\":\"el\",\"kōreana\":\"ko\",\"koroātiana\":\"hr\",\"korukoru\":\"tr\",\"kūrihi\":\"ku\",\"makerōnia\":\"mk\",\"māratihi\":\"mt\",\"marei\":\"ms\",\"mareiarama\":\"ml\",\"mongōriana\":\"mn\",\"nōwei\":\"nb\",\"pākihi\":\"eu\",\"pāniora\":\"es\",\"pēma (purumīhi)\":\"my\",\"peraruhia\":\"be\",\"perēhia\":\"fa\",\"pōrana\":\"pl\",\"potukīhi\":\"pt\",\"purukāriana\":\"bg\",\"rātini\":\"la\",\"rāwhiana\":\"lv\",\"rituānia\":\"lt\",\"romānia\":\"ro\",\"rūhia\":\"ru\",\"tai\":\"th\",\"tati\":\"nl\",\"tenemāka\":\"da\",\"teruku\":\"te\",\"tiamana\":\"de\",\"tieke\":\"cs\",\"tiorangi\":\"is\",\"tuauri kotarangi\":\"gd\",\"uhipeke\":\"uz\",\"ūkareiana\":\"uk\",\"wēra\":\"cy\",\"whitināmu\":\"vi\",\"wīwī\":\"fr\",\"αγγλικά\":\"en\",\"αζερμπαϊτζανικά\":\"az\",\"αλβανικά\":\"sq\",\"αμχαρικά\":\"am\",\"αραβικά\":\"ar\",\"αρμενικά\":\"hy\",\"βασκικά\":\"eu\",\"βιετναμεζικά\":\"vi\",\"βιρμανικά\":\"my\",\"βουλγαρικά\":\"bg\",\"γαελικά σκοτίας\":\"gd\",\"γαλικιακά\":\"gl\",\"γαλλικά\":\"fr\",\"γερμανικά\":\"de\",\"δανικά\":\"da\",\"εβραϊκά\":\"he\",\"ελληνικά\":\"el\",\"εσθονικά\":\"et\",\"εσπεράντο\":\"eo\",\"ζουλού\":\"zu\",\"ιαπωνικά\":\"ja\",\"ινδονησιακά\":\"id\",\"ιρλανδικά\":\"ga\",\"ισλανδικά\":\"is\",\"ισπανικά\":\"es\",\"ιταλικά\":\"it\",\"κανάντα\":\"kn\",\"καταλανικά\":\"ca\",\"κινέζικα (απλοποιημένα)\":\"zh-cn\",\"κινέζικα (παραδοσιακά)\":\"zh-tw\",\"κορεατικά\":\"ko\",\"κουρδικά\":\"ku\",\"κρεόλ αϊτής\":\"ht\",\"κροατικά\":\"hr\",\"λατινικά\":\"la\",\"λετονικά\":\"lv\",\"λευκορωσικά\":\"be\",\"λιθουανικά\":\"lt\",\"μαλαγιάλαμ\":\"ml\",\"μαλέι\":\"ms\",\"μαλτεζικά\":\"mt\",\"μαορί\":\"mi\",\"μαραθικά\":\"mr\",\"μογγολικά\":\"mn\",\"νορβηγικά\":\"nb\",\"ολλανδικά\":\"nl\",\"ουαλικά\":\"cy\",\"ουγγρικά\":\"hu\",\"ουζμπεκικά\":\"uz\",\"ουκρανικά\":\"uk\",\"περσικά\":\"fa\",\"πολωνικά\":\"pl\",\"πορτογαλικά\":\"pt\",\"ρουμανικά\":\"ro\",\"ρωσικά\":\"ru\",\"σερβικά\":\"sr\",\"σλαβομακεδονικά\":\"mk\",\"σλοβακικά\":\"sk\",\"σλοβενικά\":\"sl\",\"σουηδικά\":\"sv\",\"ταϊλανδεζικά\":\"th\",\"τελούγκου\":\"te\",\"τούρκικα\":\"tr\",\"τσεχικά\":\"cs\",\"φινλανδικά\":\"fi\",\"χίντι\":\"hi\",\"amxar\":\"am\",\"arman\":\"hy\",\"bolgar\":\"bg\",\"dat\":\"da\",\"fors\":\"fa\",\"fransuz\":\"fr\",\"gaiti-kreol\":\"ht\",\"galisiy\":\"gl\",\"golland\":\"nl\",\"grek\":\"el\",\"ibroniy\":\"he\",\"indonez\":\"id\",\"ingliz\":\"en\",\"irland\":\"ga\",\"island\":\"is\",\"italyan\":\"it\",\"koreys\":\"ko\",\"kurd (kurmonji)\":\"ku\",\"latish\":\"lv\",\"lotin\":\"la\",\"makedon\":\"mk\",\"maltiy\":\"mt\",\"maratxi\":\"mr\",\"mo‘g‘ul\":\"mn\",\"nemis\":\"de\",\"norveg\":\"nb\",\"ozarbayjon\":\"az\",\"portugal\":\"pt\",\"rumin\":\"ro\",\"tay\":\"th\",\"turk\":\"tr\",\"ukrain\":\"uk\",\"valliy\":\"cy\",\"venger\":\"hu\",\"xitoy (an’anaviy)\":\"zh-tw\",\"xitoy (soddalashgan)\":\"zh-cn\",\"o‘zbek\":\"uz\",\"shotland-gel\":\"gd\",\"shved\":\"sv\",\"chex\":\"cs\",\"albanska\":\"sq\",\"amharísku\":\"am\",\"arabíska\":\"ar\",\"armenska\":\"hy\",\"aserska\":\"az\",\"baskneska\":\"eu\",\"búlgarska\":\"bg\",\"búrmíska\":\"my\",\"danska\":\"da\",\"eistneska\":\"et\",\"enska\":\"en\",\"esperantó\":\"eo\",\"finnska\":\"fi\",\"franska\":\"fr\",\"galisíska\":\"gl\",\"gríska\":\"el\",\"haítískt kreólamál\":\"ht\",\"hebreska\":\"he\",\"hindí\":\"hi\",\"hollenska\":\"nl\",\"hvítrússneska\":\"be\",\"indónesíska\":\"id\",\"írska\":\"ga\",\"íslenska\":\"is\",\"ítalska\":\"it\",\"japanska\":\"ja\",\"katalónska\":\"ca\",\"kínverska (einfölduð)\":\"zh-cn\",\"kínverska (hefðbundin)\":\"zh-tw\",\"kóreska\":\"ko\",\"króatíska\":\"hr\",\"kúrdíska\":\"ku\",\"latína\":\"la\",\"lettneska\":\"lv\",\"litháíska\":\"lt\",\"makedónska\":\"mk\",\"malajíska\":\"ms\",\"maltneska\":\"mt\",\"maoríska\":\"mi\",\"maratí\":\"mr\",\"mongólska\":\"mn\",\"norska\":\"nb\",\"persneska\":\"fa\",\"portúgalska\":\"pt\",\"pólska\":\"pl\",\"rúmenska\":\"ro\",\"rússneska\":\"ru\",\"serbneska\":\"sr\",\"skosk-gelíska\":\"gd\",\"slóvakíska\":\"sk\",\"slóvenska\":\"sl\",\"spænska\":\"es\",\"súlú\":\"zu\",\"sænska\":\"sv\",\"taílenska\":\"th\",\"tékkneska\":\"cs\",\"tyrkneska\":\"tr\",\"ungverska\":\"hu\",\"úkraínska\":\"uk\",\"úsbekíska\":\"uz\",\"velska\":\"cy\",\"víetnamska\":\"vi\",\"þýska\":\"de\",\"ադրբեջաներեն\":\"az\",\"ալբաներեն\":\"sq\",\"ամհարերեն\":\"am\",\"անգլերեն\":\"en\",\"արաբերեն\":\"ar\",\"բասկերեն\":\"eu\",\"բելառուսերեն\":\"be\",\"բիրմաներեն\":\"my\",\"բուլղարերեն\":\"bg\",\"գալիսերեն\":\"gl\",\"գերմաներեն\":\"de\",\"դանիերեն\":\"da\",\"եբրայերեն\":\"he\",\"զուլուսերեն\":\"zu\",\"էսպերանտո\":\"eo\",\"էստոներեն\":\"et\",\"թայերեն\":\"th\",\"թուրքերեն\":\"tr\",\"ինդոնեզերեն\":\"id\",\"իռլանդերեն\":\"ga\",\"իսլանդերեն\":\"is\",\"իսպաներեն\":\"es\",\"իտալերեն\":\"it\",\"լատիներեն\":\"la\",\"լատվիերեն\":\"lv\",\"լեհերեն\":\"pl\",\"լիտվերեն\":\"lt\",\"խորվաթերեն\":\"hr\",\"կաննադա\":\"kn\",\"կատալաներեն\":\"ca\",\"կելտական շոտլանդերեն\":\"gd\",\"կորեերեն\":\"ko\",\"կրեոլերեն (հայիթի)\":\"ht\",\"հայերեն\":\"hy\",\"հինդի\":\"hi\",\"հոլանդերեն\":\"nl\",\"հունարեն\":\"el\",\"հունգարերեն\":\"hu\",\"ճապոներեն\":\"ja\",\"մալայալամ\":\"ml\",\"մալայերեն\":\"ms\",\"մալթերեն\":\"mt\",\"մակեդոներեն\":\"mk\",\"մաորի\":\"mi\",\"մարաթի\":\"mr\",\"մոնղոլերեն\":\"mn\",\"նորվեգերեն\":\"nb\",\"շվեդերեն\":\"sv\",\"ուզբեկերեն\":\"uz\",\"ուկրաիներեն\":\"uk\",\"չեխերեն\":\"cs\",\"չինարեն (ավանդական)\":\"zh-tw\",\"չինարեն (պարզեցված)\":\"zh-cn\",\"պարսկերեն\":\"fa\",\"պորտուգալերեն\":\"pt\",\"ռումիներեն\":\"ro\",\"ռուսերեն\":\"ru\",\"սերբերեն\":\"sr\",\"սլովակերեն\":\"sk\",\"սլովեներեն\":\"sl\",\"վալերեն\":\"cy\",\"վիետնամերեն\":\"vi\",\"տելուգու\":\"te\",\"քրդերեն (քուրմանջի)\":\"ku\",\"ֆիններեն\":\"fi\",\"ֆրանսերեն\":\"fr\",\"arabia\":\"ar\",\"baski\":\"eu\",\"burma\":\"my\",\"englanti\":\"en\",\"espanja\":\"es\",\"haitinkreoli\":\"ht\",\"heprea\":\"he\",\"hollanti\":\"nl\",\"iiri\":\"ga\",\"islanti\":\"is\",\"italia\":\"it\",\"japani\":\"ja\",\"katalaani\":\"ca\",\"kiina (perinteinen)\":\"zh-tw\",\"kiina (yksinkert.)\":\"zh-cn\",\"kreikka\":\"el\",\"kroatia\":\"hr\",\"kurdi\":\"ku\",\"kymri\":\"cy\",\"liettua\":\"lt\",\"makedonia\":\"mk\",\"malaiji\":\"ms\",\"norja\":\"nb\",\"persia\":\"fa\",\"portugali\":\"pt\",\"puola\":\"pl\",\"ranska\":\"fr\",\"ruotsi\":\"sv\",\"saksa\":\"de\",\"skottigaeli\":\"gd\",\"slovakia\":\"sk\",\"suomi\":\"fi\",\"tanska\":\"da\",\"tsekki\":\"cs\",\"turkki\":\"tr\",\"ukraina\":\"uk\",\"unkari\":\"hu\",\"uzbekki\":\"uz\",\"valkovenäjä\":\"be\",\"venäjä\":\"ru\",\"viro\":\"et\",\"albaniera\":\"sq\",\"alemana\":\"de\",\"amharera\":\"am\",\"arabiera\":\"ar\",\"armeniera\":\"hy\",\"azerbaijanera\":\"az\",\"bielorrusiera\":\"be\",\"birmaniera\":\"my\",\"bulgariera\":\"bg\",\"daniera\":\"da\",\"errumaniera\":\"ro\",\"errusiera\":\"ru\",\"eskoziako gaelera\":\"gd\",\"eslovakiera\":\"sk\",\"esloveniera\":\"sl\",\"esperantoa\":\"eo\",\"estoniera\":\"et\",\"euskara\":\"eu\",\"frantsesa\":\"fr\",\"gaelera\":\"cy\",\"galiziera\":\"gl\",\"gaztelania\":\"es\",\"greziera\":\"el\",\"hebreera\":\"he\",\"hindia\":\"hi\",\"hungariera\":\"hu\",\"indonesiera\":\"id\",\"ingelesa\":\"en\",\"irlandera\":\"ga\",\"islandiera\":\"is\",\"italiera\":\"it\",\"japoniera\":\"ja\",\"katalana\":\"ca\",\"koreera\":\"ko\",\"kreolera (haiti)\":\"ht\",\"kroaziera\":\"hr\",\"kurduera\":\"ku\",\"letoniera\":\"lv\",\"lituaniera\":\"lt\",\"malabarera\":\"ml\",\"malaysiera\":\"ms\",\"maltera\":\"mt\",\"maoriera\":\"mi\",\"marathera\":\"mr\",\"mazedoniera\":\"mk\",\"mongoliera\":\"mn\",\"nederlandera\":\"nl\",\"norvegiera\":\"nb\",\"persiera\":\"fa\",\"poloniera\":\"pl\",\"portugesa\":\"pt\",\"serbiera\":\"sr\",\"suediera\":\"sv\",\"suomiera\":\"fi\",\"telugua\":\"te\",\"thaiera\":\"th\",\"turkiera\":\"tr\",\"txekiera\":\"cs\",\"txinera (soildua)\":\"zh-cn\",\"txinera (tradizionala)\":\"zh-tw\",\"ukrainera\":\"uk\",\"uzbekera\":\"uz\",\"vietnamera\":\"vi\",\"zuluera\":\"zu\",\"albanès\":\"sq\",\"alemany\":\"de\",\"amhàric\":\"am\",\"anglès\":\"en\",\"àrab\":\"ar\",\"armeni\":\"hy\",\"àzeri\":\"az\",\"basc\":\"eu\",\"bielorús\":\"be\",\"birmà\":\"my\",\"búlgar\":\"bg\",\"castellà\":\"es\",\"català\":\"ca\",\"coreà\":\"ko\",\"crioll d'haití\":\"ht\",\"croat\":\"hr\",\"danès\":\"da\",\"eslovac\":\"sk\",\"eslovè\":\"sl\",\"estonià\":\"et\",\"finès\":\"fi\",\"francès\":\"fr\",\"gaèlic escocès\":\"gd\",\"gallec\":\"gl\",\"gal·lès\":\"cy\",\"hebreu\":\"he\",\"hongarès\":\"hu\",\"indonesi\":\"id\",\"irlandès\":\"ga\",\"islandès\":\"is\",\"italià\":\"it\",\"japonès\":\"ja\",\"letó\":\"lv\",\"lituà\":\"lt\",\"llatí\":\"la\",\"macedònic\":\"mk\",\"malai\":\"ms\",\"malaiàlam\":\"ml\",\"maltès\":\"mt\",\"neerlandès\":\"nl\",\"noruec\":\"nb\",\"polonès\":\"pl\",\"portuguès\":\"pt\",\"romanès\":\"ro\",\"serbi\":\"sr\",\"suec\":\"sv\",\"txec\":\"cs\",\"ucraïnès\":\"uk\",\"xinès (simplificat)\":\"zh-cn\",\"xinès (tradicional)\":\"zh-tw\",\"albāņu\":\"sq\",\"amharu\":\"am\",\"angļu\":\"en\",\"arābu\":\"ar\",\"armēņu\":\"hy\",\"azerbaidžāņu\":\"az\",\"baltkrievu\":\"be\",\"basku\":\"eu\",\"birmiešu\":\"my\",\"bulgāru\":\"bg\",\"čehu\":\"cs\",\"dāņu\":\"da\",\"ebreju (ivrits)\":\"he\",\"franču\":\"fr\",\"galisiešu\":\"gl\",\"grieķu\":\"el\",\"holandiešu\":\"nl\",\"horvātu\":\"hr\",\"igauņu\":\"et\",\"indonēziešu\":\"id\",\"īru\":\"ga\",\"īslandiešu\":\"is\",\"itāļu\":\"it\",\"japāņu\":\"ja\",\"katalāņu\":\"ca\",\"korejiešu\":\"ko\",\"kreolu (haiti)\":\"ht\",\"krievu\":\"ru\",\"kurdu\":\"ku\",\"ķīniešu (tradicionālā)\":\"zh-tw\",\"ķīniešu (vienkāršotā)\":\"zh-cn\",\"latīņu\":\"la\",\"latviešu\":\"lv\",\"lietuviešu\":\"lt\",\"maķedoniešu\":\"mk\",\"malajalamiešu\":\"ml\",\"malajiešu\":\"ms\",\"maltiešu\":\"mt\",\"maratu\":\"mr\",\"mongoļu\":\"mn\",\"norvēģu\":\"nb\",\"persiešu\":\"fa\",\"poļu\":\"pl\",\"portugāļu\":\"pt\",\"rumāņu\":\"ro\",\"serbu\":\"sr\",\"skotu gēlu\":\"gd\",\"slovāku\":\"sk\",\"slovēņu\":\"sl\",\"somu\":\"fi\",\"spāņu\":\"es\",\"taju\":\"th\",\"turku\":\"tr\",\"ukraiņu\":\"uk\",\"ungāru\":\"hu\",\"uzbeku\":\"uz\",\"vācu\":\"de\",\"velsiešu\":\"cy\",\"vjetnamiešu\":\"vi\",\"zviedru\":\"sv\",\"belarussia\":\"be\",\"cek\":\"cs\",\"china (aks. sederhana)\":\"zh-cn\",\"china (aks. tradisional)\":\"zh-tw\",\"finlandia\":\"fi\",\"gaelig\":\"ga\",\"gaelik skotlandia\":\"gd\",\"galisia\":\"gl\",\"inggris\":\"en\",\"islan\":\"is\",\"jepang\":\"ja\",\"katala\":\"ca\",\"lituania\":\"lt\",\"polandia\":\"pl\",\"prancis\":\"fr\",\"rumania\":\"ro\",\"swensk\":\"sv\",\"yunani\":\"el\",\"అజర్‌బైజాని\":\"az\",\"అర్మేనియన్\":\"hy\",\"అల్బేనియన్\":\"sq\",\"ఆంగ్లము\":\"en\",\"ఆమ్హారిక్\":\"am\",\"ఆరబిక్\":\"ar\",\"ఇండొనేసియన్\":\"id\",\"ఇటాలియన్\":\"it\",\"ఉజ్బెక్\":\"uz\",\"ఎస్పెరాంటో\":\"eo\",\"ఏస్టోనియన్\":\"et\",\"ఐరిష్\":\"ga\",\"ఐస్ లాండిక్\":\"is\",\"కన్నడ\":\"kn\",\"కర్డిష్\":\"ku\",\"కొరియన్\":\"ko\",\"క్యాటలాన్\":\"ca\",\"క్రొయేషియన్\":\"hr\",\"గాలిసియన్\":\"gl\",\"గ్రీక్\":\"el\",\"చెక్\":\"cs\",\"చైనీస్ (సరళమైన)\":\"zh-cn\",\"చైనీస్ (సామ్ప్రదాయమైన)\":\"zh-tw\",\"జపనీస్\":\"ja\",\"జర్మన్\":\"de\",\"జులు\":\"zu\",\"టర్కిష్\":\"tr\",\"డచ్\":\"nl\",\"డానిష్\":\"da\",\"తెలుగు\":\"te\",\"థాయ్\":\"th\",\"నార్విజియన్\":\"nb\",\"పర్షియన్\":\"fa\",\"పోర్చుగీస్\":\"pt\",\"పోలిష్\":\"pl\",\"ఫిన్నిష్\":\"fi\",\"ఫ్రెంచ్\":\"fr\",\"బర్మీస్\":\"my\",\"బల్గేరియన్\":\"bg\",\"బాస్క్\":\"eu\",\"బెలారుషియన్\":\"be\",\"మంగోలియన్\":\"mn\",\"మయోరి\":\"mi\",\"మరాఠీ\":\"mr\",\"మలయాళం\":\"ml\",\"మాలై\":\"ms\",\"మాల్టీస్\":\"mt\",\"మాసిడోనియన్\":\"mk\",\"యుక్రేనియన్\":\"uk\",\"రష్యన్\":\"ru\",\"రొమేనియన్\":\"ro\",\"లాటిన్\":\"la\",\"లాట్వియన్\":\"lv\",\"లిథువేనియన్\":\"lt\",\"వియత్నామీస్\":\"vi\",\"వెల్ష్\":\"cy\",\"సెర్బియన్\":\"sr\",\"స్కాట్స్ గేలిక్\":\"gd\",\"స్పానిష్\":\"es\",\"స్లోవక్\":\"sk\",\"స్లోవేనియన్\":\"sl\",\"స్వీడిష్\":\"sv\",\"హంగేరియన్\":\"hu\",\"హిందీ\":\"hi\",\"హీబ్రూ\":\"he\",\"హైయేటియన్ క్రియోల్\":\"ht\",\"amhárico\":\"am\",\"azerí\":\"az\",\"chino (simplificado)\":\"zh-cn\",\"chino (tradicional)\":\"zh-tw\",\"criollo haitiano\":\"ht\",\"danés\":\"da\",\"estonio\":\"et\",\"euskera\":\"eu\",\"finlandés\":\"fi\",\"gallego\":\"gl\",\"griego\":\"el\",\"japonés\":\"ja\",\"kurdo\":\"ku\",\"malayo\":\"ms\",\"noruego\":\"nb\",\"rumano\":\"ro\",\"uzbeco\":\"uz\",\"albaania\":\"sq\",\"amhaari\":\"am\",\"araabia\":\"ar\",\"armeenia\":\"hy\",\"aserbaidžaani\":\"az\",\"birma\":\"my\",\"bulgaaria\":\"bg\",\"eesti\":\"et\",\"galeegi\":\"gl\",\"haitikreooli\":\"ht\",\"heebrea\":\"he\",\"hispaania\":\"es\",\"hollandi\":\"nl\",\"horvaadi\":\"hr\",\"indoneesia\":\"id\",\"inglise\":\"en\",\"islandi\":\"is\",\"itaalia\":\"it\",\"jaapani\":\"ja\",\"kreeka\":\"el\",\"ladina\":\"la\",\"leedu\":\"lt\",\"lihtsustatud hiina\":\"zh-cn\",\"läti\":\"lv\",\"makedoonia\":\"mk\",\"malajalaami\":\"ml\",\"maoori\":\"mi\",\"mongoli\":\"mn\",\"norra\":\"nb\",\"poola\":\"pl\",\"prantsuse\":\"fr\",\"pärsia\":\"fa\",\"rootsi\":\"sv\",\"rumeenia\":\"ro\",\"slovaki\":\"sk\",\"sloveeni\":\"sl\",\"soome\":\"fi\",\"suulu\":\"zu\",\"šoti\":\"gd\",\"taani\":\"da\",\"traditsiooniline hiina\":\"zh-tw\",\"tšehhi\":\"cs\",\"türgi\":\"tr\",\"uelsi\":\"cy\",\"ungari\":\"hu\",\"usbeki\":\"uz\",\"valgevene\":\"be\",\"vene\":\"ru\",\"albanyen\":\"sq\",\"amenyen\":\"hy\",\"anglè\":\"en\",\"azèbajani\":\"az\",\"belarisyen\":\"be\",\"bilgaryen\":\"bg\",\"chinwa (senp)\":\"zh-cn\",\"chinwa (tradisyonèl)\":\"zh-tw\",\"danwa\":\"da\",\"ebre\":\"he\",\"endonezyen\":\"id\",\"endou\":\"hi\",\"estonyen\":\"et\",\"fenlandè\":\"fi\",\"franse\":\"fr\",\"gaelik ekosè\":\"gd\",\"galisyen\":\"gl\",\"grèk\":\"el\",\"ikrenyen\":\"uk\",\"ilandè\":\"ga\",\"islandè\":\"is\",\"italyen\":\"it\",\"izbèk\":\"uz\",\"japonè\":\"ja\",\"koreyen\":\"ko\",\"kreyòl ayisyen\":\"ht\",\"kurde (kurmandji)\":\"ku\",\"kwoasyen\":\"hr\",\"laten\":\"la\",\"letonyen\":\"lv\",\"lityanyen\":\"lt\",\"malè\":\"ms\",\"malt\":\"mt\",\"masedonyen\":\"mk\",\"mongolyen\":\"mn\",\"myanma (burmese)\":\"my\",\"nòvejyen\":\"nb\",\"olandè, neyèlandè\":\"nl\",\"onngaryen\":\"hu\",\"panyòl\":\"es\",\"pèsyen\":\"fa\",\"polonè\":\"pl\",\"pòtigè\":\"pt\",\"ris\":\"ru\",\"romanyen\":\"ro\",\"sèb\":\"sr\",\"slovenyen\":\"sl\",\"syedwa\":\"sv\",\"tuk\":\"tr\",\"tyèk\":\"cs\",\"vyetnamyen\":\"vi\",\"albaneg\":\"sq\",\"almaeneg\":\"de\",\"arabeg\":\"ar\",\"armeneg\":\"hy\",\"aserbaijaneg\":\"az\",\"basgeg\":\"eu\",\"belarwseg\":\"be\",\"bwlgaraidd\":\"bg\",\"catalaneg\":\"ca\",\"creol haiti\":\"ht\",\"croateg\":\"hr\",\"cymraeg\":\"cy\",\"cyrdeg (kurmandji)\":\"ku\",\"daneg\":\"da\",\"eidaleg\":\"it\",\"estoneg\":\"et\",\"fietnameg\":\"vi\",\"ffineg\":\"fi\",\"fflemeg\":\"nl\",\"ffrangeg\":\"fr\",\"gaeleg yr alban\":\"gd\",\"galisaidd\":\"gl\",\"groeg\":\"el\",\"gwyddeleg\":\"ga\",\"hebraeg\":\"he\",\"hwngareg\":\"hu\",\"iaith corea\":\"ko\",\"indonesieg\":\"id\",\"islandeg\":\"is\",\"iwcraineg\":\"uk\",\"japaneg\":\"ja\",\"latfieg\":\"lv\",\"lithwaneg\":\"lt\",\"lladin\":\"la\",\"macedoneg\":\"mk\",\"malteseg\":\"mt\",\"mongoleg\":\"mn\",\"myanmar (byrma)\":\"my\",\"norwyeg\":\"nb\",\"perseg\":\"fa\",\"portiwgaleg\":\"pt\",\"pwyleg\":\"pl\",\"rwmaneg\":\"ro\",\"rwsieg\":\"ru\",\"saesneg\":\"en\",\"sbaeneg\":\"es\",\"serbeg\":\"sr\",\"slofac\":\"sk\",\"slofenia\":\"sl\",\"swedeg\":\"sv\",\"swlw\":\"zu\",\"tsieceg\":\"cs\",\"tsieineeg (traddodiadol)\":\"zh-tw\",\"tsieineeg (wedi symleiddio)\":\"zh-cn\",\"twrceg\":\"tr\",\"usbec\":\"uz\",\"अंग्रेज़ी\":\"en\",\"अज़रबैजानी\":\"az\",\"अल्बेनियन\":\"sq\",\"आइसलैंडिक\":\"is\",\"आर्मेनियन\":\"hy\",\"इटैलियन\":\"it\",\"उज़्बेक\":\"uz\",\"एस्तोनियन\":\"et\",\"एस्पेरांटो\":\"eo\",\"ऐम्हेरिक\":\"am\",\"कन्नड़\":\"kn\",\"कुर्दिश (करमंजी)\":\"ku\",\"कैटेलन\":\"ca\",\"गैलिशियन\":\"gl\",\"चीनी (परंपरागत)\":\"zh-tw\",\"चेक\":\"cs\",\"जापानी\":\"ja\",\"ज़ुलु\":\"zu\",\"डैनिश\":\"da\",\"तुर्क\":\"tr\",\"तेलुगु\":\"te\",\"पुर्तगाली\":\"pt\",\"फ़िनिश\":\"fi\",\"फ़्रेंच\":\"fr\",\"बर्मी\":\"my\",\"बुल्गारियन\":\"bg\",\"बेलारूसीयन\":\"be\",\"बैस्क\":\"eu\",\"मलयालम\":\"ml\",\"माऔरी\":\"mi\",\"माल्टी\":\"mt\",\"मेसीडोनियन\":\"mk\",\"यूक्रेनियन\":\"uk\",\"रूसी\":\"ru\",\"रोमेनियन\":\"ro\",\"लातवियन\":\"lv\",\"लैटिन\":\"la\",\"वियतनामी\":\"vi\",\"सर्बियाई\":\"sr\",\"स्पैनिश\":\"es\",\"स्लोवाक\":\"sk\",\"स्लोवेनियन\":\"sl\",\"हंगरियन\":\"hu\",\"हीब्रू\":\"he\",\"हैतियन क्रिओल\":\"ht\",\"阿尔巴尼亚语\":\"sq\",\"阿拉伯语\":\"ar\",\"阿姆哈拉语\":\"am\",\"阿塞拜疆语\":\"az\",\"爱尔兰语\":\"ga\",\"爱沙尼亚语\":\"et\",\"巴斯克语\":\"eu\",\"白俄罗斯语\":\"be\",\"保加利亚语\":\"bg\",\"冰岛语\":\"is\",\"波兰语\":\"pl\",\"波斯语\":\"fa\",\"丹麦语\":\"da\",\"德语\":\"de\",\"俄语\":\"ru\",\"法语\":\"fr\",\"芬兰语\":\"fi\",\"海地克里奥尔语\":\"ht\",\"韩语\":\"ko\",\"荷兰语\":\"nl\",\"加利西亚语\":\"gl\",\"加泰罗尼亚语\":\"ca\",\"捷克语\":\"cs\",\"卡纳达语\":\"kn\",\"克罗地亚语\":\"hr\",\"库尔德语\":\"ku\",\"拉丁语\":\"la\",\"拉脱维亚语\":\"lv\",\"立陶宛语\":\"lt\",\"罗马尼亚语\":\"ro\",\"马耳他语\":\"mt\",\"马拉地语\":\"mr\",\"马拉雅拉姆语\":\"ml\",\"马来语\":\"ms\",\"马其顿语\":\"mk\",\"毛利语\":\"mi\",\"蒙古语\":\"mn\",\"缅甸语\":\"my\",\"南非祖鲁语\":\"zu\",\"挪威语\":\"nb\",\"葡萄牙语\":\"pt\",\"日语\":\"ja\",\"瑞典语\":\"sv\",\"塞尔维亚语\":\"sr\",\"世界语\":\"eo\",\"斯洛伐克语\":\"sk\",\"斯洛文尼亚语\":\"sl\",\"苏格兰盖尔语\":\"gd\",\"泰卢固语\":\"te\",\"泰语\":\"th\",\"土耳其语\":\"tr\",\"威尔士语\":\"cy\",\"乌克兰语\":\"uk\",\"乌兹别克语\":\"uz\",\"西班牙语\":\"es\",\"希伯来语\":\"he\",\"希腊语\":\"el\",\"匈牙利语\":\"hu\",\"亚美尼亚语\":\"hy\",\"意大利语\":\"it\",\"印地语\":\"hi\",\"印尼语\":\"id\",\"英语\":\"en\",\"越南语\":\"vi\",\"中文(繁体)\":\"zh-tw\",\"中文(简体)\":\"zh-cn\",\"азербайджански\":\"az\",\"английски\":\"en\",\"арабски\":\"ar\",\"арменски\":\"hy\",\"баски\":\"eu\",\"беларуски\":\"be\",\"бирмански\":\"my\",\"български\":\"bg\",\"галисийски\":\"gl\",\"гръцки\":\"el\",\"датски\":\"da\",\"индонезийски\":\"id\",\"ирландски\":\"ga\",\"испански\":\"es\",\"италиански\":\"it\",\"китайски (опростен)\":\"zh-cn\",\"китайски (традиционен)\":\"zh-tw\",\"корейски\":\"ko\",\"кюрдски\":\"ku\",\"латвийски\":\"lv\",\"литовски\":\"lt\",\"малайски\":\"ms\",\"малтийски\":\"mt\",\"немски\":\"de\",\"нидерландски\":\"nl\",\"норвежки\":\"nb\",\"персийски\":\"fa\",\"румънски\":\"ro\",\"словашки\":\"sk\",\"словенски\":\"sl\",\"сръбски\":\"sr\",\"тайландски\":\"th\",\"уелски\":\"cy\",\"узбекски\":\"uz\",\"финландски\":\"fi\",\"френски\":\"fr\",\"хаитянски креолски\":\"ht\",\"хърватски\":\"hr\",\"шотландски келтски\":\"gd\",\"японски\":\"ja\",\"الآيسلندية\":\"is\",\"الأذرية\":\"az\",\"الارمنية\":\"hy\",\"الإسبانية\":\"es\",\"الاسبرانتو\":\"eo\",\"الإستونية\":\"et\",\"الاسكتلندية الغالية\":\"gd\",\"الألبانية\":\"sq\",\"الألمانية\":\"de\",\"الأمهرية\":\"am\",\"الإنجليزية\":\"en\",\"الإندونيسية\":\"id\",\"الأوزبكية\":\"uz\",\"الأوكرانية\":\"uk\",\"الأيرلندية\":\"ga\",\"الإيطالية\":\"it\",\"الباسكية\":\"eu\",\"البرتغالية\":\"pt\",\"البلغارية\":\"bg\",\"البورمية\":\"my\",\"البولندية\":\"pl\",\"البيلاروسية\":\"be\",\"التايلاندية\":\"th\",\"التركية\":\"tr\",\"التشيكية\":\"cs\",\"التيلوجو\":\"te\",\"الجاليكية\":\"gl\",\"الدانماركية\":\"da\",\"الروسية\":\"ru\",\"الرومانية\":\"ro\",\"الزولوية\":\"zu\",\"السلوفاكية\":\"sk\",\"السلوفينية\":\"sl\",\"السويدية\":\"sv\",\"الصربية\":\"sr\",\"الصينية (التقليدية)\":\"zh-tw\",\"الصينية (المبسطة)\":\"zh-cn\",\"العبرية\":\"he\",\"العربية\":\"ar\",\"الفارسية\":\"fa\",\"الفرنسية\":\"fr\",\"الفنلندية\":\"fi\",\"الفيتنامية\":\"vi\",\"القطلونية\":\"ca\",\"الكانادا\":\"kn\",\"الكردية\":\"ku\",\"الكرواتية\":\"hr\",\"الكورية\":\"ko\",\"اللاتفية\":\"lv\",\"اللاتينية\":\"la\",\"اللغة الكريولية الهايتية\":\"ht\",\"الليتوانية\":\"lt\",\"المالايالامية\":\"ml\",\"المالطيّة\":\"mt\",\"الماورية\":\"mi\",\"المقدونية\":\"mk\",\"الملايو\":\"ms\",\"المنغولية\":\"mn\",\"المهراتية\":\"mr\",\"النرويجية\":\"nb\",\"الهندية\":\"hi\",\"الهنغارية\":\"hu\",\"الهولندية\":\"nl\",\"الويلزية\":\"cy\",\"اليابانية\":\"ja\",\"اليونانية\":\"el\",\"баскијски\":\"eu\",\"вијетнамски\":\"vi\",\"галски\":\"gl\",\"енглески\":\"en\",\"индонежански\":\"id\",\"јапански\":\"ja\",\"јерменски\":\"hy\",\"кинески (поједностављени)\":\"zh-cn\",\"кинески (традиционални)\":\"zh-tw\",\"креолски (хаити)\":\"ht\",\"летонски\":\"lv\",\"мађарски\":\"hu\",\"марати\":\"mr\",\"немачки\":\"de\",\"персијски\":\"fa\",\"пољски\":\"pl\",\"румунски\":\"ro\",\"словеначки\":\"sl\",\"тајски\":\"th\",\"украјински\":\"uk\",\"азербайжан\":\"az\",\"албани\":\"sq\",\"амхарик\":\"am\",\"англи\":\"en\",\"араб\":\"ar\",\"армени\":\"hy\",\"баск\":\"eu\",\"беларусь\":\"be\",\"бирм\":\"my\",\"болгар\":\"bg\",\"вьетнам\":\"vi\",\"гаити креол\":\"ht\",\"галик\":\"gl\",\"гаэл\":\"gd\",\"герман\":\"de\",\"голланд\":\"nl\",\"грек\":\"el\",\"дани\":\"da\",\"индонези\":\"id\",\"ирланд\":\"ga\",\"исланд\":\"is\",\"испани\":\"es\",\"итали\":\"it\",\"каталан\":\"ca\",\"кипр\":\"he\",\"курд\":\"ku\",\"латви\":\"lv\",\"латин\":\"la\",\"литва\":\"lt\",\"македон\":\"mk\",\"малай\":\"ms\",\"малайлам\":\"ml\",\"малти\":\"mt\",\"монгол\":\"mn\",\"норвеги\":\"nb\",\"орос\":\"ru\",\"перс\":\"fa\",\"польш\":\"pl\",\"португаль\":\"pt\",\"румын\":\"ro\",\"серби\":\"sr\",\"словак\":\"sk\",\"словени\":\"sl\",\"солонгос\":\"ko\",\"тай\":\"th\",\"турк\":\"tr\",\"тэлүгү\":\"te\",\"узбек\":\"uz\",\"украин\":\"uk\",\"унгар\":\"hu\",\"уэльс\":\"cy\",\"финлянд\":\"fi\",\"франц\":\"fr\",\"хорват\":\"hr\",\"хятад (ердийн)\":\"zh-cn\",\"хятад (уламжлалт)\":\"zh-tw\",\"чех\":\"cs\",\"швед\":\"sv\",\"эстони\":\"et\",\"япон\":\"ja\",\"amhariska\":\"am\",\"arabiska\":\"ar\",\"armeniska\":\"hy\",\"azerbajdzjanska\":\"az\",\"baskiska\":\"eu\",\"bulgariska\":\"bg\",\"burmesiska\":\"my\",\"engelska\":\"en\",\"estniska\":\"et\",\"finska\":\"fi\",\"gaeliska\":\"gd\",\"galiciska\":\"gl\",\"grekiska\":\"el\",\"haitiska\":\"ht\",\"hebreiska\":\"he\",\"indonesiska\":\"id\",\"irländska\":\"ga\",\"isländska\":\"is\",\"italienska\":\"it\",\"kanaresiska\":\"kn\",\"katalanska\":\"ca\",\"kinesiska (förenklad)\":\"zh-cn\",\"kinesiska (traditionell)\":\"zh-tw\",\"koreanska\":\"ko\",\"kroatiska\":\"hr\",\"kurdiska\":\"ku\",\"lettiska\":\"lv\",\"litauiska\":\"lt\",\"makedonska\":\"mk\",\"malaysiska\":\"ms\",\"maltesiska\":\"mt\",\"mongoliska\":\"mn\",\"nederländska\":\"nl\",\"persiska\":\"fa\",\"polska\":\"pl\",\"portugisiska\":\"pt\",\"rumänska\":\"ro\",\"ryska\":\"ru\",\"serbiska\":\"sr\",\"slovakiska\":\"sk\",\"slovenska\":\"sl\",\"spanska\":\"es\",\"svenska\":\"sv\",\"thailändska\":\"th\",\"tjeckiska\":\"cs\",\"turkiska\":\"tr\",\"tyska\":\"de\",\"ukrainska\":\"uk\",\"ungerska\":\"hu\",\"uzbekiska\":\"uz\",\"vietnamesiska\":\"vi\",\"vitryska\":\"be\",\"walesiska\":\"cy\",\"albaneză\":\"sq\",\"amharică\":\"am\",\"arabă\":\"ar\",\"armeană\":\"hy\",\"azerbaidjană\":\"az\",\"bască\":\"eu\",\"bielorusă\":\"be\",\"birmană\":\"my\",\"bulgară\":\"bg\",\"catalană\":\"ca\",\"cehă\":\"cs\",\"chineză (simplificată)\":\"zh-cn\",\"chineză (tradițională)\":\"zh-tw\",\"coreeană\":\"ko\",\"creolă haitiană\":\"ht\",\"croată\":\"hr\",\"daneză\":\"da\",\"ebraică\":\"he\",\"engleză\":\"en\",\"estonă\":\"et\",\"finlandeză\":\"fi\",\"franceză\":\"fr\",\"galeză\":\"cy\",\"galica scoțiană\":\"gd\",\"galiciană\":\"gl\",\"germană\":\"de\",\"greacă\":\"el\",\"indoneziană\":\"id\",\"irlandeză\":\"ga\",\"islandeză\":\"is\",\"italiană\":\"it\",\"japoneză\":\"ja\",\"kurdă\":\"ku\",\"latină\":\"la\",\"letonă\":\"lv\",\"lituaniană\":\"lt\",\"macedoneană\":\"mk\",\"maghiară\":\"hu\",\"malaeză\":\"ms\",\"malteză\":\"mt\",\"mongolă\":\"mn\",\"neerlandeză\":\"nl\",\"norvegiană\":\"nb\",\"persană\":\"fa\",\"poloneză\":\"pl\",\"portugheză\":\"pt\",\"română\":\"ro\",\"rusă\":\"ru\",\"sârbă\":\"sr\",\"slovacă\":\"sk\",\"slovenă\":\"sl\",\"spaniolă\":\"es\",\"suedeză\":\"sv\",\"thailandeză\":\"th\",\"turcă\":\"tr\",\"ucraineană\":\"uk\",\"uzbecă\":\"uz\",\"vietnameză\":\"vi\",\"airių\":\"ga\",\"albanų\":\"sq\",\"amharų\":\"am\",\"anglų\":\"en\",\"arabų\":\"ar\",\"armėnų\":\"hy\",\"azerbaidžaniečių\":\"az\",\"baltarusių\":\"be\",\"baskų\":\"eu\",\"birmiečių\":\"my\",\"bulgarų\":\"bg\",\"čekų\":\"cs\",\"danų\":\"da\",\"estų\":\"et\",\"galisų\":\"gl\",\"graikų\":\"el\",\"haičio kreolų\":\"ht\",\"hebrajų\":\"he\",\"indoneziečių\":\"id\",\"islandų\":\"is\",\"ispanų\":\"es\",\"italų\":\"it\",\"japonų\":\"ja\",\"kanadų\":\"kn\",\"kataloniečių\":\"ca\",\"kinų (supaprastinta)\":\"zh-cn\",\"kinų (tradicinė)\":\"zh-tw\",\"korėjiečių\":\"ko\",\"kroatų\":\"hr\",\"kurdų\":\"ku\",\"latvių\":\"lv\",\"lenkų\":\"pl\",\"lietuvių\":\"lt\",\"lotynų\":\"la\",\"makedoniečių\":\"mk\",\"malajalių\":\"ml\",\"malajiečių\":\"ms\",\"maltiečių\":\"mt\",\"maorių\":\"mi\",\"maratų\":\"mr\",\"mongolų\":\"mn\",\"norvegų\":\"nb\",\"olandų\":\"nl\",\"persų\":\"fa\",\"portugalų\":\"pt\",\"prancūzų\":\"fr\",\"rumunų\":\"ro\",\"rusų\":\"ru\",\"serbų\":\"sr\",\"slovakų\":\"sk\",\"slovėnų\":\"sl\",\"suomių\":\"fi\",\"škotų (gėlų)\":\"gd\",\"švedų\":\"sv\",\"tajų\":\"th\",\"telugų\":\"te\",\"turkų\":\"tr\",\"ukrainiečių\":\"uk\",\"uzbekų\":\"uz\",\"valų\":\"cy\",\"vengrų\":\"hu\",\"vietnamiečių\":\"vi\",\"vokiečių\":\"de\",\"zulusų\":\"zu\",\"갈리시아어\":\"gl\",\"그리스어\":\"el\",\"네덜란드어\":\"nl\",\"노르웨이어\":\"nb\",\"덴마크어\":\"da\",\"독일어\":\"de\",\"라트비아어\":\"lv\",\"라틴어\":\"la\",\"러시아어\":\"ru\",\"루마니아어\":\"ro\",\"리투아니아어\":\"lt\",\"마라티어\":\"mr\",\"마오리어\":\"mi\",\"마케도니아어\":\"mk\",\"말라얄람어\":\"ml\",\"말레이어\":\"ms\",\"몰타어\":\"mt\",\"몽골어\":\"mn\",\"미얀마어 (버마어)\":\"my\",\"바스크어\":\"eu\",\"베트남어\":\"vi\",\"벨라루스어\":\"be\",\"불가리아어\":\"bg\",\"세르비아어\":\"sr\",\"스웨덴어\":\"sv\",\"스코틀랜드 게일어\":\"gd\",\"스페인어\":\"es\",\"슬로바키아어\":\"sk\",\"슬로베니아어\":\"sl\",\"아랍어\":\"ar\",\"아르메니아어\":\"hy\",\"아이슬란드어\":\"is\",\"아이티 크리올어\":\"ht\",\"아일랜드어\":\"ga\",\"아제르바이잔어\":\"az\",\"알바니아어\":\"sq\",\"암하라어\":\"am\",\"에스토니아어\":\"et\",\"에스페란토어\":\"eo\",\"영어\":\"en\",\"우즈베크어\":\"uz\",\"우크라이나어\":\"uk\",\"웨일즈어\":\"cy\",\"이탈리아어\":\"it\",\"인도네시아어\":\"id\",\"일본어\":\"ja\",\"줄루어\":\"zu\",\"중국어(간체)\":\"zh-cn\",\"중국어(번체)\":\"zh-tw\",\"체코어\":\"cs\",\"카탈로니아어\":\"ca\",\"칸나다어\":\"kn\",\"쿠르드어\":\"ku\",\"크로아티아어\":\"hr\",\"태국어\":\"th\",\"터키어\":\"tr\",\"텔루구어\":\"te\",\"페르시아어\":\"fa\",\"포르투갈어\":\"pt\",\"폴란드어\":\"pl\",\"프랑스어\":\"fr\",\"핀란드어\":\"fi\",\"한국어\":\"ko\",\"헝가리어\":\"hu\",\"히브리어\":\"he\",\"힌디어\":\"hi\",\"albanese\":\"sq\",\"amarico\":\"am\",\"arabo\":\"ar\",\"armeno\":\"hy\",\"azero\":\"az\",\"bielorusso\":\"be\",\"bulgaro\":\"bg\",\"catalano\":\"ca\",\"ceco\":\"cs\",\"cinese (semplificato)\":\"zh-cn\",\"cinese (tradizionale)\":\"zh-tw\",\"creolo haitiano\":\"ht\",\"croato\":\"hr\",\"curdo (kurmanji)\":\"ku\",\"danese\":\"da\",\"ebraico\":\"he\",\"estone\":\"et\",\"finlandese\":\"fi\",\"francese\":\"fr\",\"gaelico scozzese\":\"gd\",\"galiziano\":\"gl\",\"gallese\":\"cy\",\"giapponese\":\"ja\",\"greco\":\"el\",\"indonesiano\":\"id\",\"inglese\":\"en\",\"irlandese\":\"ga\",\"islandese\":\"is\",\"latino\":\"la\",\"lettone\":\"lv\",\"macedone\":\"mk\",\"malese\":\"ms\",\"mongolo\":\"mn\",\"norvegese\":\"nb\",\"olandese\":\"nl\",\"persiano\":\"fa\",\"polacco\":\"pl\",\"portoghese\":\"pt\",\"rumeno\":\"ro\",\"serbo\":\"sr\",\"slovacco\":\"sk\",\"sloveno\":\"sl\",\"spagnolo\":\"es\",\"svedese\":\"sv\",\"tailandese\":\"th\",\"tedesco\":\"de\",\"ucraino\":\"uk\",\"ungherese\":\"hu\",\"quốc tế ngữ\":\"eo\",\"tiếng ả rập\":\"ar\",\"tiếng albania\":\"sq\",\"tiếng amharic\":\"am\",\"tiếng anh\":\"en\",\"tiếng armenia\":\"hy\",\"tiếng azerbaijan\":\"az\",\"tiếng ba lan\":\"pl\",\"tiếng ba tư\":\"fa\",\"tiếng basque\":\"eu\",\"tiếng belarus\":\"be\",\"tiếng bồ đào nha\":\"pt\",\"tiếng bulgaria\":\"bg\",\"tiếng catalan\":\"ca\",\"tiếng creole ở haiti\":\"ht\",\"tiếng croatia\":\"hr\",\"tiếng do thái\":\"he\",\"tiếng đan mạch\":\"da\",\"tiếng đức\":\"de\",\"tiếng estonia\":\"et\",\"tiếng gael scotland\":\"gd\",\"tiếng galicia\":\"gl\",\"tiếng hà lan\":\"nl\",\"tiếng hàn\":\"ko\",\"tiếng hindi\":\"hi\",\"tiếng hungary\":\"hu\",\"tiếng hy lạp\":\"el\",\"tiếng iceland\":\"is\",\"tiếng indonesia\":\"id\",\"tiếng ireland\":\"ga\",\"tiếng kannada\":\"kn\",\"tiếng kurd\":\"ku\",\"tiếng latinh\":\"la\",\"tiếng latvia\":\"lv\",\"tiếng litva\":\"lt\",\"tiếng mã lai\":\"ms\",\"tiếng macedonia\":\"mk\",\"tiếng malayalam\":\"ml\",\"tiếng malta\":\"mt\",\"tiếng maori\":\"mi\",\"tiếng marathi\":\"mr\",\"tiếng mông cổ\":\"mn\",\"tiếng myanmar\":\"my\",\"tiếng na uy\":\"nb\",\"tiếng nga\":\"ru\",\"tiếng nhật\":\"ja\",\"tiếng pháp\":\"fr\",\"tiếng phần lan\":\"fi\",\"tiếng rumani\":\"ro\",\"tiếng séc\":\"cs\",\"tiếng serbia\":\"sr\",\"tiếng slovak\":\"sk\",\"tiếng slovenia\":\"sl\",\"tiếng tây ban nha\":\"es\",\"tiếng telugu\":\"te\",\"tiếng thái\":\"th\",\"tiếng thổ nhĩ kỳ\":\"tr\",\"tiếng thụy điển\":\"sv\",\"tiếng trung (giản thể)\":\"zh-cn\",\"tiếng trung (phồn thể)\":\"zh-tw\",\"tiếng ukraina\":\"uk\",\"tiếng uzbek\":\"uz\",\"tiếng việt\":\"vi\",\"tiếng xứ wales\":\"cy\",\"tiếng ý\":\"it\",\"tiếng zulu\":\"zu\",\"にほんご\":\"ja\"},\"scratchToGoogleMap\":{\"zh-cn\":\"zh\",\"nb\":\"no\",\"he\":\"iw\",\"es-419\":\"es\",\"pt-br\":\"pt\",\"ja-hira\":\"ja\"},\"previouslySupported\":[\"ab\",\"ms\",\"be\",\"eo\",\"hy\",\"hi\",\"kn\",\"ht\",\"ku\",\"la\",\"mk\",\"ml\",\"mt\",\"mr\",\"mn\",\"my\",\"nn\",\"sq\",\"te\",\"uz\"]}");

/***/ }),

/***/ "./src/scaffolding/storage.js":
/*!************************************!*\
  !*** ./src/scaffolding/storage.js ***!
  \************************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var scratch_storage__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! scratch-storage */ "./node_modules/scratch-storage/src/index.js");
/* harmony import */ var scratch_storage__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(scratch_storage__WEBPACK_IMPORTED_MODULE_0__);


class StorageWithProgress extends scratch_storage__WEBPACK_IMPORTED_MODULE_0___default.a {
  constructor() {
    super();
    this._totalAssets = 0;
    this._loadedAssets = 0;
  }

  _updateProgress() {
    if (this.onprogress) {
      this.onprogress(this._totalAssets, this._loadedAssets);
    }
  }

  load(assetType, asset, assetFormat) {
    const isAsset = assetType === this.AssetType.ImageBitmap || assetType === this.AssetType.ImageVector || assetType === this.AssetType.Sound;

    if (isAsset) {
      this._totalAssets++;

      this._updateProgress();

      return super.load(assetType, asset, assetFormat).then(loadedAsset => {
        this._loadedAssets++;

        this._updateProgress();

        return loadedAsset;
      });
    }

    return super.load(assetType, asset, assetFormat);
  }

}

/* harmony default export */ __webpack_exports__["default"] = (StorageWithProgress);

/***/ }),

/***/ "./src/scaffolding/style.css":
/*!***********************************!*\
  !*** ./src/scaffolding/style.css ***!
  \***********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */ "./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js");
/* harmony import */ var _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_cjs_js_ref_7_1_style_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! !../../node_modules/css-loader/dist/cjs.js??ref--7-1!./style.css */ "./node_modules/css-loader/dist/cjs.js?!./src/scaffolding/style.css");

            

var options = {};

options.insert = (styleElement) => {
                var el = document.head || document.body || document.documentElement;
                el.insertBefore(styleElement, el.firstChild);
              };
options.singleton = false;

var update = _node_modules_style_loader_dist_runtime_injectStylesIntoStyleTag_js__WEBPACK_IMPORTED_MODULE_0___default()(_node_modules_css_loader_dist_cjs_js_ref_7_1_style_css__WEBPACK_IMPORTED_MODULE_1__["default"], options);



/* harmony default export */ __webpack_exports__["default"] = (_node_modules_css_loader_dist_cjs_js_ref_7_1_style_css__WEBPACK_IMPORTED_MODULE_1__["default"].locals || {});

/***/ }),

/***/ "./src/scaffolding/text-encoding/index.js":
/*!************************************************!*\
  !*** ./src/scaffolding/text-encoding/index.js ***!
  \************************************************/
/*! no static exports found */
/***/ (function(module, exports, __webpack_require__) {

__webpack_require__(/*! fastestsmallesttextencoderdecoder */ "./node_modules/fastestsmallesttextencoderdecoder/EncoderDecoderTogether.min.js");

module.exports = {
  TextEncoder,
  TextDecoder
};

/***/ }),

/***/ "./src/scaffolding/verify-value.js":
/*!*****************************************!*\
  !*** ./src/scaffolding/verify-value.js ***!
  \*****************************************/
/*! exports provided: isValidVariableValue, isValidListValue */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isValidVariableValue", function() { return isValidVariableValue; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isValidListValue", function() { return isValidListValue; });
const isValidVariableValue = value => typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean';
const isValidListValue = value => {
  if (!Array.isArray(value)) return false; // Array.prototype.every does not work here because we want to reject arrays with holes eg. new Array(1)

  for (let i = 0; i < value.length; i++) {
    if (!isValidVariableValue(value[i])) return false;
  }

  return true;
};

/***/ }),

/***/ "./src/scaffolding/video.js":
/*!**********************************!*\
  !*** ./src/scaffolding/video.js ***!
  \**********************************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
__webpack_require__.r(__webpack_exports__);
// https://github.com/TurboWarp/scratch-gui/blob/develop/src/lib/video/camera.js
// https://github.com/TurboWarp/scratch-gui/blob/develop/src/lib/video/video-provider.js
const getUserMedia = opts => {
  if (!navigator.mediaDevices) {
    return Promise.reject(new Error('video is not supported in this context (insecure domain?)'));
  }

  return navigator.mediaDevices.getUserMedia(opts);
}; // Single Setup For All Video Streams used by the GUI
// While VideoProvider uses a private _singleSetup
// property to ensure that each instance of a VideoProvider
// use the same setup, this ensures that all instances
// of VideoProviders use a single stream. This way, closing a camera modal
// does not affect the video on the stage, and a program running and disabling
// video on the stage will not affect the camera modal's video.


const requestStack = [];

const requestVideoStream = videoDesc => {
  let streamPromise;

  if (requestStack.length === 0) {
    streamPromise = getUserMedia({
      audio: false,
      video: videoDesc
    });
    requestStack.push(streamPromise);
  } else if (requestStack.length > 0) {
    streamPromise = requestStack[0];
    requestStack.push(true);
  }

  return streamPromise;
};

const requestDisableVideo = () => {
  requestStack.pop();
  if (requestStack.length > 0) return false;
  return true;
};

class VideoProvider {
  constructor() {
    this.mirror = true;
    this._frameCacheTimeout = 16;
    /**
     * DOM Video element
     * @private
     */

    this._video = null;
    /**
     * Usermedia stream track
     * @private
     */

    this._track = null;
    /**
     * Stores some canvas/frame data per resolution/mirror states
     */

    this._workspace = [];
  }

  static get FORMAT_IMAGE_DATA() {
    return 'image-data';
  }

  static get FORMAT_CANVAS() {
    return 'canvas';
  }
  /**
   * Dimensions the video stream is analyzed at after its rendered to the
   * sample canvas.
   * @type {Array.<number>}
   */


  static get DIMENSIONS() {
    return [480, 360];
  }
  /**
   * Order preview drawable is inserted at in the renderer.
   * @type {number}
   */


  static get ORDER() {
    return 1;
  }
  /**
   * Get the HTML video element containing the stream
   */


  get video() {
    return this._video;
  }
  /**
   * Request video be enabled.  Sets up video, creates video skin and enables preview.
   *
   * @return {Promise.<Video>} resolves a promise to this video provider when video is ready.
   */


  enableVideo() {
    this.enabled = true;
    return this._setupVideo();
  }
  /**
   * Disable video stream (turn video off)
   */


  disableVideo() {
    this.enabled = false; // If we have begun a setup process, call _teardown after it completes

    if (this._singleSetup) {
      this._singleSetup.then(this._teardown.bind(this)).catch(err => this.onError(err));
    }
  }
  /**
   * async part of disableVideo
   * @private
   */


  _teardown() {
    // we might be asked to re-enable before _teardown is called, just ignore it.
    if (this.enabled === false) {
      const disableTrack = requestDisableVideo();
      this._singleSetup = null; // by clearing refs to video and track, we should lose our hold over the camera

      this._video = null;

      if (this._track && disableTrack) {
        this._track.stop();
      }

      this._track = null;
    }
  }
  /**
   * Return frame data from the video feed in a specified dimensions, format, and mirroring.
   *
   * @param {object} frameInfo A descriptor of the frame you would like to receive.
   * @param {Array.<number>} frameInfo.dimensions [width, height] array of numbers.  Defaults to [480,360]
   * @param {boolean} frameInfo.mirror If you specificly want a mirror/non-mirror frame, defaults to true
   * @param {string} frameInfo.format Requested video format, available formats are 'image-data' and 'canvas'.
   * @param {number} frameInfo.cacheTimeout Will reuse previous image data if the time since capture is less than
   *                                        the cacheTimeout.  Defaults to 16ms.
   *
   * @return {ArrayBuffer|Canvas|string|null} Frame data in requested format, null when errors.
   */


  getFrame(_ref) {
    let {
      dimensions = VideoProvider.DIMENSIONS,
      mirror = this.mirror,
      format = VideoProvider.FORMAT_IMAGE_DATA,
      cacheTimeout = this._frameCacheTimeout
    } = _ref;

    if (!this.videoReady) {
      return null;
    }

    const [width, height] = dimensions;

    const workspace = this._getWorkspace({
      dimensions,
      mirror: Boolean(mirror)
    });

    const {
      videoWidth,
      videoHeight
    } = this._video;
    const {
      canvas,
      context,
      lastUpdate,
      cacheData
    } = workspace;
    const now = Date.now(); // if the canvas hasn't been updated...

    if (lastUpdate + cacheTimeout < now) {
      if (mirror) {
        context.scale(-1, 1);
        context.translate(width * -1, 0);
      }

      context.drawImage(this._video, // source x, y, width, height
      0, 0, videoWidth, videoHeight, // dest x, y, width, height
      0, 0, width, height); // context.resetTransform() doesn't work on Edge but the following should

      context.setTransform(1, 0, 0, 1, 0, 0);
      workspace.lastUpdate = now;
    } // each data type has it's own data cache, but the canvas is the same


    if (!cacheData[format]) {
      cacheData[format] = {
        lastUpdate: 0
      };
    }

    const formatCache = cacheData[format];

    if (formatCache.lastUpdate + cacheTimeout < now) {
      if (format === VideoProvider.FORMAT_IMAGE_DATA) {
        formatCache.lastData = context.getImageData(0, 0, width, height);
      } else if (format === VideoProvider.FORMAT_CANVAS) {
        // this will never change
        formatCache.lastUpdate = Infinity;
        formatCache.lastData = canvas;
      } else {
        console.error("video io error - unimplemented format ".concat(format)); // cache the null result forever, don't log about it again..

        formatCache.lastUpdate = Infinity;
        formatCache.lastData = null;
      } // rather than set to now, this data is as stale as it's canvas is


      formatCache.lastUpdate = Math.max(workspace.lastUpdate, formatCache.lastUpdate);
    }

    return formatCache.lastData;
  }
  /**
   * Method called when an error happens.  Default implementation is just to log error.
   *
   * @abstract
   * @param {Error} error An error object from getUserMedia or other source of error.
   */


  onError(error) {
    console.error('Unhandled video io device error', error);
  }
  /**
   * Create a video stream.
   * @private
   * @return {Promise} When video has been received, rejected if video is not received
   */


  _setupVideo() {
    // We cache the result of this setup so that we can only ever have a single
    // video/getUserMedia request happen at a time.
    if (this._singleSetup) {
      return this._singleSetup;
    }

    this._singleSetup = requestVideoStream({
      width: {
        min: 480,
        ideal: 640
      },
      height: {
        min: 360,
        ideal: 480
      }
    }).then(stream => {
      this._video = document.createElement('video'); // Use the new srcObject API, falling back to createObjectURL

      try {
        this._video.srcObject = stream;
      } catch (error) {
        this._video.src = window.URL.createObjectURL(stream);
      } // Hint to the stream that it should load. A standard way to do this
      // is add the video tag to the DOM. Since this extension wants to
      // hide the video tag and instead render a sample of the stream into
      // the webgl rendered Scratch canvas, another hint like this one is
      // needed.


      this._video.play(); // Needed for Safari/Firefox, Chrome auto-plays.


      this._track = stream.getTracks()[0];
      return this;
    }).catch(error => {
      this._singleSetup = null;
      this.onError(error);
    });
    return this._singleSetup;
  }

  get videoReady() {
    if (!this.enabled) {
      return false;
    }

    if (!this._video) {
      return false;
    }

    if (!this._track) {
      return false;
    }

    const {
      videoWidth,
      videoHeight
    } = this._video;

    if (typeof videoWidth !== 'number' || typeof videoHeight !== 'number') {
      return false;
    }

    if (videoWidth === 0 || videoHeight === 0) {
      return false;
    }

    return true;
  }
  /**
   * get an internal workspace for canvas/context/caches
   * this uses some document stuff to create a canvas and what not, probably needs abstraction
   * into the renderer layer?
   * @private
   * @return {object} A workspace for canvas/data storage.  Internal format not documented intentionally
   */


  _getWorkspace(_ref2) {
    let {
      dimensions,
      mirror
    } = _ref2;

    let workspace = this._workspace.find(space => space.dimensions.join('-') === dimensions.join('-') && space.mirror === mirror);

    if (!workspace) {
      workspace = {
        dimensions,
        mirror,
        canvas: document.createElement('canvas'),
        lastUpdate: 0,
        cacheData: {}
      };
      workspace.canvas.width = dimensions[0];
      workspace.canvas.height = dimensions[1];
      workspace.context = workspace.canvas.getContext('2d');

      this._workspace.push(workspace);
    }

    return workspace;
  }

}

/* harmony default export */ __webpack_exports__["default"] = (VideoProvider);

/***/ })

/******/ });
//# sourceMappingURL=scaffolding-min.js.map