@vitormnm/node-red-simple-opcua 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -2
- package/client/lib/opcua-client-browser.js +19 -4
- package/client/lib/opcua-client-write-service.js +14 -4
- package/client/opcua-client-config.js +38 -1
- package/client/opcua-client-utils.js +124 -0
- package/client/opcua-client.html +1 -0
- package/client/view/opcua-client.js +106 -14
- package/package.json +2 -2
- package/server/lib/opcua-address-space-alarm.js +85 -28
- package/server/lib/opcua-address-space-builder.js +653 -45
- package/server/lib/opcua-config.js +29 -12
- package/server/lib/opcua-server-events-child.js +30 -4
- package/server/lib/opcua-server-runtime-child.js +141 -9
- package/server/lib/opcua-server-runtime.js +3 -0
- package/server/lib/opcua-server-status-child.js +32 -1
- package/server/opcua-server-io.html +21 -5
- package/server/opcua-server-io.js +23 -2
- package/server/opcua-server-registry.js +8 -2
- package/server/opcua-server.css +12 -0
- package/server/opcua-server.html +2 -0
- package/server/view/opcua-server.css +11 -0
- package/server/view/opcua-server.js +240 -23
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var editorState = { objects: [], folders: [], objectsTypes: [], enumerations: [], nameSpaces: [] };
|
|
4
4
|
var expansionState = {};
|
|
5
5
|
var selectedPath = "";
|
|
6
|
+
var draggedPath = null;
|
|
6
7
|
var pendingCreate = null;
|
|
7
8
|
var pendingPasswordHashes = 0;
|
|
8
9
|
var authGroups = [];
|
|
@@ -443,6 +444,71 @@
|
|
|
443
444
|
|
|
444
445
|
function normalizeSearchTerm(value) { return String(value || "").trim().toLowerCase(); }
|
|
445
446
|
function isExpanded(path, defaultValue) { if (expansionState[path] === undefined) expansionState[path] = !!defaultValue; return expansionState[path]; }
|
|
447
|
+
function isAncestorPath(ancestorPath, descendantPath) {
|
|
448
|
+
if (ancestorPath === descendantPath) return true;
|
|
449
|
+
return descendantPath.indexOf(ancestorPath + ".") === 0;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function isValidDropTarget(srcPath, destPath) {
|
|
453
|
+
if (srcPath === destPath) return false;
|
|
454
|
+
if (isAncestorPath(srcPath, destPath)) return false;
|
|
455
|
+
if (srcPath.indexOf("virtual:") === 0 || srcPath.indexOf("nameSpaces.") === 0) return false;
|
|
456
|
+
|
|
457
|
+
var srcClass = nodeClassFromPath(srcPath);
|
|
458
|
+
|
|
459
|
+
if (destPath === "virtual:Objects") {
|
|
460
|
+
return srcClass === "Folder" || srcClass === "Object";
|
|
461
|
+
}
|
|
462
|
+
if (destPath === "virtual:Types.ObjectTypes") {
|
|
463
|
+
var srcTokens = pathToTokens(srcPath);
|
|
464
|
+
return srcClass === "ObjectType" && srcTokens.length === 2 && srcTokens[0] === "objectsTypes";
|
|
465
|
+
}
|
|
466
|
+
if (destPath === "virtual:Types.DataTypes") {
|
|
467
|
+
var srcTokens = pathToTokens(srcPath);
|
|
468
|
+
return srcClass === "Enumeration" && srcTokens.length === 2 && srcTokens[0] === "enumerations";
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (destPath.indexOf("virtual:") === 0 || destPath.indexOf("nameSpaces.") === 0) {
|
|
472
|
+
return false;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Both are real paths. Make sure they are in the same scope (Objects vs Types/ObjectTypes)
|
|
476
|
+
var srcIsTemplate = isObjectTypeModelPath(srcPath);
|
|
477
|
+
var destIsTemplate = isObjectTypeModelPath(destPath);
|
|
478
|
+
if (srcIsTemplate !== destIsTemplate) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
var destClass = nodeClassFromPath(destPath);
|
|
483
|
+
if (destClass === "Folder" || destClass === "Object" || destClass === "ObjectType") {
|
|
484
|
+
return srcClass !== "Enumeration" && srcClass !== "Namespace";
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
function findObjectPath(obj, targetObj, currentPath) {
|
|
491
|
+
if (obj === targetObj) return currentPath;
|
|
492
|
+
if (typeof obj !== "object" || obj === null) return null;
|
|
493
|
+
|
|
494
|
+
if (Array.isArray(obj)) {
|
|
495
|
+
for (var i = 0; i < obj.length; i++) {
|
|
496
|
+
var path = findObjectPath(obj[i], targetObj, currentPath ? currentPath + "." + i : String(i));
|
|
497
|
+
if (path) return path;
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
var keys = Object.keys(obj);
|
|
501
|
+
for (var i = 0; i < keys.length; i++) {
|
|
502
|
+
var k = keys[i];
|
|
503
|
+
if (k === "folders" || k === "objects" || k === "variables" || k === "methods" || k === "alarms" || k === "objectsTypes" || k === "enumerations") {
|
|
504
|
+
var path = findObjectPath(obj[k], targetObj, currentPath ? currentPath + "." + k : k);
|
|
505
|
+
if (path) return path;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
446
512
|
function nodeClassFromPath(path) {
|
|
447
513
|
if (path && path.indexOf("virtual:") === 0) return "VisualFolder";
|
|
448
514
|
var tokens = pathToTokens(path);
|
|
@@ -822,6 +888,9 @@
|
|
|
822
888
|
var row = document.createElement("div");
|
|
823
889
|
row.className = "opcua-tree-row" + (path === selectedPath ? " is-selected" : "");
|
|
824
890
|
row.setAttribute("data-path", path);
|
|
891
|
+
if (path && path.indexOf("virtual:") !== 0 && path.indexOf("nameSpaces.") !== 0) {
|
|
892
|
+
row.setAttribute("draggable", "true");
|
|
893
|
+
}
|
|
825
894
|
var label = (path && path.indexOf("virtual:") === 0) ? getVirtualNodeName(path) : (item.name || "(unnamed)");
|
|
826
895
|
var displayClass = (path && path.indexOf("virtual:") === 0) ? "Folder" : nodeClass;
|
|
827
896
|
row.innerHTML = indents
|
|
@@ -933,7 +1002,7 @@
|
|
|
933
1002
|
return;
|
|
934
1003
|
}
|
|
935
1004
|
var parentSuffix = path.indexOf("virtual:") === 0 ? "" : buildDefaultNodeIdSuffixFromEditorPath(path);
|
|
936
|
-
var defaultName = kind === "variable" ? "newVariable" : kind === "enum-variable" ? "newEnumVariable" : "newObject";
|
|
1005
|
+
var defaultName = kind === "variable" ? "newVariable" : kind === "enum-variable" ? "newEnumVariable" : kind === "folder" ? "newFolder" : kind === "method" ? "newMethod" : kind === "alarm" ? "newAlarm" : "newObject";
|
|
937
1006
|
var defaultSuffix = parentSuffix ? parentSuffix + "." + defaultName : defaultName;
|
|
938
1007
|
|
|
939
1008
|
pendingCreate = {
|
|
@@ -961,7 +1030,7 @@
|
|
|
961
1030
|
normalStateValue: 0,
|
|
962
1031
|
digitalMessage: "Digital alarm",
|
|
963
1032
|
nodeIdType: "s",
|
|
964
|
-
nodeIdValue:
|
|
1033
|
+
nodeIdValue: defaultSuffix
|
|
965
1034
|
};
|
|
966
1035
|
renderDetails();
|
|
967
1036
|
}
|
|
@@ -994,19 +1063,22 @@
|
|
|
994
1063
|
}
|
|
995
1064
|
var target = getAtPath(editorState, branchTargetPath);
|
|
996
1065
|
if (!Array.isArray(target)) return;
|
|
997
|
-
|
|
998
|
-
|
|
1066
|
+
|
|
1067
|
+
var customNodeId = "";
|
|
1068
|
+
var parentIsObjectTypeModel = isObjectTypeModelPath(parentPath);
|
|
1069
|
+
if (!parentIsObjectTypeModel && pendingCreate.nodeIdValue) {
|
|
999
1070
|
var nsId = getNodeNamespaceId(parentPath);
|
|
1000
|
-
if (pendingCreate.
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
1005
|
-
}
|
|
1006
|
-
} else {
|
|
1007
|
-
customNodeId = "ns=" + nsId + ";s=" + pendingCreate.nodeIdValue.trim();
|
|
1071
|
+
if (pendingCreate.nodeIdType === "i") {
|
|
1072
|
+
var numVal = parseInt(pendingCreate.nodeIdValue, 10);
|
|
1073
|
+
if (!isNaN(numVal)) {
|
|
1074
|
+
customNodeId = "ns=" + nsId + ";i=" + numVal;
|
|
1008
1075
|
}
|
|
1076
|
+
} else {
|
|
1077
|
+
customNodeId = "ns=" + nsId + ";s=" + pendingCreate.nodeIdValue.trim();
|
|
1009
1078
|
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
if (kind === "variable" || kind === "enum-variable") {
|
|
1010
1082
|
target.push(normalizeVariable({
|
|
1011
1083
|
name: pendingCreate.name,
|
|
1012
1084
|
displayName: pendingCreate.displayName || "",
|
|
@@ -1017,10 +1089,12 @@
|
|
|
1017
1089
|
nodeId: customNodeId
|
|
1018
1090
|
}));
|
|
1019
1091
|
} else if (kind === "folder") {
|
|
1020
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1092
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
1021
1093
|
} else if (kind === "objecttype") {
|
|
1022
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", objectsType: pendingCreate.objectsType || "", accessPermission: pendingCreate.accessPermission }));
|
|
1023
|
-
|
|
1094
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", objectsType: pendingCreate.objectsType || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
1095
|
+
if (!customNodeId) {
|
|
1096
|
+
target[target.length - 1].nodeId = buildGeneratedNodeIdForPath(branchTargetPath + "." + (target.length - 1));
|
|
1097
|
+
}
|
|
1024
1098
|
} else if (kind === "alarm") {
|
|
1025
1099
|
target.push(normalizeAlarm({
|
|
1026
1100
|
displayName: pendingCreate.displayName || "",
|
|
@@ -1039,12 +1113,13 @@
|
|
|
1039
1113
|
lowLowLimit: pendingCreate.lowLowLimit,
|
|
1040
1114
|
lowLowMessage: pendingCreate.lowLowMessage,
|
|
1041
1115
|
normalStateValue: pendingCreate.normalStateValue,
|
|
1042
|
-
digitalMessage: pendingCreate.digitalMessage
|
|
1116
|
+
digitalMessage: pendingCreate.digitalMessage,
|
|
1117
|
+
nodeId: customNodeId
|
|
1043
1118
|
}));
|
|
1044
1119
|
} else if (kind === "method") {
|
|
1045
|
-
target.push(normalizeMethod({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1120
|
+
target.push(normalizeMethod({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
1046
1121
|
} else {
|
|
1047
|
-
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission }));
|
|
1122
|
+
target.push(normalizeBranch({ name: pendingCreate.name, displayName: pendingCreate.displayName || "", accessPermission: pendingCreate.accessPermission, nodeId: customNodeId }));
|
|
1048
1123
|
}
|
|
1049
1124
|
expansionState[parentPath] = true;
|
|
1050
1125
|
pendingCreate = null;
|
|
@@ -1066,14 +1141,17 @@
|
|
|
1066
1141
|
panel.append('<div class="form-row"><label>Name</label><input type="text" id="opcua-create-name"></div>');
|
|
1067
1142
|
panel.append('<div class="form-row"><label>displayName</label><input type="text" id="opcua-create-displayname" placeholder="Leave blank to use browseName"></div>');
|
|
1068
1143
|
panel.append('<div class="form-row"><label>accessPermission</label>' + buildAccessPermissionSelect("opcua-create-accesspermission", pendingCreate.accessPermission || ["public"]) + '</div>');
|
|
1144
|
+
var parentIsObjectTypeModel = isObjectTypeModelPath(pendingCreate.parentPath);
|
|
1145
|
+
if (!parentIsObjectTypeModel) {
|
|
1146
|
+
panel.append('<div class="form-row"><label>nodeId Type</label><select id="opcua-create-nodeid-type"><option value="s">s (String)</option><option value="i">i (Numeric)</option></select></div>');
|
|
1147
|
+
panel.append('<div class="form-row" id="opcua-create-nodeid-value-row"><label id="opcua-create-nodeid-value-label">nodeId Value</label><input type="text" id="opcua-create-nodeid-value" placeholder="Leave blank for default (s)"></div>');
|
|
1148
|
+
}
|
|
1069
1149
|
if (pendingCreate.kind === "variable" || pendingCreate.kind === "enum-variable") {
|
|
1070
1150
|
var isEnum = pendingCreate.kind === "enum-variable";
|
|
1071
1151
|
var typeHtml = isEnum ? buildEnumerationSelect("opcua-create-type", pendingCreate.dataType) : '<select id="opcua-create-type"><option value="Int16">Int16</option><option value="Int32">Int32</option><option value="Int64">Int64</option><option value="Float">Float</option><option value="Boolean">Boolean</option><option value="String">String</option></select>';
|
|
1072
1152
|
panel.append('<div class="form-row"><label>dataType</label>' + typeHtml + '</div>');
|
|
1073
1153
|
panel.append('<div class="form-row"><label>Value</label><input type="text" id="opcua-create-value"></div>');
|
|
1074
1154
|
panel.append('<div class="form-row"><label>Access</label><select id="opcua-create-access"><option value="readwrite">readwrite</option><option value="readonly">readonly</option></select></div>');
|
|
1075
|
-
panel.append('<div class="form-row"><label>nodeId Type</label><select id="opcua-create-nodeid-type"><option value="s">s (String)</option><option value="i">i (Numeric)</option></select></div>');
|
|
1076
|
-
panel.append('<div class="form-row" id="opcua-create-nodeid-value-row"><label id="opcua-create-nodeid-value-label">nodeId Value</label><input type="text" id="opcua-create-nodeid-value" placeholder="Leave blank for default (s)"></div>');
|
|
1077
1155
|
}
|
|
1078
1156
|
if (pendingCreate.kind === "objecttype") {
|
|
1079
1157
|
panel.append('<div class="form-row"><label>objectsType</label>' + buildObjectTypeSelect("opcua-create-objectstype", pendingCreate.objectsType || "") + '</div>');
|
|
@@ -1103,7 +1181,7 @@
|
|
|
1103
1181
|
$("#opcua-create-name").val(pendingCreate.name);
|
|
1104
1182
|
$("#opcua-create-displayname").val(pendingCreate.displayName || "");
|
|
1105
1183
|
$("#opcua-create-accesspermission").val(normalizeAccessPermissionValues(pendingCreate.accessPermission));
|
|
1106
|
-
if (
|
|
1184
|
+
if (!parentIsObjectTypeModel) {
|
|
1107
1185
|
$("#opcua-create-nodeid-type").val(pendingCreate.nodeIdType || "s");
|
|
1108
1186
|
$("#opcua-create-nodeid-value").val(pendingCreate.nodeIdValue || "");
|
|
1109
1187
|
updateNodeIdValueInputState("create", pendingCreate.nodeIdType || "s");
|
|
@@ -1185,7 +1263,7 @@
|
|
|
1185
1263
|
panel.append('<div class="form-row"><label>browseName</label><input type="text" id="opcua-detail-name"></div>');
|
|
1186
1264
|
panel.append('<div class="form-row"><label>nodeClass</label><input type="text" id="opcua-detail-class" readonly></div>');
|
|
1187
1265
|
panel.append('<div class="form-row"><label>namespace</label><select id="opcua-detail-namespace"></select></div>');
|
|
1188
|
-
if (
|
|
1266
|
+
if (!nodeIdLocked) {
|
|
1189
1267
|
panel.append('<div class="form-row"><label>nodeId</label>' +
|
|
1190
1268
|
'<div class="opcua-nodeid-field">' +
|
|
1191
1269
|
'<span class="opcua-nodeid-prefix">ns=' + namespaceId + ';</span>' +
|
|
@@ -1273,7 +1351,7 @@
|
|
|
1273
1351
|
panel.append('<div class="form-row"><label style="width:90px;">Actions</label><div><a href="#" id="opcua-detail-edit" class="editor-button editor-button-small"><i class="fa fa-pencil"></i> Edit</a> <a href="#" id="opcua-detail-remove" class="editor-button editor-button-small"><i class="fa fa-trash"></i> Remove</a></div></div>');
|
|
1274
1352
|
$("#opcua-detail-name").val(item.name || "");
|
|
1275
1353
|
$("#opcua-detail-class").val(nodeClass);
|
|
1276
|
-
if (
|
|
1354
|
+
if (!nodeIdLocked) {
|
|
1277
1355
|
var parsed = parseNodeId(item.nodeId, buildDefaultNodeIdSuffixFromEditorPath(selectedPath));
|
|
1278
1356
|
$("#opcua-detail-nodeid-type").val(parsed.type);
|
|
1279
1357
|
$("#opcua-detail-nodeid-value").val(parsed.value);
|
|
@@ -1330,8 +1408,72 @@
|
|
|
1330
1408
|
if (selectedPath === path) selectedPath = "";
|
|
1331
1409
|
syncStateToJson(true);
|
|
1332
1410
|
renderVisualEditor();
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
function moveNode(srcPath, destPath) {
|
|
1414
|
+
var item = getAtPath(editorState, srcPath);
|
|
1415
|
+
if (!item) return;
|
|
1416
|
+
|
|
1417
|
+
var srcClass = nodeClassFromPath(srcPath);
|
|
1418
|
+
var targetCollection = null;
|
|
1419
|
+
|
|
1420
|
+
if (destPath === "virtual:Objects") {
|
|
1421
|
+
if (srcClass === "Folder") {
|
|
1422
|
+
editorState.folders = editorState.folders || [];
|
|
1423
|
+
targetCollection = editorState.folders;
|
|
1424
|
+
} else if (srcClass === "Object") {
|
|
1425
|
+
editorState.objects = editorState.objects || [];
|
|
1426
|
+
targetCollection = editorState.objects;
|
|
1427
|
+
}
|
|
1428
|
+
} else if (destPath === "virtual:Types.ObjectTypes") {
|
|
1429
|
+
editorState.objectsTypes = editorState.objectsTypes || [];
|
|
1430
|
+
targetCollection = editorState.objectsTypes;
|
|
1431
|
+
} else if (destPath === "virtual:Types.DataTypes") {
|
|
1432
|
+
editorState.enumerations = editorState.enumerations || [];
|
|
1433
|
+
targetCollection = editorState.enumerations;
|
|
1434
|
+
} else {
|
|
1435
|
+
var destParent = getAtPath(editorState, destPath);
|
|
1436
|
+
if (!destParent) return;
|
|
1437
|
+
|
|
1438
|
+
if (srcClass === "Folder") {
|
|
1439
|
+
destParent.folders = destParent.folders || [];
|
|
1440
|
+
targetCollection = destParent.folders;
|
|
1441
|
+
} else if (srcClass === "Object") {
|
|
1442
|
+
destParent.objects = destParent.objects || [];
|
|
1443
|
+
targetCollection = destParent.objects;
|
|
1444
|
+
} else if (srcClass === "Variable") {
|
|
1445
|
+
destParent.variables = destParent.variables || [];
|
|
1446
|
+
targetCollection = destParent.variables;
|
|
1447
|
+
} else if (srcClass === "Method") {
|
|
1448
|
+
destParent.methods = destParent.methods || [];
|
|
1449
|
+
targetCollection = destParent.methods;
|
|
1450
|
+
} else if (srcClass === "Alarm") {
|
|
1451
|
+
destParent.alarms = destParent.alarms || [];
|
|
1452
|
+
targetCollection = destParent.alarms;
|
|
1453
|
+
} else if (srcClass === "ObjectType") {
|
|
1454
|
+
destParent.objectsTypes = destParent.objectsTypes || [];
|
|
1455
|
+
targetCollection = destParent.objectsTypes;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
if (targetCollection) {
|
|
1460
|
+
removeAtPath(editorState, srcPath);
|
|
1461
|
+
targetCollection.push(item);
|
|
1462
|
+
|
|
1463
|
+
var newPath = findObjectPath(editorState, item, "");
|
|
1464
|
+
if (newPath) {
|
|
1465
|
+
selectedPath = newPath;
|
|
1466
|
+
expansionState[destPath] = true;
|
|
1467
|
+
} else {
|
|
1468
|
+
selectedPath = "";
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
syncStateToJson(true);
|
|
1472
|
+
renderVisualEditor();
|
|
1473
|
+
}
|
|
1333
1474
|
}
|
|
1334
1475
|
|
|
1476
|
+
|
|
1335
1477
|
function buildGroupOptions(selectedGroup) {
|
|
1336
1478
|
var currentGroup = String(selectedGroup || "");
|
|
1337
1479
|
var options = authGroups.map(function (groupName) {
|
|
@@ -1560,6 +1702,56 @@
|
|
|
1560
1702
|
} catch (error) { RED.notify("Invalid JSON: " + error.message, "error"); }
|
|
1561
1703
|
});
|
|
1562
1704
|
|
|
1705
|
+
$(document).on("dragstart", ".opcua-tree-row", function (event) {
|
|
1706
|
+
var path = $(this).attr("data-path");
|
|
1707
|
+
if (path && path.indexOf("virtual:") !== 0 && path.indexOf("nameSpaces.") !== 0) {
|
|
1708
|
+
draggedPath = path;
|
|
1709
|
+
if (event.originalEvent.dataTransfer) {
|
|
1710
|
+
event.originalEvent.dataTransfer.effectAllowed = "move";
|
|
1711
|
+
event.originalEvent.dataTransfer.setData("text/plain", path);
|
|
1712
|
+
}
|
|
1713
|
+
$(this).addClass("is-dragging");
|
|
1714
|
+
} else {
|
|
1715
|
+
event.preventDefault();
|
|
1716
|
+
}
|
|
1717
|
+
});
|
|
1718
|
+
|
|
1719
|
+
$(document).on("dragend", ".opcua-tree-row", function (event) {
|
|
1720
|
+
$(this).removeClass("is-dragging");
|
|
1721
|
+
draggedPath = null;
|
|
1722
|
+
$(".opcua-tree-row").removeClass("drag-over");
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
$(document).on("dragover", ".opcua-tree-row", function (event) {
|
|
1726
|
+
if (!draggedPath) return;
|
|
1727
|
+
event.preventDefault();
|
|
1728
|
+
|
|
1729
|
+
var row = $(this);
|
|
1730
|
+
var targetPath = row.attr("data-path");
|
|
1731
|
+
if (isValidDropTarget(draggedPath, targetPath)) {
|
|
1732
|
+
if (event.originalEvent.dataTransfer) {
|
|
1733
|
+
event.originalEvent.dataTransfer.dropEffect = "move";
|
|
1734
|
+
}
|
|
1735
|
+
$(".opcua-tree-row").not(row).removeClass("drag-over");
|
|
1736
|
+
row.addClass("drag-over");
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
$(document).on("dragleave", ".opcua-tree-row", function (event) {
|
|
1741
|
+
$(this).removeClass("drag-over");
|
|
1742
|
+
});
|
|
1743
|
+
|
|
1744
|
+
$(document).on("drop", ".opcua-tree-row", function (event) {
|
|
1745
|
+
event.preventDefault();
|
|
1746
|
+
$(".opcua-tree-row").removeClass("drag-over");
|
|
1747
|
+
|
|
1748
|
+
if (!draggedPath) return;
|
|
1749
|
+
var targetPath = $(this).attr("data-path");
|
|
1750
|
+
if (isValidDropTarget(draggedPath, targetPath)) {
|
|
1751
|
+
moveNode(draggedPath, targetPath);
|
|
1752
|
+
}
|
|
1753
|
+
});
|
|
1754
|
+
|
|
1563
1755
|
$(document).on("click", ".opcua-tree-row", function (event) {
|
|
1564
1756
|
var path = $(this).attr("data-path");
|
|
1565
1757
|
if ($(event.target).closest(".opcua-tree-twisty").length) {
|
|
@@ -1582,10 +1774,16 @@
|
|
|
1582
1774
|
if (path === "virtual:Objects") {
|
|
1583
1775
|
contextMenu.find('[data-action="add-folder"]').show();
|
|
1584
1776
|
contextMenu.find('[data-action="add-object"]').show();
|
|
1777
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1778
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1585
1779
|
} else if (path === "virtual:Types.ObjectTypes") {
|
|
1586
1780
|
contextMenu.find('[data-action="add-objecttype"]').show();
|
|
1781
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1782
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1587
1783
|
} else if (path === "virtual:Types.DataTypes") {
|
|
1588
1784
|
contextMenu.find('[data-action="add-enumeration"]').show();
|
|
1785
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1786
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1589
1787
|
}
|
|
1590
1788
|
} else {
|
|
1591
1789
|
var nodeClass = nodeClassFromPath(path);
|
|
@@ -1593,11 +1791,14 @@
|
|
|
1593
1791
|
contextMenu.find('[data-action="add-folder"]').show();
|
|
1594
1792
|
contextMenu.find('[data-action="add-object"]').show();
|
|
1595
1793
|
contextMenu.find('[data-action="add-variable"]').show();
|
|
1794
|
+
contextMenu.find('[data-action="add-objecttype"]').show();
|
|
1596
1795
|
contextMenu.find('[data-action="add-enum-variable"]').show();
|
|
1597
1796
|
contextMenu.find('[data-action="add-alarm"]').show();
|
|
1598
1797
|
contextMenu.find('[data-action="add-method"]').show();
|
|
1599
1798
|
contextMenu.find('[data-action="edit"]').show();
|
|
1600
1799
|
contextMenu.find('[data-action="remove"]').show();
|
|
1800
|
+
contextMenu.find('[data-action="expand-all-below"]').show();
|
|
1801
|
+
contextMenu.find('[data-action="collapse-all-below"]').show();
|
|
1601
1802
|
} else if (nodeClass === "Enumeration") {
|
|
1602
1803
|
contextMenu.find('[data-action="edit"]').show();
|
|
1603
1804
|
contextMenu.find('[data-action="remove"]').show();
|
|
@@ -1631,6 +1832,22 @@
|
|
|
1631
1832
|
if (action === "add-method") addNode(selectedPath, "method");
|
|
1632
1833
|
if (action === "remove") removeNode(selectedPath);
|
|
1633
1834
|
if (action === "edit") renderDetails();
|
|
1835
|
+
if (action === "expand-all-below") {
|
|
1836
|
+
function walk(p) {
|
|
1837
|
+
expansionState[p] = true;
|
|
1838
|
+
getChildrenByPath(p).forEach(walk);
|
|
1839
|
+
}
|
|
1840
|
+
walk(selectedPath);
|
|
1841
|
+
renderTree();
|
|
1842
|
+
}
|
|
1843
|
+
if (action === "collapse-all-below") {
|
|
1844
|
+
function walk(p) {
|
|
1845
|
+
expansionState[p] = false;
|
|
1846
|
+
getChildrenByPath(p).forEach(walk);
|
|
1847
|
+
}
|
|
1848
|
+
walk(selectedPath);
|
|
1849
|
+
renderTree();
|
|
1850
|
+
}
|
|
1634
1851
|
$("#opcua-tree-context-menu").hide();
|
|
1635
1852
|
});
|
|
1636
1853
|
|