467 lines
17 KiB
JavaScript
467 lines
17 KiB
JavaScript
(window["webpackJsonpGUI"] = window["webpackJsonpGUI"] || []).push([["addon-entry-swap-local-global"],{
|
|
|
|
/***/ "./node_modules/css-loader/index.js!./src/addons/addons/swap-local-global/style.css":
|
|
/*!*********************************************************************************!*\
|
|
!*** ./node_modules/css-loader!./src/addons/addons/swap-local-global/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-swap-local-global-hint {\n padding-bottom: 1rem;\n}\n.sa-swap-local-global-stage {\n /* resolves scratch class style conflict */\n display: block;\n}\n", ""]);
|
|
|
|
// exports
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./src/addons/addons/swap-local-global/_runtime_entry.js":
|
|
/*!***************************************************************!*\
|
|
!*** ./src/addons/addons/swap-local-global/_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/swap-local-global/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/swap-local-global/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/swap-local-global/userscript.js":
|
|
/*!***********************************************************!*\
|
|
!*** ./src/addons/addons/swap-local-global/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,
|
|
msg,
|
|
console
|
|
}) {
|
|
const ScratchBlocks = await addon.tab.traps.getBlockly();
|
|
const vm = addon.tab.traps.vm;
|
|
|
|
const getTargetName = target => {
|
|
if (target.isStage) {
|
|
// Stage always has an internal name of "Stage", but we want a translatable name
|
|
return ScratchBlocks.ScratchMsgs.translate("SENSING_OF_STAGE", "Stage");
|
|
}
|
|
|
|
return target.getName();
|
|
};
|
|
|
|
const getTargetsThatUseVariable = id => vm.runtime.targets.filter(target => target.isOriginal).filter(target => Object.values(target.blocks._blocks).find(block => block.fields.LIST && block.fields.LIST.id === id || block.fields.VARIABLE && block.fields.VARIABLE.id === id)); // https://github.com/LLK/scratch-vm/blob/7c6f1e44fb0a9b0d0279225cd4c62fbe59b6af54/src/engine/blocks.js#L388-L394
|
|
|
|
|
|
const getTargetsWithLocalVariableNamed = (name, type) => vm.runtime.targets.filter(target => target.isOriginal && target.lookupVariableByNameAndType(name, type, true));
|
|
|
|
const getVmVariable = id => vm.editingTarget.lookupVariableById(id);
|
|
|
|
const isStageSelected = () => vm.editingTarget.isStage;
|
|
|
|
const deleteVariableWithoutDeletingBlocks = (workspace, variable) => {
|
|
// variable can be an ID or an actual Blockly variable object
|
|
if (typeof variable === "string") {
|
|
variable = workspace.getVariableById(variable);
|
|
}
|
|
|
|
workspace.variableMap_.deleteVariable(variable);
|
|
};
|
|
|
|
const syncBlockVariableNameWithActualVariableName = (workspace, id) => {
|
|
const variable = workspace.getVariableById(id);
|
|
|
|
for (const block of workspace.getAllBlocks()) {
|
|
block.updateVarName(variable);
|
|
}
|
|
};
|
|
|
|
let _undoRedoPreserveStateCallback = null;
|
|
|
|
const finishUndoRedoState = () => {
|
|
if (_undoRedoPreserveStateCallback) {
|
|
_undoRedoPreserveStateCallback();
|
|
|
|
_undoRedoPreserveStateCallback = null;
|
|
}
|
|
}; // https://github.com/LLK/scratch-blocks/blob/0d6012df1e18e66d82c1247f1f6d772a719982a7/core/variable_events.js#L194
|
|
|
|
|
|
const customUndoVarDelete = function customUndoVarDelete(forward) {
|
|
const workspace = this.getEventWorkspace_();
|
|
|
|
if (forward) {
|
|
_undoRedoPreserveStateCallback = beginPreservingState(workspace, this.varId);
|
|
deleteVariableWithoutDeletingBlocks(workspace, this.varId);
|
|
} else {
|
|
workspace.createVariable(this.varName, this.varType, this.varId, this.isLocal, this.isCloud);
|
|
finishUndoRedoState();
|
|
}
|
|
}; // https://github.com/LLK/scratch-blocks/blob/0d6012df1e18e66d82c1247f1f6d772a719982a7/core/variable_events.js#L131
|
|
|
|
|
|
const customUndoVarCreate = function customUndoVarCreate(forward) {
|
|
const workspace = this.getEventWorkspace_();
|
|
|
|
if (forward) {
|
|
workspace.createVariable(this.varName, this.varType, this.varId, this.isLocal, this.isCloud);
|
|
finishUndoRedoState();
|
|
} else {
|
|
_undoRedoPreserveStateCallback = beginPreservingState(workspace, this.varId);
|
|
deleteVariableWithoutDeletingBlocks(workspace, this.varId);
|
|
}
|
|
};
|
|
|
|
const flushBlocklyEventQueue = () => ScratchBlocks.Events.fireNow_();
|
|
|
|
const beginPreservingState = (workspace, id) => {
|
|
// oldMonitorState is an instance of https://github.com/LLK/scratch-vm/blob/develop/src/engine/monitor-record.js or undefined
|
|
const oldMonitorState = vm.runtime._monitorState.get(id);
|
|
|
|
const oldVmVariable = getVmVariable(id);
|
|
return () => {
|
|
flushBlocklyEventQueue();
|
|
const newVmVariable = getVmVariable(id);
|
|
|
|
if (newVmVariable) {
|
|
newVmVariable.value = oldVmVariable.value;
|
|
} // Update the variable monitor (state is maintained separately)
|
|
|
|
|
|
if (oldMonitorState) {
|
|
if (oldMonitorState.visible) {
|
|
vm.runtime.monitorBlocks.changeBlock({
|
|
id,
|
|
element: "checkbox",
|
|
value: true
|
|
});
|
|
}
|
|
|
|
const isLocal = !vm.runtime.getTargetForStage().variables[id];
|
|
let newMonitorState = oldMonitorState;
|
|
|
|
if (isLocal) {
|
|
const target = vm.editingTarget;
|
|
newMonitorState = newMonitorState.set("targetId", target.id);
|
|
newMonitorState = newMonitorState.set("spriteName", target.getName());
|
|
} else {
|
|
newMonitorState = newMonitorState.set("targetId", null);
|
|
newMonitorState = newMonitorState.set("spriteName", null);
|
|
}
|
|
|
|
if (newVmVariable.name !== oldVmVariable.name) {
|
|
const monitorBlocks = vm.runtime.monitorBlocks;
|
|
const block = monitorBlocks.getBlock(id);
|
|
|
|
if (block) {
|
|
newMonitorState = newMonitorState.set("params", monitorBlocks._getBlockParams(block));
|
|
}
|
|
}
|
|
|
|
vm.runtime.requestAddMonitor(newMonitorState);
|
|
}
|
|
|
|
if (newVmVariable.name !== oldVmVariable.name) {
|
|
syncBlockVariableNameWithActualVariableName(workspace, id);
|
|
}
|
|
};
|
|
};
|
|
|
|
const convertVariable = (oldBlocklyVariable, newLocal, newCloud) => {
|
|
const CLOUD_PREFIX = "☁ ";
|
|
const name = oldBlocklyVariable.name;
|
|
const id = oldBlocklyVariable.getId();
|
|
const type = oldBlocklyVariable.type;
|
|
const isLocal = oldBlocklyVariable.isLocal;
|
|
const isCloud = oldBlocklyVariable.isCloud;
|
|
|
|
if (isLocal === newLocal && isCloud === newCloud) {
|
|
return;
|
|
} // Cloud variables must always be global
|
|
|
|
|
|
if (newCloud && newLocal) {
|
|
alert(msg("cant-convert-cloud"));
|
|
return;
|
|
}
|
|
|
|
const editingTarget = vm.editingTarget;
|
|
|
|
if (isLocal !== newLocal) {
|
|
if (newLocal) {
|
|
// Stage cannot have local variables
|
|
if (isStageSelected()) {
|
|
alert(msg("cant-convert-stage"));
|
|
return;
|
|
} // Variables used by unfocused sprites cannot be made local
|
|
// That includes cases where the variable is used by multiple sprites and where it's only used by an unfocused sprite
|
|
|
|
|
|
const targets = getTargetsThatUseVariable(id);
|
|
|
|
if (!targets.every(i => i === editingTarget)) {
|
|
if (targets.length > 1) {
|
|
alert(msg("cant-convert-to-local", {
|
|
sprites: targets.map(getTargetName).join(", ")
|
|
}));
|
|
} else {
|
|
alert(msg("cant-convert-used-elsewhere", {
|
|
sprite: getTargetName(targets[0])
|
|
}));
|
|
}
|
|
|
|
return;
|
|
}
|
|
} else {
|
|
// Global variables must not conflict with any local variables
|
|
const targets = getTargetsWithLocalVariableNamed(name, type).filter(target => target !== editingTarget);
|
|
|
|
if (targets.length > 0) {
|
|
alert(msg("cant-convert-conflict", {
|
|
sprites: targets.map(getTargetName).join(", ")
|
|
}));
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
let newName = name;
|
|
|
|
if (isCloud !== newCloud) {
|
|
if (newCloud) {
|
|
newName = CLOUD_PREFIX + name;
|
|
} else if (name.startsWith(CLOUD_PREFIX)) {
|
|
newName = name.replace(CLOUD_PREFIX, "");
|
|
}
|
|
}
|
|
|
|
const workspace = oldBlocklyVariable.workspace;
|
|
const finishPreservingState = beginPreservingState(workspace, id);
|
|
ScratchBlocks.Events.setGroup(true);
|
|
|
|
try {
|
|
deleteVariableWithoutDeletingBlocks(workspace, oldBlocklyVariable);
|
|
workspace.createVariable(newName, type, id, newLocal, newCloud);
|
|
} finally {
|
|
ScratchBlocks.Events.setGroup(false);
|
|
} // 2 items will be added to the queue: a variable create and delete
|
|
// We override their undo handlers to make undo/redo work properly
|
|
|
|
|
|
flushBlocklyEventQueue();
|
|
const stack = workspace.undoStack_;
|
|
const createEvent = stack[stack.length - 1];
|
|
const deleteEvent = stack[stack.length - 2];
|
|
|
|
if (createEvent instanceof ScratchBlocks.Events.VarCreate && deleteEvent instanceof ScratchBlocks.Events.VarDelete) {
|
|
createEvent.run = customUndoVarCreate;
|
|
deleteEvent.run = customUndoVarDelete;
|
|
}
|
|
|
|
finishPreservingState();
|
|
};
|
|
|
|
const canUserUseCloudVariables = () => {
|
|
const blocksWrapper = document.querySelector('[class^="gui_blocks-wrapper_"]');
|
|
let internalNode = blocksWrapper[addon.tab.traps.getInternalKey(blocksWrapper)];
|
|
|
|
while (true) {
|
|
var _internalNode$stateNo, _internalNode$stateNo2;
|
|
|
|
if (!internalNode) {
|
|
return false;
|
|
}
|
|
|
|
const canUseCloud = (_internalNode$stateNo = internalNode.stateNode) === null || _internalNode$stateNo === void 0 ? void 0 : (_internalNode$stateNo2 = _internalNode$stateNo.props) === null || _internalNode$stateNo2 === void 0 ? void 0 : _internalNode$stateNo2.canUseCloud;
|
|
|
|
if (typeof canUseCloud === "boolean") {
|
|
return canUseCloud;
|
|
}
|
|
|
|
internalNode = internalNode.child;
|
|
}
|
|
};
|
|
|
|
const addMoreOptionsToPrompt = variable => {
|
|
if (addon.self.disabled) {
|
|
return;
|
|
}
|
|
|
|
const promptBody = document.querySelector('[class^="prompt_body_"]');
|
|
|
|
if (!promptBody) {
|
|
return;
|
|
}
|
|
|
|
const headerTitle = promptBody.parentElement.querySelector('[class^="modal_header-item_"]');
|
|
|
|
if (headerTitle) {
|
|
if (variable.type === "") {
|
|
headerTitle.textContent = msg("edit-variable-header");
|
|
} else {
|
|
headerTitle.textContent = msg("edit-list-header");
|
|
}
|
|
}
|
|
|
|
const root = document.createElement("div");
|
|
addon.tab.displayNoneWhileDisabled(root);
|
|
|
|
const createLabeledInput = (text, value) => {
|
|
const outer = document.createElement("label");
|
|
const input = document.createElement("input");
|
|
|
|
if (value === "checkbox") {
|
|
input.type = "checkbox";
|
|
} else {
|
|
input.name = "variableScopeOption";
|
|
input.type = "radio";
|
|
input.value = value;
|
|
}
|
|
|
|
outer.appendChild(input);
|
|
const label = document.createElement("span");
|
|
label.textContent = text;
|
|
outer.appendChild(label);
|
|
return {
|
|
outer,
|
|
label,
|
|
input
|
|
};
|
|
};
|
|
|
|
const promptDisabledClass = addon.tab.scratchClass("prompt_disabled-label");
|
|
const noLocalsInStageSection = document.createElement("div");
|
|
noLocalsInStageSection.className = addon.tab.scratchClass("prompt_info-message", "prompt_cloud-option", {
|
|
others: "sa-swap-local-global-stage"
|
|
});
|
|
noLocalsInStageSection.appendChild(Object.assign(document.createElement("span"), {
|
|
textContent: addon.tab.scratchMessage("gui.gui.variablePromptAllSpritesMessage")
|
|
}));
|
|
const scopeSection = document.createElement("div");
|
|
scopeSection.className = addon.tab.scratchClass("prompt_options-row", "prompt_cloud-option");
|
|
const forAllSprites = createLabeledInput(addon.tab.scratchMessage("gui.gui.variableScopeOptionAllSprites"), "global");
|
|
const forThisSpriteOnly = createLabeledInput(addon.tab.scratchMessage("gui.gui.variableScopeOptionSpriteOnly"), "local");
|
|
forAllSprites.input.checked = !variable.isLocal;
|
|
forThisSpriteOnly.input.checked = variable.isLocal;
|
|
scopeSection.appendChild(forAllSprites.outer);
|
|
scopeSection.appendChild(forThisSpriteOnly.outer);
|
|
const cloudSection = document.createElement("div");
|
|
cloudSection.className = addon.tab.scratchClass("prompt_cloud-option");
|
|
const cloudCheckbox = createLabeledInput(addon.tab.scratchMessage("gui.gui.cloudVariableOption"), "checkbox");
|
|
cloudCheckbox.input.checked = variable.isCloud;
|
|
|
|
if (!vm.runtime.canAddCloudVariable() && !variable.isCloud) {
|
|
cloudCheckbox.input.disabled = true;
|
|
cloudSection.classList.add(promptDisabledClass);
|
|
}
|
|
|
|
cloudSection.appendChild(cloudCheckbox.outer);
|
|
|
|
const updateDisabledInputs = () => {
|
|
const thisSpriteOnlyDisabled = cloudCheckbox.input.checked;
|
|
forThisSpriteOnly.input.disabled = thisSpriteOnlyDisabled;
|
|
forThisSpriteOnly.label.classList.toggle(promptDisabledClass, thisSpriteOnlyDisabled);
|
|
|
|
if (thisSpriteOnlyDisabled) {
|
|
forAllSprites.input.click();
|
|
}
|
|
};
|
|
|
|
cloudCheckbox.input.addEventListener("change", updateDisabledInputs);
|
|
updateDisabledInputs();
|
|
let isAnythingConfigurable = false;
|
|
|
|
if (isStageSelected()) {
|
|
root.appendChild(noLocalsInStageSection);
|
|
} else {
|
|
isAnythingConfigurable = true;
|
|
root.appendChild(scopeSection);
|
|
}
|
|
|
|
if (variable.type === "" && canUserUseCloudVariables()) {
|
|
isAnythingConfigurable = true;
|
|
root.appendChild(cloudSection);
|
|
}
|
|
|
|
if (isAnythingConfigurable) {
|
|
root.prepend(Object.assign(document.createElement("div"), {
|
|
textContent: msg("edit"),
|
|
className: "sa-swap-local-global-hint"
|
|
}));
|
|
}
|
|
|
|
promptBody.insertBefore(root, promptBody.lastChild);
|
|
return {
|
|
isLocal: () => forThisSpriteOnly.input.checked,
|
|
isCloud: () => cloudCheckbox.input.checked
|
|
};
|
|
}; // https://github.com/LLK/scratch-blocks/blob/c5014f61e2e538e99601a9e0cb39e339e44c3910/core/variables.js#L470
|
|
|
|
|
|
const originalRenameVariable = ScratchBlocks.Variables.renameVariable;
|
|
|
|
ScratchBlocks.Variables.renameVariable = function (workspace, variable, opt_callback) {
|
|
const ret = originalRenameVariable.call(this, workspace, variable, (...args) => {
|
|
if (opt_callback) {
|
|
opt_callback(...args);
|
|
}
|
|
|
|
if (!addon.self.disabled && prompt) {
|
|
convertVariable(variable, prompt.isLocal(), prompt.isCloud());
|
|
}
|
|
});
|
|
const prompt = addMoreOptionsToPrompt(variable);
|
|
return ret;
|
|
};
|
|
|
|
addon.tab.createBlockContextMenu((items, block) => {
|
|
if (!addon.self.disabled && (block.getCategory() === "data" || block.getCategory() === "data-lists")) {
|
|
const variable = block.workspace.getVariableById(block.getVars()[0]);
|
|
|
|
if (variable) {
|
|
if (items.length > 0) {
|
|
if (items[0].text === ScratchBlocks.ScratchMsgs.translate("RENAME_VARIABLE")) {
|
|
items[0].text = msg("edit-variable-option");
|
|
} else if (items[0].text === ScratchBlocks.ScratchMsgs.translate("RENAME_LIST")) {
|
|
items[0].text = msg("edit-list-option");
|
|
}
|
|
}
|
|
|
|
items.push({
|
|
enabled: true,
|
|
separator: true,
|
|
text: msg("to-".concat(variable.isLocal ? "global" : "local")),
|
|
callback: () => convertVariable(variable, !variable.isLocal, variable.isCloud)
|
|
});
|
|
}
|
|
}
|
|
|
|
return items;
|
|
}, {
|
|
flyout: true,
|
|
blocks: true
|
|
});
|
|
});
|
|
|
|
/***/ })
|
|
|
|
}]);
|
|
//# sourceMappingURL=addon-entry-swap-local-global.js.map
|