/******/ (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, { 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 = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = "./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-vm/src/extension-support/extension-worker.js"); /******/ }) /************************************************************************/ /******/ ({ /***/ "./node_modules/babel-loader/lib/index.js?!./node_modules/scratch-vm/src/extension-support/extension-worker.js": /*!*******************************************************************************************************************!*\ !*** ./node_modules/babel-loader/lib??ref--4!./node_modules/scratch-vm/src/extension-support/extension-worker.js ***! \*******************************************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { /* WEBPACK VAR INJECTION */(function(global) {/* eslint-env worker */ 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 dispatch = __webpack_require__(/*! ../dispatch/worker-dispatch */ "./node_modules/scratch-vm/src/dispatch/worker-dispatch.js"); const log = __webpack_require__(/*! ../util/log */ "./node_modules/scratch-vm/src/util/log.js"); const TargetType = __webpack_require__(/*! ../extension-support/target-type */ "./node_modules/scratch-vm/src/extension-support/target-type.js"); const { isWorker } = __webpack_require__(/*! ./tw-extension-worker-context */ "./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js"); const loadScripts = url => { if (isWorker) { importScripts(url); } else { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.onload = () => resolve(); script.onerror = () => { reject(new Error("Error in sandboxed script: ".concat(url, ". Check the console for more information."))); }; script.src = url; document.body.appendChild(script); }); } }; class ExtensionWorker { constructor() { this.nextExtensionId = 0; this.initialRegistrations = []; this.firstRegistrationPromise = new Promise(resolve => { this.firstRegistrationCallback = resolve; }); dispatch.waitForConnection.then(() => { dispatch.call('extensions', 'allocateWorker').then(async x => { const [id, extension] = x; this.workerId = id; try { await loadScripts(extension); await this.firstRegistrationPromise; const initialRegistrations = this.initialRegistrations; this.initialRegistrations = null; Promise.all(initialRegistrations).then(() => dispatch.call('extensions', 'onWorkerInit', id)); } catch (e) { log.error(e); dispatch.call('extensions', 'onWorkerInit', id, "".concat(e)); } }); }); this.extensions = []; } register(extensionObject) { const extensionId = this.nextExtensionId++; this.extensions.push(extensionObject); const serviceName = "extension.".concat(this.workerId, ".").concat(extensionId); const promise = dispatch.setService(serviceName, extensionObject).then(() => dispatch.call('extensions', 'registerExtensionService', serviceName)); if (this.initialRegistrations) { this.firstRegistrationCallback(); this.initialRegistrations.push(promise); } return promise; } } global.Scratch = global.Scratch || {}; global.Scratch.ArgumentType = ArgumentType; global.Scratch.BlockType = BlockType; global.Scratch.TargetType = TargetType; /** * Expose only specific parts of the worker to extensions. */ const extensionWorker = new ExtensionWorker(); global.Scratch.extensions = { register: extensionWorker.register.bind(extensionWorker) }; global.ScratchExtensions = __webpack_require__(/*! ./tw-scratchx-compatibility-layer */ "./node_modules/scratch-vm/src/extension-support/tw-scratchx-compatibility-layer.js"); /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js"))) /***/ }), /***/ "./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/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.} */ 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, ...args) { 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, ...args) { try { const { provider, isRemote } = this._getServiceProvider(service); if (provider) { 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, ...args) { 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, ...args) { 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/dispatch/worker-dispatch.js": /*!*****************************************************************!*\ !*** ./node_modules/scratch-vm/src/dispatch/worker-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"); const { centralDispatchService } = __webpack_require__(/*! ../extension-support/tw-extension-worker-context */ "./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js"); /** * This class provides a Worker with the means to participate in the message dispatch system managed by CentralDispatch. * 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 {CentralDispatch} */ class WorkerDispatch extends SharedDispatch { constructor() { super(); /** * This promise will be resolved when we have successfully connected to central dispatch. * @type {Promise} * @see {waitForConnection} * @private */ this._connectionPromise = new Promise(resolve => { this._onConnect = resolve; }); /** * Map of service name to local service provider. * If a service is not listed here, it is assumed to be provided by another context (another Worker or the main * thread). * @see {setService} * @type {object} */ this.services = {}; this._onMessage = this._onMessage.bind(this, centralDispatchService); if (typeof self !== 'undefined') { self.onmessage = this._onMessage; } } /** * @returns {Promise} a promise which will resolve upon connection to central dispatch. If you need to make a call * immediately on "startup" you can attach a 'then' to this promise. * @example * dispatch.waitForConnection.then(() => { * dispatch.call('myService', 'hello'); * }) */ get waitForConnection() { return this._connectionPromise; } /** * 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) { if (this.services.hasOwnProperty(service)) { log.warn("Worker dispatch replacing existing service provider for ".concat(service)); } this.services[service] = provider; return this.waitForConnection.then(() => this._remoteCall(centralDispatchService, 'dispatch', 'setService', service)); } /** * 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) { // if we don't have a local service by this name, contact central dispatch by calling `postMessage` on self const provider = this.services[service]; return { provider: provider || centralDispatchService, isRemote: !provider }; } /** * 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 'handshake': promise = this._onConnect(); break; case 'terminate': // Don't close until next tick, after sending confirmation back setTimeout(() => self.close(), 0); promise = Promise.resolve(); break; default: log.error("Worker dispatch received message for unknown method: ".concat(message.method)); } return promise; } } module.exports = new WorkerDispatch(); /***/ }), /***/ "./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/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-worker-context.js": /*!**************************************************************************************!*\ !*** ./node_modules/scratch-vm/src/extension-support/tw-extension-worker-context.js ***! \**************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports) { module.exports = { isWorker: true, // centralDispatchService is the object to call postMessage() on to send a message to parent. centralDispatchService: self }; /***/ }), /***/ "./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} [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 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/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/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; /***/ }) /******/ }); //# sourceMappingURL=extension-worker.6862f7d9a00ef5285990.js.map