(window["webpackJsonpGUI"] = window["webpackJsonpGUI"] || []).push([["addon-default-entry"],{ /***/ "./node_modules/css-loader/index.js!./src/addons/addons/color-picker/style.css": /*!****************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/color-picker/style.css ***! \****************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".sa-color-picker {\n display: flex;\n}\n\n.sa-color-picker-code {\n margin: 8px 0;\n}\n\n.sa-color-picker-paint {\n margin-top: 16px;\n margin-bottom: 4px;\n}\n\n.sa-color-picker > .sa-color-picker-color {\n border: none;\n border-top-left-radius: 1rem;\n border-bottom-left-radius: 1rem;\n padding: 0;\n padding-left: 0.6rem;\n padding-right: 0.4rem;\n margin-left: 0.5rem;\n outline: none;\n box-sizing: border-box;\n width: 3rem;\n height: 2rem;\n}\n[theme=\"dark\"] .sa-color-picker > .sa-color-picker-color {\n background: var(--ui-secondary);\n}\n\n.sa-color-picker > .sa-color-picker-text {\n box-sizing: border-box;\n width: calc(150px - 3rem);\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n}\n\n[dir=\"rtl\"] .sa-color-picker > .sa-color-picker-color {\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n border-top-right-radius: 1rem;\n border-bottom-right-radius: 1rem;\n margin-left: 0;\n margin-right: 0.5rem;\n}\n\n[dir=\"rtl\"] .sa-color-picker > .sa-color-picker-text {\n border-top-left-radius: 1rem;\n border-bottom-left-radius: 1rem;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n}\n\nbody.sa-hide-eye-dropper-background div[class*=\"stage_color-picker-background\"] {\n /* Do not show eye dropper background if the color picker is \"fake\" */\n display: none;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/editor-comment-previews/userstyle.css": /*!*******************************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/editor-comment-previews/userstyle.css ***! \*******************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".sa-comment-preview-outer {\n position: fixed;\n top: 0;\n left: 0;\n z-index: 100000000;\n pointer-events: none;\n}\n\n.sa-comment-preview-inner {\n width: calc(200px - 16px);\n max-height: calc(132px - 8px);\n padding: 8px;\n overflow: hidden;\n\n font-size: 12px;\n white-space: pre-wrap;\n pointer-events: none;\n\n color: rgb(87, 94, 117);\n background-color: rgb(255 255 255 / 90%);\n border-style: none;\n border-radius: 8px;\n filter: drop-shadow(0px 5px 5px rgb(0 0 0 / 10%));\n\n transform: perspective(200px);\n}\n\n@supports (backdrop-filter: blur(16px)) {\n .sa-comment-preview-inner {\n background-color: rgb(255 255 255 / 75%);\n backdrop-filter: blur(16px);\n }\n}\n\n.sa-comment-preview-fade {\n transition: opacity 0.1s, filter 0.1s, transform 0.1s linear;\n}\n\n.sa-comment-preview-hidden {\n opacity: 0;\n filter: none;\n transform: perspective(200px) translateZ(-20px);\n}\n\n.sa-comment-preview-reduce-transparency {\n background-color: rgb(255 255 255);\n backdrop-filter: none;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/editor-searchable-dropdowns/userscript.css": /*!************************************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/editor-searchable-dropdowns/userscript.css ***! \************************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".u-dropdown-searchbar {\n width: 100%;\n box-sizing: border-box;\n /* based on styles for the title input */\n color: white;\n background-color: hsla(0, 100%, 100%, 0.25);\n border: 1px solid hsla(0, 0%, 0%, 0.15);\n padding: 0.5rem;\n outline: none;\n transition: 0.25s ease-out;\n font-size: 13px;\n font-weight: bold;\n border-radius: 4px;\n}\n.u-dropdown-searchbar:hover {\n background-color: hsla(0, 100%, 100%, 0.5);\n}\n.u-dropdown-searchbar:focus {\n background-color: white;\n color: black;\n}\n.blocklyDropDownDiv .goog-menu {\n overflow-x: hidden;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/editor-theme3/compatibility.css": /*!*************************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/editor-theme3/compatibility.css ***! \*************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, "/* Imported by other addons */\n\n.sa-block-color {\n --sa-block-colored-background: var(--sa-block-background-primary);\n --sa-block-colored-background-secondary: var(--sa-block-field-background);\n --sa-block-bright-background: var(--sa-block-background-primary);\n --sa-block-text: white;\n --sa-block-gray-text: white;\n --sa-block-colored-text: var(--sa-block-background-primary);\n --sa-block-text-on-bright-background: white;\n}\n\n.sa-block-color-motion {\n --sa-block-background-primary: var(--editorTheme3-motion-primary, #4c97ff);\n --sa-block-background-secondary: var(--editorTheme3-motion-secondary, #4280d7);\n --sa-block-background-tertiary: var(--editorTheme3-motion-tertiary, #3373cc);\n --sa-block-field-background: var(--editorTheme3-motion-field, #3373cc);\n}\n\n.sa-block-color-looks {\n --sa-block-background-primary: var(--editorTheme3-looks-primary, #9966ff);\n --sa-block-background-secondary: var(--editorTheme3-looks-secondary, #855cd6);\n --sa-block-background-tertiary: var(--editorTheme3-looks-tertiary, #774dcb);\n --sa-block-field-background: var(--editorTheme3-looks-field, #774dcb);\n}\n\n.sa-block-color-sounds {\n --sa-block-background-primary: var(--editorTheme3-sounds-primary, #cf63cf);\n --sa-block-background-secondary: var(--editorTheme3-sounds-secondary, #c94fc9);\n --sa-block-background-tertiary: var(--editorTheme3-sounds-tertiary, #bd42bd);\n --sa-block-field-background: var(--editorTheme3-sounds-field, #bd42bd);\n}\n\n.sa-block-color-events {\n --sa-block-background-primary: var(--editorTheme3-event-primary, #ffbf00);\n --sa-block-background-secondary: var(--editorTheme3-event-secondary, #e6ac00);\n --sa-block-background-tertiary: var(--editorTheme3-event-tertiary, #cc9900);\n --sa-block-field-background: var(--editorTheme3-event-field, #cc9900);\n}\n\n.sa-block-color-control {\n --sa-block-background-primary: var(--editorTheme3-control-primary, #ffab19);\n --sa-block-background-secondary: var(--editorTheme3-control-secondary, #ec9c13);\n --sa-block-background-tertiary: var(--editorTheme3-control-tertiary, #cf8b17);\n --sa-block-field-background: var(--editorTheme3-control-field, #cf8b17);\n}\n\n.sa-block-color-sensing {\n --sa-block-background-primary: var(--editorTheme3-sensing-primary, #5cb1d6);\n --sa-block-background-secondary: var(--editorTheme3-sensing-secondary, #47a8d1);\n --sa-block-background-tertiary: var(--editorTheme3-sensing-tertiary, #2e8eb8);\n --sa-block-field-background: var(--editorTheme3-sensing-field, #2e8eb8);\n}\n\n.sa-block-color-operators {\n --sa-block-background-primary: var(--editorTheme3-operators-primary, #59c059);\n --sa-block-background-secondary: var(--editorTheme3-operators-secondary, #46b946);\n --sa-block-background-tertiary: var(--editorTheme3-operators-tertiary, #389438);\n --sa-block-field-background: var(--editorTheme3-operators-field, #389438);\n}\n\n.sa-block-color-data {\n --sa-block-background-primary: var(--editorTheme3-data-primary, #ff8c1a);\n --sa-block-background-secondary: var(--editorTheme3-data-secondary, #ff8000);\n --sa-block-background-tertiary: var(--editorTheme3-data-tertiary, #db6e00);\n --sa-block-field-background: var(--editorTheme3-data-field, #db6e00);\n}\n\n.sa-block-color-data-lists,\n.sa-block-color-list {\n --sa-block-background-primary: var(--editorTheme3-data_lists-primary, #ff661a);\n --sa-block-background-secondary: var(--editorTheme3-data_lists-secondary, #ff5500);\n --sa-block-background-tertiary: var(--editorTheme3-data_lists-tertiary, #e64d00);\n --sa-block-field-background: var(--editorTheme3-data_lists-field, #e64d00);\n}\n\n.sa-block-color-more,\n.sa-block-color-null {\n --sa-block-background-primary: var(--editorTheme3-more-primary, #ff6680);\n --sa-block-background-secondary: var(--editorTheme3-more-secondary, #ff4d6a);\n --sa-block-background-tertiary: var(--editorTheme3-more-tertiary, #ff3355);\n --sa-block-field-background: var(--editorTheme3-more-field, #ff3355);\n}\n\n.sa-block-color-pen {\n --sa-block-background-primary: var(--editorTheme3-pen-primary, #0fbd8c);\n --sa-block-background-secondary: var(--editorTheme3-pen-secondary, #0da57a);\n --sa-block-background-tertiary: var(--editorTheme3-pen-tertiary, #0b8e69);\n --sa-block-field-background: var(--editorTheme3-pen-field, #0b8e69);\n}\n\n.sa-block-color-addon-custom-block {\n --sa-block-background-primary: var(--editorTheme3-sa-primary, #29beb8);\n --sa-block-background-secondary: var(--editorTheme3-sa-secondary, #3aa8a4);\n --sa-block-background-tertiary: var(--editorTheme3-sa-tertiary, #3aa8a4);\n --sa-block-field-background: var(--editorTheme3-sa-field, #3aa8a4);\n}\n\n.sa-block-color-TurboWarp {\n --sa-block-background-primary: var(--editorTheme3-tw-primary, #ff4c4c);\n --sa-block-background-secondary: var(--editorTheme3-tw-secondary, #e64444);\n --sa-block-background-tertiary: var(--editorTheme3-tw-tertiary, #e64444);\n --sa-block-field-background: var(--editorTheme3-tw-field, #e64444);\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/find-bar/userstyle.css": /*!****************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/find-bar/userstyle.css ***! \****************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports exports.i(__webpack_require__(/*! -!../../../../node_modules/css-loader!../editor-theme3/compatibility.css */ "./node_modules/css-loader/index.js!./src/addons/addons/editor-theme3/compatibility.css"), ""); // module exports.push([module.i, ".sa-find-bar {\n display: flex;\n align-items: center;\n white-space: nowrap;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n width: 100%;\n height: 100%;\n margin-left: 1em;\n}\n.sa-find-bar[hidden] {\n /* !important to override displayNoneWhileDisabled */\n display: none !important;\n}\n\n.sa-find-wrapper {\n overflow: visible;\n position: relative;\n height: 2rem;\n width: 100%;\n max-width: 16em;\n}\n\n.sa-find-dropdown-out {\n display: block;\n top: -6px;\n z-index: 100;\n width: 100%;\n max-width: 16em;\n position: relative;\n padding: 4px;\n border: none;\n border-radius: 4px;\n margin-top: 6px;\n}\n\n.sa-find-dropdown-out.visible {\n position: absolute;\n width: 16em;\n box-shadow: 0px 0px 8px 1px var(--ui-black-transparent, rgba(0, 0, 0, 0.3));\n background-color: var(--ui-primary, white);\n}\n\n/* We need to modify Scratch styles so that the place where the find bar is injected */\n/* has actually correct size information, which is used to make the find bar not cover up controls */\n[class*=\"gui_tab-list_\"] {\n width: 100%;\n}\n[class*=\"gui_tab_\"] {\n flex-grow: 0;\n}\n\n.sa-find-input {\n width: 100%;\n box-sizing: border-box !important;\n /* !important required for extension, because CSS injection method (and hence order) differs from addon */\n height: 1.5rem;\n\n /* Change Scratch default styles */\n border-radius: 0.25rem;\n font-size: 0.75rem;\n padding-left: 0.4em;\n}\n\n.sa-find-input:focus {\n /* Change Scratch default styles */\n box-shadow: none;\n}\n\n.sa-find-dropdown {\n display: none;\n position: relative;\n padding: 0.2em 0;\n font-size: 0.75rem;\n line-height: 1;\n overflow-y: auto;\n min-height: 128px;\n max-height: 65vh;\n user-select: none;\n max-width: 100%;\n margin-top: 6px;\n border: none;\n}\n\n.sa-find-dropdown-out.visible > .sa-find-dropdown {\n display: block;\n}\n\n.sa-find-dropdown > li {\n display: block;\n padding: 0.5em 0.3em;\n white-space: nowrap;\n margin: 0;\n font-weight: bold;\n text-overflow: ellipsis;\n overflow: hidden;\n}\n\n.sa-find-dropdown > li > b {\n background-color: #aaffaa;\n color: black;\n}\n\n/* Drop down items */\n.sa-find-dropdown > li:hover,\n.sa-find-dropdown > li.sel {\n color: var(--sa-block-text-on-bright-background);\n cursor: pointer;\n}\n\n.sa-find-dropdown > li::before {\n content: \"\\25CF \"; /* ● */\n}\n\n.sa-find-flag {\n color: #4cbf56;\n}\n/* .sa-find-dropdown added for specificity */\n.sa-find-dropdown > .sa-find-flag:hover,\n.sa-find-dropdown > .sa-find-flag.sel {\n background-color: #4cbf56;\n color: white;\n}\n\n.sa-find-dropdown .sa-block-color {\n color: var(--sa-block-colored-text);\n}\n.sa-find-dropdown .sa-block-color:hover,\n.sa-find-dropdown .sa-block-color.sel {\n background-color: var(--sa-block-bright-background);\n}\n\n.sa-find-carousel {\n font-weight: normal;\n position: absolute;\n right: 0;\n white-space: nowrap;\n background-color: inherit;\n z-index: 1;\n padding: 0;\n}\n\n.sa-find-carousel-control {\n padding: 0 6px;\n}\n\n.sa-find-carousel-control:hover {\n color: #ffff80;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/folders/style.css": /*!***********************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/folders/style.css ***! \***********************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".sa-folders-contextmenu-item {\n max-width: 250px;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n[sa-folders-context-type=\"folder\"] .react-contextmenu > :not(.sa-ctx-menu) {\n display: none;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/middle-click-popup/userstyle.css": /*!**************************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/middle-click-popup/userstyle.css ***! \**************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports exports.i(__webpack_require__(/*! -!../../../../node_modules/css-loader!../editor-theme3/compatibility.css */ "./node_modules/css-loader/index.js!./src/addons/addons/editor-theme3/compatibility.css"), ""); // module exports.push([module.i, "/* Find Input Box */\n.sa-float-bar-input {\n width: 100%;\n box-sizing: border-box !important;\n /* !important required for extension, because CSS injection method (and hence order) differs from addon */\n height: 1.5rem;\n\n /* Change Scratch default styles */\n border-radius: 0.25rem;\n font-size: 0.75rem;\n padding-left: 0.4em;\n}\n[theme=\"dark\"] input.s3devInp {\n color: #eee;\n background: #3333;\n}\n[theme=\"dark\"] input.s3devInp:hover {\n background: #333;\n}\n\n.sa-float-bar-input:focus {\n /* Change Scratch default styles */\n box-shadow: none;\n}\n\n/* Drop down from find button */\n.sa-float-bar-dropdown-out {\n display: block;\n top: -6px;\n z-index: 100;\n max-width: 16em;\n padding: 4px;\n position: absolute;\n width: 16em;\n box-shadow: 0px 0px 8px 1px var(--ui-black-transparent, rgba(0, 0, 0, 0.3));\n background-color: var(--ui-primary, white);\n border: none;\n border-radius: 4px;\n}\n\n/* Drop down from find button */\n.sa-float-bar-dropdown {\n display: none;\n position: relative;\n padding: 0.2em 0;\n font-size: 0.75rem;\n line-height: 1;\n overflow-y: auto;\n min-height: 128px;\n user-select: none;\n max-width: 100%;\n max-height: 200px;\n margin-bottom: 0;\n}\n\n.sa-float-bar-dropdown-out.vis .sa-float-bar-dropdown {\n display: block;\n border: none;\n}\n\n/* Drop down items */\n.sa-float-bar-dropdown > li {\n display: block;\n padding: 0.5em 0.3em;\n white-space: nowrap;\n margin: 0;\n font-weight: bold;\n text-overflow: ellipsis;\n overflow: hidden;\n cursor: pointer;\n}\n\n.sa-float-bar-dropdown > li > b {\n background-color: #aaffaa;\n color: black;\n}\n\n.sa-float-bar-dropdown > li {\n height: 19px;\n padding: 3px 8px;\n margin: 2px 0.3em;\n box-sizing: border-box;\n position: relative;\n background-color: var(--sa-block-colored-background);\n color: var(--sa-block-text);\n font-weight: bold;\n width: min-content;\n}\n.sa-float-bar-dropdown > li:hover,\n.sa-float-bar-dropdown > li.sel {\n background-color: var(--sa-block-colored-background-secondary);\n}\n\n.sa-float-bar-dropdown > li.sa-hat {\n border-radius: 14px 14px 3px 3px;\n}\n.sa-float-bar-dropdown > li.sa-block {\n border-radius: 3px;\n}\n.sa-float-bar-dropdown > li.sa-reporter {\n border-radius: 10px;\n}\n\n.sa-float-bar-dropdown > li.sa-boolean {\n width: min-content;\n}\n.sa-float-bar-dropdown > li.sa-boolean::before {\n content: \"\";\n position: absolute;\n left: 0;\n top: 0;\n width: 0;\n height: 0;\n border-right: 9px solid transparent;\n border-top: 9px solid var(--ui-primary, white);\n border-bottom: 10px solid var(--ui-primary, white);\n}\n.sa-float-bar-dropdown > li.sa-boolean::after {\n content: \"\";\n position: absolute;\n right: 0;\n top: 0;\n width: 0;\n height: 0;\n border-left: 9px solid transparent;\n border-top: 9px solid var(--ui-primary, white);\n border-bottom: 10px solid var(--ui-primary, white);\n}\n[theme=\"dark\"] .s3devDD > li.boolean::before {\n border-top-color: #111;\n border-bottom-color: #111;\n}\n[theme=\"dark\"] .s3devDD > li.boolean::after {\n border-top-color: #111;\n border-bottom-color: #111;\n}\n\n.sa-float-bar {\n display: flex;\n white-space: nowrap;\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n\n position: absolute;\n min-width: 128px;\n background-color: white;\n border-radius: 4px;\n box-shadow: rgba(0, 0, 0, 0.3) 0 0 3px, rgba(0, 0, 0, 0.2) 0 3px 10px;\n\n z-index: 999;\n}\n[theme=\"dark\"] #s3devFloatingBar {\n background-color: #111;\n}\n\n.sa-float-bar-dropdown > li > b {\n background-color: rgba(0, 0, 0, 0.6);\n color: white;\n}\n\n[data-highlighted=\"true\"] {\n background-color: hsla(30, 100%, 55%, 1) !important; /* orange */\n color: white !important;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/onion-skinning/style.css": /*!******************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/onion-skinning/style.css ***! \******************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".sa-onion-button {\n position: relative;\n}\n.sa-onion-button:focus-within {\n background-color: hsla(0, 100%, 65%, 0.2);\n}\n[theme=\"dark\"] .sa-onion-image {\n filter: brightness(0) invert(0.8);\n}\n.sa-onion-button[data-enabled=\"true\"] .sa-onion-image {\n filter: brightness(0) invert(1);\n}\n.sa-onion-button[data-enabled=\"true\"] {\n background-color: #ff4c4c;\n}\n\n.sa-onion-group {\n position: relative;\n flex-direction: row;\n}\n\n.sa-onion-settings-wrapper {\n position: absolute;\n justify-items: center;\n left: 50%;\n width: 1.95rem;\n height: 1.95rem;\n display: grid;\n}\n\n.sa-onion-settings {\n position: absolute;\n bottom: 100%;\n /* based on the styles for the color dropdown */\n padding: 4px;\n border-radius: 4px;\n border: 1px solid var(--paint-ui-pane-border, #ddd);\n box-shadow: 0px 0px 8px 1px rgba(0, 0, 0, 0.3);\n transition-property: bottom, opacity;\n transition-duration: 500ms;\n transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);\n opacity: 0;\n pointer-events: none;\n background: var(--ui-primary, white);\n min-height: 100%;\n min-width: 100%;\n display: flex;\n flex-direction: column;\n gap: 0.25em;\n}\n.sa-onion-settings[data-visible=\"true\"] {\n bottom: calc(100% + 22px);\n pointer-events: auto;\n opacity: 1;\n}\n\n.sa-onion-settings-line {\n display: flex;\n justify-content: flex-end;\n align-items: baseline;\n gap: 0.25em;\n}\n\n.sa-onion-settings-input {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n text-align: center;\n border: 0;\n background: transparent;\n -moz-appearance: textfield;\n border: 0;\n outline: 0;\n}\n\n.sa-onion-settings-input::-webkit-outer-spin-button,\n.sa-onion-settings-input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n\n.sa-onion-settings-tip {\n position: absolute;\n bottom: 0;\n transform: translateY(100%);\n right: calc(50% - 7px);\n}\n.sa-onion-settings-polygon {\n fill: var(--ui-primary, white);\n stroke: var(--paint-ui-pane-border, #ddd);\n}\n\n.sa-onion-settings-label {\n white-space: nowrap;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/css-loader/index.js!./src/addons/addons/pick-colors-from-stage/style.css": /*!**************************************************************************************!*\ !*** ./node_modules/css-loader!./src/addons/addons/pick-colors-from-stage/style.css ***! \**************************************************************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false); // imports // module exports.push([module.i, ".sa-stage-color-picker-picking [class^=\"stage_color-picker-background_\"] {\n display: none;\n}\n", ""]); // exports /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/editor-devtools/icon--close.svg": /*!*************************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/editor-devtools/icon--close.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,PHN2ZyBpZD0iTGF5ZXJfMSIgZGF0YS1uYW1lPSJMYXllciAxIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA3LjQ4IDcuNDgiPjxkZWZzPjxzdHlsZT4uY2xzLTF7ZmlsbDpub25lO3N0cm9rZTojZmZmO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utd2lkdGg6MnB4O308L3N0eWxlPjwvZGVmcz48dGl0bGU+aWNvbi0tYWRkPC90aXRsZT48bGluZSBjbGFzcz0iY2xzLTEiIHgxPSIzLjc0IiB5MT0iNi40OCIgeDI9IjMuNzQiIHkyPSIxIi8+PGxpbmUgY2xhc3M9ImNscy0xIiB4MT0iMSIgeTE9IjMuNzQiIHgyPSI2LjQ4IiB5Mj0iMy43NCIvPjwvc3ZnPg=="); /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/folders/folder.svg": /*!************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/folders/folder.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,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTAgMGgyNHYyNEgweiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0xMCA0SDRjLTEuMSAwLTEuOTkuOS0xLjk5IDJMMiAxOGMwIDEuMS45IDIgMiAyaDE2YzEuMSAwIDItLjkgMi0yVjhjMC0xLjEtLjktMi0yLTJoLThsLTItMnoiLz48L3N2Zz4="); /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/decrement.svg": /*!**********************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/decrement.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,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCI+PHBhdGggc3R5bGU9ImZpbGw6IzU3NWU3NTtzdHJva2Utd2lkdGg6LjczNDczNiIgZD0iTTMuMjUgMTEuMzU4aDE3LjUwMXYxLjI4NUgzLjI1eiIvPjwvc3ZnPg=="); /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/increment.svg": /*!**********************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/increment.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,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggc3R5bGU9ImZpbGw6IzU3NWU3NTtzdHJva2Utd2lkdGg6LjczNDczNiIgZD0iTTMuMjUgMTEuMzU4aDE3LjUwMXYxLjI4NUgzLjI1eiIvPjxwYXRoIHRyYW5zZm9ybT0icm90YXRlKDkwKSIgc3R5bGU9ImZpbGw6IzU3NWU3NTtzdHJva2Utd2lkdGg6LjczNDczNiIgZD0iTTMuMjUtMTIuNjQyaDE3LjUwMXYxLjI4NUgzLjI1eiIvPjwvc3ZnPg=="); /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/settings.svg": /*!*********************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/settings.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,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCI+PHBhdGggc3R5bGU9Im9wYWNpdHk6Ljc1O2ZpbGw6bm9uZTtzdHJva2U6IzAwMDtzdHJva2Utd2lkdGg6NDtzdHJva2UtbGluZWNhcDpyb3VuZDtzdHJva2UtbGluZWpvaW46cm91bmQ7c3Ryb2tlLW1pdGVybGltaXQ6NDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLW9wYWNpdHk6MSIgZD0iTTU2IDE2djk2IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMi4zNzYgLTIuMzc2KSBzY2FsZSguMjI0NjIpIi8+PHBhdGggc3R5bGU9Im9wYWNpdHk6LjU7ZmlsbDpub25lO3N0cm9rZTojMDAwO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJNNDAgMzJ2NjQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjM3NiAtMi4zNzYpIHNjYWxlKC4yMjQ2MikiLz48cGF0aCBzdHlsZT0ib3BhY2l0eTouMjU7ZmlsbDpub25lO3N0cm9rZTojMDAwO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjptaXRlcjtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJNMjQgNDh2MzIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjM3NiAtMi4zNzYpIHNjYWxlKC4yMjQ2MikiLz48cGF0aCBzdHlsZT0ib3BhY2l0eTouNzU7ZmlsbDpub25lO3N0cm9rZTojMDAwO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDo0O3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2Utb3BhY2l0eToxIiBkPSJNNzIgMTZ2OTYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjM3NiAtMi4zNzYpIHNjYWxlKC4yMjQ2MikiLz48cGF0aCBzdHlsZT0ib3BhY2l0eTouNTtmaWxsOm5vbmU7c3Ryb2tlOiMwMDA7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIGQ9Ik04OCAzMnY2NCIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIuMzc2IC0yLjM3Nikgc2NhbGUoLjIyNDYyKSIvPjxwYXRoIHN0eWxlPSJvcGFjaXR5Oi4yNTtmaWxsOm5vbmU7c3Ryb2tlOiMwMDA7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLWxpbmVjYXA6cm91bmQ7c3Ryb2tlLWxpbmVqb2luOm1pdGVyO3N0cm9rZS1taXRlcmxpbWl0OjQ7c3Ryb2tlLWRhc2hhcnJheTpub25lO3N0cm9rZS1vcGFjaXR5OjEiIGQ9Ik0xMDQgNDh2MzIiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjM3NiAtMi4zNzYpIHNjYWxlKC4yMjQ2MikiLz48L3N2Zz4="); /***/ }), /***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/toggle.svg": /*!*******************************************************************************************!*\ !*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/onion-skinning/toggle.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,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCI+PGcgc3Ryb2tlLXdpZHRoPSIxLjIiIHN0cm9rZT0iIzAwMTAyNiI+PGcgc3R5bGU9Im9wYWNpdHk6LjU7c3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lIj48cGF0aCBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6bm9ybWFsO2ZpbGw6bm9uZTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MCIgZmlsbD0iI2JmYmZiZiIgZD0iTTY4LjQ1IDMyLjQ1N2MuMy0uMi44LS4xLjkuM2wyLjYgMTAuN3M2LjQgNC43IDguMyA4YzMuMiA1LjUgMy4zIDEwIDMuMyAxMHM3LjEgMi4xIDguMyA3LjhjMS4yIDUuNy0zLjIgMTYuNS0yMiAyMC4yLTE4LjggMy43LTMzLjktMS40LTQxLTEyLjgtNy4xLTExLjQgNC4xLTI1IDMuNS0yNC4ybC0yLjEtMTcuOWMtLjEtLjQuNC0uNy44LS41bDEyLjEgNy45czQuNS0xLjcgOS4yLTEuOWMyLjgtLjIgNS4yIDAgNy41LjR6IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtNC42NTIgLTQuNjUpIHNjYWxlKC4yMzI5KSIvPjxwYXRoIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTpub3JtYWw7ZmlsbDpub25lO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIGQ9Ik0xMDEuNjUgNjIuOTU3Yy00LjcgMy44LTExLjkgMy45LTExLjkgMy45bTEwLjYgNy4zYy02LjMuNS0xMC4yLTEuNC0xMC4yLTEuNG0tNjguOC0xMC4xczguNiAyLjggMTIuMSA1LjltLjMgMy41Yy00LjMgMS43LTExLjcuNi0xMS43LjYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC00LjY1MiAtNC42NSkgc2NhbGUoLjIzMjkpIi8+PC9nPjxnIHN0eWxlPSJvcGFjaXR5Oi43NTtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmUiPjxwYXRoIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTpub3JtYWw7ZmlsbDpub25lO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowIiBmaWxsPSIjYmZiZmJmIiBkPSJNNjguNDUgMzIuNDU3Yy4zLS4yLjgtLjEuOS4zbDIuNiAxMC43czYuNCA0LjcgOC4zIDhjMy4yIDUuNSAzLjMgMTAgMy4zIDEwczcuMSAyLjEgOC4zIDcuOGMxLjIgNS43LTMuMiAxNi41LTIyIDIwLjItMTguOCAzLjctMzMuOS0xLjQtNDEtMTIuOC03LjEtMTEuNCA0LjEtMjUgMy41LTI0LjJsLTIuMS0xNy45Yy0uMS0uNC40LS43LjgtLjVsMTIuMSA3LjlzNC41LTEuNyA5LjItMS45YzIuOC0uMiA1LjIgMCA3LjUuNHoiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjMyMyAtMi4zMjEpIHNjYWxlKC4yMzI5KSIvPjxwYXRoIHN0eWxlPSJtaXgtYmxlbmQtbW9kZTpub3JtYWw7ZmlsbDpub25lO2ZpbGwtcnVsZTpldmVub2RkO3N0cm9rZS13aWR0aDo0O3N0cm9rZS1taXRlcmxpbWl0OjEwO3N0cm9rZS1kYXNoYXJyYXk6bm9uZTtzdHJva2UtZGFzaG9mZnNldDowIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiIGQ9Ik0xMDEuNjUgNjIuOTU3Yy00LjcgMy44LTExLjkgMy45LTExLjkgMy45bTEwLjYgNy4zYy02LjMuNS0xMC4yLTEuNC0xMC4yLTEuNG0tNjguOC0xMC4xczguNiAyLjggMTIuMSA1LjltLjMgMy41Yy00LjMgMS43LTExLjcuNi0xMS43LjYiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yLjMyMyAtMi4zMjEpIHNjYWxlKC4yMzI5KSIvPjwvZz48ZyBzdHlsZT0ic3Ryb2tlLXdpZHRoOjQ7c3Ryb2tlLW1pdGVybGltaXQ6MTA7c3Ryb2tlLWRhc2hhcnJheTpub25lIj48cGF0aCBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6bm9ybWFsO2ZpbGw6I2ZmZjtmaWxsLW9wYWNpdHk6MTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MCIgZmlsbD0iI2JmYmZiZiIgZD0iTTczLjQ1IDM3LjQ1N2MuMy0uMi44LS4xLjkuM2wyLjYgMTAuN3M2LjQgNC43IDguMyA4YzMuMiA1LjUgMy4zIDEwIDMuMyAxMHM3LjEgMi4xIDguMyA3LjhjMS4yIDUuNy0zLjIgMTYuNS0yMiAyMC4yLTE4LjggMy43LTMzLjktMS40LTQxLTEyLjgtNy4xLTExLjQgNC4xLTI1IDMuNS0yNC4ybC0yLjEtMTcuOWMtLjEtLjQuNC0uNy44LS41bDEyLjEgNy45czQuNS0xLjcgOS4yLTEuOWMyLjgtLjIgNS4yIDAgNy41LjR6IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS4xNTkgLTEuMTU3KSBzY2FsZSguMjMyOSkiLz48cGF0aCBzdHlsZT0ibWl4LWJsZW5kLW1vZGU6bm9ybWFsO2ZpbGw6bm9uZTtmaWxsLXJ1bGU6ZXZlbm9kZDtzdHJva2Utd2lkdGg6NDtzdHJva2UtbWl0ZXJsaW1pdDoxMDtzdHJva2UtZGFzaGFycmF5Om5vbmU7c3Ryb2tlLWRhc2hvZmZzZXQ6MCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBmaWxsPSJub25lIiBkPSJNMTA2LjY1IDY3Ljk1N2MtNC43IDMuOC0xMS45IDMuOS0xMS45IDMuOW0xMC42IDcuM2MtNi4zLjUtMTAuMi0xLjQtMTAuMi0xLjRtLTY4LjgtMTAuMXM4LjYgMi44IDEyLjEgNS45bS4zIDMuNWMtNC4zIDEuNy0xMS43LjYtMTEuNy42IiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMS4xNTkgLTEuMTU3KSBzY2FsZSguMjMyOSkiLz48L2c+PC9nPjwvc3ZnPg=="); /***/ }), /***/ "./src/addons/addons/bitmap-copy/_runtime_entry.js": /*!*********************************************************!*\ !*** ./src/addons/addons/bitmap-copy/_runtime_entry.js ***! \*********************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/bitmap-copy/userscript.js"); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"] }; /***/ }), /***/ "./src/addons/addons/bitmap-copy/userscript.js": /*!*****************************************************!*\ !*** ./src/addons/addons/bitmap-copy/userscript.js ***! \*****************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (async ({ addon, console }) => { if (!addon.tab.redux.state) return console.warn("Redux is not available!"); addon.tab.redux.initialize(); addon.tab.redux.addEventListener("statechanged", ({ detail }) => { if (addon.self.disabled) return; const e = detail; if (!e.action || e.action.type !== "scratch-paint/clipboard/SET") return; const items = e.next.scratchPaint.clipboard.items; if (items.length !== 1) return; const firstItem = items[0]; // TODO vector support if (!Array.isArray(firstItem) || firstItem[0] !== "Raster") return console.log("copied element is vector"); const dataURL = firstItem[1].source; addon.tab.copyImage(dataURL).then(() => console.log("Image successfully copied")).catch(e => console.error("Image could not be copied: ".concat(e))); }); }); /***/ }), /***/ "./src/addons/addons/block-cherry-picking/_runtime_entry.js": /*!******************************************************************!*\ !*** ./src/addons/addons/block-cherry-picking/_runtime_entry.js ***! \******************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/block-cherry-picking/userscript.js"); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"] }; /***/ }), /***/ "./src/addons/addons/block-cherry-picking/userscript.js": /*!**************************************************************!*\ !*** ./src/addons/addons/block-cherry-picking/userscript.js ***! \**************************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _block_duplicate_module_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../block-duplicate/module.js */ "./src/addons/addons/block-duplicate/module.js"); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console }) { const update = () => { _block_duplicate_module_js__WEBPACK_IMPORTED_MODULE_0__["setCherryPicking"](!addon.self.disabled, addon.settings.get("invertDrag")); }; addon.self.addEventListener("disabled", update); addon.self.addEventListener("reenabled", update); addon.settings.addEventListener("change", update); update(); _block_duplicate_module_js__WEBPACK_IMPORTED_MODULE_0__["load"](addon); }); /***/ }), /***/ "./src/addons/addons/block-duplicate/_runtime_entry.js": /*!*************************************************************!*\ !*** ./src/addons/addons/block-duplicate/_runtime_entry.js ***! \*************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/block-duplicate/userscript.js"); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"] }; /***/ }), /***/ "./src/addons/addons/block-duplicate/module.js": /*!*****************************************************!*\ !*** ./src/addons/addons/block-duplicate/module.js ***! \*****************************************************/ /*! exports provided: setCherryPicking, setDuplication, load */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setCherryPicking", function() { return setCherryPicking; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setDuplication", function() { return setDuplication; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "load", function() { return load; }); let enableCherryPicking = false; let invertCherryPicking = false; function setCherryPicking(newEnabled, newInverted) { enableCherryPicking = newEnabled; // If cherry picking is disabled, also disable invert. Duplicating blocks can still cause // this setting to be used. invertCherryPicking = newEnabled && newInverted; } let enableDuplication = false; function setDuplication(newEnabled) { enableDuplication = newEnabled; } // mostRecentEvent_ is sometimes a fake event, so we can't rely on reading its properties. let ctrlOrMetaPressed = false; let altPressed = false; document.addEventListener("mousedown", function (e) { ctrlOrMetaPressed = e.ctrlKey || e.metaKey; altPressed = e.altKey; }, { capture: true }); let loaded = false; async function load(addon) { if (loaded) { return; } loaded = true; const ScratchBlocks = await addon.tab.traps.getBlockly(); // https://github.com/LLK/scratch-blocks/blob/912b8cc728bea8fd91af85078c64fcdbfe21c87a/core/gesture.js#L454 const originalStartDraggingBlock = ScratchBlocks.Gesture.prototype.startDraggingBlock_; ScratchBlocks.Gesture.prototype.startDraggingBlock_ = function (...args) { let block = this.targetBlock_; // Scratch uses fake mouse events to implement right click > duplicate const isRightClickDuplicate = !(this.mostRecentEvent_ instanceof MouseEvent); const isDuplicating = enableDuplication && altPressed && !isRightClickDuplicate && !this.flyout_ && !this.shouldDuplicateOnDrag_ && this.targetBlock_.type !== "procedures_definition"; const isCherryPickingInverted = invertCherryPicking && !isRightClickDuplicate && block.getParent(); const canCherryPick = enableCherryPicking || isDuplicating; const isCherryPicking = canCherryPick && ctrlOrMetaPressed === !isCherryPickingInverted && !block.isShadow(); if (isDuplicating || isCherryPicking) { if (!ScratchBlocks.Events.getGroup()) { // Scratch will disable grouping on its own later. ScratchBlocks.Events.setGroup(true); } } if (isDuplicating) { // Based on https://github.com/LLK/scratch-blocks/blob/feda366947432b9d82a4f212f43ff6d4ab6f252f/core/scratch_blocks_utils.js#L171 // Setting this.shouldDuplicateOnDrag_ = true does NOT work because it doesn't call changeObscuredShadowIds this.startWorkspace_.setResizesEnabled(false); ScratchBlocks.Events.disable(); let newBlock; try { const xmlBlock = ScratchBlocks.Xml.blockToDom(block); newBlock = ScratchBlocks.Xml.domToBlock(xmlBlock, this.startWorkspace_); ScratchBlocks.scratchBlocksUtils.changeObscuredShadowIds(newBlock); const xy = block.getRelativeToSurfaceXY(); newBlock.moveBy(xy.x, xy.y); } catch (e) { console.error(e); } ScratchBlocks.Events.enable(); if (newBlock) { block = newBlock; this.targetBlock_ = newBlock; if (ScratchBlocks.Events.isEnabled()) { ScratchBlocks.Events.fire(new ScratchBlocks.Events.BlockCreate(newBlock)); } } } if (isCherryPicking) { if (isRightClickDuplicate || isDuplicating) { const nextBlock = block.getNextBlock(); if (nextBlock) { nextBlock.dispose(); } } block.unplug(true); } return originalStartDraggingBlock.call(this, ...args); }; } /***/ }), /***/ "./src/addons/addons/block-duplicate/userscript.js": /*!*********************************************************!*\ !*** ./src/addons/addons/block-duplicate/userscript.js ***! \*********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _module_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./module.js */ "./src/addons/addons/block-duplicate/module.js"); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console }) { const update = () => { _module_js__WEBPACK_IMPORTED_MODULE_0__["setDuplication"](!addon.self.disabled); }; addon.self.addEventListener("disabled", update); addon.self.addEventListener("reenabled", update); update(); _module_js__WEBPACK_IMPORTED_MODULE_0__["load"](addon); }); /***/ }), /***/ "./src/addons/addons/block-switching/_runtime_entry.js": /*!*************************************************************!*\ !*** ./src/addons/addons/block-switching/_runtime_entry.js ***! \*************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/block-switching/userscript.js"); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"] }; /***/ }), /***/ "./src/addons/addons/block-switching/userscript.js": /*!*********************************************************!*\ !*** ./src/addons/addons/block-switching/userscript.js ***! \*********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console, msg }) { const ScratchBlocks = await addon.tab.traps.getBlockly(); const vm = addon.tab.traps.vm; let blockSwitches = {}; let procedureSwitches = {}; const noopSwitch = { isNoop: true }; const randomColor = () => { const num = Math.floor(Math.random() * 256 * 256 * 256); return "#".concat(num.toString(16).padStart(6, "0")); }; const buildSwitches = () => { blockSwitches = {}; procedureSwitches = {}; if (addon.settings.get("motion")) { blockSwitches["motion_turnright"] = [noopSwitch, { opcode: "motion_turnleft" }]; blockSwitches["motion_turnleft"] = [{ opcode: "motion_turnright" }, noopSwitch]; blockSwitches["motion_setx"] = [noopSwitch, { opcode: "motion_changexby", remapInputName: { X: "DX" } }, { opcode: "motion_sety", remapInputName: { X: "Y" } }, { opcode: "motion_changeyby", remapInputName: { X: "DY" } }]; blockSwitches["motion_changexby"] = [{ opcode: "motion_setx", remapInputName: { DX: "X" } }, noopSwitch, { opcode: "motion_sety", remapInputName: { DX: "Y" } }, { opcode: "motion_changeyby", remapInputName: { DX: "DY" } }]; blockSwitches["motion_sety"] = [{ opcode: "motion_setx", remapInputName: { Y: "X" } }, { opcode: "motion_changexby", remapInputName: { Y: "DX" } }, noopSwitch, { opcode: "motion_changeyby", remapInputName: { Y: "DY" } }]; blockSwitches["motion_changeyby"] = [{ opcode: "motion_setx", remapInputName: { DY: "X" } }, { opcode: "motion_changexby", remapInputName: { DY: "DX" } }, { opcode: "motion_sety", remapInputName: { DY: "Y" } }, noopSwitch]; blockSwitches["motion_xposition"] = [noopSwitch, { opcode: "motion_yposition" }]; blockSwitches["motion_yposition"] = [{ opcode: "motion_xposition" }, noopSwitch]; } if (addon.settings.get("looks")) { blockSwitches["looks_seteffectto"] = [noopSwitch, { opcode: "looks_changeeffectby", remapInputName: { VALUE: "CHANGE" } }]; blockSwitches["looks_changeeffectby"] = [{ opcode: "looks_seteffectto", remapInputName: { CHANGE: "VALUE" } }, noopSwitch]; blockSwitches["looks_setsizeto"] = [noopSwitch, { opcode: "looks_changesizeby", remapInputName: { SIZE: "CHANGE" } }]; blockSwitches["looks_changesizeby"] = [{ opcode: "looks_setsizeto", remapInputName: { CHANGE: "SIZE" } }, noopSwitch]; blockSwitches["looks_costumenumbername"] = [noopSwitch, { opcode: "looks_backdropnumbername" }]; blockSwitches["looks_backdropnumbername"] = [{ opcode: "looks_costumenumbername" }, noopSwitch]; blockSwitches["looks_show"] = [noopSwitch, { opcode: "looks_hide" }]; blockSwitches["looks_hide"] = [{ opcode: "looks_show" }, noopSwitch]; blockSwitches["looks_nextcostume"] = [noopSwitch, { opcode: "looks_nextbackdrop" }]; blockSwitches["looks_nextbackdrop"] = [{ opcode: "looks_nextcostume" }, noopSwitch]; blockSwitches["looks_say"] = [noopSwitch, { opcode: "looks_sayforsecs", createInputs: { SECS: { shadowType: "math_number", value: "2" } } }, { opcode: "looks_think" }, { opcode: "looks_thinkforsecs", createInputs: { SECS: { shadowType: "math_number", value: "2" } } }]; blockSwitches["looks_think"] = [{ opcode: "looks_say" }, { opcode: "looks_sayforsecs", createInputs: { SECS: { shadowType: "math_number", value: "2" } } }, noopSwitch, { opcode: "looks_thinkforsecs", createInputs: { SECS: { shadowType: "math_number", value: "2" } } }]; blockSwitches["looks_sayforsecs"] = [{ opcode: "looks_say", splitInputs: ["SECS"] }, { opcode: "looks_think", splitInputs: ["SECS"] }, noopSwitch, { opcode: "looks_thinkforsecs" }]; blockSwitches["looks_thinkforsecs"] = [{ opcode: "looks_say", splitInputs: ["SECS"] }, { opcode: "looks_think", splitInputs: ["SECS"] }, { opcode: "looks_sayforsecs" }, noopSwitch]; blockSwitches["looks_switchbackdropto"] = [noopSwitch, { opcode: "looks_switchbackdroptoandwait" }]; blockSwitches["looks_switchbackdroptoandwait"] = [{ opcode: "looks_switchbackdropto" }, noopSwitch]; blockSwitches["looks_gotofrontback"] = [noopSwitch, { opcode: "looks_goforwardbackwardlayers", remapInputName: { FRONT_BACK: "FORWARD_BACKWARD" }, mapFieldValues: { FRONT_BACK: { front: "forward", back: "backward" } }, createInputs: { NUM: { shadowType: "math_integer", value: "1" } } }]; blockSwitches["looks_goforwardbackwardlayers"] = [{ opcode: "looks_gotofrontback", splitInputs: ["NUM"], remapInputName: { FORWARD_BACKWARD: "FRONT_BACK" }, mapFieldValues: { FORWARD_BACKWARD: { forward: "front", backward: "back" } } }, noopSwitch]; } if (addon.settings.get("sound")) { blockSwitches["sound_play"] = [noopSwitch, { opcode: "sound_playuntildone" }]; blockSwitches["sound_playuntildone"] = [{ opcode: "sound_play" }, noopSwitch]; blockSwitches["sound_seteffectto"] = [noopSwitch, { opcode: "sound_changeeffectby" }]; blockSwitches["sound_changeeffectby"] = [{ opcode: "sound_seteffectto" }, noopSwitch]; blockSwitches["sound_setvolumeto"] = [noopSwitch, { opcode: "sound_changevolumeby" }]; blockSwitches["sound_changevolumeby"] = [{ opcode: "sound_setvolumeto" }, noopSwitch]; } if (addon.settings.get("event")) { blockSwitches["event_broadcast"] = [noopSwitch, { opcode: "event_broadcastandwait" }]; blockSwitches["event_broadcastandwait"] = [{ opcode: "event_broadcast" }, noopSwitch]; } if (addon.settings.get("control")) { blockSwitches["control_if"] = [noopSwitch, { opcode: "control_if_else" }]; blockSwitches["control_if_else"] = [{ opcode: "control_if", splitInputs: ["SUBSTACK2"] }, noopSwitch]; blockSwitches["control_repeat_until"] = [noopSwitch, { opcode: "control_wait_until", splitInputs: ["SUBSTACK"] }, { opcode: "control_forever", splitInputs: ["CONDITION"] }]; blockSwitches["control_forever"] = [{ opcode: "control_repeat_until" }, noopSwitch]; blockSwitches["control_wait_until"] = [{ opcode: "control_repeat_until" }, noopSwitch]; } if (addon.settings.get("operator")) { blockSwitches["operator_equals"] = [{ opcode: "operator_gt" }, noopSwitch, { opcode: "operator_lt" }]; blockSwitches["operator_gt"] = [noopSwitch, { opcode: "operator_equals" }, { opcode: "operator_lt" }]; blockSwitches["operator_lt"] = [{ opcode: "operator_gt" }, { opcode: "operator_equals" }, noopSwitch]; blockSwitches["operator_add"] = [noopSwitch, { opcode: "operator_subtract" }, { opcode: "operator_multiply" }, { opcode: "operator_divide" }, { opcode: "operator_mod" }]; blockSwitches["operator_subtract"] = [{ opcode: "operator_add" }, noopSwitch, { opcode: "operator_multiply" }, { opcode: "operator_divide" }, { opcode: "operator_mod" }]; blockSwitches["operator_multiply"] = [{ opcode: "operator_add" }, { opcode: "operator_subtract" }, noopSwitch, { opcode: "operator_divide" }, { opcode: "operator_mod" }]; blockSwitches["operator_divide"] = [{ opcode: "operator_add" }, { opcode: "operator_subtract" }, { opcode: "operator_multiply" }, noopSwitch, { opcode: "operator_mod" }]; blockSwitches["operator_mod"] = [{ opcode: "operator_add" }, { opcode: "operator_subtract" }, { opcode: "operator_multiply" }, { opcode: "operator_divide" }, noopSwitch]; blockSwitches["operator_and"] = [noopSwitch, { opcode: "operator_or" }]; blockSwitches["operator_or"] = [{ opcode: "operator_and" }, noopSwitch]; } if (addon.settings.get("sensing")) { blockSwitches["sensing_mousex"] = [noopSwitch, { opcode: "sensing_mousey" }]; blockSwitches["sensing_mousey"] = [{ opcode: "sensing_mousex" }, noopSwitch]; blockSwitches["sensing_touchingcolor"] = [noopSwitch, { opcode: "sensing_coloristouchingcolor", createInputs: { COLOR2: { shadowType: "colour_picker", value: randomColor } } }]; blockSwitches["sensing_coloristouchingcolor"] = [{ opcode: "sensing_touchingcolor", splitInputs: ["COLOR2"] }, noopSwitch]; } if (addon.settings.get("data")) { blockSwitches["data_setvariableto"] = [noopSwitch, { opcode: "data_changevariableby", remapShadowType: { VALUE: "math_number" } }]; blockSwitches["data_changevariableby"] = [{ opcode: "data_setvariableto", remapShadowType: { VALUE: "text" } }, noopSwitch]; blockSwitches["data_showvariable"] = [noopSwitch, { opcode: "data_hidevariable" }]; blockSwitches["data_hidevariable"] = [{ opcode: "data_showvariable" }, noopSwitch]; blockSwitches["data_showlist"] = [noopSwitch, { opcode: "data_hidelist" }]; blockSwitches["data_hidelist"] = [{ opcode: "data_showlist" }, noopSwitch]; blockSwitches["data_replaceitemoflist"] = [noopSwitch, { opcode: "data_insertatlist" }]; blockSwitches["data_insertatlist"] = [{ opcode: "data_replaceitemoflist" }, noopSwitch]; blockSwitches["data_deleteoflist"] = [noopSwitch, { opcode: "data_deletealloflist", splitInputs: ["INDEX"] }]; blockSwitches["data_deletealloflist"] = [{ opcode: "data_deleteoflist", createInputs: { INDEX: { shadowType: "math_integer", value: "1" } } }, noopSwitch]; } if (addon.settings.get("extension")) { blockSwitches["pen_penDown"] = [noopSwitch, { opcode: "pen_penUp" }]; blockSwitches["pen_penUp"] = [{ opcode: "pen_penDown" }, noopSwitch]; blockSwitches["pen_setPenColorParamTo"] = [noopSwitch, { opcode: "pen_changePenColorParamBy" }]; blockSwitches["pen_changePenColorParamBy"] = [{ opcode: "pen_setPenColorParamTo" }, noopSwitch]; blockSwitches["pen_setPenHueToNumber"] = [noopSwitch, { opcode: "pen_changePenHueBy" }]; blockSwitches["pen_changePenHueBy"] = [{ opcode: "pen_setPenHueToNumber" }, noopSwitch]; blockSwitches["pen_setPenShadeToNumber"] = [noopSwitch, { opcode: "pen_changePenShadeBy" }]; blockSwitches["pen_changePenShadeBy"] = [{ opcode: "pen_setPenShadeToNumber" }, noopSwitch]; blockSwitches["pen_setPenSizeTo"] = [noopSwitch, { opcode: "pen_changePenSizeBy" }]; blockSwitches["pen_changePenSizeBy"] = [{ opcode: "pen_setPenSizeTo" }, noopSwitch]; blockSwitches["music_setTempo"] = [noopSwitch, { opcode: "music_changeTempo" }]; blockSwitches["music_changeTempo"] = [{ opcode: "music_setTempo" }, noopSwitch]; } if (addon.settings.get("sa")) { const logProc = "\u200B\u200Blog\u200B\u200B %s"; const warnProc = "\u200B\u200Bwarn\u200B\u200B %s"; const errorProc = "\u200B\u200Berror\u200B\u200B %s"; const logMessage = msg("debugger_log"); const warnMessage = msg("debugger_warn"); const errorMessage = msg("debugger_error"); const logSwitch = { mutate: { proccode: logProc }, msg: logMessage }; const warnSwitch = { mutate: { proccode: warnProc }, msg: warnMessage }; const errorSwitch = { mutate: { proccode: errorProc }, msg: errorMessage }; procedureSwitches[logProc] = [{ msg: logMessage, isNoop: true }, warnSwitch, errorSwitch]; procedureSwitches[warnProc] = [logSwitch, { msg: warnMessage, isNoop: true }, errorSwitch]; procedureSwitches[errorProc] = [logSwitch, warnSwitch, { msg: errorMessage, isNoop: true }]; } // Switching for these is implemented by Scratch. We only define them here to optionally add a border. // Because we don't implement the switching ourselves, this is not controlled by the data category option. blockSwitches["data_variable"] = []; blockSwitches["data_listcontents"] = []; }; buildSwitches(); addon.settings.addEventListener("change", buildSwitches); /** * @param {*} workspace * @param {Element} xmlBlock */ const pasteBlockXML = (workspace, xmlBlock) => { // Similar to https://github.com/LLK/scratch-blocks/blob/7575c9a0f2c267676569c4b102b76d77f35d9fd6/core/workspace_svg.js#L1020 // but without the collision checking. const block = ScratchBlocks.Xml.domToBlock(xmlBlock, workspace); const x = +xmlBlock.getAttribute("x"); const y = +xmlBlock.getAttribute("y"); // Don't need to handle RTL here block.moveBy(x, y); return block; }; /** * @param {string} shadowType The type of shadow eg. "math_number" * @returns {string} The name of the shadow's inner field that contains the user-visible value */ const getShadowFieldName = shadowType => { // This is non-comprehensive. if (shadowType === "text") { return "TEXT"; } if (shadowType === "colour_picker") { return "COLOUR"; } return "NUM"; }; /** * @template T * @param {T|()=>T} value * @returns {T} */ const callIfFunction = value => { if (typeof value === "function") { return value(); } return value; }; const menuCallbackFactory = (block, opcodeData) => () => { if (opcodeData.isNoop) { return; } if (opcodeData.fieldValue) { block.setFieldValue(opcodeData.fieldValue, "VALUE"); return; } try { ScratchBlocks.Events.setGroup(true); const workspace = block.workspace; const blocksToBringToForeground = []; // Split inputs before we clone the block. if (opcodeData.splitInputs) { for (const inputName of opcodeData.splitInputs) { const input = block.getInput(inputName); if (!input) { continue; } const connection = input.connection; if (!connection) { continue; } if (connection.isConnected()) { const targetBlock = connection.targetBlock(); if (targetBlock.isShadow()) {// Deleting shadows is handled later. } else { connection.disconnect(); blocksToBringToForeground.push(targetBlock); } } } } // Make a copy of the block with the proper type set. // It doesn't seem to be possible to change a Block's type after it's created, so we'll just make a new block instead. const xml = ScratchBlocks.Xml.blockToDom(block); // blockToDomWithXY's handling of RTL is strange, so we encode the position ourselves. const position = block.getRelativeToSurfaceXY(); xml.setAttribute("x", position.x); xml.setAttribute("y", position.y); if (opcodeData.opcode) { xml.setAttribute("type", opcodeData.opcode); } const parentBlock = block.getParent(); let parentConnection; let blockConnectionType; if (parentBlock) { // If the block has a parent, find the parent -> child connection that will be reattached later. const parentConnections = parentBlock.getConnections_(); parentConnection = parentConnections.find(c => c.targetConnection && c.targetConnection.sourceBlock_ === block); // There's two types of connections from child -> parent. We need to figure out which one is used. const blockConnections = block.getConnections_(); const blockToParentConnection = blockConnections.find(c => c.targetConnection && c.targetConnection.sourceBlock_ === parentBlock); blockConnectionType = blockToParentConnection.type; } // Array.from creates a clone of the children list. This is important as we may remove // children as we iterate. for (const child of Array.from(xml.children)) { const oldName = child.getAttribute("name"); // Any inputs that were supposed to be split that were not should be removed. // (eg. shadow inputs) if (opcodeData.splitInputs && opcodeData.splitInputs.includes(oldName)) { xml.removeChild(child); continue; } const newName = opcodeData.remapInputName && opcodeData.remapInputName[oldName]; if (newName) { child.setAttribute("name", newName); } const newShadowType = opcodeData.remapShadowType && opcodeData.remapShadowType[oldName]; if (newShadowType) { const valueNode = child.firstChild; const fieldNode = valueNode.firstChild; valueNode.setAttribute("type", newShadowType); fieldNode.setAttribute("name", getShadowFieldName(newShadowType)); } const fieldValueMap = opcodeData.mapFieldValues && opcodeData.mapFieldValues[oldName]; if (fieldValueMap && child.tagName === "FIELD") { const oldValue = child.innerText; const newValue = fieldValueMap[oldValue]; if (typeof newValue === "string") { child.innerText = newValue; } } } if (opcodeData.mutate) { const mutation = xml.querySelector("mutation"); for (const [key, value] of Object.entries(opcodeData.mutate)) { mutation.setAttribute(key, value); } } if (opcodeData.createInputs) { for (const [inputName, inputData] of Object.entries(opcodeData.createInputs)) { const valueElement = document.createElement("value"); valueElement.setAttribute("name", inputName); const shadowElement = document.createElement("shadow"); shadowElement.setAttribute("type", inputData.shadowType); const shadowFieldElement = document.createElement("field"); shadowFieldElement.setAttribute("name", getShadowFieldName(inputData.shadowType)); shadowFieldElement.innerText = callIfFunction(inputData.value); shadowElement.appendChild(shadowFieldElement); valueElement.appendChild(shadowElement); xml.appendChild(valueElement); } } // Remove the old block and insert the new one. block.dispose(); const newBlock = pasteBlockXML(workspace, xml); if (parentConnection) { // Search for the same type of connection on the new block as on the old block. const newBlockConnections = newBlock.getConnections_(); const newBlockConnection = newBlockConnections.find(c => c.type === blockConnectionType); newBlockConnection.connect(parentConnection); } for (const otherBlock of blocksToBringToForeground) { // By re-appending the element, we move it to the end, which will make it display // on top. const svgRoot = otherBlock.getSvgRoot(); svgRoot.parentNode.appendChild(svgRoot); } } finally { ScratchBlocks.Events.setGroup(false); } }; const uniques = array => [...new Set(array)]; addon.tab.createBlockContextMenu((items, block) => { if (!addon.self.disabled) { const type = block.type; let switches = blockSwitches[block.type] || []; const customArgsMode = addon.settings.get("customargs") ? addon.settings.get("customargsmode") : "off"; if (customArgsMode !== "off" && ["argument_reporter_boolean", "argument_reporter_string_number"].includes(type) && // if the arg is a shadow, it's in a procedures_prototype so we don't want it to be switchable !block.isShadow()) { const customBlocks = getCustomBlocks(); if (customArgsMode === "all") { switch (type) { case "argument_reporter_string_number": switches = Object.values(customBlocks).map(cb => cb.stringArgs).flat(1); break; case "argument_reporter_boolean": switches = Object.values(customBlocks).map(cb => cb.boolArgs).flat(1); break; } } else if (customArgsMode === "defOnly") { const root = block.getRootBlock(); if (root.type !== "procedures_definition") return items; const customBlockObj = customBlocks[root.getChildren(true)[0].getProcCode()]; switch (type) { case "argument_reporter_string_number": switches = customBlockObj.stringArgs; break; case "argument_reporter_boolean": switches = customBlockObj.boolArgs; break; } } const currentValue = block.getFieldValue("VALUE"); switches = uniques(switches).map(i => ({ isNoop: i === currentValue, fieldValue: i, msg: i })); } if (block.type === "procedures_call") { const proccode = block.getProcCode(); if (procedureSwitches[proccode]) { switches = procedureSwitches[proccode]; } } if (!addon.settings.get("noop")) { switches = switches.filter(i => !i.isNoop); } switches.forEach((opcodeData, i) => { const makeSpaceItemIndex = items.findIndex(obj => obj._isDevtoolsFirstItem); const insertBeforeIndex = makeSpaceItemIndex !== -1 ? // If "make space" button exists, add own items before it makeSpaceItemIndex : // If there's no such button, insert at end items.length; const text = opcodeData.msg ? opcodeData.msg : opcodeData.opcode ? msg(opcodeData.opcode) : msg(block.type); items.splice(insertBeforeIndex, 0, { enabled: true, text, callback: menuCallbackFactory(block, opcodeData), separator: i === 0 }); }); if (block.type === "data_variable" || block.type === "data_listcontents") { // Add top border to first variable (if it exists) const delBlockIndex = items.findIndex(item => item.text === ScratchBlocks.Msg.DELETE_BLOCK); // firstVariableItem might be undefined, a variable to switch to, // or an item added by editor-devtools (or any addon before this one) const firstVariableItem = items[delBlockIndex + 1]; if (firstVariableItem) firstVariableItem.separator = true; } } return items; }, { blocks: true }); // https://github.com/LLK/scratch-blocks/blob/abbfe93136fef57fdfb9a077198b0bc64726f012/blocks_vertical/procedures.js#L207-L215 // Returns a list like ["%s", "%d"] const parseArguments = code => code.split(/(?=[^\\]%[nbs])/g).map(i => i.trim()).filter(i => i.charAt(0) === "%").map(i => i.substring(0, 2)); const getCustomBlocks = () => { const customBlocks = {}; const target = vm.editingTarget; Object.values(target.blocks._blocks).filter(block => block.opcode === "procedures_prototype").forEach(block => { const procCode = block.mutation.proccode; const argumentNames = JSON.parse(block.mutation.argumentnames); // argumentdefaults is unreliable, so we have to parse the procedure code to determine argument types const parsedArguments = parseArguments(procCode); const stringArgs = []; const boolArgs = []; for (let i = 0; i < argumentNames.length; i++) { if (parsedArguments[i] === "%b") { boolArgs.push(argumentNames[i]); } else { stringArgs.push(argumentNames[i]); } } customBlocks[procCode] = { stringArgs, boolArgs }; }); return customBlocks; }; }); /***/ }), /***/ "./src/addons/addons/color-picker/_runtime_entry.js": /*!**********************************************************!*\ !*** ./src/addons/addons/color-picker/_runtime_entry.js ***! \**********************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/color-picker/userscript.js"); /* harmony import */ var _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-loader!./style.css */ "./node_modules/css-loader/index.js!./src/addons/addons/color-picker/style.css"); /* harmony import */ var _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_style_css__WEBPACK_IMPORTED_MODULE_1__); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"], "style.css": _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1___default.a }; /***/ }), /***/ "./src/addons/addons/color-picker/code-editor.js": /*!*******************************************************!*\ !*** ./src/addons/addons/color-picker/code-editor.js ***! \*******************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _libraries_common_cs_normalize_color_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../libraries/common/cs/normalize-color.js */ "./src/addons/libraries/common/cs/normalize-color.js"); /* harmony import */ var _libraries_common_cs_rate_limiter_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../libraries/common/cs/rate-limiter.js */ "./src/addons/libraries/common/cs/rate-limiter.js"); /* harmony import */ var _libraries_thirdparty_cs_tinycolor_min_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../libraries/thirdparty/cs/tinycolor-min.js */ "./src/addons/libraries/thirdparty/cs/tinycolor-min.js"); /* harmony default export */ __webpack_exports__["default"] = (async ({ addon, console, msg }) => { // 250-ms rate limit const rateLimiter = new _libraries_common_cs_rate_limiter_js__WEBPACK_IMPORTED_MODULE_1__["default"](250); const getColor = element => { const { children } = element.parentElement; // h: 0 - 360 const h = children[1].getAttribute("aria-valuenow"); // s: 0 - 1 const s = children[3].getAttribute("aria-valuenow"); // v: 0 - 255, divide by 255 const vMultipliedBy255 = children[5].getAttribute("aria-valuenow"); const v = Number(vMultipliedBy255) / 255; return Object(_libraries_thirdparty_cs_tinycolor_min_js__WEBPACK_IMPORTED_MODULE_2__["default"])("hsv(".concat(h, ", ").concat(s, ", ").concat(v || 0, ")")).toHexString(); }; const setColor = (hex, element) => { hex = Object(_libraries_common_cs_normalize_color_js__WEBPACK_IMPORTED_MODULE_0__["normalizeHex"])(hex); if (!addon.tab.redux.state || !addon.tab.redux.state.scratchGui) return; // The only way to reliably set color is to invoke eye dropper via click() // then faking that the eye dropper reported the value. const onEyeDropperClosed = ({ detail }) => { if (detail.action.type !== "scratch-gui/color-picker/DEACTIVATE_COLOR_PICKER") return; addon.tab.redux.removeEventListener("statechanged", onEyeDropperClosed); setTimeout(() => { document.body.classList.remove("sa-hide-eye-dropper-background"); }, 50); }; const onEyeDropperOpened = ({ detail }) => { if (detail.action.type !== "scratch-gui/color-picker/ACTIVATE_COLOR_PICKER") return; addon.tab.redux.removeEventListener("statechanged", onEyeDropperOpened); addon.tab.redux.addEventListener("statechanged", onEyeDropperClosed); setTimeout(() => { addon.tab.redux.dispatch({ type: "scratch-gui/color-picker/DEACTIVATE_COLOR_PICKER", color: hex }); }, 50); }; addon.tab.redux.addEventListener("statechanged", onEyeDropperOpened); document.body.classList.add("sa-hide-eye-dropper-background"); element.click(); }; const addColorPicker = () => { const element = document.querySelector("button.scratchEyedropper"); rateLimiter.abort(false); addon.tab.redux.initialize(); const defaultColor = getColor(element); const saColorPicker = Object.assign(document.createElement("div"), { className: "sa-color-picker sa-color-picker-code" }); addon.tab.displayNoneWhileDisabled(saColorPicker, { display: "flex" }); const saColorPickerColor = Object.assign(document.createElement("input"), { className: "sa-color-picker-color sa-color-picker-code-color", type: "color", value: defaultColor || "#000000" }); const saColorPickerText = Object.assign(document.createElement("input"), { className: addon.tab.scratchClass("input_input-form", { others: "sa-color-picker-text sa-color-picker-code-text" }), type: "text", pattern: "^#?([0-9a-fA-F]{3}){1,2}$", placeholder: msg("hex"), value: defaultColor || "" }); saColorPickerColor.addEventListener("input", () => rateLimiter.limit(() => setColor(saColorPickerText.value = saColorPickerColor.value, element))); saColorPickerText.addEventListener("change", () => { const { value } = saColorPickerText; if (!Object(_libraries_common_cs_normalize_color_js__WEBPACK_IMPORTED_MODULE_0__["getHexRegex"])().test(value)) return; setColor(saColorPickerColor.value = Object(_libraries_common_cs_normalize_color_js__WEBPACK_IMPORTED_MODULE_0__["normalizeHex"])(value), element); }); saColorPicker.appendChild(saColorPickerColor); saColorPicker.appendChild(saColorPickerText); element.parentElement.insertBefore(saColorPicker, element); }; const ScratchBlocks = await addon.tab.traps.getBlockly(); const originalShowEditor = ScratchBlocks.FieldColourSlider.prototype.showEditor_; ScratchBlocks.FieldColourSlider.prototype.showEditor_ = function (...args) { const r = originalShowEditor.call(this, ...args); addColorPicker(); return r; }; }); /***/ }), /***/ "./src/addons/addons/color-picker/userscript.js": /*!******************************************************!*\ !*** ./src/addons/addons/color-picker/userscript.js ***! \******************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _code_editor_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./code-editor.js */ "./src/addons/addons/color-picker/code-editor.js"); /* harmony default export */ __webpack_exports__["default"] = (async api => { Object(_code_editor_js__WEBPACK_IMPORTED_MODULE_0__["default"])(api); }); /***/ }), /***/ "./src/addons/addons/editor-comment-previews/_runtime_entry.js": /*!*********************************************************************!*\ !*** ./src/addons/addons/editor-comment-previews/_runtime_entry.js ***! \*********************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/editor-comment-previews/userscript.js"); /* harmony import */ var _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-loader!./userstyle.css */ "./node_modules/css-loader/index.js!./src/addons/addons/editor-comment-previews/userstyle.css"); /* harmony import */ var _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1__); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"], "userstyle.css": _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1___default.a }; /***/ }), /***/ "./src/addons/addons/editor-comment-previews/userscript.js": /*!*****************************************************************!*\ !*** ./src/addons/addons/editor-comment-previews/userscript.js ***! \*****************************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console }) { const vm = addon.tab.traps.vm; const updateStyles = () => { previewInner.classList.toggle("sa-comment-preview-delay", addon.settings.get("delay") !== "none"); previewInner.classList.toggle("sa-comment-preview-reduce-transparency", addon.settings.get("reduce-transparency")); previewInner.classList.toggle("sa-comment-preview-fade", !addon.settings.get("reduce-animation")); }; const afterDelay = cb => { if (!previewInner.classList.contains("sa-comment-preview-hidden")) { // If not hidden, updating immediately is preferred cb(); return; } const delay = addon.settings.get("delay"); if (delay === "long") return setTimeout(cb, 500); if (delay === "short") return setTimeout(cb, 300); cb(); }; let hoveredElement = null; let showTimeout = null; let mouseX = 0; let mouseY = 0; let doNotShowUntilMoveMouse = false; const previewOuter = document.createElement("div"); previewOuter.classList.add("sa-comment-preview-outer"); const previewInner = document.createElement("div"); previewInner.classList.add("sa-comment-preview-inner"); previewInner.classList.add("sa-comment-preview-hidden"); updateStyles(); addon.settings.addEventListener("change", updateStyles); previewOuter.appendChild(previewInner); document.body.appendChild(previewOuter); const getBlock = id => vm.editingTarget.blocks.getBlock(id) || vm.runtime.flyoutBlocks.getBlock(id); const getComment = block => block && block.comment && vm.editingTarget.comments[block.comment]; const getProcedureDefinitionBlock = procCode => { const procedurePrototype = Object.values(vm.editingTarget.blocks._blocks).find(i => i.opcode === "procedures_prototype" && i.mutation.proccode === procCode); if (procedurePrototype) { // Usually `parent` will exist but sometimes it doesn't if (procedurePrototype.parent) { return getBlock(procedurePrototype.parent); } const id = procedurePrototype.id; return Object.values(vm.editingTarget.blocks._blocks).find(i => i.opcode === "procedures_definition" && i.inputs.custom_block && i.inputs.custom_block.block === id); } return null; }; const setText = text => { previewInner.innerText = text; previewInner.classList.remove("sa-comment-preview-hidden"); updateMousePosition(); }; const updateMousePosition = () => { previewOuter.style.transform = "translate(".concat(mouseX + 8, "px, ").concat(mouseY + 8, "px)"); }; const hidePreview = () => { if (hoveredElement) { hoveredElement = null; previewInner.classList.add("sa-comment-preview-hidden"); } }; document.addEventListener("mouseover", e => { if (addon.self.disabled) { return; } clearTimeout(showTimeout); if (doNotShowUntilMoveMouse) { return; } const el = e.target.closest(".blocklyBubbleCanvas > g, .blocklyBlockCanvas .blocklyDraggable[data-id]"); if (el === hoveredElement) { // Nothing to do. return; } if (!el) { hidePreview(); return; } let text = null; if (addon.settings.get("hover-view") && e.target.closest(".blocklyBubbleCanvas > g") && // Hovering over the thin line that connects comments to blocks should never show a preview !e.target.closest("line")) { const collapsedText = el.querySelector("text.scratchCommentText"); if (collapsedText.getAttribute("display") !== "none") { const textarea = el.querySelector("textarea"); text = textarea.value; } } else if (e.target.closest(".blocklyBlockCanvas .blocklyDraggable[data-id]")) { const id = el.dataset.id; const block = getBlock(id); const comment = getComment(block); if (addon.settings.get("hover-view-block") && comment) { text = comment.text; } else if (block && block.opcode === "procedures_call" && addon.settings.get("hover-view-procedure")) { const procCode = block.mutation.proccode; const procedureDefinitionBlock = getProcedureDefinitionBlock(procCode); const procedureComment = getComment(procedureDefinitionBlock); if (procedureComment) { text = procedureComment.text; } } } if (text !== null && text.trim() !== "") { showTimeout = afterDelay(() => { hoveredElement = el; setText(text); }); } else { hidePreview(); } }); document.addEventListener("mousemove", e => { mouseX = e.clientX; mouseY = e.clientY; doNotShowUntilMoveMouse = false; if (addon.settings.get("follow-mouse") && !previewInner.classList.contains("sa-comment-preview-hidden")) { updateMousePosition(); } }); document.addEventListener("mousedown", () => { hidePreview(); doNotShowUntilMoveMouse = true; }, { capture: true }); }); /***/ }), /***/ "./src/addons/addons/editor-devtools/DevTools.js": /*!*******************************************************!*\ !*** ./src/addons/addons/editor-devtools/DevTools.js ***! \*******************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DevTools; }); /* harmony import */ var _DomHelpers_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./DomHelpers.js */ "./src/addons/addons/editor-devtools/DomHelpers.js"); /* harmony import */ var _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./UndoGroup.js */ "./src/addons/addons/editor-devtools/UndoGroup.js"); // import ShowBroadcast from "./show-broadcast.js"; class DevTools { constructor(addon, msg, m) { this.addon = addon; this.msg = msg; this.m = m; /** * @type {VirtualMachine} */ this.domHelpers = new _DomHelpers_js__WEBPACK_IMPORTED_MODULE_0__["default"](addon); this.codeTab = null; this.costTab = null; this.costTabBody = null; this.selVarID = null; this.canShare = false; this.mouseXY = { x: 0, y: 0 }; } async init() { this.addContextMenus(); while (true) { const root = await this.addon.tab.waitForElement("ul[class*=gui_tab-list_]", { markAsSeen: true, reduxEvents: ["scratch-gui/mode/SET_PLAYER", "fontsLoaded/SET_FONTS_LOADED", "scratch-gui/locales/SELECT_LOCALE"], reduxCondition: state => !state.scratchGui.mode.isPlayerOnly }); this.initInner(root); } } async addContextMenus() { const blockly = await this.addon.tab.traps.getBlockly(); const oldCleanUpFunc = blockly.WorkspaceSvg.prototype.cleanUp; const self = this; blockly.WorkspaceSvg.prototype.cleanUp = function () { if (self.addon.settings.get("enableCleanUpPlus")) { self.doCleanUp(); } else { oldCleanUpFunc.call(this); } }; let originalMsg = blockly.Msg.CLEAN_UP; if (this.addon.settings.get("enableCleanUpPlus")) blockly.Msg.CLEAN_UP = this.m("clean-plus"); this.addon.settings.addEventListener("change", () => { if (this.addon.settings.get("enableCleanUpPlus")) blockly.Msg.CLEAN_UP = this.m("clean-plus");else blockly.Msg.CLEAN_UP = originalMsg; }); this.addon.tab.createBlockContextMenu((items, block) => { items.push({ enabled: blockly.clipboardXml_, text: this.m("paste"), separator: true, _isDevtoolsFirstItem: true, callback: () => { let ids = this.getTopBlockIDs(); document.dispatchEvent(new KeyboardEvent("keydown", { keyCode: 86, ctrlKey: true, griff: true })); setTimeout(() => { this.beginDragOfNewBlocksNotInIDs(ids); }, 10); } }); return items; }, { workspace: true }); this.addon.tab.createBlockContextMenu((items, block) => { items.push({ enabled: true, text: this.m("make-space"), _isDevtoolsFirstItem: true, callback: () => { this.doCleanUp(block); }, separator: true }, { enabled: true, text: this.m("copy-all"), callback: () => { this.eventCopyClick(block); }, separator: true }, { enabled: true, text: this.m("copy-block"), callback: () => { this.eventCopyClick(block, 1); } }, { enabled: true, text: this.m("cut-block"), callback: () => { this.eventCopyClick(block, 2); } }); // const BROADCAST_BLOCKS = ["event_whenbroadcastreceived", "event_broadcast", "event_broadcastandwait"]; // if (BROADCAST_BLOCKS.includes(block.type)) { // // Show Broadcast // const broadcastId = this.showBroadcastSingleton.getAssociatedBroadcastId(block.id); // if (broadcastId) { // ["Senders", "Receivers"].forEach((showKey, i) => { // items.push({ // enabled: true, // text: this.msg(`show-${showKey}`.toLowerCase()), // callback: () => { // this.showBroadcastSingleton[`show${showKey}`](broadcastId); // }, // separator: i == 0, // }); // }); // } // } return items; }, { blocks: true }); this.addon.tab.createBlockContextMenu((items, block) => { if (block.getCategory() === "data" || block.getCategory() === "data-lists") { this.selVarID = block.getVars()[0]; items.push({ enabled: true, text: this.m("swap", { var: block.getCategory() === "data" ? this.m("variables") : this.m("lists") }), callback: async () => { let wksp = this.getWorkspace(); let v = wksp.getVariableById(this.selVarID); // prompt() returns Promise in desktop app let varName = await window.prompt(this.msg("replace", { name: v.name })); if (varName) { this.doReplaceVariable(this.selVarID, varName, v.type); } }, separator: true }); } return items; }, { blocks: true, flyout: true }); } getWorkspace() { return Blockly.getMainWorkspace(); } isCostumeEditor() { return this.costTab.className.indexOf("gui_is-selected") >= 0; } /** * A nicely ordered version of the top blocks * @returns {[Blockly.Block]} */ getTopBlocks() { let result = this.getOrderedTopBlockColumns(); let columns = result.cols; /** * @type {[[Blockly.Block]]} */ let topBlocks = []; for (const col of columns) { topBlocks = topBlocks.concat(col.blocks); } return topBlocks; } /** * A much nicer way of laying out the blocks into columns */ doCleanUp(block) { let workspace = this.getWorkspace(); let makeSpaceForBlock = block && block.getRootBlock(); _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].startUndoGroup(workspace); let result = this.getOrderedTopBlockColumns(true); let columns = result.cols; let orphanCount = result.orphans.blocks.length; if (orphanCount > 0 && !block) { let message = this.msg("orphaned", { count: orphanCount }); if (confirm(message)) { for (const block of result.orphans.blocks) { block.dispose(); } } else { columns.unshift(result.orphans); } } let cursorX = 48; let maxWidths = result.maxWidths; for (const column of columns) { let cursorY = 64; let maxWidth = 0; for (const block of column.blocks) { let extraWidth = block === makeSpaceForBlock ? 380 : 0; let extraHeight = block === makeSpaceForBlock ? 480 : 72; let xy = block.getRelativeToSurfaceXY(); if (cursorX - xy.x !== 0 || cursorY - xy.y !== 0) { block.moveBy(cursorX - xy.x, cursorY - xy.y); } let heightWidth = block.getHeightWidth(); cursorY += heightWidth.height + extraHeight; let maxWidthWithComments = maxWidths[block.id] || 0; maxWidth = Math.max(maxWidth, Math.max(heightWidth.width + extraWidth, maxWidthWithComments)); } cursorX += maxWidth + 96; } let topComments = workspace.getTopComments(); for (const comment of topComments) { if (comment.setVisible) { comment.setVisible(false); comment.needsAutoPositioning_ = true; comment.setVisible(true); } } setTimeout(() => { // Locate unused local variables... let workspace = this.getWorkspace(); let map = workspace.getVariableMap(); let vars = map.getVariablesOfType(""); let unusedLocals = []; for (const row of vars) { if (row.isLocal) { let usages = map.getVariableUsesById(row.getId()); if (!usages || usages.length === 0) { unusedLocals.push(row); } } } if (unusedLocals.length > 0) { const unusedCount = unusedLocals.length; let message = this.msg("unused-var", { count: unusedCount }); for (let i = 0; i < unusedLocals.length; i++) { let orphan = unusedLocals[i]; if (i > 0) { message += ", "; } message += orphan.name; } if (confirm(message)) { for (const orphan of unusedLocals) { workspace.deleteVariableById(orphan.getId()); } } } // Locate unused local lists... let lists = map.getVariablesOfType("list"); let unusedLists = []; for (const row of lists) { if (row.isLocal) { let usages = map.getVariableUsesById(row.getId()); if (!usages || usages.length === 0) { unusedLists.push(row); } } } if (unusedLists.length > 0) { const unusedCount = unusedLists.length; let message = this.msg("unused-list", { count: unusedCount }); for (let i = 0; i < unusedLists.length; i++) { let orphan = unusedLists[i]; if (i > 0) { message += ", "; } message += orphan.name; } if (confirm(message)) { for (const orphan of unusedLists) { workspace.deleteVariableById(orphan.getId()); } } } _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].endUndoGroup(workspace); }, 100); } /** * Badly Orphaned - might want to delete these! * @param topBlock * @returns {boolean} */ isBlockAnOrphan(topBlock) { return !!topBlock.outputConnection; } /** * Split the top blocks into ordered columns * @param separateOrphans true to keep all orphans separate * @returns {{orphans: {blocks: [Block], x: number, count: number}, cols: [Col]}} */ getOrderedTopBlockColumns(separateOrphans) { let w = this.getWorkspace(); let topBlocks = w.getTopBlocks(); let maxWidths = {}; if (separateOrphans) { let topComments = w.getTopComments(); // todo: tie comments to blocks... find widths and width of block stack row... for (const comment of topComments) { // comment.autoPosition_(); // Hiding and showing repositions the comment right next to it's block - nice! if (comment.setVisible) { comment.setVisible(false); comment.needsAutoPositioning_ = true; comment.setVisible(true); // let bb = comment.block_.svgPath_.getBBox(); let right = comment.getBoundingRectangle().bottomRight.x; // Get top block for stack... let root = comment.block_.getRootBlock(); let left = root.getBoundingRectangle().topLeft.x; maxWidths[root.id] = Math.max(right - left, maxWidths[root.id] || 0); } } } // Default scratch ordering is horrid... Lets try something more clever. /** * @type {Col[]} */ let cols = []; const TOLERANCE = 256; let orphans = { x: -999999, count: 0, blocks: [] }; for (const topBlock of topBlocks) { // let r = b.getBoundingRectangle(); let position = topBlock.getRelativeToSurfaceXY(); /** * @type {Col} */ let bestCol = null; let bestError = TOLERANCE; if (separateOrphans && this.isBlockAnOrphan(topBlock)) { orphans.blocks.push(topBlock); continue; } // Find best columns for (const col of cols) { let err = Math.abs(position.x - col.x); if (err < bestError) { bestError = err; bestCol = col; } } if (bestCol) { // We found a column that we fitted into bestCol.x = (bestCol.x * bestCol.count + position.x) / ++bestCol.count; // re-average the columns as more items get added... bestCol.blocks.push(topBlock); } else { // Create a new column cols.push(new Col(position.x, 1, [topBlock])); } } // if (orphans.blocks.length > 0) { // cols.push(orphans); // } // Sort columns, then blocks inside the columns cols.sort((a, b) => a.x - b.x); for (const col of cols) { col.blocks.sort((a, b) => a.getRelativeToSurfaceXY().y - b.getRelativeToSurfaceXY().y); } return { cols: cols, orphans: orphans, maxWidths: maxWidths }; } /** * Find all the uses of a named variable. * @param {string} id ID of the variable to find. * @return {!Array.} Array of block usages. */ getVariableUsesById(id) { let uses = []; let topBlocks = this.getTopBlocks(true); // todo: Confirm this was the right getTopBlocks? for (const topBlock of topBlocks) { /** @type {!Array} */ let kids = topBlock.getDescendants(); for (const block of kids) { /** @type {!Array} */ let blockVariables = block.getVarModels(); if (blockVariables) { for (const blockVar of blockVariables) { if (blockVar.getId() === id) { uses.push(block); } } } } } return uses; } /** * Quick and dirty replace all instances of one variable / list with another variable / list * @param varId original variable name * @param newVarName new variable name * @param type type of variable ("" = variable, anything else is a list? */ doReplaceVariable(varId, newVarName, type) { let wksp = this.getWorkspace(); let v = wksp.getVariable(newVarName, type); if (!v) { alert(this.msg("var-not-exist")); return; } let newVId = v.getId(); _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].startUndoGroup(wksp); let blocks = this.getVariableUsesById(varId); for (const block of blocks) { try { if (type === "") { block.getField("VARIABLE").setValue(newVId); } else { block.getField("LIST").setValue(newVId); } } catch (e) {// ignore } } _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].endUndoGroup(wksp); } /* function doInjectScripts(codeString) { let w = getWorkspace(); let xml = new XML(); // document.implementation.createDocument(null, "xml"); let x = xml.xmlDoc.firstChild; let tree = math.parse(codeString); console.log(tree); const binaryOperatorTypes = { add: "operator_add", subtract: "operator_subtract", this.multiply: "operator_multiply", divide: "operator_divide", }; const BLOCK_TYPE = { number: "math_number", text: "text", }; function translateMathToXml(x, tree, shadowType) { let xShadowField = null; if (shadowType) { let xShadow = xml.newXml(x, "shadow", { type: shadowType }); if (shadowType === BLOCK_TYPE.number) { xShadowField = xml.newXml(xShadow, "field", { name: "NUM" }); } else if (shadowType === BLOCK_TYPE.text) { xShadowField = xml.newXml(xShadow, "field", { name: "TEXT" }); } } if (!tree || !tree.type) { return; } if (tree.type === "OperatorNode") { let operatorType = binaryOperatorTypes[tree.fn]; if (operatorType) { let xOp = newXml(x, "block", { type: operatorType }); translateMathToXml(xml.newXml(xOp, "value", { name: "NUM1" }), tree.args[0], BLOCK_TYPE.number); translateMathToXml(xml.newXml(xOp, "value", { name: "NUM2" }), tree.args[1], BLOCK_TYPE.number); return; } return; } if (tree.type === "ConstantNode") { // number or text in quotes if (xShadowField) { xml.setAttr(xShadowField, { text: tree.value }); } return; } if (tree.type === "SymbolNode") { // variable let xVar = xml.newXml(x, "block", { type: "data_variable" }); xml.newXml(xVar, "field", { name: "VARIABLE", text: tree.name }); return; } if (tree.type === "FunctionNode") { // Method Call if (tree.fn.name === "join") { let xOp = newXml(x, "block", { type: "operator_join" }); translateMathToXml(xml.newXml(xOp, "value", { name: "STRING1" }), tree.args[0], BLOCK_TYPE.text); translateMathToXml(xml.newXml(xOp, "value", { name: "STRING2" }), tree.args[1], BLOCK_TYPE.text); return; } } } translateMathToXml(x, tree); console.log(x); let ids = Blockly.Xml.domToWorkspace(x, w); console.log(ids); } */ /* function clickInject(e) { let codeString = window.prompt("Griffpatch: Enter an expression (i.e. a+2*3)"); if (codeString) { doInjectScripts(codeString); } e.preventDefault(); return false; } */ /** * Returns a Set of the top blocks in this workspace / sprite * @returns {Set} Set of top blocks */ getTopBlockIDs() { let wksp = this.getWorkspace(); let topBlocks = wksp.getTopBlocks(); let ids = new Set(); for (const block of topBlocks) { ids.add(block.id); } return ids; } /** * Initiates a drag event for all block stacks except those in the set of ids. * But why? - Because we know all the ids of the existing stacks before we paste / duplicate - so we can find the * new stack by excluding all the known ones. * @param ids Set of previously known ids */ beginDragOfNewBlocksNotInIDs(ids) { if (!this.addon.settings.get("enablePasteBlocksAtMouse")) { return; } let wksp = this.getWorkspace(); let topBlocks = wksp.getTopBlocks(); for (const block of topBlocks) { if (!ids.has(block.id)) { // console.log("I found a new block!!! - " + block.id); // todo: move the block to the mouse pointer? let mouseXYClone = { x: this.mouseXY.x, y: this.mouseXY.y }; block.setIntersects(true); // fixes offscreen block pasting in TurboWarp this.domHelpers.triggerDragAndDrop(block.svgPath_, null, mouseXYClone); } } } updateMousePosition(e) { this.mouseXY.x = e.clientX; this.mouseXY.y = e.clientY; } eventMouseMove(e) { this.updateMousePosition(e); } eventKeyDown(e) { const switchCostume = up => { // todo: select previous costume let selected = this.costTabBody.querySelector("div[class*='sprite-selector-item_is-selected']"); let node = up ? selected.parentNode.previousSibling : selected.parentNode.nextSibling; if (node) { let wrapper = node.closest("div[class*=gui_flex-wrapper]"); node.querySelector("div[class^='sprite-selector-item_sprite-name']").click(); node.scrollIntoView({ behavior: "auto", block: "center", inline: "start" }); wrapper.scrollTop = 0; } }; if (document.URL.indexOf("editor") <= 0) { return; } let ctrlKey = e.ctrlKey || e.metaKey; if (e.keyCode === 37 && ctrlKey) { // Ctrl + Left Arrow Key if (document.activeElement.tagName === "INPUT") { return; } if (this.isCostumeEditor()) { switchCostume(true); e.cancelBubble = true; e.preventDefault(); return true; } } if (e.keyCode === 39 && ctrlKey) { // Ctrl + Right Arrow Key if (document.activeElement.tagName === "INPUT") { return; } if (this.isCostumeEditor()) { switchCostume(false); e.cancelBubble = true; e.preventDefault(); return true; } } if (e.keyCode === 86 && ctrlKey && !e.griff) { // Ctrl + V // Set a timeout so we can take control of the paste after the event let ids = this.getTopBlockIDs(); setTimeout(() => { this.beginDragOfNewBlocksNotInIDs(ids); }, 10); } // if (e.keyCode === 220 && (!document.activeElement || document.activeElement.tagName === 'INPUT')) { // // } } eventCopyClick(block, blockOnly) { let wksp = this.getWorkspace(); if (block) { block.select(); let next = blockOnly ? block.getNextBlock() : null; if (next) { next.unplug(false); // setParent(null); } // separate child temporarily document.dispatchEvent(new KeyboardEvent("keydown", { keyCode: 67, ctrlKey: true })); if (next || blockOnly === 2) { setTimeout(() => { if (next) { wksp.undo(); // undo the unplug above... } if (blockOnly === 2) { _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].startUndoGroup(wksp); block.dispose(true); _UndoGroup_js__WEBPACK_IMPORTED_MODULE_1__["default"].endUndoGroup(wksp); } }, 0); } } } eventMouseDown(e) { this.updateMousePosition(e); } eventMouseUp(e) { this.updateMousePosition(e); } initInner(root) { let guiTabs = root.childNodes; if (this.codeTab && guiTabs[0] !== this.codeTab) { // We have been CHANGED!!! - Happens when going to project page, and then back inside again!!! this.domHelpers.unbindAllEvents(); } this.codeTab = guiTabs[0]; this.costTab = guiTabs[1]; this.costTabBody = document.querySelector("div[aria-labelledby=" + this.costTab.id + "]"); this.domHelpers.bindOnce(document, "keydown", (...e) => this.eventKeyDown(...e), true); this.domHelpers.bindOnce(document, "mousemove", (...e) => this.eventMouseMove(...e), true); this.domHelpers.bindOnce(document, "mousedown", (...e) => this.eventMouseDown(...e), true); // true to capture all mouse downs 'before' the dom events handle them this.domHelpers.bindOnce(document, "mouseup", (...e) => this.eventMouseUp(...e), true); } } class Col { /** * @param x {Number} x position (for ordering) * @param count {Number} * @param blocks {[Block]} */ constructor(x, count, blocks) { /** * x position (for ordering) * @type {Number} */ this.x = x; /** * @type {Number} */ this.count = count; /** * @type {[Blockly.Block]} */ this.blocks = blocks; } } /***/ }), /***/ "./src/addons/addons/editor-devtools/DomHelpers.js": /*!*********************************************************!*\ !*** ./src/addons/addons/editor-devtools/DomHelpers.js ***! \*********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return DomHelpers; }); class DomHelpers { constructor(addon) { this.addon = addon; this.vm = addon.tab.traps.vm; /** * @type {eventDetails[]} */ this.events = []; } /** * Simulate a drag and drop programmatically through javascript * @param selectorDrag * @param selectorDrop * @param mouseXY * @param [shiftKey=false] * @returns {boolean} */ triggerDragAndDrop(selectorDrag, selectorDrop, mouseXY, shiftKey) { // function for triggering mouse events shiftKey = shiftKey || false; let fireMouseEvent = function fireMouseEvent(type, elem, centerX, centerY) { let evt = document.createEvent("MouseEvents"); evt.initMouseEvent(type, true, true, window, 1, 1, 1, centerX, centerY, shiftKey, false, false, false, 0, elem); elem.dispatchEvent(evt); }; // fetch target elements let elemDrag = selectorDrag; // document.querySelector(selectorDrag); let elemDrop = selectorDrop; // document.querySelector(selectorDrop); if (!elemDrag /* || !elemDrop*/ ) { return false; } // calculate positions let pos = elemDrag.getBoundingClientRect(); let center1X = Math.floor((pos.left + pos.right) / 2); let center1Y = Math.floor((pos.top + pos.bottom) / 2); // mouse over dragged element and mousedown fireMouseEvent("mouseover", elemDrag, center1X, center1Y); fireMouseEvent("mousedown", elemDrag, center1X, center1Y); // start dragging process over to drop target fireMouseEvent("dragstart", elemDrag, center1X, center1Y); fireMouseEvent("drag", elemDrag, center1X, center1Y); fireMouseEvent("mousemove", elemDrag, center1X, center1Y); if (!elemDrop) { if (mouseXY) { // console.log(mouseXY); let center2X = mouseXY.x; let center2Y = mouseXY.y; fireMouseEvent("drag", elemDrag, center2X, center2Y); fireMouseEvent("mousemove", elemDrag, center2X, center2Y); } return false; } pos = elemDrop.getBoundingClientRect(); let center2X = Math.floor((pos.left + pos.right) / 2); let center2Y = Math.floor((pos.top + pos.bottom) / 2); fireMouseEvent("drag", elemDrag, center2X, center2Y); fireMouseEvent("mousemove", elemDrop, center2X, center2Y); // trigger dragging process on top of drop target fireMouseEvent("mouseenter", elemDrop, center2X, center2Y); fireMouseEvent("dragenter", elemDrop, center2X, center2Y); fireMouseEvent("mouseover", elemDrop, center2X, center2Y); fireMouseEvent("dragover", elemDrop, center2X, center2Y); // release dragged element on top of drop target fireMouseEvent("drop", elemDrop, center2X, center2Y); fireMouseEvent("dragend", elemDrag, center2X, center2Y); fireMouseEvent("mouseup", elemDrag, center2X, center2Y); return true; } bindOnce(dom, event, func, capture) { capture = !!capture; dom.removeEventListener(event, func, capture); dom.addEventListener(event, func, capture); this.events.push(new eventDetails(dom, event, func, capture)); } unbindAllEvents() { for (const event of this.events) { event.dom.removeEventListener(event.event, event.func, event.capture); } this.events = []; } } /** * A record of an event */ class eventDetails { constructor(dom, event, func, capture) { this.dom = dom; this.event = event; this.func = func; this.capture = capture; } } /***/ }), /***/ "./src/addons/addons/editor-devtools/UndoGroup.js": /*!********************************************************!*\ !*** ./src/addons/addons/editor-devtools/UndoGroup.js ***! \********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return UndoGroup; }); /** * This class is dedicated to maintaining the Undo stack of Blockly * It allows us to initiate an undo group such that all subsequent operations are recorded as a single * undoable transaction. */ class UndoGroup { /** * Start an Undo group - begin recording * @param workspace the workspace */ static startUndoGroup(workspace) { const undoStack = workspace.undoStack_; if (undoStack.length) { undoStack[undoStack.length - 1]._devtoolsLastUndo = true; } } /** * End an Undo group - stops recording * @param workspace the workspace */ static endUndoGroup(workspace) { const undoStack = workspace.undoStack_; // Events (responsible for undoStack updates) are delayed with a setTimeout(f, 0) // https://github.com/LLK/scratch-blocks/blob/f159a1779e5391b502d374fb2fdd0cb5ca43d6a2/core/events.js#L182 setTimeout(() => { const group = generateUID(); for (let i = undoStack.length - 1; i >= 0 && !undoStack[i]._devtoolsLastUndo; i--) { undoStack[i].group = group; } }, 0); } } /** * https://github.com/LLK/scratch-blocks/blob/f159a1779e5391b502d374fb2fdd0cb5ca43d6a2/core/events.js#L182 * @returns {string} * @private */ function generateUID() { const CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%()*+,-./:;=?@[]^_`{|}~"; let result = ""; for (let i = 0; i < 20; i++) { result += CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]; } return result; } /***/ }), /***/ "./src/addons/addons/editor-devtools/_runtime_entry.js": /*!*************************************************************!*\ !*** ./src/addons/addons/editor-devtools/_runtime_entry.js ***! \*************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/editor-devtools/userscript.js"); /* harmony import */ var _url_loader_icon_close_svg__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! url-loader!./icon--close.svg */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/editor-devtools/icon--close.svg"); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"], "icon--close.svg": _url_loader_icon_close_svg__WEBPACK_IMPORTED_MODULE_1__["default"] }; /***/ }), /***/ "./src/addons/addons/editor-devtools/userscript.js": /*!*********************************************************!*\ !*** ./src/addons/addons/editor-devtools/userscript.js ***! \*********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _DevTools_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./DevTools.js */ "./src/addons/addons/editor-devtools/DevTools.js"); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console, msg, safeMsg: m }) { const devTools = new _DevTools_js__WEBPACK_IMPORTED_MODULE_0__["default"](addon, msg, m); devTools.init(); }); /***/ }), /***/ "./src/addons/addons/editor-searchable-dropdowns/_runtime_entry.js": /*!*************************************************************************!*\ !*** ./src/addons/addons/editor-searchable-dropdowns/_runtime_entry.js ***! \*************************************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/editor-searchable-dropdowns/userscript.js"); /* harmony import */ var _css_loader_userscript_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-loader!./userscript.css */ "./node_modules/css-loader/index.js!./src/addons/addons/editor-searchable-dropdowns/userscript.css"); /* harmony import */ var _css_loader_userscript_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_userscript_css__WEBPACK_IMPORTED_MODULE_1__); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"], "userscript.css": _css_loader_userscript_css__WEBPACK_IMPORTED_MODULE_1___default.a }; /***/ }), /***/ "./src/addons/addons/editor-searchable-dropdowns/userscript.js": /*!*********************************************************************!*\ !*** ./src/addons/addons/editor-searchable-dropdowns/userscript.js ***! \*********************************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, console, msg }) { const Blockly = await addon.tab.traps.getBlockly(); const vm = addon.tab.traps.vm; const SCRATCH_ITEMS_TO_HIDE = ["RENAME_VARIABLE_ID", "DELETE_VARIABLE_ID", "NEW_BROADCAST_MESSAGE_ID", // From rename-broadcasts addon "RENAME_BROADCAST_MESSAGE_ID"]; const canUseAsGlobalVariableName = (name, type) => { return !vm.runtime.getAllVarNamesOfType(type).includes(name); }; const canUseAsLocalVariableName = (name, type) => { return !vm.editingTarget.lookupVariableByNameAndType(name, type); }; const ADDON_ITEMS = { createGlobalVariable: { enabled: name => canUseAsGlobalVariableName(name, ""), createVariable: (workspace, name) => workspace.createVariable(name) }, createLocalVariable: { enabled: name => canUseAsLocalVariableName(name, ""), createVariable: (workspace, name) => workspace.createVariable(name, "", null, true) }, createGlobalList: { enabled: name => canUseAsGlobalVariableName(name, "list"), createVariable: (workspace, name) => workspace.createVariable(name, "list") }, createLocalList: { enabled: name => canUseAsLocalVariableName(name, "list"), createVariable: (workspace, name) => workspace.createVariable(name, "list", null, true) }, createBroadcast: { enabled: name => canUseAsGlobalVariableName(name, "broadcast_msg"), createVariable: (workspace, name) => workspace.createVariable(name, "broadcast_msg") } }; let blocklyDropDownContent = null; let blocklyDropdownMenu = null; let searchBar = null; // Contains DOM and addon state let items = []; let searchedItems = []; // Tracks internal Scratch state let currentDropdownOptions = []; let resultOfLastGetOptions = []; const oldDropDownDivShow = Blockly.DropDownDiv.show; Blockly.DropDownDiv.show = function (...args) { blocklyDropdownMenu = document.querySelector(".blocklyDropdownMenu"); if (!blocklyDropdownMenu) { return oldDropDownDivShow.call(this, ...args); } blocklyDropdownMenu.focus = () => {}; // no-op focus() so it can't steal it from the search bar searchBar = document.createElement("input"); addon.tab.displayNoneWhileDisabled(searchBar, { display: "flex" }); searchBar.type = "text"; searchBar.addEventListener("input", updateSearch); searchBar.addEventListener("keydown", handleKeyDownEvent); searchBar.classList.add("u-dropdown-searchbar"); blocklyDropdownMenu.insertBefore(searchBar, blocklyDropdownMenu.firstChild); items = Array.from(blocklyDropdownMenu.children).filter(child => child.tagName !== "INPUT").map(element => ({ element, text: element.textContent })); currentDropdownOptions = resultOfLastGetOptions; updateSearch(); // Call the original show method after adding everything so that it can perform the correct size calculations const ret = oldDropDownDivShow.call(this, ...args); // Lock the size of the dropdown blocklyDropDownContent = Blockly.DropDownDiv.getContentDiv(); blocklyDropDownContent.style.width = getComputedStyle(blocklyDropDownContent).width; blocklyDropDownContent.style.height = getComputedStyle(blocklyDropDownContent).height; // This is really strange, but if you don't reinsert the search bar into the DOM then focus() doesn't work blocklyDropdownMenu.insertBefore(searchBar, blocklyDropdownMenu.firstChild); searchBar.focus(); return ret; }; const oldDropDownDivClearContent = Blockly.DropDownDiv.clearContent; Blockly.DropDownDiv.clearContent = function () { oldDropDownDivClearContent.call(this); items = []; searchedItems = []; Blockly.DropDownDiv.content_.style.height = ""; }; const oldFieldDropdownGetOptions = Blockly.FieldDropdown.prototype.getOptions; Blockly.FieldDropdown.prototype.getOptions = function () { const options = oldFieldDropdownGetOptions.call(this); const block = this.sourceBlock_; const isStage = vm.editingTarget && vm.editingTarget.isStage; if (block) { if (block.category_ === "data") { options.push(getMenuItemMessage("createGlobalVariable")); if (!isStage) { options.push(getMenuItemMessage("createLocalVariable")); } } else if (block.category_ === "data-lists") { options.push(getMenuItemMessage("createGlobalList")); if (!isStage) { options.push(getMenuItemMessage("createLocalList")); } } else if (block.type === "event_broadcast_menu" || block.type === "event_whenbroadcastreceived") { options.push(getMenuItemMessage("createBroadcast")); } } // Options aren't normally stored anywhere, so we'll store them ourselves. resultOfLastGetOptions = options; return options; }; const oldFieldVariableOnItemSelected = Blockly.FieldVariable.prototype.onItemSelected; Blockly.FieldVariable.prototype.onItemSelected = function (menu, menuItem) { const sourceBlock = this.sourceBlock_; if (sourceBlock && sourceBlock.workspace && searchBar.value.length !== 0) { const workspace = sourceBlock.workspace; const optionId = menuItem.getValue(); if (Object.prototype.hasOwnProperty.call(ADDON_ITEMS, optionId)) { const addonItem = ADDON_ITEMS[optionId]; Blockly.Events.setGroup(true); const variable = addonItem.createVariable(workspace, searchBar.value.trim()); if (this.sourceBlock_) this.setValue(variable.getId()); Blockly.Events.setGroup(false); return; } } return oldFieldVariableOnItemSelected.call(this, menu, menuItem); }; function selectItem(item, click) { // You can't just use click() or focus() because Blockly uses mousedown and mouseup handlers, not click handlers. item.dispatchEvent(new MouseEvent("mousedown", { relatedTarget: item, bubbles: true })); if (click) item.dispatchEvent(new MouseEvent("mouseup", { relatedTarget: item, bubbles: true })); // Scroll the item into view if it is offscreen. const itemTop = item.offsetTop; const itemEnd = itemTop + item.offsetHeight; const scrollTop = blocklyDropDownContent.scrollTop; const scrollHeight = blocklyDropDownContent.offsetHeight; const scrollEnd = scrollTop + scrollHeight; if (scrollTop > itemTop) { blocklyDropDownContent.scrollTop = itemTop; } else if (itemEnd > scrollEnd) { blocklyDropDownContent.scrollTop = itemEnd - scrollHeight; } } function performSearch() { const rawQuery = searchBar.value.trim(); const query = rawQuery.trim().toLowerCase(); const rank = (item, index) => { // Negative number will hide // Higher numbers will appear first const option = currentDropdownOptions[index]; const optionId = option[1]; if (SCRATCH_ITEMS_TO_HIDE.includes(optionId)) { return query ? -1 : 0; } else if (Object.prototype.hasOwnProperty.call(ADDON_ITEMS, optionId)) { if (!query) { return -1; } const addonInfo = ADDON_ITEMS[optionId]; if (addonInfo.enabled(rawQuery)) { item.element.lastChild.lastChild.textContent = getMenuItemMessage(optionId)[0]; return 0; } return -1; } const itemText = item.text.toLowerCase(); if (query === itemText) { return 2; } if (itemText.startsWith(query)) { return 1; } if (itemText.includes(query)) { return 0; } return -1; }; return items.map((item, index) => ({ item, score: rank(item, index) })).sort(({ score: scoreA }, { score: scoreB }) => Math.max(0, scoreB) - Math.max(0, scoreA)); } function updateSearch() { const previousSearchedItems = searchedItems; searchedItems = performSearch(); let needToUpdateDOM = previousSearchedItems.length !== searchedItems.length; if (!needToUpdateDOM) { for (let i = 0; i < searchedItems.length; i++) { if (searchedItems[i].item !== previousSearchedItems[i].item) { needToUpdateDOM = true; break; } } } if (needToUpdateDOM && previousSearchedItems.length > 0) { for (const { item } of previousSearchedItems) { item.element.remove(); } for (const { item } of searchedItems) { blocklyDropdownMenu.appendChild(item.element); } } for (const { item, score } of searchedItems) { item.element.hidden = score < 0; } } function handleKeyDownEvent(event) { if (event.key === "Enter") { // Reimplement enter to select item to account for hidden items and default to the top item. event.stopPropagation(); event.preventDefault(); const selectedItem = document.querySelector(".goog-menuitem-highlight"); if (selectedItem && !selectedItem.hidden) { selectItem(selectedItem, true); return; } const selectedBlock = Blockly.selected; if (searchBar.value === "" && selectedBlock) { if (selectedBlock.type === "event_broadcast" || selectedBlock.type === "event_broadcastandwait" || selectedBlock.type === "event_whenbroadcastreceived") { // The top item of these dropdowns is always "New message" // When pressing enter on an empty search bar, we close the dropdown instead of making a new broadcast. Blockly.DropDownDiv.hide(); return; } } for (const { item } of searchedItems) { if (!item.element.hidden) { selectItem(item.element, true); break; } } // If there is no top value, do nothing and leave the dropdown open } else if (event.key === "Escape") { Blockly.DropDownDiv.hide(); } else if (event.key === "ArrowDown" || event.key === "ArrowUp") { // Reimplement keyboard navigation to account for hidden items. event.preventDefault(); event.stopPropagation(); const items = searchedItems.filter(i => i.score >= 0).map(i => i.item); if (items.length === 0) { return; } let selectedIndex = -1; for (let i = 0; i < items.length; i++) { if (items[i].element.classList.contains("goog-menuitem-highlight")) { selectedIndex = i; break; } } const lastIndex = items.length - 1; let newIndex = 0; if (event.key === "ArrowDown") { if (selectedIndex === -1 || selectedIndex === lastIndex) { newIndex = 0; } else { newIndex = selectedIndex + 1; } } else { if (selectedIndex === -1 || selectedIndex === 0) { newIndex = lastIndex; } else { newIndex = selectedIndex - 1; } } selectItem(items[newIndex].element, false); } } function getMenuItemMessage(message) { var _searchBar; // Format used internally by Scratch: // [human readable name, internal name] return [msg(message, { name: ((_searchBar = searchBar) === null || _searchBar === void 0 ? void 0 : _searchBar.value.trim()) || "" }), message]; } }); /***/ }), /***/ "./src/addons/addons/find-bar/_runtime_entry.js": /*!******************************************************!*\ !*** ./src/addons/addons/find-bar/_runtime_entry.js ***! \******************************************************/ /*! exports provided: resources */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; }); /* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/find-bar/userscript.js"); /* harmony import */ var _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-loader!./userstyle.css */ "./node_modules/css-loader/index.js!./src/addons/addons/find-bar/userstyle.css"); /* harmony import */ var _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1__); /* generated by pull.js */ const resources = { "userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"], "userstyle.css": _css_loader_userstyle_css__WEBPACK_IMPORTED_MODULE_1___default.a }; /***/ }), /***/ "./src/addons/addons/find-bar/blockly/BlockItem.js": /*!*********************************************************!*\ !*** ./src/addons/addons/find-bar/blockly/BlockItem.js ***! \*********************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "default", function() { return BlockItem; }); class BlockItem { constructor(cls, procCode, labelID, y) { this.cls = cls; this.procCode = procCode; this.labelID = labelID; this.y = y; this.lower = procCode.toLowerCase(); /** * An Array of block ids * @type {Array.} */ this.clones = null; this.eventName = null; } /** * True if the blockID matches a black represented by this BlockItem * @param id * @returns {boolean} */ matchesID(id) { if (this.labelID === id) { return true; } if (this.clones) { for (const cloneID of this.clones) { if (cloneID === id) { return true; } } } return false; } } /***/ }), /***/ "./src/addons/addons/find-bar/userscript.js": /*!**************************************************!*\ !*** ./src/addons/addons/find-bar/userscript.js ***! \**************************************************/ /*! exports provided: default */ /***/ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; __webpack_require__.r(__webpack_exports__); /* harmony import */ var _blockly_BlockItem_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./blockly/BlockItem.js */ "./src/addons/addons/find-bar/blockly/BlockItem.js"); /* harmony import */ var _blockly_BlockInstance_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./blockly/BlockInstance.js */ "./src/addons/addons/find-bar/blockly/BlockInstance.js"); /* harmony import */ var _blockly_Utils_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./blockly/Utils.js */ "./src/addons/addons/find-bar/blockly/Utils.js"); /* harmony default export */ __webpack_exports__["default"] = (async function ({ addon, msg, console }) { const Blockly = await addon.tab.traps.getBlockly(); class FindBar { constructor() { this.utils = new _blockly_Utils_js__WEBPACK_IMPORTED_MODULE_2__["default"](addon); this.prevValue = ""; this.findBarOuter = null; this.findWrapper = null; this.findInput = null; this.dropdownOut = null; this.dropdown = new Dropdown(this.utils); document.addEventListener("keydown", e => this.eventKeyDown(e), true); } get workspace() { return Blockly.getMainWorkspace(); } createDom(root) { this.findBarOuter = document.createElement("div"); this.findBarOuter.className = "sa-find-bar"; addon.tab.displayNoneWhileDisabled(this.findBarOuter, { display: "flex" }); root.appendChild(this.findBarOuter); this.findWrapper = this.findBarOuter.appendChild(document.createElement("span")); this.findWrapper.className = "sa-find-wrapper"; this.dropdownOut = this.findWrapper.appendChild(document.createElement("label")); this.dropdownOut.className = "sa-find-dropdown-out"; this.findInput = this.dropdownOut.appendChild(document.createElement("input")); this.findInput.className = addon.tab.scratchClass("input_input-form", { others: "sa-find-input" }); // for