mirror of
https://github.com/overte-org/overte.git
synced 2025-06-15 22:59:16 +02:00
453 lines
20 KiB
JavaScript
453 lines
20 KiB
JavaScript
//
|
|
// renderWithZonesManager.js
|
|
//
|
|
// Created by Alezia Kurdis on January 28th, 2024.
|
|
// Copyright 2024 Overte e.V.
|
|
//
|
|
// This script is to manage the zone in the property renderWithZones more efficiently in the Create Application.
|
|
// It allows a global view over a specific selection with possibility to
|
|
// REPLACE, REMOVE or ADD zones on those properties more efficiently.
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
//
|
|
let rwzmSelectedId = [];
|
|
let rwzmAllZonesList = [];
|
|
let rwzmUsedZonesList = [];
|
|
let rwzmSelectedEntitiesData = [];
|
|
let rwzmUndo = [];
|
|
|
|
let enforceLocked = false;
|
|
|
|
const RWZ_ZONE_SCAN_RADIUS = 27713; //maximal radius to cover the entire domain.
|
|
|
|
let rwzmOverlayWebWindow = null;
|
|
|
|
function renderWithZonesManager(entityIDs, highlightedID = "") {
|
|
if (rwzmGetCheckSum(entityIDs) !== rwzmGetCheckSum(rwzmSelectedId)) {
|
|
rwzmUndo = [];
|
|
}
|
|
rwzmSelectedId = entityIDs;
|
|
if (entityIDs.length === 0) {
|
|
audioFeedback.rejection();
|
|
Window.alert("You have nothing selected.");
|
|
return;
|
|
} else {
|
|
rwzmAllZonesList = [];
|
|
rwzmUsedZonesList = [];
|
|
rwzmSelectedEntitiesData = [];
|
|
rwzmAllZonesList = rwzmGetExistingZoneList();
|
|
|
|
let properties;
|
|
let i = 0;
|
|
let j = 0;
|
|
let rwzmData = {};
|
|
for (i = 0; i < entityIDs.length; i++ ){
|
|
properties = Entities.getEntityProperties(entityIDs[i], ["renderWithZones", "locked", "name", "type"]);
|
|
//Identify the unique zone used in renderWithZones properties of the entities and make the list of this in rwzmUsedZonesList
|
|
if (properties.renderWithZones.length > 0) {
|
|
for (j = 0; j < properties.renderWithZones.length; j++ ){
|
|
if (rwzmUsedZonesList.indexOf(properties.renderWithZones[j]) === -1) {
|
|
rwzmUsedZonesList.push(properties.renderWithZones[j]);
|
|
}
|
|
}
|
|
}
|
|
//Make the list of entities, with their id, renderWithZones, locked, in rwzmSelectedEntitiesData
|
|
rwzmData = {
|
|
"id": entityIDs[i],
|
|
"name": properties.name,
|
|
"type": properties.type,
|
|
"renderWithZones": properties.renderWithZones,
|
|
"locked": properties.locked
|
|
};
|
|
rwzmSelectedEntitiesData.push(rwzmData);
|
|
}
|
|
}
|
|
|
|
if (rwzmOverlayWebWindow === null) {
|
|
rwzmOverlayWebWindow = new OverlayWebWindow({
|
|
title: "RenderWithZones Manager",
|
|
source: Script.resolvePath("renderWithZonesManager.html"),
|
|
width: 1100,
|
|
height: 600
|
|
});
|
|
|
|
rwzmOverlayWebWindow.closed.connect(uiHasClosed);
|
|
rwzmOverlayWebWindow.webEventReceived.connect(webEventReceiver);
|
|
}
|
|
|
|
rwzmGenerateUI(highlightedID);
|
|
}
|
|
|
|
function rwzmGetCheckSum(array) {
|
|
let i = 0;
|
|
let sum = 0;
|
|
let strForm = JSON.stringify(array);
|
|
for (i = 0; i < strForm.length; i++) {
|
|
sum = sum + strForm.charCodeAt(i);
|
|
}
|
|
return sum;
|
|
}
|
|
function uiHasClosed() {
|
|
rwzmOverlayWebWindow.closed.disconnect(uiHasClosed);
|
|
rwzmOverlayWebWindow.webEventReceived.disconnect(webEventReceiver);
|
|
rwzmOverlayWebWindow = null;
|
|
}
|
|
|
|
function rwzmGenerateUI(highlightedID) {
|
|
let canUnlock = Entities.canAdjustLocks();
|
|
let uiContent = "";
|
|
let i = 0;
|
|
let k = 0;
|
|
let zones = "";
|
|
let name = "";
|
|
let elementClass = "";
|
|
let firstClass = "";
|
|
let isLocked = "";
|
|
let selectionBox = "";
|
|
let setCheck = "";
|
|
let addAction = "";
|
|
let toHighlight = "";
|
|
let viewBtnCaption = "";
|
|
let warning = false;
|
|
uiContent = uiContent + ' <h1>RenderWithZones Manager</h1><hr>\n';
|
|
if (canUnlock) {
|
|
if (enforceLocked) {
|
|
setCheck = " checked";
|
|
} else {
|
|
setCheck = "";
|
|
}
|
|
}
|
|
uiContent = uiContent + ' <table>\n';
|
|
uiContent = uiContent + ' <tr valign = "top">\n';
|
|
uiContent = uiContent + ' <td style="width: 40%">\n';
|
|
if (rwzmUndo.length === 0) {
|
|
undoBtnCode = "";
|
|
} else {
|
|
undoBtnCode = '<button class="greyBtn small" onClick="undo();">Undo</button>';
|
|
}
|
|
uiContent = uiContent + ' <table><tr><td style="width: 40%;"><h2>Visibility Zones:</h2></td><td style="width: 60%; text-align: right;">' + undoBtnCode + '</td></tr></table>\n';
|
|
uiContent = uiContent + ' <div class="listContainer"><table>\n';
|
|
uiContent = uiContent + ' <tr style="width: 100%"><td class="cells header" style="width: 60%;">';
|
|
uiContent = uiContent + '<b>ZONES</b></td><td class="cells header" style="width: 40%;"><b>ACTIONS (On listed entities)</b></td></tr>\n';
|
|
for (i = 0; i < rwzmUsedZonesList.length; i++ ) {
|
|
name = rwzmGetZoneName(rwzmUsedZonesList[i]);
|
|
elementClass = "line";
|
|
firstClass = "cells";
|
|
if (name === "") {
|
|
name = rwzmGenerateUnidentifiedZoneName(rwzmUsedZonesList[i]);
|
|
elementClass = "errorline";
|
|
warning = true;
|
|
}
|
|
toHighlight = rwzmUsedZonesList[i];
|
|
viewBtnCaption = "View";
|
|
if ( rwzmUsedZonesList[i] === highlightedID) {
|
|
toHighlight = "";
|
|
viewBtnCaption = "Hide";
|
|
firstClass = "highlightedCells";
|
|
if (elementClass === "line") {
|
|
elementClass = "lineInverted";
|
|
}
|
|
}
|
|
uiContent = uiContent + ' <tr style="width: 100%"><td class="' + firstClass + ' ' + elementClass;
|
|
uiContent = uiContent + '"><div style = "width:100%; height:100%; padding: 3px;" onClick = "highlight(' + "'";
|
|
uiContent = uiContent + toHighlight + "'" +');">' + rwzmGetTruncatedString(name, 30) + '</div></td><td class="' + firstClass + ' ' + elementClass + '">';
|
|
uiContent = uiContent + '<button class="greyBtn small" onclick="highlight(' + "'" + toHighlight + "'" +');">' + viewBtnCaption + '</button>';
|
|
uiContent = uiContent + '<button class="redBtn" onclick="removeZoneOnAllEntities(' + "'" + rwzmUsedZonesList[i] + "'" +');">Remove</button>';
|
|
uiContent = uiContent + '<button class="blueBtn" onclick="replaceZoneOnAllEntities(' + "'" + rwzmUsedZonesList[i] + "'" +');">Replace</button></td></tr>\n';
|
|
}
|
|
uiContent = uiContent + ' </table></div>\n';
|
|
uiContent = uiContent + ' </td>\n';
|
|
uiContent = uiContent + ' <td style="width: 3%"> </td>\n';
|
|
uiContent = uiContent + ' <td style="width: 57%">\n';
|
|
uiContent = uiContent + ' <table><tr><td style="width: 15%;"><h2>Entities:</h2></td><td style="width: 30%;"><button class="addbtn" onClick="addZoneToEntity(getIdsFromScope());">Add to Selected</button></td>';
|
|
uiContent = uiContent + '<td style="width: 55%; text-align: right;"><input type="checkbox" id = "enforceLocked"'+ setCheck + ' onClick="refresh()";> <font style="color: #3bc7ff;">Modify locked entities for me.</font></td></tr></table>\n';
|
|
uiContent = uiContent + ' <div class="listContainer"><table>\n';
|
|
uiContent = uiContent + ' <tr style="width: 100%;"><td class="cells header" style="width: 5%;">';
|
|
uiContent = uiContent + '<input type="checkbox" id="fullScope" onClick="selectAllOrNone();"></td><td class="cells header" style="width: 3%;">';
|
|
uiContent = uiContent + '<font class="hifiGlyphs"></font></td><td class="cells header" style = "width: 45%;"><b>ENTITIES</b></td><td class="cells header" style = "width: 40%;">';
|
|
uiContent = uiContent + '<b>RENDER WITH ZONES</b></td><td style = "width: 7%;"class="cells header"> </td></tr>\n';
|
|
for (i = 0; i < rwzmSelectedEntitiesData.length; i++ ) {
|
|
elementClass = "line";
|
|
firstClass = "cells";
|
|
if (rwzmSelectedEntitiesData[i].renderWithZones.indexOf(highlightedID) !== -1) {
|
|
firstClass = "highlightedCells";
|
|
elementClass = "lineInverted";
|
|
}
|
|
zones = " ";
|
|
if (rwzmSelectedEntitiesData[i].renderWithZones.length > 0) {
|
|
for (k = 0; k < rwzmSelectedEntitiesData[i].renderWithZones.length; k++ ) {
|
|
name = rwzmGetTruncatedString(rwzmGetZoneName(rwzmSelectedEntitiesData[i].renderWithZones[k]),30);
|
|
if (name === "") {
|
|
name = rwzmGetTruncatedString(rwzmGenerateUnidentifiedZoneName(rwzmSelectedEntitiesData[i].renderWithZones[k]),30);
|
|
}
|
|
if ((canUnlock && enforceLocked && rwzmSelectedEntitiesData[i].locked) || !rwzmSelectedEntitiesData[i].locked) {
|
|
name = name + " <span class='delBtn' onClick='removeZoneFromRWZ(" + '"' + rwzmSelectedEntitiesData[i].id + '", "' + rwzmSelectedEntitiesData[i].renderWithZones[k] + '"' + ");'>⮾</span>";
|
|
}
|
|
|
|
if (k === 0) {
|
|
zones = zones + name;
|
|
} else {
|
|
zones = zones + "<br>" + name;
|
|
}
|
|
}
|
|
}
|
|
isLocked = " ";
|
|
selectionBox = " ";
|
|
addAction = " ";
|
|
if ((canUnlock && enforceLocked && rwzmSelectedEntitiesData[i].locked) || !rwzmSelectedEntitiesData[i].locked) {
|
|
addAction = "<button class='addbtn' onClick='addZoneToEntity([" + '"' + rwzmSelectedEntitiesData[i].id + '"' + "]);'>Add</button>";
|
|
selectionBox = '<input type="checkbox" name="entitiesScope" value = "' + rwzmSelectedEntitiesData[i].id + '">';
|
|
}
|
|
if (rwzmSelectedEntitiesData[i].locked) {
|
|
if (canUnlock) {
|
|
isLocked = ""; //Locked
|
|
} else {
|
|
isLocked = "🛇"; //Forbidden
|
|
}
|
|
}
|
|
uiContent = uiContent + ' <tr style="width: 100%"><td class="' + firstClass + ' ' + elementClass + '">' + selectionBox;
|
|
uiContent = uiContent + '</td><td class="' + firstClass + ' ' + elementClass + '"><font class="hifiGlyphs">' + isLocked + '</font></td><td class="';
|
|
uiContent = uiContent + firstClass + ' ' + elementClass + '">' + rwzmSelectedEntitiesData[i].type + ' - ' + rwzmGetTruncatedString(rwzmSelectedEntitiesData[i].name, 30) + '</b></td><td class="' + firstClass;
|
|
uiContent = uiContent + ' ' + elementClass + '">' + zones + '</td><td class="' + firstClass + ' ' + elementClass + '">' + addAction + '</td></tr>\n';
|
|
}
|
|
uiContent = uiContent + ' </table>\n';
|
|
uiContent = uiContent + ' </td>\n';
|
|
uiContent = uiContent + ' </tr>\n';
|
|
uiContent = uiContent + ' </table></div>\n';
|
|
uiContent = uiContent + ' <input type = "hidden" id = "highlightedID" value = "' + highlightedID + '">\n';
|
|
if (warning) {
|
|
uiContent = uiContent + ' <div class="warning"><b>WARNING</b>: The "<b>ZONE NOT FOUND</b>" visibility zones might simply not be loaded if too far and small. Please, verify before.</div>\n';
|
|
}
|
|
//Zone selector Add
|
|
uiContent = uiContent + ' <div id="rwzmAddZoneSelector">\n';
|
|
uiContent = uiContent + ' <h2>Select the zone to add:</h2><div class="zoneSelectorContainer">\n';
|
|
for (i = 0; i < rwzmAllZonesList.length; i++ ) {
|
|
uiContent = uiContent + " <button class = 'zoneSelectorButton' onClick='addThisZone(" + '"' + rwzmAllZonesList[i].id + '"' + ");'>" + rwzmAllZonesList[i].name + "</button><br>\n";
|
|
}
|
|
uiContent = uiContent + ' </div><div style="width: 98%; text-align: right;"><button class = "greyBtn" onclick="cancelZoneSelector();">Cancel</button></div>\n';
|
|
uiContent = uiContent + ' </div>\n';
|
|
//Zone selector Replace
|
|
uiContent = uiContent + ' <div id="rwzmReplaceZoneSelector">\n';
|
|
uiContent = uiContent + ' <h2>Select the replacement zone:</h2><div class="zoneSelectorContainer">\n';
|
|
for (i = 0; i < rwzmAllZonesList.length; i++ ) {
|
|
uiContent = uiContent + " <button class = 'zoneSelectorButton' onClick='replaceByThisZone(" + '"' + rwzmAllZonesList[i].id + '"' + ");'>" + rwzmAllZonesList[i].name + "</button><br>\n";
|
|
}
|
|
uiContent = uiContent + ' </div><div style="width: 98%; text-align: right;"><button class = "greyBtn" onclick="cancelZoneSelector();">Cancel</button></div>\n';
|
|
uiContent = uiContent + ' </div>\n';
|
|
|
|
Script.setTimeout(function () {
|
|
rwzmOverlayWebWindow.emitScriptEvent(uiContent);
|
|
}, 300);
|
|
}
|
|
|
|
function rwzmGetZoneName(id) {
|
|
let k = 0;
|
|
let name = "";
|
|
for (k = 0; k < rwzmAllZonesList.length; k++) {
|
|
if (rwzmAllZonesList[k].id === id) {
|
|
name = rwzmAllZonesList[k].name;
|
|
break;
|
|
}
|
|
}
|
|
return name;
|
|
}
|
|
|
|
function rwzmGenerateUnidentifiedZoneName(id) {
|
|
let partialID = id.substr(1,8);
|
|
return "ZONE NOT FOUND (" + partialID +")";
|
|
}
|
|
|
|
function rwzmGetExistingZoneList() {
|
|
var center = { "x": 0, "y": 0, "z": 0 };
|
|
var existingZoneIDs = Entities.findEntitiesByType("Zone", center, RWZ_ZONE_SCAN_RADIUS);
|
|
var listExistingZones = [];
|
|
var thisZone = {};
|
|
var properties;
|
|
for (var k = 0; k < existingZoneIDs.length; k++) {
|
|
properties = Entities.getEntityProperties(existingZoneIDs[k], ["name"]);
|
|
thisZone = {
|
|
"id": existingZoneIDs[k],
|
|
"name": properties.name
|
|
};
|
|
listExistingZones.push(thisZone);
|
|
}
|
|
listExistingZones.sort(rwzmZoneSortOrder);
|
|
return listExistingZones;
|
|
}
|
|
|
|
function rwzmZoneSortOrder(a, b) {
|
|
var nameA = a.name.toUpperCase();
|
|
var nameB = b.name.toUpperCase();
|
|
if (nameA > nameB) {
|
|
return 1;
|
|
} else if (nameA < nameB) {
|
|
return -1;
|
|
}
|
|
if (a.name > b.name) {
|
|
return 1;
|
|
} else if (a.name < b.name) {
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function rwzmRemoveZoneFromEntity(id, zoneID, forceLocked, highlightedID) {
|
|
rwzmUndo = [];
|
|
let properties = Entities.getEntityProperties(id, ["renderWithZones", "locked"]);
|
|
|
|
let newRenderWithZones = [];
|
|
let i = 0;
|
|
for (i = 0; i < properties.renderWithZones.length; i++) {
|
|
if (properties.renderWithZones[i] !== zoneID) {
|
|
newRenderWithZones.push(properties.renderWithZones[i]);
|
|
}
|
|
}
|
|
|
|
if (forceLocked && properties.locked) {
|
|
Entities.editEntity(id, {"locked": false});
|
|
Entities.editEntity(id, {"renderWithZones": newRenderWithZones, "locked": properties.locked});
|
|
} else {
|
|
Entities.editEntity(id, {"renderWithZones": newRenderWithZones});
|
|
}
|
|
rwzmUndo.push({"id": id, "renderWithZones": properties.renderWithZones});
|
|
renderWithZonesManager(rwzmSelectedId, highlightedID);
|
|
}
|
|
|
|
function rwzmAddZonesToEntities(ids, zoneID, forceLocked, highlightedID) {
|
|
rwzmUndo = [];
|
|
let k = 0;
|
|
let j = 0;
|
|
let properties;
|
|
let newRenderWithZones = [];
|
|
for (k = 0; k < ids.length; k++) {
|
|
properties = Entities.getEntityProperties(ids[k], ["renderWithZones", "locked"]);
|
|
newRenderWithZones = [];
|
|
|
|
for (j = 0; j < properties.renderWithZones.length; j++) {
|
|
if (properties.renderWithZones[j] !== zoneID) {
|
|
newRenderWithZones.push(properties.renderWithZones[j]);
|
|
}
|
|
}
|
|
newRenderWithZones.push(zoneID);
|
|
if (forceLocked && properties.locked) {
|
|
Entities.editEntity(ids[k], {"locked": false});
|
|
Entities.editEntity(ids[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked});
|
|
} else {
|
|
Entities.editEntity(ids[k], {"renderWithZones": newRenderWithZones});
|
|
}
|
|
rwzmUndo.push({"id": ids[k], "renderWithZones": properties.renderWithZones});
|
|
}
|
|
renderWithZonesManager(rwzmSelectedId, highlightedID);
|
|
}
|
|
|
|
function rwzmRemoveZoneFromAllEntities(zoneID, forceLocked, highlightedID) {
|
|
rwzmUndo = [];
|
|
let k = 0;
|
|
let j = 0;
|
|
let properties;
|
|
let newRenderWithZones = [];
|
|
for (k = 0; k < rwzmSelectedId.length; k++) {
|
|
properties = Entities.getEntityProperties(rwzmSelectedId[k], ["renderWithZones", "locked"]);
|
|
newRenderWithZones = [];
|
|
|
|
for (j = 0; j < properties.renderWithZones.length; j++) {
|
|
if (properties.renderWithZones[j] !== zoneID) {
|
|
newRenderWithZones.push(properties.renderWithZones[j]);
|
|
}
|
|
}
|
|
if (forceLocked && properties.locked) {
|
|
Entities.editEntity(rwzmSelectedId[k], {"locked": false});
|
|
Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked});
|
|
} else {
|
|
Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones});
|
|
}
|
|
rwzmUndo.push({"id": rwzmSelectedId[k], "renderWithZones": properties.renderWithZones});
|
|
}
|
|
renderWithZonesManager(rwzmSelectedId, highlightedID);
|
|
}
|
|
|
|
function rwzmReplaceZoneOnAllEntities(targetZoneID, replacementZoneID, forceLocked, highlightedID) {
|
|
rwzmUndo = [];
|
|
let k = 0;
|
|
let j = 0;
|
|
let properties;
|
|
let newRenderWithZones = [];
|
|
for (k = 0; k < rwzmSelectedId.length; k++) {
|
|
properties = Entities.getEntityProperties(rwzmSelectedId[k], ["renderWithZones", "locked"]);
|
|
newRenderWithZones = [];
|
|
|
|
for (j = 0; j < properties.renderWithZones.length; j++) {
|
|
if (properties.renderWithZones[j] !== targetZoneID) {
|
|
newRenderWithZones.push(properties.renderWithZones[j]);
|
|
} else {
|
|
newRenderWithZones.push(replacementZoneID);
|
|
}
|
|
}
|
|
if (forceLocked && properties.locked) {
|
|
Entities.editEntity(rwzmSelectedId[k], {"locked": false});
|
|
Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones, "locked": properties.locked});
|
|
} else {
|
|
Entities.editEntity(rwzmSelectedId[k], {"renderWithZones": newRenderWithZones});
|
|
}
|
|
rwzmUndo.push({"id": rwzmSelectedId[k], "renderWithZones": properties.renderWithZones});
|
|
}
|
|
renderWithZonesManager(rwzmSelectedId, highlightedID);
|
|
}
|
|
|
|
function rwzmGetTruncatedString(str, max) {
|
|
if (str.length > max) {
|
|
return str.substr(0, max-1) + "…";
|
|
} else {
|
|
return str;
|
|
}
|
|
}
|
|
|
|
function rwzmUndoLastAction(highlightedID) {
|
|
let k = 0;
|
|
let properties;
|
|
let locked;
|
|
for (k = 0; k < rwzmUndo.length; k++) {
|
|
locked = Entities.getEntityProperties(rwzmUndo[k].id, ["locked"]).locked;
|
|
if (locked) {
|
|
Entities.editEntity(rwzmUndo[k].id, {"locked": false});
|
|
Entities.editEntity(rwzmUndo[k].id, {"renderWithZones": rwzmUndo[k].renderWithZones, "locked": locked});
|
|
} else {
|
|
Entities.editEntity(rwzmUndo[k].id, {"renderWithZones": rwzmUndo[k].renderWithZones});
|
|
}
|
|
}
|
|
rwzmUndo = [];
|
|
renderWithZonesManager(rwzmSelectedId, highlightedID);
|
|
}
|
|
|
|
function webEventReceiver (message) {
|
|
try {
|
|
var data = JSON.parse(message);
|
|
} catch(e) {
|
|
print("renderWithZonesManager.js: Error parsing JSON");
|
|
return;
|
|
}
|
|
if (data.action === "highlight") {
|
|
enforceLocked = data.enforceLocked;
|
|
renderWithZonesManager(rwzmSelectedId, data.id);
|
|
} else if (data.action === "removeZoneFromEntity") {
|
|
enforceLocked = data.enforceLocked;
|
|
rwzmRemoveZoneFromEntity(data.id, data.zoneID, data.enforceLocked, data.highlightedID);
|
|
} else if (data.action === "addZoneToEntities") {
|
|
enforceLocked = data.enforceLocked;
|
|
rwzmAddZonesToEntities(data.ids, data.zoneID, data.enforceLocked, data.highlightedID);
|
|
} else if (data.action === "refresh") {
|
|
enforceLocked = data.enforceLocked;
|
|
renderWithZonesManager(rwzmSelectedId, data.highlightedID);
|
|
} else if (data.action === "removeZoneOnAllEntities") {
|
|
enforceLocked = data.enforceLocked;
|
|
rwzmRemoveZoneFromAllEntities(data.zoneID, data.enforceLocked, data.highlightedID);
|
|
} else if (data.action === "replaceZoneOnAllEntities") {
|
|
enforceLocked = data.enforceLocked;
|
|
rwzmReplaceZoneOnAllEntities(data.targetZoneID, data.replacementZoneID, data.enforceLocked, data.highlightedID);
|
|
} else if (data.action === "undo") {
|
|
enforceLocked = data.enforceLocked;
|
|
rwzmUndoLastAction(data.highlightedID);
|
|
}
|
|
}
|
|
|