@node-red/editor-client 4.1.2 → 5.0.0-beta.1
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/locales/en-US/editor.json +1 -0
- package/package.json +1 -1
- package/public/red/about +24 -0
- package/public/red/images/explorer.svg +1 -0
- package/public/red/keymap.json +2 -1
- package/public/red/red.js +2080 -445
- package/public/red/red.min.js +3 -3
- package/public/red/style.min.css +2 -2
- package/public/red/tours/4.1/images/missing-modules.png +0 -0
- package/public/red/tours/4.1/images/node-docs.png +0 -0
- package/public/red/tours/4.1/images/update-notification.png +0 -0
- package/public/red/tours/4.1/welcome.js +126 -0
- package/public/red/tours/welcome.js +24 -104
package/public/red/red.js
CHANGED
|
@@ -878,7 +878,7 @@ var RED = (function() {
|
|
|
878
878
|
RED.user.init();
|
|
879
879
|
RED.notifications.init();
|
|
880
880
|
RED.library.init();
|
|
881
|
-
RED.
|
|
881
|
+
RED.sidebar.init();
|
|
882
882
|
RED.eventLog.init();
|
|
883
883
|
|
|
884
884
|
if (RED.settings.get('externalModules.palette.allowInstall', true) !== false) {
|
|
@@ -887,7 +887,6 @@ var RED = (function() {
|
|
|
887
887
|
console.log("Palette editor disabled");
|
|
888
888
|
}
|
|
889
889
|
|
|
890
|
-
RED.sidebar.init();
|
|
891
890
|
|
|
892
891
|
if (RED.settings.theme("projects.enabled",false)) {
|
|
893
892
|
RED.projects.init();
|
|
@@ -904,23 +903,35 @@ var RED = (function() {
|
|
|
904
903
|
RED.diagnostics.init();
|
|
905
904
|
RED.diff.init();
|
|
906
905
|
|
|
907
|
-
|
|
908
906
|
RED.deploy.init(RED.settings.theme("deployButton",null));
|
|
907
|
+
RED.keyboard.init(() => {
|
|
908
|
+
buildMainMenu();
|
|
909
909
|
|
|
910
|
-
|
|
911
|
-
|
|
910
|
+
// Register the core set of sidebar panels now the menu is ready to receive items
|
|
911
|
+
RED.palette.init();
|
|
912
|
+
RED.sidebar.info.init();
|
|
913
|
+
RED.sidebar.info.outliner.init();
|
|
914
|
+
RED.sidebar.help.init();
|
|
915
|
+
RED.sidebar.config.init();
|
|
916
|
+
RED.sidebar.context.init();
|
|
917
|
+
// hide sidebar at start if screen rather narrow...
|
|
918
|
+
if ($("#red-ui-editor").width() < 600) { RED.menu.setSelected("menu-item-sidebar", false); }
|
|
912
919
|
|
|
913
|
-
|
|
914
|
-
RED.runtime.init()
|
|
920
|
+
RED.envVar.init();
|
|
915
921
|
|
|
916
|
-
|
|
917
|
-
RED.
|
|
918
|
-
|
|
919
|
-
|
|
922
|
+
RED.nodes.init();
|
|
923
|
+
RED.runtime.init()
|
|
924
|
+
|
|
925
|
+
if (RED.settings.theme("multiplayer.enabled",false)) {
|
|
926
|
+
RED.multiplayer.init()
|
|
927
|
+
}
|
|
928
|
+
RED.comms.connect();
|
|
920
929
|
|
|
921
|
-
|
|
930
|
+
$("#red-ui-main-container").show();
|
|
931
|
+
RED.events.emit("sidebar:resize")
|
|
922
932
|
|
|
923
|
-
|
|
933
|
+
loadPluginList();
|
|
934
|
+
});
|
|
924
935
|
}
|
|
925
936
|
|
|
926
937
|
|
|
@@ -929,13 +940,18 @@ var RED = (function() {
|
|
|
929
940
|
var logo = $('<span class="red-ui-header-logo"></span>').appendTo(header);
|
|
930
941
|
$('<ul class="red-ui-header-toolbar hide"></ul>').appendTo(header);
|
|
931
942
|
$('<div id="red-ui-header-shade" class="hide"></div>').appendTo(header);
|
|
932
|
-
$('<div id="red-ui-main-container"
|
|
943
|
+
$('<div id="red-ui-main-container">'+
|
|
944
|
+
'<div id="red-ui-sidebar-left"></div>'+
|
|
933
945
|
'<div id="red-ui-workspace"></div>'+
|
|
934
|
-
'<div id="red-ui-editor-stack" tabindex="-1"></div>'+
|
|
935
|
-
'<div id="red-ui-palette"></div>'+
|
|
936
946
|
'<div id="red-ui-sidebar"></div>'+
|
|
937
|
-
'<div id="red-ui-
|
|
947
|
+
'<div id="red-ui-editor-stack" tabindex="-1"></div>'+
|
|
948
|
+
// '<div id="red-ui-palette"></div>'+
|
|
938
949
|
'</div>').appendTo(options.target);
|
|
950
|
+
|
|
951
|
+
// Don't use the `hide` class on this container, as the show reverts it to block rather
|
|
952
|
+
// than the expected flex. So hide via jQuery as it'll track the show state internally.
|
|
953
|
+
options.target.find('#red-ui-main-container').hide()
|
|
954
|
+
|
|
939
955
|
$('<div id="red-ui-editor-plugin-configs"></div>').appendTo(options.target);
|
|
940
956
|
$('<div id="red-ui-editor-node-configs"></div>').appendTo(options.target);
|
|
941
957
|
$('<div id="red-ui-full-shade" class="hide"></div>').appendTo(options.target);
|
|
@@ -13203,7 +13219,7 @@ RED.menu = (function() {
|
|
|
13203
13219
|
} else {
|
|
13204
13220
|
for (var i=0;i<groupItems.length;i++) {
|
|
13205
13221
|
var groupItem = groupItems[i];
|
|
13206
|
-
var label = $(groupItem).find(".red-ui-menu-label").
|
|
13222
|
+
var label = $(groupItem).find(".red-ui-menu-label span").text();
|
|
13207
13223
|
if (opt.label < label) {
|
|
13208
13224
|
$(groupItem).before(item);
|
|
13209
13225
|
break;
|
|
@@ -13293,6 +13309,13 @@ RED.panels = (function() {
|
|
|
13293
13309
|
$(children[1]).addClass("red-ui-panel");
|
|
13294
13310
|
|
|
13295
13311
|
var separator = $('<div class="red-ui-panels-separator"></div>').insertAfter(children[0]);
|
|
13312
|
+
if (options.invisibleSeparator) {
|
|
13313
|
+
if (!vertical) {
|
|
13314
|
+
throw new Error("invisibleSeparator option is only valid for vertical panels");
|
|
13315
|
+
}
|
|
13316
|
+
separator.addClass("red-ui-panels-separator-invisible");
|
|
13317
|
+
$('<div class="red-ui-panels-separator-handle"></div>').appendTo(separator)
|
|
13318
|
+
}
|
|
13296
13319
|
var startPosition;
|
|
13297
13320
|
var panelSizes = [];
|
|
13298
13321
|
var modifiedSizes = false;
|
|
@@ -14869,7 +14892,6 @@ RED.tabs = (function() {
|
|
|
14869
14892
|
ul.find("li.red-ui-tab.active .red-ui-tab-label").css({paddingLeft:""})
|
|
14870
14893
|
}
|
|
14871
14894
|
}
|
|
14872
|
-
|
|
14873
14895
|
}
|
|
14874
14896
|
|
|
14875
14897
|
ul.find("li.red-ui-tab a")
|
|
@@ -15369,7 +15391,8 @@ RED.tabs = (function() {
|
|
|
15369
15391
|
pinnedButtons["__menu__"].appendTo(collapsedButtonsRow);
|
|
15370
15392
|
updateTabWidths();
|
|
15371
15393
|
}
|
|
15372
|
-
}
|
|
15394
|
+
},
|
|
15395
|
+
container: wrapper
|
|
15373
15396
|
}
|
|
15374
15397
|
return tabAPI;
|
|
15375
15398
|
}
|
|
@@ -17932,13 +17955,13 @@ RED.deploy = (function() {
|
|
|
17932
17955
|
$("#red-ui-header-shade").show();
|
|
17933
17956
|
$("#red-ui-editor-shade").show();
|
|
17934
17957
|
$("#red-ui-palette-shade").show();
|
|
17935
|
-
$("
|
|
17958
|
+
$(".red-ui-sidebar-shade").show();
|
|
17936
17959
|
}
|
|
17937
17960
|
function shadeHide() {
|
|
17938
17961
|
$("#red-ui-header-shade").hide();
|
|
17939
17962
|
$("#red-ui-editor-shade").hide();
|
|
17940
17963
|
$("#red-ui-palette-shade").hide();
|
|
17941
|
-
$("
|
|
17964
|
+
$(".red-ui-sidebar-shade").hide();
|
|
17942
17965
|
}
|
|
17943
17966
|
function deployButtonSetBusy(){
|
|
17944
17967
|
$(".red-ui-deploy-button-content").css('opacity',0);
|
|
@@ -19752,11 +19775,11 @@ RED.diagnostics = (function () {
|
|
|
19752
19775
|
diffTable.finish();
|
|
19753
19776
|
diffTable.list.show();
|
|
19754
19777
|
},300);
|
|
19755
|
-
$("
|
|
19778
|
+
$(".red-ui-sidebar-shade").show();
|
|
19756
19779
|
},
|
|
19757
19780
|
close: function() {
|
|
19758
19781
|
diffVisible = false;
|
|
19759
|
-
$("
|
|
19782
|
+
$(".red-ui-sidebar-shade").hide();
|
|
19760
19783
|
|
|
19761
19784
|
},
|
|
19762
19785
|
show: function() {
|
|
@@ -22683,11 +22706,29 @@ RED.view = (function() {
|
|
|
22683
22706
|
node_height = 30,
|
|
22684
22707
|
dblClickInterval = 650;
|
|
22685
22708
|
|
|
22709
|
+
var cancelInProgressAnimation = null; // For smooth zoom animation
|
|
22710
|
+
|
|
22686
22711
|
var touchLongPressTimeout = 1000,
|
|
22687
22712
|
startTouchDistance = 0,
|
|
22688
22713
|
startTouchCenter = [],
|
|
22689
22714
|
moveTouchCenter = [],
|
|
22690
|
-
touchStartTime = 0
|
|
22715
|
+
touchStartTime = 0,
|
|
22716
|
+
gesture = {};
|
|
22717
|
+
|
|
22718
|
+
var spacebarPressed = false;
|
|
22719
|
+
|
|
22720
|
+
// Momentum scrolling state
|
|
22721
|
+
var scrollVelocity = { x: 0, y: 0 };
|
|
22722
|
+
var lastScrollTime = 0;
|
|
22723
|
+
var lastScrollPos = { x: 0, y: 0 };
|
|
22724
|
+
var scrollAnimationId = null;
|
|
22725
|
+
var momentumActive = false;
|
|
22726
|
+
|
|
22727
|
+
// Bounce effect parameters
|
|
22728
|
+
var BOUNCE_DAMPING = 0.6;
|
|
22729
|
+
var BOUNCE_TENSION = 0.3;
|
|
22730
|
+
var MIN_VELOCITY = 0.5;
|
|
22731
|
+
var FRICTION = 0.95;
|
|
22691
22732
|
|
|
22692
22733
|
var workspaceScrollPositions = {};
|
|
22693
22734
|
|
|
@@ -22964,6 +23005,24 @@ RED.view = (function() {
|
|
|
22964
23005
|
function init() {
|
|
22965
23006
|
|
|
22966
23007
|
chart = $("#red-ui-workspace-chart");
|
|
23008
|
+
|
|
23009
|
+
// Add invisible spacer div to ensure scrollable area matches canvas dimensions
|
|
23010
|
+
// At minimum zoom with "cover" behavior, SVG may be smaller than viewport in one dimension
|
|
23011
|
+
// This spacer forces the browser to calculate scrollWidth/Height based on full canvas size
|
|
23012
|
+
// Browser's maxScroll = scrollWidth - viewport will then correctly show canvas edges
|
|
23013
|
+
var scrollSpacer = $('<div>')
|
|
23014
|
+
.css({
|
|
23015
|
+
position: 'absolute',
|
|
23016
|
+
top: 0,
|
|
23017
|
+
left: 0,
|
|
23018
|
+
width: space_width + 'px',
|
|
23019
|
+
height: space_height + 'px',
|
|
23020
|
+
pointerEvents: 'none',
|
|
23021
|
+
visibility: 'hidden'
|
|
23022
|
+
})
|
|
23023
|
+
.attr('id', 'red-ui-workspace-scroll-spacer')
|
|
23024
|
+
.appendTo(chart);
|
|
23025
|
+
|
|
22967
23026
|
chart.on('contextmenu', function(evt) {
|
|
22968
23027
|
if (RED.view.DEBUG) {
|
|
22969
23028
|
console.warn("contextmenu", { mouse_mode, event: d3.event });
|
|
@@ -23008,8 +23067,9 @@ RED.view = (function() {
|
|
|
23008
23067
|
lasso.remove();
|
|
23009
23068
|
lasso = null;
|
|
23010
23069
|
}
|
|
23011
|
-
} else if (mouse_mode === RED.state.PANNING
|
|
23012
|
-
|
|
23070
|
+
} else if (mouse_mode === RED.state.PANNING) {
|
|
23071
|
+
// ensure the cursor is set to grab when re-entering the canvas while panning
|
|
23072
|
+
outer.style('cursor', 'grabbing');
|
|
23013
23073
|
} else if (slicePath) {
|
|
23014
23074
|
if (d3.event.buttons !== 2) {
|
|
23015
23075
|
slicePath.remove();
|
|
@@ -23026,11 +23086,15 @@ RED.view = (function() {
|
|
|
23026
23086
|
if (RED.touch.radialMenu.active()) {
|
|
23027
23087
|
return;
|
|
23028
23088
|
}
|
|
23089
|
+
// End gesture when touches end
|
|
23090
|
+
RED.view.zoomAnimator.endGesture();
|
|
23029
23091
|
canvasMouseUp.call(this);
|
|
23030
23092
|
})
|
|
23031
23093
|
.on("touchcancel", function() {
|
|
23032
23094
|
if (RED.view.DEBUG) { console.warn("eventLayer.touchcancel", mouse_mode); }
|
|
23033
23095
|
d3.event.preventDefault();
|
|
23096
|
+
// End gesture when touches are cancelled
|
|
23097
|
+
RED.view.zoomAnimator.endGesture();
|
|
23034
23098
|
canvasMouseUp.call(this);
|
|
23035
23099
|
})
|
|
23036
23100
|
.on("touchstart", function() {
|
|
@@ -23056,6 +23120,20 @@ RED.view = (function() {
|
|
|
23056
23120
|
touch1["pageY"]+(a/2)
|
|
23057
23121
|
]
|
|
23058
23122
|
startTouchDistance = Math.sqrt((a*a)+(b*b));
|
|
23123
|
+
|
|
23124
|
+
// Store initial scale for ratio-based zoom calculation
|
|
23125
|
+
gesture = {
|
|
23126
|
+
initialScale: scaleFactor,
|
|
23127
|
+
initialDistance: startTouchDistance,
|
|
23128
|
+
mode: null // Will be determined on first significant move
|
|
23129
|
+
};
|
|
23130
|
+
|
|
23131
|
+
// Start gesture with fixed focal point (store in workspace coordinates)
|
|
23132
|
+
var focalPoint = [
|
|
23133
|
+
(touch0["pageX"] + touch1["pageX"]) / 2 - offset.left,
|
|
23134
|
+
(touch0["pageY"] + touch1["pageY"]) / 2 - offset.top
|
|
23135
|
+
];
|
|
23136
|
+
RED.view.zoomAnimator.startGesture(focalPoint, scaleFactor, scrollPos, scaleFactor);
|
|
23059
23137
|
} else {
|
|
23060
23138
|
var obj = d3.select(document.body);
|
|
23061
23139
|
touch0 = d3.event.touches.item(0);
|
|
@@ -23105,33 +23183,93 @@ RED.view = (function() {
|
|
|
23105
23183
|
var offset = chart.offset();
|
|
23106
23184
|
var scrollPos = [chart.scrollLeft(),chart.scrollTop()];
|
|
23107
23185
|
var moveTouchDistance = Math.sqrt((a*a)+(b*b));
|
|
23108
|
-
|
|
23109
|
-
|
|
23110
|
-
|
|
23186
|
+
|
|
23187
|
+
// Calculate center point of two fingers
|
|
23188
|
+
var currentTouchCenter = [
|
|
23189
|
+
(touch0["pageX"] + touch1["pageX"]) / 2,
|
|
23190
|
+
(touch0["pageY"] + touch1["pageY"]) / 2
|
|
23111
23191
|
];
|
|
23112
23192
|
|
|
23113
23193
|
if (!isNaN(moveTouchDistance)) {
|
|
23114
|
-
|
|
23115
|
-
|
|
23116
|
-
|
|
23117
|
-
|
|
23118
|
-
|
|
23119
|
-
|
|
23120
|
-
|
|
23121
|
-
|
|
23122
|
-
|
|
23123
|
-
|
|
23194
|
+
// Determine gesture mode on first significant movement
|
|
23195
|
+
if (!gesture.mode) {
|
|
23196
|
+
var distanceChange = Math.abs(moveTouchDistance - startTouchDistance);
|
|
23197
|
+
var centerChange = moveTouchCenter ?
|
|
23198
|
+
Math.sqrt(Math.pow(currentTouchCenter[0] - moveTouchCenter[0], 2) +
|
|
23199
|
+
Math.pow(currentTouchCenter[1] - moveTouchCenter[1], 2)) : 0;
|
|
23200
|
+
|
|
23201
|
+
// Lock into zoom mode if distance changes significantly (>10px)
|
|
23202
|
+
// Lock into pan mode if center moves significantly (>5px) without distance change
|
|
23203
|
+
if (distanceChange > 10) {
|
|
23204
|
+
gesture.mode = 'zoom';
|
|
23205
|
+
} else if (centerChange > 5) {
|
|
23206
|
+
gesture.mode = 'pan';
|
|
23207
|
+
}
|
|
23208
|
+
}
|
|
23209
|
+
|
|
23210
|
+
// Once mode is determined, stay in that mode for the entire gesture
|
|
23211
|
+
if (gesture.mode === 'zoom') {
|
|
23212
|
+
oldScaleFactor = scaleFactor;
|
|
23213
|
+
// Use smooth ratio-based scaling for natural pinch-to-zoom
|
|
23214
|
+
var zoomRatio = moveTouchDistance / startTouchDistance;
|
|
23215
|
+
var minZoom = calculateMinZoom();
|
|
23216
|
+
var newScaleFactor = Math.min(RED.view.zoomConstants.MAX_ZOOM,
|
|
23217
|
+
Math.max(minZoom, gesture.initialScale * zoomRatio));
|
|
23218
|
+
|
|
23219
|
+
// Use gesture state management to maintain fixed focal point
|
|
23220
|
+
var gestureState = RED.view.zoomAnimator.updateGesture(newScaleFactor);
|
|
23221
|
+
|
|
23222
|
+
// Only call zoomView if scale is actually changing (not at limits)
|
|
23223
|
+
if (Math.abs(scaleFactor - newScaleFactor) >= 0.001) {
|
|
23224
|
+
// Get focal point converted back to current screen coordinates
|
|
23225
|
+
var currentScrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
23226
|
+
var focalPoint = RED.view.zoomAnimator.getGestureFocalPoint(currentScrollPos, scaleFactor);
|
|
23227
|
+
|
|
23228
|
+
if (focalPoint) {
|
|
23229
|
+
// Use the fixed focal point from gesture start (converted from workspace coords)
|
|
23230
|
+
zoomView(newScaleFactor, focalPoint);
|
|
23231
|
+
} else {
|
|
23232
|
+
// Fallback to current behavior if gesture not active
|
|
23233
|
+
var touchCenter = [
|
|
23234
|
+
touch1["pageX"]+(b/2),
|
|
23235
|
+
touch1["pageY"]+(a/2)
|
|
23236
|
+
];
|
|
23237
|
+
var pinchCenter = [
|
|
23238
|
+
touchCenter[0] - offset.left,
|
|
23239
|
+
touchCenter[1] - offset.top
|
|
23240
|
+
];
|
|
23241
|
+
zoomView(newScaleFactor, pinchCenter);
|
|
23242
|
+
}
|
|
23243
|
+
}
|
|
23244
|
+
} else if (gesture.mode === 'pan' || !gesture.mode) {
|
|
23245
|
+
// Two-finger pan: allow immediate panning even if mode not determined
|
|
23246
|
+
// Clear touchStartTime to prevent issues with next gesture
|
|
23247
|
+
if (touchStartTime) {
|
|
23248
|
+
clearTimeout(touchStartTime);
|
|
23249
|
+
touchStartTime = null;
|
|
23250
|
+
}
|
|
23251
|
+
if (moveTouchCenter) {
|
|
23252
|
+
var dx = currentTouchCenter[0] - moveTouchCenter[0];
|
|
23253
|
+
var dy = currentTouchCenter[1] - moveTouchCenter[1];
|
|
23254
|
+
|
|
23255
|
+
// Pan the canvas
|
|
23256
|
+
var currentScroll = [chart.scrollLeft(), chart.scrollTop()];
|
|
23257
|
+
chart.scrollLeft(currentScroll[0] - dx);
|
|
23258
|
+
chart.scrollTop(currentScroll[1] - dy);
|
|
23259
|
+
RED.events.emit("view:navigate");
|
|
23260
|
+
}
|
|
23261
|
+
// Update the center for next move
|
|
23262
|
+
moveTouchCenter = currentTouchCenter;
|
|
23263
|
+
}
|
|
23124
23264
|
|
|
23125
|
-
|
|
23126
|
-
chart.scrollTop(scrollPos[1]+deltaTouchCenter[1]);
|
|
23127
|
-
redraw();
|
|
23265
|
+
// Don't update startTouchDistance - keep initial distance for ratio calculation
|
|
23128
23266
|
}
|
|
23129
23267
|
}
|
|
23130
23268
|
d3.event.preventDefault();
|
|
23131
23269
|
});
|
|
23132
|
-
|
|
23133
|
-
|
|
23134
|
-
|
|
23270
|
+
|
|
23271
|
+
const handleChartKeyboardEvents = (event) => {
|
|
23272
|
+
// Handle Alt toggle for pulling nodes out of groups
|
|
23135
23273
|
if (mouse_mode === RED.state.MOVING_ACTIVE && event.key === 'Alt' && groupAddParentGroup) {
|
|
23136
23274
|
RED.nodes.group(groupAddParentGroup).dirty = true
|
|
23137
23275
|
for (let n = 0; n<movingSet.length(); n++) {
|
|
@@ -23150,10 +23288,67 @@ RED.view = (function() {
|
|
|
23150
23288
|
}
|
|
23151
23289
|
}
|
|
23152
23290
|
RED.view.redraw()
|
|
23291
|
+
} else if (event.keyCode === 32 || event.key === ' ') {
|
|
23292
|
+
if (mouse_mode === RED.state.PANNING) {
|
|
23293
|
+
// Already in panning mode - just prevent the event default handler
|
|
23294
|
+
event.preventDefault()
|
|
23295
|
+
event.stopPropagation()
|
|
23296
|
+
if (event.type === 'keyup' && spacebarPressed) {
|
|
23297
|
+
spacebarPressed = false
|
|
23298
|
+
}
|
|
23299
|
+
} else if (mouse_mode === RED.state.DEFAULT) {
|
|
23300
|
+
// Handle spacebar for panning
|
|
23301
|
+
event.preventDefault();
|
|
23302
|
+
event.stopPropagation();
|
|
23303
|
+
if (event.type === "keydown" && !spacebarPressed) {
|
|
23304
|
+
spacebarPressed = true;
|
|
23305
|
+
// Change cursor to grab hand when spacebar is pressed
|
|
23306
|
+
outer.style('cursor', 'grab');
|
|
23307
|
+
} else if (event.type === "keyup" && spacebarPressed) {
|
|
23308
|
+
spacebarPressed = false;
|
|
23309
|
+
// Revert cursor when spacebar is released
|
|
23310
|
+
outer.style('cursor', '');
|
|
23311
|
+
}
|
|
23312
|
+
}
|
|
23153
23313
|
}
|
|
23154
23314
|
}
|
|
23155
|
-
|
|
23156
|
-
|
|
23315
|
+
chart.on("keydown", handleChartKeyboardEvents)
|
|
23316
|
+
chart.on("keyup", handleChartKeyboardEvents)
|
|
23317
|
+
|
|
23318
|
+
// // // Window-level keyup listener to catch spacebar release when cursor is outside canvas
|
|
23319
|
+
// // function handleWindowSpacebarUp(e) {
|
|
23320
|
+
// // if ((e.keyCode === 32 || e.key === ' ') && spacebarPressed) {
|
|
23321
|
+
// // spacebarPressed = false;
|
|
23322
|
+
// // // Revert cursor when spacebar is released outside canvas
|
|
23323
|
+
// // outer.style('cursor', '');
|
|
23324
|
+
// // e.preventDefault();
|
|
23325
|
+
// // e.stopPropagation();
|
|
23326
|
+
// // }
|
|
23327
|
+
// // }
|
|
23328
|
+
// // chart.on("keyup", handleSpacebarToggle)
|
|
23329
|
+
// // chart.on("keydown", handleSpacebarToggle)
|
|
23330
|
+
// // Additional window-level keyup listener to ensure spacebar state is cleared
|
|
23331
|
+
// // when cursor leaves canvas area while spacebar is held
|
|
23332
|
+
// window.addEventListener("keyup", handleWindowSpacebarUp)
|
|
23333
|
+
|
|
23334
|
+
// // Reset spacebar state when window loses focus to prevent stuck state
|
|
23335
|
+
// window.addEventListener("blur", function() {
|
|
23336
|
+
// if (spacebarPressed) {
|
|
23337
|
+
// spacebarPressed = false;
|
|
23338
|
+
// // Revert cursor when window loses focus
|
|
23339
|
+
// outer.style('cursor', '');
|
|
23340
|
+
// }
|
|
23341
|
+
// })
|
|
23342
|
+
|
|
23343
|
+
// Recalculate minimum zoom when window resizes
|
|
23344
|
+
$(window).on("resize.red-ui-view", function() {
|
|
23345
|
+
// Recalculate minimum zoom to ensure canvas fits in viewport
|
|
23346
|
+
var newMinZoom = calculateMinZoom();
|
|
23347
|
+
// If current zoom is below new minimum, adjust it
|
|
23348
|
+
if (scaleFactor < newMinZoom) {
|
|
23349
|
+
zoomView(newMinZoom);
|
|
23350
|
+
}
|
|
23351
|
+
})
|
|
23157
23352
|
|
|
23158
23353
|
// Workspace Background
|
|
23159
23354
|
eventLayer.append("svg:rect")
|
|
@@ -23245,22 +23440,194 @@ RED.view = (function() {
|
|
|
23245
23440
|
'<button class="red-ui-footer-button" id="red-ui-view-zoom-out"><i class="fa fa-minus"></i></button>'+
|
|
23246
23441
|
'<button class="red-ui-footer-button" id="red-ui-view-zoom-zero"><i class="fa fa-circle-o"></i></button>'+
|
|
23247
23442
|
'<button class="red-ui-footer-button" id="red-ui-view-zoom-in"><i class="fa fa-plus"></i></button>'+
|
|
23443
|
+
'<button class="red-ui-footer-button" id="red-ui-view-zoom-fit"><i class="fa fa-compress"></i></button>'+
|
|
23248
23444
|
'</span>')
|
|
23249
23445
|
})
|
|
23250
23446
|
|
|
23251
|
-
$("#red-ui-view-zoom-out").on("click", zoomOut);
|
|
23447
|
+
$("#red-ui-view-zoom-out").on("click", function() { zoomOut(); });
|
|
23252
23448
|
RED.popover.tooltip($("#red-ui-view-zoom-out"),RED._('actions.zoom-out'),'core:zoom-out');
|
|
23253
23449
|
$("#red-ui-view-zoom-zero").on("click", zoomZero);
|
|
23254
23450
|
RED.popover.tooltip($("#red-ui-view-zoom-zero"),RED._('actions.zoom-reset'),'core:zoom-reset');
|
|
23255
|
-
$("#red-ui-view-zoom-in").on("click", zoomIn);
|
|
23451
|
+
$("#red-ui-view-zoom-in").on("click", function() { zoomIn(); });
|
|
23256
23452
|
RED.popover.tooltip($("#red-ui-view-zoom-in"),RED._('actions.zoom-in'),'core:zoom-in');
|
|
23257
|
-
|
|
23258
|
-
|
|
23453
|
+
$("#red-ui-view-zoom-fit").on("click", zoomToFitAll);
|
|
23454
|
+
RED.popover.tooltip($("#red-ui-view-zoom-fit"),RED._('actions.zoom-fit'),'core:zoom-fit');
|
|
23455
|
+
// Legacy mouse wheel handler - disabled in favor of modern wheel event
|
|
23456
|
+
// chart.on("DOMMouseScroll mousewheel", function (evt) {
|
|
23457
|
+
// if ( evt.altKey || spacebarPressed ) {
|
|
23458
|
+
// evt.preventDefault();
|
|
23459
|
+
// evt.stopPropagation();
|
|
23460
|
+
// // Get cursor position relative to the chart
|
|
23461
|
+
// var offset = chart.offset();
|
|
23462
|
+
// var cursorPos = [
|
|
23463
|
+
// evt.originalEvent.pageX - offset.left,
|
|
23464
|
+
// evt.originalEvent.pageY - offset.top
|
|
23465
|
+
// ];
|
|
23466
|
+
// var move = -(evt.originalEvent.detail) || evt.originalEvent.wheelDelta;
|
|
23467
|
+
// if (move <= 0) { zoomOut(cursorPos); }
|
|
23468
|
+
// else { zoomIn(cursorPos); }
|
|
23469
|
+
// }
|
|
23470
|
+
// });
|
|
23471
|
+
|
|
23472
|
+
// Modern wheel event handler for better trackpad support (pinch-to-zoom) and momentum
|
|
23473
|
+
var momentumTimer = null;
|
|
23474
|
+
var trackpadGestureTimer = null;
|
|
23475
|
+
var lastWheelEventTime = 0;
|
|
23476
|
+
var wheelEventContinuityThreshold = 100; // Events within 100ms are same gesture
|
|
23477
|
+
var gestureEndThreshold = 500; // 500ms+ gap means gesture ended
|
|
23478
|
+
|
|
23479
|
+
// Prevent browser zoom on non-canvas areas
|
|
23480
|
+
document.addEventListener("wheel", function(e) {
|
|
23481
|
+
if (e.ctrlKey && !e.target.closest('#red-ui-workspace-chart')) {
|
|
23482
|
+
e.preventDefault();
|
|
23483
|
+
}
|
|
23484
|
+
}, { passive: false });
|
|
23485
|
+
|
|
23486
|
+
chart.on("wheel", function(evt) {
|
|
23487
|
+
if (mouse_mode === RED.state.PANNING) {
|
|
23488
|
+
// Ignore wheel events while panning
|
|
23489
|
+
return;
|
|
23490
|
+
}
|
|
23491
|
+
// ctrlKey is set during pinch gestures on trackpads
|
|
23492
|
+
if (evt.ctrlKey || evt.altKey || spacebarPressed) {
|
|
23259
23493
|
evt.preventDefault();
|
|
23260
23494
|
evt.stopPropagation();
|
|
23261
|
-
|
|
23262
|
-
|
|
23263
|
-
|
|
23495
|
+
|
|
23496
|
+
var currentTime = Date.now();
|
|
23497
|
+
var timeSinceLastEvent = currentTime - lastWheelEventTime;
|
|
23498
|
+
|
|
23499
|
+
// Get cursor position relative to the chart
|
|
23500
|
+
var offset = chart.offset();
|
|
23501
|
+
var cursorPos = [
|
|
23502
|
+
evt.originalEvent.pageX - offset.left,
|
|
23503
|
+
evt.originalEvent.pageY - offset.top
|
|
23504
|
+
];
|
|
23505
|
+
var delta = evt.originalEvent.deltaY;
|
|
23506
|
+
|
|
23507
|
+
// For trackpad pinch (Ctrl+wheel), use smooth proportional zoom
|
|
23508
|
+
if (evt.ctrlKey && !evt.altKey && !spacebarPressed) {
|
|
23509
|
+
// Detect input device: trackpad has small deltas, mouse wheel has large deltas
|
|
23510
|
+
var isTrackpadInput = Math.abs(delta) < 50;
|
|
23511
|
+
// Invert delta: spreading fingers (negative deltaY) should zoom in
|
|
23512
|
+
var scaleDelta = RED.view.zoomAnimator.calculateZoomDelta(scaleFactor, -delta, isTrackpadInput);
|
|
23513
|
+
var minZoom = calculateMinZoom();
|
|
23514
|
+
var newScale = Math.min(RED.view.zoomConstants.MAX_ZOOM,
|
|
23515
|
+
Math.max(minZoom, scaleFactor + scaleDelta));
|
|
23516
|
+
|
|
23517
|
+
// Session-based gesture tracking:
|
|
23518
|
+
// - If no active gesture OR gap > gestureEndThreshold, start new gesture
|
|
23519
|
+
// - If gap < wheelEventContinuityThreshold, continue current gesture
|
|
23520
|
+
// - If gap between continuity and end threshold, keep current gesture but don't update focal point
|
|
23521
|
+
|
|
23522
|
+
if (!RED.view.zoomAnimator.isGestureActive() || timeSinceLastEvent > gestureEndThreshold) {
|
|
23523
|
+
// Start new gesture session - store focal point in workspace coordinates
|
|
23524
|
+
var scrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
23525
|
+
RED.view.zoomAnimator.startGesture(cursorPos, scaleFactor, scrollPos, scaleFactor);
|
|
23526
|
+
} else if (timeSinceLastEvent <= wheelEventContinuityThreshold) {
|
|
23527
|
+
// Events are continuous - this is the same gesture, focal point remains locked
|
|
23528
|
+
// No need to update focal point
|
|
23529
|
+
}
|
|
23530
|
+
// For gaps between continuity and end threshold, keep existing gesture state
|
|
23531
|
+
|
|
23532
|
+
// Update gesture with new scale, maintaining locked focal point
|
|
23533
|
+
RED.view.zoomAnimator.updateGesture(newScale);
|
|
23534
|
+
// Only call zoomView if scale is actually changing (not at limits)
|
|
23535
|
+
if (Math.abs(scaleFactor - newScale) >= 0.001) {
|
|
23536
|
+
// Get focal point converted back to current screen coordinates
|
|
23537
|
+
var currentScrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
23538
|
+
var focalPoint = RED.view.zoomAnimator.getGestureFocalPoint(currentScrollPos, scaleFactor);
|
|
23539
|
+
zoomView(newScale, focalPoint); // Direct call, no animation
|
|
23540
|
+
}
|
|
23541
|
+
|
|
23542
|
+
// Update last event time for continuity tracking
|
|
23543
|
+
lastWheelEventTime = currentTime;
|
|
23544
|
+
|
|
23545
|
+
// Reset gesture timeout - end gesture when no more events come in for gestureEndThreshold
|
|
23546
|
+
if (trackpadGestureTimer) {
|
|
23547
|
+
clearTimeout(trackpadGestureTimer);
|
|
23548
|
+
}
|
|
23549
|
+
trackpadGestureTimer = setTimeout(function() {
|
|
23550
|
+
RED.view.zoomAnimator.endGesture();
|
|
23551
|
+
trackpadGestureTimer = null;
|
|
23552
|
+
// Store zoom level when gesture completes
|
|
23553
|
+
if (RED.settings.get("editor.view.view-store-zoom")) {
|
|
23554
|
+
RED.settings.setLocal('zoom-level', scaleFactor.toFixed(1));
|
|
23555
|
+
}
|
|
23556
|
+
}, gestureEndThreshold); // Use 500ms timeout for gesture end detection
|
|
23557
|
+
} else {
|
|
23558
|
+
// Regular Alt+scroll or Space+scroll - use smooth zoom without animation
|
|
23559
|
+
// Detect input device: trackpad has small deltas, mouse wheel has large deltas
|
|
23560
|
+
var isTrackpadInput = Math.abs(delta) < 50;
|
|
23561
|
+
var scaleDelta = RED.view.zoomAnimator.calculateZoomDelta(scaleFactor, -delta, isTrackpadInput);
|
|
23562
|
+
var minZoom = calculateMinZoom();
|
|
23563
|
+
var newScale = Math.min(RED.view.zoomConstants.MAX_ZOOM,
|
|
23564
|
+
Math.max(minZoom, scaleFactor + scaleDelta));
|
|
23565
|
+
|
|
23566
|
+
// Use gesture tracking for stable focal point like trackpad pinch
|
|
23567
|
+
if (!RED.view.zoomAnimator.isGestureActive() || timeSinceLastEvent > gestureEndThreshold) {
|
|
23568
|
+
// Start new gesture session - store focal point in workspace coordinates
|
|
23569
|
+
var scrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
23570
|
+
RED.view.zoomAnimator.startGesture(cursorPos, scaleFactor, scrollPos, scaleFactor);
|
|
23571
|
+
} else if (timeSinceLastEvent <= wheelEventContinuityThreshold) {
|
|
23572
|
+
// Events are continuous - same gesture, focal point remains locked
|
|
23573
|
+
}
|
|
23574
|
+
|
|
23575
|
+
// Update gesture with new scale, maintaining locked focal point
|
|
23576
|
+
RED.view.zoomAnimator.updateGesture(newScale);
|
|
23577
|
+
|
|
23578
|
+
// Only zoom if scale is actually changing
|
|
23579
|
+
if (Math.abs(scaleFactor - newScale) >= 0.001) {
|
|
23580
|
+
// Get focal point converted back to current screen coordinates
|
|
23581
|
+
var currentScrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
23582
|
+
var focalPoint = RED.view.zoomAnimator.getGestureFocalPoint(currentScrollPos, scaleFactor);
|
|
23583
|
+
zoomView(newScale, focalPoint);
|
|
23584
|
+
}
|
|
23585
|
+
|
|
23586
|
+
// Update last event time for continuity tracking
|
|
23587
|
+
lastWheelEventTime = currentTime;
|
|
23588
|
+
|
|
23589
|
+
// Reset gesture timeout
|
|
23590
|
+
if (trackpadGestureTimer) {
|
|
23591
|
+
clearTimeout(trackpadGestureTimer);
|
|
23592
|
+
}
|
|
23593
|
+
trackpadGestureTimer = setTimeout(function() {
|
|
23594
|
+
RED.view.zoomAnimator.endGesture();
|
|
23595
|
+
trackpadGestureTimer = null;
|
|
23596
|
+
// Store zoom level when gesture completes
|
|
23597
|
+
if (RED.settings.get("editor.view.view-store-zoom")) {
|
|
23598
|
+
RED.settings.setLocal('zoom-level', scaleFactor.toFixed(1));
|
|
23599
|
+
}
|
|
23600
|
+
}, gestureEndThreshold);
|
|
23601
|
+
}
|
|
23602
|
+
} else {
|
|
23603
|
+
// Regular scroll - prevent default and manually handle both axes
|
|
23604
|
+
evt.preventDefault();
|
|
23605
|
+
evt.stopPropagation();
|
|
23606
|
+
|
|
23607
|
+
// Apply scroll deltas directly to both axes
|
|
23608
|
+
var deltaX = evt.originalEvent.deltaX;
|
|
23609
|
+
var deltaY = evt.originalEvent.deltaY;
|
|
23610
|
+
|
|
23611
|
+
chart.scrollLeft(chart.scrollLeft() + deltaX);
|
|
23612
|
+
chart.scrollTop(chart.scrollTop() + deltaY);
|
|
23613
|
+
|
|
23614
|
+
// Emit navigate event for minimap
|
|
23615
|
+
RED.events.emit("view:navigate");
|
|
23616
|
+
|
|
23617
|
+
// Track velocity and apply momentum
|
|
23618
|
+
handleScroll();
|
|
23619
|
+
|
|
23620
|
+
// Cancel previous momentum timer
|
|
23621
|
+
if (momentumTimer) {
|
|
23622
|
+
clearTimeout(momentumTimer);
|
|
23623
|
+
}
|
|
23624
|
+
|
|
23625
|
+
// Start momentum after scroll stops
|
|
23626
|
+
momentumTimer = setTimeout(function() {
|
|
23627
|
+
if (Math.abs(scrollVelocity.x) > MIN_VELOCITY || Math.abs(scrollVelocity.y) > MIN_VELOCITY) {
|
|
23628
|
+
startMomentumScroll();
|
|
23629
|
+
}
|
|
23630
|
+
}, 100);
|
|
23264
23631
|
}
|
|
23265
23632
|
});
|
|
23266
23633
|
|
|
@@ -23431,6 +23798,12 @@ RED.view = (function() {
|
|
|
23431
23798
|
});
|
|
23432
23799
|
chart.on("blur", function() {
|
|
23433
23800
|
$("#red-ui-workspace-tabs").removeClass("red-ui-workspace-focussed");
|
|
23801
|
+
// Reset spacebar state when chart loses focus to prevent stuck state
|
|
23802
|
+
if (spacebarPressed) {
|
|
23803
|
+
spacebarPressed = false;
|
|
23804
|
+
// Revert cursor when chart loses focus
|
|
23805
|
+
outer.style('cursor', '');
|
|
23806
|
+
}
|
|
23434
23807
|
});
|
|
23435
23808
|
|
|
23436
23809
|
RED.actions.add("core:copy-selection-to-internal-clipboard",copySelection);
|
|
@@ -23495,6 +23868,7 @@ RED.view = (function() {
|
|
|
23495
23868
|
RED.actions.add("core:zoom-in",zoomIn);
|
|
23496
23869
|
RED.actions.add("core:zoom-out",zoomOut);
|
|
23497
23870
|
RED.actions.add("core:zoom-reset",zoomZero);
|
|
23871
|
+
RED.actions.add("core:zoom-fit",zoomToFitAll);
|
|
23498
23872
|
RED.actions.add("core:enable-selected-nodes", function() { setSelectedNodeState(false)});
|
|
23499
23873
|
RED.actions.add("core:disable-selected-nodes", function() { setSelectedNodeState(true)});
|
|
23500
23874
|
|
|
@@ -23610,6 +23984,9 @@ RED.view = (function() {
|
|
|
23610
23984
|
RED.settings.setLocal('scroll-positions', JSON.stringify(workspaceScrollPositions) )
|
|
23611
23985
|
}
|
|
23612
23986
|
chart.on("scroll", function() {
|
|
23987
|
+
// Track scroll velocity for momentum
|
|
23988
|
+
handleScroll();
|
|
23989
|
+
|
|
23613
23990
|
if (RED.settings.get("editor.view.view-store-position")) {
|
|
23614
23991
|
if (onScrollTimer) {
|
|
23615
23992
|
clearTimeout(onScrollTimer)
|
|
@@ -23887,12 +24264,26 @@ RED.view = (function() {
|
|
|
23887
24264
|
return;
|
|
23888
24265
|
}
|
|
23889
24266
|
|
|
24267
|
+
// Spacebar + left click for panning
|
|
24268
|
+
if (spacebarPressed && d3.event.button === 0) {
|
|
24269
|
+
d3.event.preventDefault();
|
|
24270
|
+
d3.event.stopPropagation();
|
|
24271
|
+
mouse_mode = RED.state.PANNING;
|
|
24272
|
+
mouse_position = [d3.event.pageX,d3.event.pageY]
|
|
24273
|
+
scroll_position = [chart.scrollLeft(),chart.scrollTop()];
|
|
24274
|
+
// Change cursor to grabbing while actively panning
|
|
24275
|
+
outer.style('cursor', 'grabbing');
|
|
24276
|
+
return;
|
|
24277
|
+
}
|
|
24278
|
+
|
|
23890
24279
|
if (d3.event.button === 1) {
|
|
23891
24280
|
// Middle Click pan
|
|
23892
24281
|
d3.event.preventDefault();
|
|
23893
24282
|
mouse_mode = RED.state.PANNING;
|
|
23894
24283
|
mouse_position = [d3.event.pageX,d3.event.pageY]
|
|
23895
24284
|
scroll_position = [chart.scrollLeft(),chart.scrollTop()];
|
|
24285
|
+
// Change cursor to grabbing while actively panning
|
|
24286
|
+
outer.style('cursor', 'grabbing');
|
|
23896
24287
|
return;
|
|
23897
24288
|
}
|
|
23898
24289
|
if (d3.event.button === 2) {
|
|
@@ -24424,6 +24815,30 @@ RED.view = (function() {
|
|
|
24424
24815
|
redraw();
|
|
24425
24816
|
}
|
|
24426
24817
|
|
|
24818
|
+
function startPanning () {
|
|
24819
|
+
|
|
24820
|
+
}
|
|
24821
|
+
window.addEventListener('mousemove', windowMouseMove)
|
|
24822
|
+
window.addEventListener('touchmove', windowMouseMove)
|
|
24823
|
+
|
|
24824
|
+
function windowMouseMove (event) {
|
|
24825
|
+
if (mouse_mode === RED.state.PANNING) {
|
|
24826
|
+
let pos = [event.pageX, event.pageY]
|
|
24827
|
+
if (event.touches) {
|
|
24828
|
+
let touch0 = event.touches.item(0)
|
|
24829
|
+
pos = [touch0.pageX, touch0.pageY]
|
|
24830
|
+
}
|
|
24831
|
+
const deltaPos = [
|
|
24832
|
+
mouse_position[0]-pos[0],
|
|
24833
|
+
mouse_position[1]-pos[1]
|
|
24834
|
+
]
|
|
24835
|
+
chart.scrollLeft(scroll_position[0]+deltaPos[0])
|
|
24836
|
+
chart.scrollTop(scroll_position[1]+deltaPos[1])
|
|
24837
|
+
RED.events.emit("view:navigate");
|
|
24838
|
+
return
|
|
24839
|
+
}
|
|
24840
|
+
}
|
|
24841
|
+
|
|
24427
24842
|
function canvasMouseMove() {
|
|
24428
24843
|
var i;
|
|
24429
24844
|
var node;
|
|
@@ -24438,18 +24853,8 @@ RED.view = (function() {
|
|
|
24438
24853
|
//console.log(d3.mouse(this),container.offsetWidth,container.offsetHeight,container.scrollLeft,container.scrollTop);
|
|
24439
24854
|
|
|
24440
24855
|
if (mouse_mode === RED.state.PANNING) {
|
|
24441
|
-
|
|
24442
|
-
|
|
24443
|
-
var touch0 = d3.event.touches.item(0);
|
|
24444
|
-
pos = [touch0.pageX, touch0.pageY];
|
|
24445
|
-
}
|
|
24446
|
-
var deltaPos = [
|
|
24447
|
-
mouse_position[0]-pos[0],
|
|
24448
|
-
mouse_position[1]-pos[1]
|
|
24449
|
-
];
|
|
24450
|
-
|
|
24451
|
-
chart.scrollLeft(scroll_position[0]+deltaPos[0])
|
|
24452
|
-
chart.scrollTop(scroll_position[1]+deltaPos[1])
|
|
24856
|
+
// A window-level handler is used for panning so the mouse can leave the confines of the chart
|
|
24857
|
+
// but continue panning
|
|
24453
24858
|
return
|
|
24454
24859
|
}
|
|
24455
24860
|
|
|
@@ -24793,6 +25198,12 @@ RED.view = (function() {
|
|
|
24793
25198
|
}
|
|
24794
25199
|
if (mouse_mode === RED.state.PANNING) {
|
|
24795
25200
|
resetMouseVars();
|
|
25201
|
+
// Revert to grab cursor if spacebar still held, otherwise clear cursor
|
|
25202
|
+
if (spacebarPressed) {
|
|
25203
|
+
outer.style('cursor', 'grab');
|
|
25204
|
+
} else {
|
|
25205
|
+
outer.style('cursor', '');
|
|
25206
|
+
}
|
|
24796
25207
|
return
|
|
24797
25208
|
}
|
|
24798
25209
|
if (mouse_mode === RED.state.SELECTING_NODE) {
|
|
@@ -25124,39 +25535,454 @@ RED.view = (function() {
|
|
|
25124
25535
|
|
|
25125
25536
|
}
|
|
25126
25537
|
|
|
25127
|
-
function
|
|
25128
|
-
|
|
25129
|
-
|
|
25538
|
+
function calculateMinZoom() {
|
|
25539
|
+
// Calculate the minimum zoom to ensure canvas always fills the viewport (no empty space)
|
|
25540
|
+
var viewportWidth = chart.width();
|
|
25541
|
+
var viewportHeight = chart.height();
|
|
25542
|
+
|
|
25543
|
+
// Canvas is 8000x8000, calculate zoom to cover viewport
|
|
25544
|
+
var zoomToFitWidth = viewportWidth / space_width;
|
|
25545
|
+
var zoomToFitHeight = viewportHeight / space_height;
|
|
25546
|
+
|
|
25547
|
+
// Use the LARGER zoom to ensure canvas covers entire viewport (no empty space visible)
|
|
25548
|
+
var calculatedMinZoom = Math.max(zoomToFitWidth, zoomToFitHeight);
|
|
25549
|
+
|
|
25550
|
+
// Return the larger of the calculated min or the configured min
|
|
25551
|
+
// This ensures canvas always fills the viewport
|
|
25552
|
+
return Math.max(calculatedMinZoom, RED.view.zoomConstants.MIN_ZOOM);
|
|
25553
|
+
}
|
|
25554
|
+
|
|
25555
|
+
// Track focal point for sequential button/hotkey zoom operations
|
|
25556
|
+
// Store in workspace coordinates so it remains valid after viewport shifts
|
|
25557
|
+
var buttonZoomWorkspaceCenter = null;
|
|
25558
|
+
var buttonZoomTimeout = null;
|
|
25559
|
+
var BUTTON_ZOOM_FOCAL_TIMEOUT = 1000; // ms - time to keep same focal point
|
|
25560
|
+
|
|
25561
|
+
function zoomIn(focalPoint) {
|
|
25562
|
+
if (scaleFactor < RED.view.zoomConstants.MAX_ZOOM) {
|
|
25563
|
+
var useFocalPoint = null;
|
|
25564
|
+
|
|
25565
|
+
// If focalPoint is explicitly provided (e.g., from wheel/pinch), use it directly
|
|
25566
|
+
if (focalPoint) {
|
|
25567
|
+
useFocalPoint = focalPoint;
|
|
25568
|
+
} else {
|
|
25569
|
+
// For button/hotkey zoom, maintain the same workspace center across sequential zooms
|
|
25570
|
+
if (!buttonZoomWorkspaceCenter) {
|
|
25571
|
+
// First button zoom - calculate and store workspace center
|
|
25572
|
+
var screenSize = [chart.width(), chart.height()];
|
|
25573
|
+
var scrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
25574
|
+
// Convert viewport center to workspace coordinates
|
|
25575
|
+
buttonZoomWorkspaceCenter = [
|
|
25576
|
+
(scrollPos[0] + screenSize[0]/2) / scaleFactor,
|
|
25577
|
+
(scrollPos[1] + screenSize[1]/2) / scaleFactor
|
|
25578
|
+
];
|
|
25579
|
+
}
|
|
25580
|
+
|
|
25581
|
+
// ALWAYS use viewport center as focal point (fixed screen position)
|
|
25582
|
+
// The stored workspace center will be kept at this screen position
|
|
25583
|
+
var screenSize = [chart.width(), chart.height()];
|
|
25584
|
+
useFocalPoint = [screenSize[0]/2, screenSize[1]/2];
|
|
25585
|
+
|
|
25586
|
+
// Reset timeout
|
|
25587
|
+
clearTimeout(buttonZoomTimeout);
|
|
25588
|
+
buttonZoomTimeout = setTimeout(function() {
|
|
25589
|
+
buttonZoomWorkspaceCenter = null;
|
|
25590
|
+
}, BUTTON_ZOOM_FOCAL_TIMEOUT);
|
|
25591
|
+
}
|
|
25592
|
+
|
|
25593
|
+
animatedZoomView(scaleFactor + RED.view.zoomConstants.ZOOM_STEP, useFocalPoint, buttonZoomWorkspaceCenter);
|
|
25594
|
+
}
|
|
25595
|
+
}
|
|
25596
|
+
function zoomOut(focalPoint) {
|
|
25597
|
+
var minZoom = calculateMinZoom();
|
|
25598
|
+
if (scaleFactor > minZoom) {
|
|
25599
|
+
var useFocalPoint = null;
|
|
25600
|
+
|
|
25601
|
+
if (focalPoint) {
|
|
25602
|
+
useFocalPoint = focalPoint;
|
|
25603
|
+
} else {
|
|
25604
|
+
if (!buttonZoomWorkspaceCenter) {
|
|
25605
|
+
var screenSize = [chart.width(), chart.height()];
|
|
25606
|
+
var scrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
25607
|
+
buttonZoomWorkspaceCenter = [
|
|
25608
|
+
(scrollPos[0] + screenSize[0]/2) / scaleFactor,
|
|
25609
|
+
(scrollPos[1] + screenSize[1]/2) / scaleFactor
|
|
25610
|
+
];
|
|
25611
|
+
}
|
|
25612
|
+
|
|
25613
|
+
// ALWAYS use viewport center as focal point (fixed screen position)
|
|
25614
|
+
var screenSize = [chart.width(), chart.height()];
|
|
25615
|
+
useFocalPoint = [screenSize[0]/2, screenSize[1]/2];
|
|
25616
|
+
|
|
25617
|
+
clearTimeout(buttonZoomTimeout);
|
|
25618
|
+
buttonZoomTimeout = setTimeout(function() {
|
|
25619
|
+
buttonZoomWorkspaceCenter = null;
|
|
25620
|
+
}, BUTTON_ZOOM_FOCAL_TIMEOUT);
|
|
25621
|
+
}
|
|
25622
|
+
|
|
25623
|
+
animatedZoomView(Math.max(scaleFactor - RED.view.zoomConstants.ZOOM_STEP, minZoom), useFocalPoint, buttonZoomWorkspaceCenter);
|
|
25130
25624
|
}
|
|
25131
25625
|
}
|
|
25132
|
-
function
|
|
25133
|
-
|
|
25134
|
-
|
|
25626
|
+
function zoomZero() {
|
|
25627
|
+
// Reset button zoom focal point for zoom reset
|
|
25628
|
+
clearTimeout(buttonZoomTimeout);
|
|
25629
|
+
buttonZoomWorkspaceCenter = null;
|
|
25630
|
+
animatedZoomView(1);
|
|
25631
|
+
}
|
|
25632
|
+
|
|
25633
|
+
function zoomToFitAll() {
|
|
25634
|
+
// Refresh active nodes to ensure we have the latest
|
|
25635
|
+
updateActiveNodes();
|
|
25636
|
+
|
|
25637
|
+
// Get all nodes in active workspace
|
|
25638
|
+
if (!activeNodes || activeNodes.length === 0) {
|
|
25639
|
+
return; // No nodes to fit
|
|
25640
|
+
}
|
|
25641
|
+
|
|
25642
|
+
// Calculate bounding box of all nodes
|
|
25643
|
+
var minX = Infinity, minY = Infinity;
|
|
25644
|
+
var maxX = -Infinity, maxY = -Infinity;
|
|
25645
|
+
|
|
25646
|
+
activeNodes.forEach(function(node) {
|
|
25647
|
+
var nodeLeft = node.x - node.w / 2;
|
|
25648
|
+
var nodeRight = node.x + node.w / 2;
|
|
25649
|
+
var nodeTop = node.y - node.h / 2;
|
|
25650
|
+
var nodeBottom = node.y + node.h / 2;
|
|
25651
|
+
|
|
25652
|
+
minX = Math.min(minX, nodeLeft);
|
|
25653
|
+
maxX = Math.max(maxX, nodeRight);
|
|
25654
|
+
minY = Math.min(minY, nodeTop);
|
|
25655
|
+
maxY = Math.max(maxY, nodeBottom);
|
|
25656
|
+
});
|
|
25657
|
+
|
|
25658
|
+
// Add padding around nodes for visual breathing room
|
|
25659
|
+
var padding = 80;
|
|
25660
|
+
minX -= padding;
|
|
25661
|
+
minY -= padding;
|
|
25662
|
+
maxX += padding;
|
|
25663
|
+
maxY += padding;
|
|
25664
|
+
|
|
25665
|
+
// Calculate dimensions of bounding box
|
|
25666
|
+
var boundingWidth = maxX - minX;
|
|
25667
|
+
var boundingHeight = maxY - minY;
|
|
25668
|
+
|
|
25669
|
+
// Get viewport dimensions
|
|
25670
|
+
var viewportWidth = chart.width();
|
|
25671
|
+
var viewportHeight = chart.height();
|
|
25672
|
+
|
|
25673
|
+
// Calculate zoom level that fits bounding box in viewport
|
|
25674
|
+
var zoomX = viewportWidth / boundingWidth;
|
|
25675
|
+
var zoomY = viewportHeight / boundingHeight;
|
|
25676
|
+
var targetZoom = Math.min(zoomX, zoomY);
|
|
25677
|
+
|
|
25678
|
+
// Respect minimum and maximum zoom limits
|
|
25679
|
+
var minZoom = calculateMinZoom();
|
|
25680
|
+
targetZoom = Math.max(minZoom, Math.min(RED.view.zoomConstants.MAX_ZOOM, targetZoom));
|
|
25681
|
+
|
|
25682
|
+
// Calculate center point of bounding box in workspace coordinates
|
|
25683
|
+
var centerX = (minX + maxX) / 2;
|
|
25684
|
+
var centerY = (minY + maxY) / 2;
|
|
25685
|
+
|
|
25686
|
+
// Reset button zoom focal point for zoom-to-fit
|
|
25687
|
+
clearTimeout(buttonZoomTimeout);
|
|
25688
|
+
buttonZoomWorkspaceCenter = null;
|
|
25689
|
+
|
|
25690
|
+
// Pass the bounding box center as workspace center
|
|
25691
|
+
// This ensures the nodes are centered in viewport after zoom
|
|
25692
|
+
var focalPoint = [viewportWidth / 2, viewportHeight / 2];
|
|
25693
|
+
|
|
25694
|
+
// If zoom level won't change significantly, animate just the pan
|
|
25695
|
+
if (Math.abs(scaleFactor - targetZoom) < 0.01) {
|
|
25696
|
+
var targetScrollLeft = centerX * scaleFactor - viewportWidth / 2;
|
|
25697
|
+
var targetScrollTop = centerY * scaleFactor - viewportHeight / 2;
|
|
25698
|
+
|
|
25699
|
+
// Calculate pan distance to determine duration (match zoom animation logic)
|
|
25700
|
+
var startScrollLeft = chart.scrollLeft();
|
|
25701
|
+
var startScrollTop = chart.scrollTop();
|
|
25702
|
+
var panDistance = Math.sqrt(
|
|
25703
|
+
Math.pow(targetScrollLeft - startScrollLeft, 2) +
|
|
25704
|
+
Math.pow(targetScrollTop - startScrollTop, 2)
|
|
25705
|
+
);
|
|
25706
|
+
|
|
25707
|
+
// Use similar duration calculation as zoom: scale with distance
|
|
25708
|
+
// Normalize by viewport diagonal for consistent feel
|
|
25709
|
+
var viewportDiagonal = Math.sqrt(viewportWidth * viewportWidth + viewportHeight * viewportHeight);
|
|
25710
|
+
var relativeDistance = panDistance / viewportDiagonal;
|
|
25711
|
+
// Duration scales with distance, matching zoom animation feel
|
|
25712
|
+
var duration = Math.max(200, Math.min(350, relativeDistance * RED.view.zoomConstants.DEFAULT_ZOOM_DURATION * 4));
|
|
25713
|
+
|
|
25714
|
+
RED.view.zoomAnimator.easeToValuesRAF({
|
|
25715
|
+
fromValues: {
|
|
25716
|
+
scrollLeft: startScrollLeft,
|
|
25717
|
+
scrollTop: startScrollTop
|
|
25718
|
+
},
|
|
25719
|
+
toValues: {
|
|
25720
|
+
scrollLeft: targetScrollLeft,
|
|
25721
|
+
scrollTop: targetScrollTop
|
|
25722
|
+
},
|
|
25723
|
+
duration: duration,
|
|
25724
|
+
onStep: function(values) {
|
|
25725
|
+
chart.scrollLeft(values.scrollLeft);
|
|
25726
|
+
chart.scrollTop(values.scrollTop);
|
|
25727
|
+
},
|
|
25728
|
+
onStart: function() {
|
|
25729
|
+
RED.events.emit("view:navigate");
|
|
25730
|
+
}
|
|
25731
|
+
});
|
|
25732
|
+
} else {
|
|
25733
|
+
animatedZoomView(targetZoom, focalPoint, [centerX, centerY]);
|
|
25135
25734
|
}
|
|
25136
25735
|
}
|
|
25137
|
-
|
|
25736
|
+
|
|
25138
25737
|
function searchFlows() { RED.actions.invoke("core:search", $(this).data("term")); }
|
|
25139
25738
|
function searchPrev() { RED.actions.invoke("core:search-previous"); }
|
|
25140
25739
|
function searchNext() { RED.actions.invoke("core:search-next"); }
|
|
25141
25740
|
|
|
25142
25741
|
|
|
25143
|
-
function zoomView(factor) {
|
|
25742
|
+
function zoomView(factor, focalPoint) {
|
|
25743
|
+
// Early return if scale factor isn't actually changing
|
|
25744
|
+
// This prevents focal point shifts when at zoom limits
|
|
25745
|
+
if (Math.abs(scaleFactor - factor) < 0.001) {
|
|
25746
|
+
return;
|
|
25747
|
+
}
|
|
25748
|
+
// Make scale 1 'sticky'
|
|
25749
|
+
if (Math.abs(1.0 - factor) < 0.02) {
|
|
25750
|
+
factor = 1
|
|
25751
|
+
}
|
|
25752
|
+
|
|
25753
|
+
console.log(factor)
|
|
25144
25754
|
var screenSize = [chart.width(),chart.height()];
|
|
25145
25755
|
var scrollPos = [chart.scrollLeft(),chart.scrollTop()];
|
|
25146
|
-
var
|
|
25756
|
+
var oldScaleFactor = scaleFactor;
|
|
25757
|
+
|
|
25758
|
+
// Calculate workspace coordinates of the point that should remain fixed
|
|
25759
|
+
var center;
|
|
25760
|
+
if (focalPoint) {
|
|
25761
|
+
// focalPoint is in screen coordinates, convert to workspace coordinates
|
|
25762
|
+
center = [(scrollPos[0] + focalPoint[0])/oldScaleFactor, (scrollPos[1] + focalPoint[1])/oldScaleFactor];
|
|
25763
|
+
} else {
|
|
25764
|
+
// Default to viewport center in workspace coordinates
|
|
25765
|
+
center = [(scrollPos[0] + screenSize[0]/2)/oldScaleFactor, (scrollPos[1] + screenSize[1]/2)/oldScaleFactor];
|
|
25766
|
+
}
|
|
25767
|
+
|
|
25147
25768
|
scaleFactor = factor;
|
|
25148
|
-
|
|
25149
|
-
|
|
25150
|
-
|
|
25151
|
-
|
|
25769
|
+
|
|
25770
|
+
// Calculate new scroll position to keep the center point at the same screen position
|
|
25771
|
+
if (focalPoint) {
|
|
25772
|
+
// Keep the focal point at the same screen position
|
|
25773
|
+
chart.scrollLeft(center[0] * scaleFactor - focalPoint[0]);
|
|
25774
|
+
chart.scrollTop(center[1] * scaleFactor - focalPoint[1]);
|
|
25775
|
+
} else {
|
|
25776
|
+
// Keep viewport center on the same workspace coordinates
|
|
25777
|
+
var newScrollLeft = center[0] * scaleFactor - screenSize[0]/2;
|
|
25778
|
+
var newScrollTop = center[1] * scaleFactor - screenSize[1]/2;
|
|
25779
|
+
chart.scrollLeft(newScrollLeft);
|
|
25780
|
+
chart.scrollTop(newScrollTop);
|
|
25781
|
+
}
|
|
25152
25782
|
|
|
25153
25783
|
RED.view.navigator.resize();
|
|
25154
25784
|
redraw();
|
|
25785
|
+
RED.events.emit("view:navigate");
|
|
25155
25786
|
if (RED.settings.get("editor.view.view-store-zoom")) {
|
|
25156
25787
|
RED.settings.setLocal('zoom-level', factor.toFixed(1))
|
|
25157
25788
|
}
|
|
25158
25789
|
}
|
|
25159
25790
|
|
|
25791
|
+
function animatedZoomView(targetFactor, focalPoint, workspaceCenter) {
|
|
25792
|
+
// Cancel any in-progress animation
|
|
25793
|
+
if (cancelInProgressAnimation) {
|
|
25794
|
+
cancelInProgressAnimation();
|
|
25795
|
+
cancelInProgressAnimation = null;
|
|
25796
|
+
}
|
|
25797
|
+
|
|
25798
|
+
// Calculate the actual minimum zoom to fit canvas
|
|
25799
|
+
var minZoom = calculateMinZoom();
|
|
25800
|
+
|
|
25801
|
+
// Clamp target factor to valid range
|
|
25802
|
+
targetFactor = Math.max(minZoom,
|
|
25803
|
+
Math.min(RED.view.zoomConstants.MAX_ZOOM, targetFactor));
|
|
25804
|
+
|
|
25805
|
+
// If we're already at the target, no need to animate
|
|
25806
|
+
// Use a more tolerant threshold to account for floating-point precision
|
|
25807
|
+
if (Math.abs(scaleFactor - targetFactor) < 0.01) {
|
|
25808
|
+
return;
|
|
25809
|
+
}
|
|
25810
|
+
// Make scale 1 'sticky'
|
|
25811
|
+
if (Math.abs(1.0 - targetFactor) < 0.02) {
|
|
25812
|
+
targetFactor = 1
|
|
25813
|
+
}
|
|
25814
|
+
|
|
25815
|
+
|
|
25816
|
+
var startFactor = scaleFactor;
|
|
25817
|
+
var screenSize = [chart.width(), chart.height()];
|
|
25818
|
+
var scrollPos = [chart.scrollLeft(), chart.scrollTop()];
|
|
25819
|
+
|
|
25820
|
+
// Calculate the focal point in workspace coordinates (will remain constant)
|
|
25821
|
+
var center;
|
|
25822
|
+
if (workspaceCenter) {
|
|
25823
|
+
// Use the provided workspace center directly (for button zoom focal point locking)
|
|
25824
|
+
center = workspaceCenter;
|
|
25825
|
+
} else if (focalPoint) {
|
|
25826
|
+
// focalPoint is in screen coordinates, convert to workspace coordinates
|
|
25827
|
+
center = [(scrollPos[0] + focalPoint[0])/scaleFactor, (scrollPos[1] + focalPoint[1])/scaleFactor];
|
|
25828
|
+
} else {
|
|
25829
|
+
// Default to viewport center
|
|
25830
|
+
center = [(scrollPos[0] + screenSize[0]/2)/scaleFactor, (scrollPos[1] + screenSize[1]/2)/scaleFactor];
|
|
25831
|
+
}
|
|
25832
|
+
|
|
25833
|
+
// Calculate duration based on relative zoom change to maintain consistent velocity
|
|
25834
|
+
// Use logarithmic scaling since zoom feels exponential to the user
|
|
25835
|
+
var zoomRatio = targetFactor / startFactor;
|
|
25836
|
+
var logChange = Math.abs(Math.log(zoomRatio));
|
|
25837
|
+
// Scale duration more aggressively: multiply by 2 for stronger effect
|
|
25838
|
+
// At extreme zoom levels, animation will be noticeably longer
|
|
25839
|
+
var duration = Math.max(200, Math.min(350, logChange / 0.693 * RED.view.zoomConstants.DEFAULT_ZOOM_DURATION * 2));
|
|
25840
|
+
|
|
25841
|
+
// Start the animation
|
|
25842
|
+
cancelInProgressAnimation = RED.view.zoomAnimator.easeToValuesRAF({
|
|
25843
|
+
fromValues: {
|
|
25844
|
+
zoom: startFactor
|
|
25845
|
+
},
|
|
25846
|
+
toValues: {
|
|
25847
|
+
zoom: targetFactor
|
|
25848
|
+
},
|
|
25849
|
+
duration: duration,
|
|
25850
|
+
interpolateValue: true, // Use exponential interpolation for zoom
|
|
25851
|
+
onStep: function(values) {
|
|
25852
|
+
var currentFactor = values.zoom;
|
|
25853
|
+
scaleFactor = currentFactor;
|
|
25854
|
+
|
|
25855
|
+
// Calculate new scroll position to maintain focal point
|
|
25856
|
+
var currentScreenSize = [chart.width(), chart.height()];
|
|
25857
|
+
var newScrollPos;
|
|
25858
|
+
|
|
25859
|
+
if (focalPoint) {
|
|
25860
|
+
// Keep the focal point at the same screen position
|
|
25861
|
+
newScrollPos = [
|
|
25862
|
+
center[0] * scaleFactor - focalPoint[0],
|
|
25863
|
+
center[1] * scaleFactor - focalPoint[1]
|
|
25864
|
+
];
|
|
25865
|
+
} else {
|
|
25866
|
+
// Keep viewport center steady
|
|
25867
|
+
newScrollPos = [
|
|
25868
|
+
center[0] * scaleFactor - currentScreenSize[0]/2,
|
|
25869
|
+
center[1] * scaleFactor - currentScreenSize[1]/2
|
|
25870
|
+
];
|
|
25871
|
+
}
|
|
25872
|
+
|
|
25873
|
+
chart.scrollLeft(newScrollPos[0]);
|
|
25874
|
+
chart.scrollTop(newScrollPos[1]);
|
|
25875
|
+
|
|
25876
|
+
// During animation, only update the scale transform, not the full redraw
|
|
25877
|
+
// This is much more performant with many nodes
|
|
25878
|
+
eventLayer.attr("transform", "scale(" + scaleFactor + ")");
|
|
25879
|
+
outer.attr("width", space_width * scaleFactor).attr("height", space_height * scaleFactor);
|
|
25880
|
+
RED.view.navigator.resize();
|
|
25881
|
+
},
|
|
25882
|
+
onStart: function() {
|
|
25883
|
+
// Show minimap when zoom animation starts
|
|
25884
|
+
RED.events.emit("view:navigate");
|
|
25885
|
+
},
|
|
25886
|
+
onEnd: function() {
|
|
25887
|
+
cancelInProgressAnimation = null;
|
|
25888
|
+
// Ensure scaleFactor is exactly the target to prevent precision issues
|
|
25889
|
+
scaleFactor = targetFactor;
|
|
25890
|
+
// Full redraw at the end to ensure everything is correct
|
|
25891
|
+
redraw();
|
|
25892
|
+
if (RED.settings.get("editor.view.view-store-zoom")) {
|
|
25893
|
+
RED.settings.setLocal('zoom-level', targetFactor.toFixed(1));
|
|
25894
|
+
}
|
|
25895
|
+
},
|
|
25896
|
+
onCancel: function() {
|
|
25897
|
+
cancelInProgressAnimation = null;
|
|
25898
|
+
// Ensure scaleFactor is set to current target on cancel
|
|
25899
|
+
scaleFactor = targetFactor;
|
|
25900
|
+
}
|
|
25901
|
+
});
|
|
25902
|
+
}
|
|
25903
|
+
|
|
25904
|
+
// Momentum scrolling functions
|
|
25905
|
+
function startMomentumScroll() {
|
|
25906
|
+
if (scrollAnimationId) {
|
|
25907
|
+
cancelAnimationFrame(scrollAnimationId);
|
|
25908
|
+
}
|
|
25909
|
+
momentumActive = true;
|
|
25910
|
+
animateMomentumScroll();
|
|
25911
|
+
}
|
|
25912
|
+
|
|
25913
|
+
function animateMomentumScroll() {
|
|
25914
|
+
if (!momentumActive) {
|
|
25915
|
+
return;
|
|
25916
|
+
}
|
|
25917
|
+
|
|
25918
|
+
var scrollX = chart.scrollLeft();
|
|
25919
|
+
var scrollY = chart.scrollTop();
|
|
25920
|
+
var maxScrollX = chart[0].scrollWidth - chart.width();
|
|
25921
|
+
var maxScrollY = chart[0].scrollHeight - chart.height();
|
|
25922
|
+
|
|
25923
|
+
// Apply friction
|
|
25924
|
+
scrollVelocity.x *= FRICTION;
|
|
25925
|
+
scrollVelocity.y *= FRICTION;
|
|
25926
|
+
|
|
25927
|
+
// Check for edges and apply bounce
|
|
25928
|
+
var newScrollX = scrollX + scrollVelocity.x;
|
|
25929
|
+
var newScrollY = scrollY + scrollVelocity.y;
|
|
25930
|
+
|
|
25931
|
+
// Bounce effect at edges
|
|
25932
|
+
if (newScrollX < 0) {
|
|
25933
|
+
newScrollX = 0;
|
|
25934
|
+
scrollVelocity.x = -scrollVelocity.x * BOUNCE_DAMPING;
|
|
25935
|
+
} else if (newScrollX > maxScrollX) {
|
|
25936
|
+
newScrollX = maxScrollX;
|
|
25937
|
+
scrollVelocity.x = -scrollVelocity.x * BOUNCE_DAMPING;
|
|
25938
|
+
}
|
|
25939
|
+
|
|
25940
|
+
if (newScrollY < 0) {
|
|
25941
|
+
newScrollY = 0;
|
|
25942
|
+
scrollVelocity.y = -scrollVelocity.y * BOUNCE_DAMPING;
|
|
25943
|
+
} else if (newScrollY > maxScrollY) {
|
|
25944
|
+
newScrollY = maxScrollY;
|
|
25945
|
+
scrollVelocity.y = -scrollVelocity.y * BOUNCE_DAMPING;
|
|
25946
|
+
}
|
|
25947
|
+
|
|
25948
|
+
// Apply new scroll position
|
|
25949
|
+
chart.scrollLeft(newScrollX);
|
|
25950
|
+
chart.scrollTop(newScrollY);
|
|
25951
|
+
|
|
25952
|
+
// Stop if velocity is too small
|
|
25953
|
+
if (Math.abs(scrollVelocity.x) < MIN_VELOCITY && Math.abs(scrollVelocity.y) < MIN_VELOCITY) {
|
|
25954
|
+
momentumActive = false;
|
|
25955
|
+
scrollVelocity.x = 0;
|
|
25956
|
+
scrollVelocity.y = 0;
|
|
25957
|
+
} else {
|
|
25958
|
+
scrollAnimationId = requestAnimationFrame(animateMomentumScroll);
|
|
25959
|
+
}
|
|
25960
|
+
}
|
|
25961
|
+
|
|
25962
|
+
function handleScroll() {
|
|
25963
|
+
var now = Date.now();
|
|
25964
|
+
var scrollX = chart.scrollLeft();
|
|
25965
|
+
var scrollY = chart.scrollTop();
|
|
25966
|
+
|
|
25967
|
+
if (lastScrollTime) {
|
|
25968
|
+
var dt = now - lastScrollTime;
|
|
25969
|
+
if (dt > 0 && dt < 100) { // Only calculate velocity for recent scrolls
|
|
25970
|
+
scrollVelocity.x = (scrollX - lastScrollPos.x) / dt * 16; // Normalize to 60fps
|
|
25971
|
+
scrollVelocity.y = (scrollY - lastScrollPos.y) / dt * 16;
|
|
25972
|
+
}
|
|
25973
|
+
}
|
|
25974
|
+
|
|
25975
|
+
lastScrollTime = now;
|
|
25976
|
+
lastScrollPos.x = scrollX;
|
|
25977
|
+
lastScrollPos.y = scrollY;
|
|
25978
|
+
|
|
25979
|
+
// Cancel any ongoing momentum animation
|
|
25980
|
+
if (scrollAnimationId) {
|
|
25981
|
+
cancelAnimationFrame(scrollAnimationId);
|
|
25982
|
+
scrollAnimationId = null;
|
|
25983
|
+
}
|
|
25984
|
+
}
|
|
25985
|
+
|
|
25160
25986
|
function selectNone() {
|
|
25161
25987
|
if (mouse_mode === RED.state.MOVING || mouse_mode === RED.state.MOVING_ACTIVE) {
|
|
25162
25988
|
return;
|
|
@@ -25861,6 +26687,9 @@ RED.view = (function() {
|
|
|
25861
26687
|
|
|
25862
26688
|
function portMouseDown(d,portType,portIndex, evt) {
|
|
25863
26689
|
if (RED.view.DEBUG) { console.warn("portMouseDown", mouse_mode,d,portType,portIndex); }
|
|
26690
|
+
if (spacebarPressed) {
|
|
26691
|
+
return
|
|
26692
|
+
}
|
|
25864
26693
|
clearSuggestedFlow();
|
|
25865
26694
|
RED.contextMenu.hide();
|
|
25866
26695
|
evt = evt || d3.event;
|
|
@@ -26290,6 +27119,9 @@ RED.view = (function() {
|
|
|
26290
27119
|
(d3.event || event).stopPropagation();
|
|
26291
27120
|
return;
|
|
26292
27121
|
}
|
|
27122
|
+
if (spacebarPressed) {
|
|
27123
|
+
return
|
|
27124
|
+
}
|
|
26293
27125
|
clearTimeout(portLabelHoverTimeout);
|
|
26294
27126
|
var active = (mouse_mode!=RED.state.JOINING && mouse_mode != RED.state.QUICK_JOINING) || // Not currently joining - all ports active
|
|
26295
27127
|
(
|
|
@@ -26446,6 +27278,9 @@ RED.view = (function() {
|
|
|
26446
27278
|
}
|
|
26447
27279
|
function nodeMouseDown(d) {
|
|
26448
27280
|
if (RED.view.DEBUG) { console.warn("nodeMouseDown", mouse_mode,d); }
|
|
27281
|
+
if (spacebarPressed) {
|
|
27282
|
+
return
|
|
27283
|
+
}
|
|
26449
27284
|
clearSuggestedFlow()
|
|
26450
27285
|
focusView();
|
|
26451
27286
|
RED.contextMenu.hide();
|
|
@@ -26624,6 +27459,9 @@ RED.view = (function() {
|
|
|
26624
27459
|
|
|
26625
27460
|
function nodeMouseOver(d) {
|
|
26626
27461
|
if (RED.view.DEBUG) { console.warn("nodeMouseOver", mouse_mode,d); }
|
|
27462
|
+
if (spacebarPressed) {
|
|
27463
|
+
return
|
|
27464
|
+
}
|
|
26627
27465
|
if (mouse_mode === 0 || mouse_mode === RED.state.SELECTING_NODE) {
|
|
26628
27466
|
if (mouse_mode === RED.state.SELECTING_NODE && selectNodesOptions && selectNodesOptions.filter) {
|
|
26629
27467
|
if (selectNodesOptions.filter(d)) {
|
|
@@ -26797,6 +27635,9 @@ RED.view = (function() {
|
|
|
26797
27635
|
if (RED.view.DEBUG) {
|
|
26798
27636
|
console.warn("groupMouseDown", { mouse_mode, point: mouse, event: d3.event });
|
|
26799
27637
|
}
|
|
27638
|
+
if (spacebarPressed) {
|
|
27639
|
+
return
|
|
27640
|
+
}
|
|
26800
27641
|
RED.contextMenu.hide();
|
|
26801
27642
|
focusView();
|
|
26802
27643
|
if (d3.event.button === 1) {
|
|
@@ -27256,6 +28097,15 @@ RED.view = (function() {
|
|
|
27256
28097
|
eventLayer.attr("transform","scale("+scaleFactor+")");
|
|
27257
28098
|
outer.attr("width", space_width*scaleFactor).attr("height", space_height*scaleFactor);
|
|
27258
28099
|
|
|
28100
|
+
// Update scroll spacer to match scaled canvas size
|
|
28101
|
+
// This ensures scrollable area = canvas area
|
|
28102
|
+
// Browser calculates maxScroll = scrollWidth - viewport, which correctly
|
|
28103
|
+
// allows scrolling to see the far edges of canvas without going beyond
|
|
28104
|
+
$('#red-ui-workspace-scroll-spacer').css({
|
|
28105
|
+
width: (space_width * scaleFactor) + 'px',
|
|
28106
|
+
height: (space_height * scaleFactor) + 'px'
|
|
28107
|
+
});
|
|
28108
|
+
|
|
27259
28109
|
// Don't bother redrawing nodes if we're drawing links
|
|
27260
28110
|
if (forceFullRedraw || showAllLinkPorts !== -1 || mouse_mode != RED.state.JOINING) {
|
|
27261
28111
|
forceFullRedraw = false
|
|
@@ -29616,7 +30466,7 @@ RED.view = (function() {
|
|
|
29616
30466
|
selectNodes: function(options) {
|
|
29617
30467
|
$("#red-ui-workspace-tabs-shade").show();
|
|
29618
30468
|
$("#red-ui-palette-shade").show();
|
|
29619
|
-
$("
|
|
30469
|
+
$(".red-ui-sidebar-shade").show();
|
|
29620
30470
|
$("#red-ui-header-shade").show();
|
|
29621
30471
|
$("#red-ui-workspace").addClass("red-ui-workspace-select-mode");
|
|
29622
30472
|
|
|
@@ -29638,7 +30488,7 @@ RED.view = (function() {
|
|
|
29638
30488
|
clearSelection();
|
|
29639
30489
|
$("#red-ui-workspace-tabs-shade").hide();
|
|
29640
30490
|
$("#red-ui-palette-shade").hide();
|
|
29641
|
-
$("
|
|
30491
|
+
$(".red-ui-sidebar-shade").hide();
|
|
29642
30492
|
$("#red-ui-header-shade").hide();
|
|
29643
30493
|
$("#red-ui-workspace").removeClass("red-ui-workspace-select-mode");
|
|
29644
30494
|
resetMouseVars();
|
|
@@ -29707,7 +30557,283 @@ RED.view = (function() {
|
|
|
29707
30557
|
applySuggestedFlow
|
|
29708
30558
|
};
|
|
29709
30559
|
})();
|
|
29710
|
-
|
|
30560
|
+
;/**
|
|
30561
|
+
* Zoom configuration constants
|
|
30562
|
+
*/
|
|
30563
|
+
RED.view.zoomConstants = {
|
|
30564
|
+
// Zoom limits
|
|
30565
|
+
MIN_ZOOM: 0.05, // Default minimum, will be dynamically calculated to fit canvas
|
|
30566
|
+
MAX_ZOOM: 2.0,
|
|
30567
|
+
|
|
30568
|
+
// Zoom step for keyboard/button controls
|
|
30569
|
+
ZOOM_STEP: 0.2,
|
|
30570
|
+
|
|
30571
|
+
// Animation settings
|
|
30572
|
+
DEFAULT_ZOOM_DURATION: 125, // ms, faster animation
|
|
30573
|
+
|
|
30574
|
+
// Gesture thresholds
|
|
30575
|
+
PINCH_THRESHOLD: 10, // minimum pixel movement to trigger zoom
|
|
30576
|
+
|
|
30577
|
+
// Momentum and friction for smooth scrolling
|
|
30578
|
+
FRICTION: 0.92,
|
|
30579
|
+
BOUNCE_DAMPING: 0.6
|
|
30580
|
+
};;/**
|
|
30581
|
+
* Copyright JS Foundation and other contributors, http://js.foundation
|
|
30582
|
+
*
|
|
30583
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
30584
|
+
* you may not use this file except in compliance with the License.
|
|
30585
|
+
* You may obtain a copy of the License at
|
|
30586
|
+
*
|
|
30587
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
30588
|
+
*
|
|
30589
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
30590
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
30591
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
30592
|
+
* See the License for the specific language governing permissions and
|
|
30593
|
+
* limitations under the License.
|
|
30594
|
+
**/
|
|
30595
|
+
|
|
30596
|
+
RED.view.zoomAnimator = (function() {
|
|
30597
|
+
|
|
30598
|
+
/**
|
|
30599
|
+
* Easing function for smooth deceleration
|
|
30600
|
+
* Creates natural-feeling animation curves
|
|
30601
|
+
* @param {number} t - Progress from 0 to 1
|
|
30602
|
+
* @returns {number} - Eased value from 0 to 1
|
|
30603
|
+
*/
|
|
30604
|
+
function easeOut(t) {
|
|
30605
|
+
// Cubic ease-out for smooth deceleration
|
|
30606
|
+
return 1 - Math.pow(1 - t, 3);
|
|
30607
|
+
}
|
|
30608
|
+
|
|
30609
|
+
/**
|
|
30610
|
+
* Animate values using requestAnimationFrame with easing
|
|
30611
|
+
* Based on Excalidraw's implementation for smooth zoom transitions
|
|
30612
|
+
*
|
|
30613
|
+
* @param {Object} options - Animation options
|
|
30614
|
+
* @param {Object} options.fromValues - Starting values object
|
|
30615
|
+
* @param {Object} options.toValues - Target values object
|
|
30616
|
+
* @param {Function} options.onStep - Callback for each animation frame
|
|
30617
|
+
* @param {number} [options.duration=250] - Animation duration in ms
|
|
30618
|
+
* @param {Function} [options.interpolateValue] - Custom interpolation function
|
|
30619
|
+
* @param {Function} [options.onStart] - Animation start callback
|
|
30620
|
+
* @param {Function} [options.onEnd] - Animation end callback
|
|
30621
|
+
* @param {Function} [options.onCancel] - Animation cancel callback
|
|
30622
|
+
* @returns {Function} - Cancel function to stop animation
|
|
30623
|
+
*/
|
|
30624
|
+
function easeToValuesRAF(options) {
|
|
30625
|
+
const {
|
|
30626
|
+
fromValues,
|
|
30627
|
+
toValues,
|
|
30628
|
+
onStep,
|
|
30629
|
+
duration = 250,
|
|
30630
|
+
interpolateValue,
|
|
30631
|
+
onStart,
|
|
30632
|
+
onEnd,
|
|
30633
|
+
onCancel
|
|
30634
|
+
} = options;
|
|
30635
|
+
|
|
30636
|
+
let startTime = null;
|
|
30637
|
+
let animationId = null;
|
|
30638
|
+
let cancelled = false;
|
|
30639
|
+
|
|
30640
|
+
function step(timestamp) {
|
|
30641
|
+
if (cancelled) {
|
|
30642
|
+
return;
|
|
30643
|
+
}
|
|
30644
|
+
|
|
30645
|
+
if (!startTime) {
|
|
30646
|
+
startTime = timestamp;
|
|
30647
|
+
if (onStart) {
|
|
30648
|
+
onStart();
|
|
30649
|
+
}
|
|
30650
|
+
}
|
|
30651
|
+
|
|
30652
|
+
const elapsed = timestamp - startTime;
|
|
30653
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
30654
|
+
const easedProgress = easeOut(progress);
|
|
30655
|
+
|
|
30656
|
+
const interpolatedValues = {};
|
|
30657
|
+
|
|
30658
|
+
for (const key in fromValues) {
|
|
30659
|
+
if (fromValues.hasOwnProperty(key)) {
|
|
30660
|
+
const from = fromValues[key];
|
|
30661
|
+
const to = toValues[key];
|
|
30662
|
+
|
|
30663
|
+
if (interpolateValue && key === 'zoom') {
|
|
30664
|
+
// Special interpolation for zoom to feel more natural
|
|
30665
|
+
// Exponential interpolation preserves relative zoom feel
|
|
30666
|
+
interpolatedValues[key] = from * Math.pow(to / from, easedProgress);
|
|
30667
|
+
} else {
|
|
30668
|
+
// Linear interpolation for other values
|
|
30669
|
+
interpolatedValues[key] = from + (to - from) * easedProgress;
|
|
30670
|
+
}
|
|
30671
|
+
}
|
|
30672
|
+
}
|
|
30673
|
+
|
|
30674
|
+
onStep(interpolatedValues);
|
|
30675
|
+
|
|
30676
|
+
if (progress < 1) {
|
|
30677
|
+
animationId = requestAnimationFrame(step);
|
|
30678
|
+
} else {
|
|
30679
|
+
if (onEnd) {
|
|
30680
|
+
onEnd();
|
|
30681
|
+
}
|
|
30682
|
+
}
|
|
30683
|
+
}
|
|
30684
|
+
|
|
30685
|
+
animationId = requestAnimationFrame(step);
|
|
30686
|
+
|
|
30687
|
+
// Return cancel function
|
|
30688
|
+
return function cancel() {
|
|
30689
|
+
cancelled = true;
|
|
30690
|
+
if (animationId) {
|
|
30691
|
+
cancelAnimationFrame(animationId);
|
|
30692
|
+
}
|
|
30693
|
+
if (onCancel) {
|
|
30694
|
+
onCancel();
|
|
30695
|
+
}
|
|
30696
|
+
};
|
|
30697
|
+
}
|
|
30698
|
+
|
|
30699
|
+
/**
|
|
30700
|
+
* Calculate smooth zoom delta with acceleration
|
|
30701
|
+
* Provides consistent zoom speed regardless of input device
|
|
30702
|
+
*
|
|
30703
|
+
* @param {number} currentScale - Current zoom scale
|
|
30704
|
+
* @param {number} delta - Input delta (wheel, gesture, etc)
|
|
30705
|
+
* @param {boolean} isTrackpad - Whether input is from trackpad
|
|
30706
|
+
* @returns {number} - Calculated zoom delta
|
|
30707
|
+
*/
|
|
30708
|
+
function calculateZoomDelta(currentScale, delta, isTrackpad) {
|
|
30709
|
+
// Normalize delta across different input devices
|
|
30710
|
+
let normalizedDelta = delta;
|
|
30711
|
+
|
|
30712
|
+
if (isTrackpad) {
|
|
30713
|
+
// Trackpad deltas are typically smaller and more frequent
|
|
30714
|
+
normalizedDelta = delta * 0.005; // Reduced from 0.01 for gentler zoom
|
|
30715
|
+
} else {
|
|
30716
|
+
// Mouse wheel deltas are larger and less frequent
|
|
30717
|
+
// Reduce zoom out speed more than zoom in
|
|
30718
|
+
normalizedDelta = delta > 0 ? 0.06 : -0.08; // Reduced from 0.1, asymmetric for gentler zoom out
|
|
30719
|
+
}
|
|
30720
|
+
|
|
30721
|
+
// Apply gentler acceleration based on current zoom level
|
|
30722
|
+
// Less aggressive acceleration to prevent rapid zoom out
|
|
30723
|
+
const acceleration = Math.max(0.7, Math.min(1.1, 1 / currentScale)); // Reduced from 0.5-1.2 to 0.7-1.1
|
|
30724
|
+
|
|
30725
|
+
return normalizedDelta * acceleration;
|
|
30726
|
+
}
|
|
30727
|
+
|
|
30728
|
+
/**
|
|
30729
|
+
* Gesture state management for consistent focal points
|
|
30730
|
+
*/
|
|
30731
|
+
const gestureState = {
|
|
30732
|
+
active: false,
|
|
30733
|
+
initialFocalPoint: null, // Will store workspace coordinates
|
|
30734
|
+
initialScale: 1,
|
|
30735
|
+
currentScale: 1,
|
|
30736
|
+
lastDistance: 0,
|
|
30737
|
+
scrollPosAtStart: null, // Store initial scroll position
|
|
30738
|
+
scaleFatorAtStart: 1 // Store initial scale factor
|
|
30739
|
+
};
|
|
30740
|
+
|
|
30741
|
+
/**
|
|
30742
|
+
* Start a zoom gesture with fixed focal point
|
|
30743
|
+
* @param {Array} focalPoint - [x, y] coordinates of focal point in workspace
|
|
30744
|
+
* @param {number} scale - Initial scale value
|
|
30745
|
+
* @param {Array} scrollPos - Current scroll position [x, y]
|
|
30746
|
+
* @param {number} currentScaleFactor - Current scale factor for coordinate conversion
|
|
30747
|
+
*/
|
|
30748
|
+
function startGesture(focalPoint, scale, scrollPos, currentScaleFactor) {
|
|
30749
|
+
gestureState.active = true;
|
|
30750
|
+
// Store the focal point in workspace coordinates for stability
|
|
30751
|
+
// This ensures the point remains fixed even if scroll changes due to canvas edge constraints
|
|
30752
|
+
if (focalPoint && scrollPos && currentScaleFactor) {
|
|
30753
|
+
gestureState.initialFocalPoint = [
|
|
30754
|
+
(scrollPos[0] + focalPoint[0]) / currentScaleFactor,
|
|
30755
|
+
(scrollPos[1] + focalPoint[1]) / currentScaleFactor
|
|
30756
|
+
];
|
|
30757
|
+
gestureState.scrollPosAtStart = [...scrollPos];
|
|
30758
|
+
gestureState.scaleFatorAtStart = currentScaleFactor;
|
|
30759
|
+
} else {
|
|
30760
|
+
gestureState.initialFocalPoint = focalPoint ? [...focalPoint] : null;
|
|
30761
|
+
}
|
|
30762
|
+
gestureState.initialScale = scale;
|
|
30763
|
+
gestureState.currentScale = scale;
|
|
30764
|
+
return gestureState;
|
|
30765
|
+
}
|
|
30766
|
+
|
|
30767
|
+
/**
|
|
30768
|
+
* Update gesture maintaining fixed focal point
|
|
30769
|
+
* @param {number} newScale - New scale value
|
|
30770
|
+
* @returns {Object} - Gesture state with fixed focal point
|
|
30771
|
+
*/
|
|
30772
|
+
function updateGesture(newScale) {
|
|
30773
|
+
if (!gestureState.active) {
|
|
30774
|
+
return null;
|
|
30775
|
+
}
|
|
30776
|
+
|
|
30777
|
+
gestureState.currentScale = newScale;
|
|
30778
|
+
|
|
30779
|
+
return {
|
|
30780
|
+
scale: newScale,
|
|
30781
|
+
focalPoint: gestureState.initialFocalPoint,
|
|
30782
|
+
active: gestureState.active
|
|
30783
|
+
};
|
|
30784
|
+
}
|
|
30785
|
+
|
|
30786
|
+
/**
|
|
30787
|
+
* End the current gesture
|
|
30788
|
+
*/
|
|
30789
|
+
function endGesture() {
|
|
30790
|
+
gestureState.active = false;
|
|
30791
|
+
gestureState.initialFocalPoint = null;
|
|
30792
|
+
gestureState.lastDistance = 0;
|
|
30793
|
+
}
|
|
30794
|
+
|
|
30795
|
+
/**
|
|
30796
|
+
* Check if a gesture is currently active
|
|
30797
|
+
*/
|
|
30798
|
+
function isGestureActive() {
|
|
30799
|
+
return gestureState.active;
|
|
30800
|
+
}
|
|
30801
|
+
|
|
30802
|
+
/**
|
|
30803
|
+
* Get the fixed focal point for the current gesture
|
|
30804
|
+
* @param {Array} currentScrollPos - Current scroll position [x, y]
|
|
30805
|
+
* @param {number} currentScaleFactor - Current scale factor
|
|
30806
|
+
* @returns {Array} - Focal point in screen coordinates or null
|
|
30807
|
+
*/
|
|
30808
|
+
function getGestureFocalPoint(currentScrollPos, currentScaleFactor) {
|
|
30809
|
+
if (!gestureState.initialFocalPoint) {
|
|
30810
|
+
return null;
|
|
30811
|
+
}
|
|
30812
|
+
|
|
30813
|
+
// If we stored workspace coordinates, convert back to screen coordinates
|
|
30814
|
+
if (gestureState.scrollPosAtStart && currentScrollPos && currentScaleFactor) {
|
|
30815
|
+
// Convert workspace coordinates back to current screen coordinates
|
|
30816
|
+
return [
|
|
30817
|
+
gestureState.initialFocalPoint[0] * currentScaleFactor - currentScrollPos[0],
|
|
30818
|
+
gestureState.initialFocalPoint[1] * currentScaleFactor - currentScrollPos[1]
|
|
30819
|
+
];
|
|
30820
|
+
}
|
|
30821
|
+
|
|
30822
|
+
return gestureState.initialFocalPoint;
|
|
30823
|
+
}
|
|
30824
|
+
|
|
30825
|
+
return {
|
|
30826
|
+
easeOut: easeOut,
|
|
30827
|
+
easeToValuesRAF: easeToValuesRAF,
|
|
30828
|
+
calculateZoomDelta: calculateZoomDelta,
|
|
30829
|
+
gestureState: gestureState,
|
|
30830
|
+
startGesture: startGesture,
|
|
30831
|
+
updateGesture: updateGesture,
|
|
30832
|
+
endGesture: endGesture,
|
|
30833
|
+
isGestureActive: isGestureActive,
|
|
30834
|
+
getGestureFocalPoint: getGestureFocalPoint
|
|
30835
|
+
};
|
|
30836
|
+
})();;RED.view.annotations = (function() {
|
|
29711
30837
|
|
|
29712
30838
|
var annotations = {};
|
|
29713
30839
|
|
|
@@ -29907,139 +31033,205 @@ RED.view = (function() {
|
|
|
29907
31033
|
**/
|
|
29908
31034
|
|
|
29909
31035
|
|
|
29910
|
-
|
|
29911
|
-
|
|
29912
|
-
|
|
29913
|
-
|
|
29914
|
-
|
|
29915
|
-
|
|
29916
|
-
|
|
29917
|
-
|
|
29918
|
-
|
|
29919
|
-
|
|
29920
|
-
|
|
29921
|
-
|
|
29922
|
-
|
|
29923
|
-
|
|
29924
|
-
|
|
29925
|
-
|
|
29926
|
-
|
|
29927
|
-
|
|
29928
|
-
|
|
29929
|
-
|
|
29930
|
-
|
|
29931
|
-
|
|
29932
|
-
|
|
29933
|
-
|
|
29934
|
-
|
|
29935
|
-
|
|
29936
|
-
|
|
29937
|
-
|
|
29938
|
-
|
|
29939
|
-
|
|
29940
|
-
|
|
29941
|
-
|
|
29942
|
-
|
|
29943
|
-
|
|
29944
|
-
|
|
29945
|
-
|
|
29946
|
-
|
|
29947
|
-
|
|
29948
|
-
|
|
29949
|
-
|
|
29950
|
-
|
|
29951
|
-
|
|
29952
|
-
|
|
29953
|
-
|
|
29954
|
-
|
|
29955
|
-
|
|
29956
|
-
|
|
29957
|
-
|
|
29958
|
-
|
|
29959
|
-
|
|
29960
|
-
|
|
29961
|
-
|
|
29962
|
-
|
|
29963
|
-
|
|
29964
|
-
|
|
29965
|
-
|
|
29966
|
-
|
|
29967
|
-
|
|
29968
|
-
|
|
29969
|
-
|
|
29970
|
-
|
|
29971
|
-
|
|
29972
|
-
|
|
29973
|
-
|
|
29974
|
-
|
|
29975
|
-
|
|
29976
|
-
|
|
29977
|
-
|
|
29978
|
-
|
|
29979
|
-
|
|
29980
|
-
|
|
29981
|
-
|
|
29982
|
-
|
|
29983
|
-
|
|
29984
|
-
|
|
29985
|
-
|
|
29986
|
-
|
|
29987
|
-
|
|
29988
|
-
|
|
29989
|
-
|
|
29990
|
-
|
|
29991
|
-
|
|
29992
|
-
|
|
29993
|
-
|
|
29994
|
-
|
|
29995
|
-
|
|
29996
|
-
|
|
29997
|
-
|
|
29998
|
-
|
|
29999
|
-
|
|
30000
|
-
|
|
30001
|
-
|
|
30002
|
-
|
|
30003
|
-
|
|
30004
|
-
|
|
30005
|
-
|
|
30006
|
-
|
|
30007
|
-
|
|
30008
|
-
|
|
30009
|
-
|
|
30010
|
-
|
|
30011
|
-
|
|
30012
|
-
|
|
30013
|
-
|
|
30014
|
-
|
|
30015
|
-
|
|
30016
|
-
|
|
30017
|
-
|
|
30018
|
-
|
|
30019
|
-
|
|
30020
|
-
|
|
30021
|
-
|
|
30022
|
-
|
|
30023
|
-
|
|
30024
|
-
|
|
30025
|
-
|
|
30026
|
-
|
|
30027
|
-
|
|
30028
|
-
|
|
30029
|
-
|
|
30030
|
-
|
|
30031
|
-
|
|
30032
|
-
|
|
30033
|
-
|
|
30034
|
-
|
|
30035
|
-
|
|
30036
|
-
|
|
31036
|
+
RED.view.navigator = (function() {
|
|
31037
|
+
var nav_scale = 50;
|
|
31038
|
+
var nav_width = 8000/nav_scale;
|
|
31039
|
+
var nav_height = 8000/nav_scale;
|
|
31040
|
+
var navContainer;
|
|
31041
|
+
var navBox;
|
|
31042
|
+
var navBorder;
|
|
31043
|
+
var navVis;
|
|
31044
|
+
var scrollPos;
|
|
31045
|
+
var scaleFactor;
|
|
31046
|
+
var chartSize;
|
|
31047
|
+
var dimensions;
|
|
31048
|
+
var isDragging;
|
|
31049
|
+
var isShowing = false;
|
|
31050
|
+
var toggleTimeout;
|
|
31051
|
+
var autoHideTimeout;
|
|
31052
|
+
var isManuallyToggled = false;
|
|
31053
|
+
var isTemporaryShow = false;
|
|
31054
|
+
function refreshNodes() {
|
|
31055
|
+
if (!isShowing) {
|
|
31056
|
+
return;
|
|
31057
|
+
}
|
|
31058
|
+
var navNode = navVis.selectAll(".red-ui-navigator-node").data(RED.view.getActiveNodes(),function(d){return d.id});
|
|
31059
|
+
navNode.exit().remove();
|
|
31060
|
+
navNode.enter().insert("rect")
|
|
31061
|
+
.attr('class','red-ui-navigator-node')
|
|
31062
|
+
.attr("pointer-events", "none");
|
|
31063
|
+
navNode.each(function(d) {
|
|
31064
|
+
d3.select(this).attr("x",function(d) { return (d.x-d.w/2)/nav_scale })
|
|
31065
|
+
.attr("y",function(d) { return (d.y-d.h/2)/nav_scale })
|
|
31066
|
+
.attr("width",function(d) { return Math.max(9,d.w/nav_scale) })
|
|
31067
|
+
.attr("height",function(d) { return Math.max(3,d.h/nav_scale) })
|
|
31068
|
+
.attr("fill",function(d) { return RED.utils.getNodeColor(d.type,d._def);})
|
|
31069
|
+
});
|
|
31070
|
+
}
|
|
31071
|
+
function onScroll() {
|
|
31072
|
+
if (!isDragging) {
|
|
31073
|
+
resizeNavBorder();
|
|
31074
|
+
}
|
|
31075
|
+
}
|
|
31076
|
+
function resizeNavBorder() {
|
|
31077
|
+
if (navBorder) {
|
|
31078
|
+
scaleFactor = RED.view.scale();
|
|
31079
|
+
chartSize = [ $("#red-ui-workspace-chart").width(), $("#red-ui-workspace-chart").height()];
|
|
31080
|
+
scrollPos = [$("#red-ui-workspace-chart").scrollLeft(),$("#red-ui-workspace-chart").scrollTop()];
|
|
31081
|
+
// Convert scroll position (in scaled pixels) to workspace coordinates, then to minimap coordinates
|
|
31082
|
+
// scrollPos is in scaled canvas pixels, divide by scaleFactor to get workspace coords
|
|
31083
|
+
if (chartSize[0] > 0 && chartSize[1] > 0) {
|
|
31084
|
+
navBorder.attr('x',scrollPos[0]/scaleFactor/nav_scale)
|
|
31085
|
+
.attr('y',scrollPos[1]/scaleFactor/nav_scale)
|
|
31086
|
+
.attr('width',chartSize[0]/nav_scale/scaleFactor)
|
|
31087
|
+
.attr('height',chartSize[1]/nav_scale/scaleFactor)
|
|
31088
|
+
}
|
|
31089
|
+
}
|
|
31090
|
+
}
|
|
31091
|
+
function show () {
|
|
31092
|
+
if (!isShowing) {
|
|
31093
|
+
isShowing = true;
|
|
31094
|
+
clearTimeout(autoHideTimeout);
|
|
31095
|
+
$("#red-ui-view-navigate").addClass("selected");
|
|
31096
|
+
resizeNavBorder();
|
|
31097
|
+
refreshNodes();
|
|
31098
|
+
$("#red-ui-workspace-chart").on("scroll",onScroll);
|
|
31099
|
+
navContainer.addClass('red-ui-navigator-container');
|
|
31100
|
+
navContainer.show();
|
|
31101
|
+
clearTimeout(toggleTimeout)
|
|
31102
|
+
toggleTimeout = setTimeout(function() {
|
|
31103
|
+
navContainer.addClass('red-ui-navigator-visible');
|
|
31104
|
+
}, 10);
|
|
31105
|
+
}
|
|
31106
|
+
}
|
|
31107
|
+
function hide () {
|
|
31108
|
+
if (isShowing) {
|
|
31109
|
+
isShowing = false;
|
|
31110
|
+
isTemporaryShow = false;
|
|
31111
|
+
isManuallyToggled = false;
|
|
31112
|
+
clearTimeout(autoHideTimeout);
|
|
31113
|
+
navContainer.removeClass('red-ui-navigator-visible');
|
|
31114
|
+
clearTimeout(toggleTimeout)
|
|
31115
|
+
toggleTimeout = setTimeout(function() {
|
|
31116
|
+
navContainer.hide();
|
|
31117
|
+
}, 300);
|
|
31118
|
+
$("#red-ui-workspace-chart").off("scroll",onScroll);
|
|
31119
|
+
$("#red-ui-view-navigate").removeClass("selected");
|
|
31120
|
+
}
|
|
31121
|
+
}
|
|
31122
|
+
function toggle() {
|
|
31123
|
+
if (!isShowing) {
|
|
31124
|
+
isManuallyToggled = true;
|
|
31125
|
+
show()
|
|
31126
|
+
} else {
|
|
31127
|
+
isManuallyToggled = false;
|
|
31128
|
+
hide()
|
|
31129
|
+
}
|
|
31130
|
+
}
|
|
31131
|
+
function setupAutoHide () {
|
|
31132
|
+
clearTimeout(autoHideTimeout);
|
|
31133
|
+
autoHideTimeout = setTimeout(function() {
|
|
31134
|
+
hide()
|
|
31135
|
+
}, 2000)
|
|
31136
|
+
}
|
|
31137
|
+
function showTemporary() {
|
|
31138
|
+
if (!isManuallyToggled) {
|
|
31139
|
+
isTemporaryShow = true
|
|
31140
|
+
clearTimeout(autoHideTimeout);
|
|
31141
|
+
show()
|
|
31142
|
+
setupAutoHide()
|
|
31143
|
+
}
|
|
31144
|
+
}
|
|
31145
|
+
return {
|
|
31146
|
+
init: function() {
|
|
31147
|
+
$(window).on("resize", resizeNavBorder);
|
|
31148
|
+
RED.events.on("sidebar:resize",resizeNavBorder);
|
|
31149
|
+
RED.actions.add("core:toggle-navigator",toggle);
|
|
31150
|
+
navContainer = $('<div>').css({
|
|
31151
|
+
"position":"absolute",
|
|
31152
|
+
"bottom":$("#red-ui-workspace-footer").height() + 2,
|
|
31153
|
+
"right": 4,
|
|
31154
|
+
zIndex: 1
|
|
31155
|
+
}).addClass('red-ui-navigator-container').appendTo("#red-ui-workspace").hide();
|
|
31156
|
+
navBox = d3.select(navContainer[0])
|
|
31157
|
+
.append("svg:svg")
|
|
31158
|
+
.attr("width", nav_width)
|
|
31159
|
+
.attr("height", nav_height)
|
|
31160
|
+
.attr("pointer-events", "all")
|
|
31161
|
+
.attr("id","red-ui-navigator-canvas")
|
|
31162
|
+
navVis = navBox.append("svg:g")
|
|
31163
|
+
|
|
31164
|
+
navBox.append("rect").attr("x",0).attr("y",0).attr("width",nav_width).attr("height",nav_height).style({
|
|
31165
|
+
fill:"none",
|
|
31166
|
+
stroke:"none",
|
|
31167
|
+
pointerEvents:"all"
|
|
31168
|
+
}).on("mousedown", function() {
|
|
31169
|
+
// Update these in case they have changed
|
|
31170
|
+
scaleFactor = RED.view.scale();
|
|
31171
|
+
chartSize = [ $("#red-ui-workspace-chart").width(), $("#red-ui-workspace-chart").height()];
|
|
31172
|
+
dimensions = [chartSize[0]/nav_scale/scaleFactor, chartSize[1]/nav_scale/scaleFactor];
|
|
31173
|
+
var newX = Math.max(0,Math.min(d3.event.offsetX+dimensions[0]/2,nav_width)-dimensions[0]);
|
|
31174
|
+
var newY = Math.max(0,Math.min(d3.event.offsetY+dimensions[1]/2,nav_height)-dimensions[1]);
|
|
31175
|
+
navBorder.attr('x',newX).attr('y',newY);
|
|
31176
|
+
isDragging = true;
|
|
31177
|
+
$("#red-ui-workspace-chart").scrollLeft(newX*nav_scale*scaleFactor);
|
|
31178
|
+
$("#red-ui-workspace-chart").scrollTop(newY*nav_scale*scaleFactor);
|
|
31179
|
+
}).on("mousemove", function() {
|
|
31180
|
+
if (!isDragging) { return }
|
|
31181
|
+
if (d3.event.buttons === 0) {
|
|
31182
|
+
isDragging = false;
|
|
31183
|
+
return;
|
|
31184
|
+
}
|
|
31185
|
+
var newX = Math.max(0,Math.min(d3.event.offsetX+dimensions[0]/2,nav_width)-dimensions[0]);
|
|
31186
|
+
var newY = Math.max(0,Math.min(d3.event.offsetY+dimensions[1]/2,nav_height)-dimensions[1]);
|
|
31187
|
+
navBorder.attr('x',newX).attr('y',newY);
|
|
31188
|
+
$("#red-ui-workspace-chart").scrollLeft(newX*nav_scale*scaleFactor);
|
|
31189
|
+
$("#red-ui-workspace-chart").scrollTop(newY*nav_scale*scaleFactor);
|
|
31190
|
+
}).on("mouseup", function() {
|
|
31191
|
+
isDragging = false;
|
|
31192
|
+
}).on("mouseenter", function () {
|
|
31193
|
+
if (isTemporaryShow) {
|
|
31194
|
+
// If user hovers over the minimap while it's temporarily shown, keep it shown
|
|
31195
|
+
clearTimeout(autoHideTimeout);
|
|
31196
|
+
}
|
|
31197
|
+
}).on("mouseleave", function () {
|
|
31198
|
+
if (isTemporaryShow) {
|
|
31199
|
+
// Restart the auto-hide timer after mouse leaves the minimap
|
|
31200
|
+
setupAutoHide()
|
|
31201
|
+
}
|
|
31202
|
+
})
|
|
31203
|
+
navBorder = navBox.append("rect").attr("class","red-ui-navigator-border")
|
|
31204
|
+
RED.statusBar.add({
|
|
31205
|
+
id: "view-navigator",
|
|
31206
|
+
align: "right",
|
|
31207
|
+
element: $('<button class="red-ui-footer-button-toggle single" id="red-ui-view-navigate"><i class="fa fa-map-o"></i></button>')
|
|
31208
|
+
})
|
|
30037
31209
|
|
|
30038
31210
|
$("#red-ui-view-navigate").on("click", function(evt) {
|
|
30039
31211
|
evt.preventDefault();
|
|
30040
31212
|
toggle();
|
|
30041
31213
|
})
|
|
30042
31214
|
RED.popover.tooltip($("#red-ui-view-navigate"),RED._('actions.toggle-navigator'),'core:toggle-navigator');
|
|
31215
|
+
|
|
31216
|
+
// Listen for canvas interactions to show minimap temporarily
|
|
31217
|
+
// Only show on actual pan/zoom navigation, not selection changes
|
|
31218
|
+
// RED.events.on("view:navigate", function() {
|
|
31219
|
+
// showTemporary();
|
|
31220
|
+
// });
|
|
31221
|
+
|
|
31222
|
+
// Show minimap briefly when workspace changes (includes initial load)
|
|
31223
|
+
// RED.events.on("workspace:change", function(event) {
|
|
31224
|
+
// // Only show if there's an active workspace with nodes
|
|
31225
|
+
// if (event.workspace && RED.nodes.getWorkspaceOrder().length > 0) {
|
|
31226
|
+
// // Small delay to ensure nodes are rendered
|
|
31227
|
+
// setTimeout(function() {
|
|
31228
|
+
// var activeNodes = RED.nodes.filterNodes({z: event.workspace});
|
|
31229
|
+
// if (activeNodes.length > 0) {
|
|
31230
|
+
// showTemporary();
|
|
31231
|
+
// }
|
|
31232
|
+
// }, 100);
|
|
31233
|
+
// }
|
|
31234
|
+
// });
|
|
30043
31235
|
},
|
|
30044
31236
|
refresh: refreshNodes,
|
|
30045
31237
|
resize: resizeNavBorder,
|
|
@@ -31507,19 +32699,65 @@ RED.view.tools = (function() {
|
|
|
31507
32699
|
* limitations under the License.
|
|
31508
32700
|
**/
|
|
31509
32701
|
RED.sidebar = (function() {
|
|
32702
|
+
const sidebars = {
|
|
32703
|
+
primary: {
|
|
32704
|
+
id: 'primary',
|
|
32705
|
+
direction: 'right',
|
|
32706
|
+
menuToggle: 'menu-item-sidebar',
|
|
32707
|
+
minimumWidth: 180,
|
|
32708
|
+
maximumWidth: 800,
|
|
32709
|
+
defaultWidth: 300,
|
|
32710
|
+
defaultTopHeight: 0.7
|
|
32711
|
+
},
|
|
32712
|
+
secondary: {
|
|
32713
|
+
id: 'secondary',
|
|
32714
|
+
direction: 'left',
|
|
32715
|
+
menuToggle: 'menu-item-palette',
|
|
32716
|
+
minimumWidth: 180,
|
|
32717
|
+
maximumWidth: 800,
|
|
32718
|
+
// Make LH side slightly narrower by default as its the palette that doesn't require a lot of width
|
|
32719
|
+
defaultWidth: 210,
|
|
32720
|
+
defaultTopHeight: 1
|
|
32721
|
+
}
|
|
32722
|
+
}
|
|
32723
|
+
const defaultSidebarConfiguration = {
|
|
32724
|
+
primary: [ ['info','help','config','context'], ['debug'] ],
|
|
32725
|
+
secondary: [ ['explorer','palette'], [] ]
|
|
32726
|
+
}
|
|
32727
|
+
|
|
32728
|
+
const knownTabs = {};
|
|
32729
|
+
|
|
32730
|
+
function exportSidebarState () {
|
|
32731
|
+
const state = {
|
|
32732
|
+
primary: [[], []],
|
|
32733
|
+
secondary: [[], []]
|
|
32734
|
+
}
|
|
32735
|
+
function getTabButtons(tabBar) {
|
|
32736
|
+
const result = []
|
|
32737
|
+
tabBar.children('button').each(function() {
|
|
32738
|
+
const tabId = $(this).attr('data-tab-id');
|
|
32739
|
+
if (tabId) {
|
|
32740
|
+
result.push(tabId);
|
|
32741
|
+
}
|
|
32742
|
+
})
|
|
32743
|
+
return result
|
|
32744
|
+
}
|
|
32745
|
+
state.primary[0] = getTabButtons(sidebars.primary.tabBars.top.container);
|
|
32746
|
+
state.primary[1] = getTabButtons(sidebars.primary.tabBars.bottom.container);
|
|
32747
|
+
state.secondary[0] = getTabButtons(sidebars.secondary.tabBars.top.container);
|
|
32748
|
+
state.secondary[1] = getTabButtons(sidebars.secondary.tabBars.bottom.container);
|
|
32749
|
+
|
|
32750
|
+
RED.settings.set('editor.sidebar.state', state)
|
|
32751
|
+
}
|
|
31510
32752
|
|
|
31511
|
-
//$('#sidebar').tabs();
|
|
31512
|
-
var sidebar_tabs;
|
|
31513
|
-
var knownTabs = {};
|
|
31514
32753
|
|
|
31515
32754
|
// We store the current sidebar tab id in localStorage as 'last-sidebar-tab'
|
|
31516
32755
|
// This is restored when the editor is reloaded.
|
|
31517
|
-
// We use
|
|
32756
|
+
// We use sidebars.primary.tabs.onchange to update localStorage. However that will
|
|
31518
32757
|
// also get triggered when the first tab gets added to the tabs - typically
|
|
31519
32758
|
// the 'info' tab. So we use the following variable to store the retrieved
|
|
31520
32759
|
// value from localStorage before we start adding the actual tabs
|
|
31521
|
-
|
|
31522
|
-
|
|
32760
|
+
let lastSessionSelectedTabs = {}
|
|
31523
32761
|
|
|
31524
32762
|
function addTab(title,content,closeable,visible) {
|
|
31525
32763
|
var options;
|
|
@@ -31536,10 +32774,55 @@ RED.sidebar = (function() {
|
|
|
31536
32774
|
} else if (typeof title === "object") {
|
|
31537
32775
|
options = title;
|
|
31538
32776
|
}
|
|
32777
|
+
options.target = options.target || 'primary';
|
|
32778
|
+
let targetTabButtonIndex = -1 // Append to end by default
|
|
32779
|
+
|
|
32780
|
+
// Check the saved sidebar state to see if this tab should be added to the primary or secondary sidebar
|
|
32781
|
+
let savedState = RED.settings.get('editor.sidebar.state', defaultSidebarConfiguration)
|
|
32782
|
+
if (true || typeof savedState.primary[0] === 'string' || typeof savedState.secondary[0] === 'string') {
|
|
32783
|
+
// This is a beta.0 format. Reset it for beta.1
|
|
32784
|
+
savedState = defaultSidebarConfiguration
|
|
32785
|
+
RED.settings.set('editor.sidebar.state', savedState)
|
|
32786
|
+
}
|
|
32787
|
+
let targetSidebar = null
|
|
32788
|
+
let targetSection = null
|
|
32789
|
+
if (savedState) {
|
|
32790
|
+
let sidebarState
|
|
32791
|
+
if (savedState.secondary[0].includes(options.id)) {
|
|
32792
|
+
options.target = 'secondary'
|
|
32793
|
+
sidebarState = savedState.secondary[0]
|
|
32794
|
+
targetSidebar = sidebars.secondary
|
|
32795
|
+
targetSection = 'top'
|
|
32796
|
+
} else if (savedState.secondary[1].includes(options.id)) {
|
|
32797
|
+
options.target = 'secondary'
|
|
32798
|
+
sidebarState = savedState.secondary[1]
|
|
32799
|
+
targetSidebar = sidebars.secondary
|
|
32800
|
+
targetSection = 'bottom'
|
|
32801
|
+
} else if (savedState.primary[0].includes(options.id)) {
|
|
32802
|
+
options.target = 'primary'
|
|
32803
|
+
sidebarState = savedState.primary[0]
|
|
32804
|
+
targetSidebar = sidebars.primary
|
|
32805
|
+
targetSection = 'top'
|
|
32806
|
+
} else if (savedState.primary[1].includes(options.id)) {
|
|
32807
|
+
options.target = 'primary'
|
|
32808
|
+
sidebarState = savedState.primary[1]
|
|
32809
|
+
targetSidebar = sidebars.primary
|
|
32810
|
+
targetSection = 'bottom'
|
|
32811
|
+
}
|
|
32812
|
+
if (targetSidebar) {
|
|
32813
|
+
// This tab was found in the saved sidebar state. Now find the target position for the tab button
|
|
32814
|
+
targetTabButtonIndex = sidebarState.indexOf(options.id)
|
|
32815
|
+
}
|
|
32816
|
+
}
|
|
32817
|
+
|
|
32818
|
+
|
|
32819
|
+
targetSidebar = targetSidebar || (options.target === 'secondary' ? sidebars.secondary : sidebars.primary);
|
|
32820
|
+
targetSection = targetSection || 'top'
|
|
32821
|
+
options.targetSection = targetSection;
|
|
31539
32822
|
|
|
31540
32823
|
delete options.closeable;
|
|
31541
32824
|
|
|
31542
|
-
options.wrapper = $('<div>',{style:"height:100%"}).appendTo(
|
|
32825
|
+
options.wrapper = $('<div>',{style:"height:100%"}).appendTo(targetSidebar.sections[targetSection].content)
|
|
31543
32826
|
options.wrapper.append(options.content);
|
|
31544
32827
|
options.wrapper.hide();
|
|
31545
32828
|
|
|
@@ -31548,11 +32831,12 @@ RED.sidebar = (function() {
|
|
|
31548
32831
|
}
|
|
31549
32832
|
|
|
31550
32833
|
if (options.toolbar) {
|
|
31551
|
-
|
|
32834
|
+
targetSidebar.sections[targetSection].footer.append(options.toolbar);
|
|
31552
32835
|
$(options.toolbar).hide();
|
|
31553
32836
|
}
|
|
31554
32837
|
var id = options.id;
|
|
31555
32838
|
|
|
32839
|
+
// console.log('menu', options.id, options.name)
|
|
31556
32840
|
RED.menu.addItem("menu-item-view-menu",{
|
|
31557
32841
|
id:"menu-item-view-menu-"+options.id,
|
|
31558
32842
|
label:options.name,
|
|
@@ -31565,208 +32849,543 @@ RED.sidebar = (function() {
|
|
|
31565
32849
|
options.iconClass = options.iconClass || "fa fa-square-o"
|
|
31566
32850
|
|
|
31567
32851
|
knownTabs[options.id] = options;
|
|
32852
|
+
options.tabButton = $('<button></button>')
|
|
32853
|
+
// Insert the tab button at the correct index
|
|
32854
|
+
if (targetTabButtonIndex === -1) {
|
|
32855
|
+
// Append to end
|
|
32856
|
+
targetSidebar.tabBars[targetSection].addButton(options.tabButton)
|
|
32857
|
+
} else {
|
|
32858
|
+
// Insert before the item at targetTabButtonIndex
|
|
32859
|
+
targetSidebar.tabBars[targetSection].addButton(options.tabButton, targetTabButtonIndex)
|
|
32860
|
+
}
|
|
32861
|
+
options.tabButton.attr('data-tab-id', options.id)
|
|
31568
32862
|
|
|
31569
|
-
|
|
31570
|
-
|
|
32863
|
+
options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
|
|
32864
|
+
if (options.icon) {
|
|
32865
|
+
$('<i>',{class: 'red-ui-sidebar-tab-icon', style:"mask-image: url("+options.icon+"); -webkit-mask-image: url("+options.icon+");"}).appendTo(options.tabButton);
|
|
32866
|
+
} else if (options.iconClass) {
|
|
32867
|
+
$('<i>',{class:options.iconClass}).appendTo(options.tabButton);
|
|
31571
32868
|
}
|
|
32869
|
+
options.tabButton.on('mouseup', function(evt) {
|
|
32870
|
+
if (draggingTabButton) {
|
|
32871
|
+
draggingTabButton = false
|
|
32872
|
+
return
|
|
32873
|
+
}
|
|
32874
|
+
const targetSidebar = options.target === 'secondary' ? sidebars.secondary : sidebars.primary;
|
|
32875
|
+
//
|
|
32876
|
+
if (targetSidebar.tabBars[options.targetSection].active === options.id && RED.menu.isSelected(targetSidebar.menuToggle)) {
|
|
32877
|
+
RED.menu.setSelected(targetSidebar.menuToggle, false);
|
|
32878
|
+
} else {
|
|
32879
|
+
RED.sidebar.show(options.id)
|
|
32880
|
+
}
|
|
32881
|
+
})
|
|
32882
|
+
if (targetSidebar.sections[targetSection].content.children().length === 1) {
|
|
32883
|
+
RED.sidebar.show(options.id)
|
|
32884
|
+
}
|
|
32885
|
+
targetSidebar.resizeSidebarTabBar()
|
|
31572
32886
|
}
|
|
31573
32887
|
|
|
31574
32888
|
function removeTab(id) {
|
|
31575
|
-
|
|
31576
|
-
|
|
31577
|
-
|
|
31578
|
-
knownTabs[id].footer
|
|
32889
|
+
if (knownTabs[id]) {
|
|
32890
|
+
const targetSidebar = knownTabs[id].target === 'secondary' ? sidebars.secondary : sidebars.primary;
|
|
32891
|
+
$(knownTabs[id].wrapper).remove();
|
|
32892
|
+
if (knownTabs[id].footer) {
|
|
32893
|
+
knownTabs[id].footer.remove();
|
|
32894
|
+
}
|
|
32895
|
+
targetSidebar.tabBar.find('button[data-tab-id="'+id+'"]').remove()
|
|
32896
|
+
RED.menu.removeItem("menu-item-view-menu-"+id);
|
|
32897
|
+
if (knownTabs[id].onremove) {
|
|
32898
|
+
knownTabs[id].onremove.call(knownTabs[id]);
|
|
32899
|
+
}
|
|
32900
|
+
delete knownTabs[id];
|
|
32901
|
+
const firstTab = targetSidebar.tabBar.find('button').first().attr('data-tab-id');
|
|
32902
|
+
if (firstTab) {
|
|
32903
|
+
RED.sidebar.show(firstTab);
|
|
32904
|
+
}
|
|
31579
32905
|
}
|
|
31580
|
-
delete knownTabs[id];
|
|
31581
|
-
RED.menu.removeItem("menu-item-view-menu-"+id);
|
|
31582
32906
|
}
|
|
31583
32907
|
|
|
31584
|
-
|
|
31585
|
-
|
|
32908
|
+
function moveTab(id, srcSidebar, srcPosition, targetSidebar, targetPosition) {
|
|
32909
|
+
const options = knownTabs[id];
|
|
32910
|
+
options.target = targetSidebar.id;
|
|
32911
|
+
options.targetSection = targetPosition;
|
|
31586
32912
|
|
|
31587
|
-
|
|
31588
|
-
|
|
31589
|
-
|
|
31590
|
-
|
|
31591
|
-
|
|
31592
|
-
|
|
31593
|
-
|
|
31594
|
-
sidebarSeparator.start = ui.position.left;
|
|
31595
|
-
sidebarSeparator.chartWidth = $("#red-ui-workspace").width();
|
|
31596
|
-
sidebarSeparator.chartRight = winWidth-$("#red-ui-workspace").width()-$("#red-ui-workspace").offset().left-2;
|
|
31597
|
-
sidebarSeparator.dragging = true;
|
|
31598
|
-
|
|
31599
|
-
if (!RED.menu.isSelected("menu-item-sidebar")) {
|
|
31600
|
-
sidebarSeparator.opening = true;
|
|
31601
|
-
var newChartRight = 7;
|
|
31602
|
-
$("#red-ui-sidebar").addClass("closing");
|
|
31603
|
-
$("#red-ui-workspace").css("right",newChartRight);
|
|
31604
|
-
$("#red-ui-editor-stack").css("right",newChartRight+1);
|
|
31605
|
-
$("#red-ui-sidebar").width(0);
|
|
31606
|
-
RED.menu.setSelected("menu-item-sidebar",true);
|
|
31607
|
-
RED.events.emit("sidebar:resize");
|
|
31608
|
-
}
|
|
31609
|
-
sidebarSeparator.width = $("#red-ui-sidebar").width();
|
|
31610
|
-
},
|
|
31611
|
-
drag: function(event,ui) {
|
|
31612
|
-
var d = ui.position.left-sidebarSeparator.start;
|
|
31613
|
-
var newSidebarWidth = sidebarSeparator.width-d;
|
|
31614
|
-
if (sidebarSeparator.opening) {
|
|
31615
|
-
newSidebarWidth -= 3;
|
|
31616
|
-
}
|
|
32913
|
+
$(options.wrapper).appendTo(targetSidebar.sections[targetPosition].content);
|
|
32914
|
+
if (options.toolbar) {
|
|
32915
|
+
targetSidebar.sections[targetPosition].footer.append(options.toolbar);
|
|
32916
|
+
}
|
|
32917
|
+
// Reset the tooltip so its left/right direction is recalculated
|
|
32918
|
+
options.tabButtonTooltip.delete()
|
|
32919
|
+
options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
|
|
31617
32920
|
|
|
31618
|
-
|
|
31619
|
-
|
|
31620
|
-
|
|
31621
|
-
|
|
31622
|
-
|
|
31623
|
-
|
|
31624
|
-
|
|
32921
|
+
if (targetSidebar.sections[targetPosition].content.children().length === 1) {
|
|
32922
|
+
RED.sidebar.show(options.id)
|
|
32923
|
+
}
|
|
32924
|
+
if (srcSidebar.sections[srcPosition].content.children().length === 0 && srcPosition === 'bottom') {
|
|
32925
|
+
srcSidebar.hideSection(srcPosition)
|
|
32926
|
+
} else if (targetSidebar.sections[targetPosition].hidden) {
|
|
32927
|
+
targetSidebar.showSection(targetPosition)
|
|
32928
|
+
}
|
|
32929
|
+
}
|
|
31625
32930
|
|
|
31626
|
-
|
|
31627
|
-
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
|
|
31633
|
-
|
|
31634
|
-
|
|
32931
|
+
let draggingTabButton = false
|
|
32932
|
+
|
|
32933
|
+
function setupSidebarTabs(sidebar) {
|
|
32934
|
+
const tabBar = $('<div class="red-ui-sidebar-tab-bar"></div>').addClass('red-ui-sidebar-' + sidebar.direction);
|
|
32935
|
+
if (sidebar.direction === 'right') {
|
|
32936
|
+
tabBar.insertAfter(sidebar.container);
|
|
32937
|
+
} else if (sidebar.direction === 'left') {
|
|
32938
|
+
tabBar.insertBefore(sidebar.container);
|
|
32939
|
+
}
|
|
32940
|
+
|
|
32941
|
+
// TODO: make this an API object, not just a jQuery object
|
|
32942
|
+
sidebar.tabBars = {
|
|
32943
|
+
top: setupTabSection(sidebar, tabBar, 'top'),
|
|
32944
|
+
bottom: setupTabSection(sidebar, tabBar, 'bottom')
|
|
32945
|
+
}
|
|
32946
|
+
sidebar.tabBar = sidebar.tabBars.top.container;
|
|
32947
|
+
sidebar.resizeSidebarTabBar = function () {
|
|
32948
|
+
sidebar.tabBars.top.resizeSidebarTabBar();
|
|
32949
|
+
sidebar.tabBars.bottom.resizeSidebarTabBar();
|
|
32950
|
+
}
|
|
32951
|
+
sidebar.hideSection = function (position) {
|
|
32952
|
+
sidebar.sections.bottom.container.hide()
|
|
32953
|
+
sidebar.sections.bottom.hidden = true
|
|
32954
|
+
sidebar.sections.top.container.css('flex-grow', '1')
|
|
32955
|
+
sidebar.tabBars.top.container.css('flex-grow', '1')
|
|
32956
|
+
sidebar.tabBars.bottom.container.css('flex-grow', '0')
|
|
32957
|
+
sidebar.tabBars.bottom.container.css('height', '60px')
|
|
32958
|
+
|
|
32959
|
+
sidebar.resizeSidebar()
|
|
32960
|
+
}
|
|
32961
|
+
sidebar.showSection = function (position) {
|
|
32962
|
+
sidebar.sections.bottom.container.show()
|
|
32963
|
+
sidebar.sections.bottom.hidden = false
|
|
32964
|
+
sidebar.sections.top.container.css('flex-grow', '0')
|
|
32965
|
+
sidebar.sections.top.container.css('height', '70%')
|
|
32966
|
+
sidebar.tabBars.top.container.css('flex-grow', '')
|
|
32967
|
+
sidebar.tabBars.bottom.container.css('flex-grow', '')
|
|
32968
|
+
sidebar.tabBars.bottom.container.css('height', '')
|
|
32969
|
+
|
|
32970
|
+
sidebar.resizeSidebar()
|
|
32971
|
+
}
|
|
32972
|
+
}
|
|
32973
|
+
|
|
32974
|
+
function setupTabSection(sidebar, tabBar, position) {
|
|
32975
|
+
const tabBarButtonsContainer = $('<div class="red-ui-sidebar-tab-bar-buttons"></div>').appendTo(tabBar);
|
|
32976
|
+
const tabOverflowButton = $('<button class="red-ui-sidebar-tab-bar-overflow-button"><i class="fa fa-ellipsis-h"></i></button>').appendTo(tabBarButtonsContainer);
|
|
32977
|
+
tabOverflowButton.hide()
|
|
32978
|
+
tabOverflowButton.on('click', function(evt) {
|
|
32979
|
+
try {
|
|
32980
|
+
const menuOptions = []
|
|
32981
|
+
tabBarButtonsContainer.find('button:not(.red-ui-sidebar-tab-bar-overflow-button)').each(function () {
|
|
32982
|
+
if ($(this).is(':visible')) {
|
|
32983
|
+
return
|
|
32984
|
+
}
|
|
32985
|
+
const tabId = $(this).attr('data-tab-id')
|
|
32986
|
+
const tabOptions = knownTabs[tabId]
|
|
32987
|
+
menuOptions.push({
|
|
32988
|
+
label: tabOptions.name,
|
|
32989
|
+
onselect: function() {
|
|
32990
|
+
RED.sidebar.show(tabId)
|
|
31635
32991
|
}
|
|
31636
|
-
}
|
|
31637
|
-
|
|
31638
|
-
|
|
32992
|
+
})
|
|
32993
|
+
})
|
|
32994
|
+
if (menuOptions.length === 0) {
|
|
32995
|
+
return
|
|
32996
|
+
}
|
|
32997
|
+
const menu = RED.menu.init({ options: menuOptions });
|
|
32998
|
+
menu.attr("id",sidebar.container.attr('id')+"-menu");
|
|
32999
|
+
menu.css({
|
|
33000
|
+
position: "absolute"
|
|
33001
|
+
})
|
|
33002
|
+
menu.appendTo("body");
|
|
33003
|
+
var elementPos = tabOverflowButton.offset();
|
|
33004
|
+
menu.css({
|
|
33005
|
+
top: (elementPos.top+tabOverflowButton.height()- menu.height() - 10)+"px",
|
|
33006
|
+
left: (elementPos.left - menu.width() - 3)+"px"
|
|
33007
|
+
})
|
|
33008
|
+
$(".red-ui-menu.red-ui-menu-dropdown").hide();
|
|
33009
|
+
setTimeout(() => {
|
|
33010
|
+
$(document).on("click.red-ui-sidebar-tabmenu", function(evt) {
|
|
33011
|
+
$(document).off("click.red-ui-sidebar-tabmenu");
|
|
33012
|
+
menu.remove();
|
|
33013
|
+
});
|
|
33014
|
+
}, 0)
|
|
33015
|
+
menu.show();
|
|
33016
|
+
} catch (err) {
|
|
33017
|
+
console.log(err)
|
|
33018
|
+
}
|
|
33019
|
+
})
|
|
33020
|
+
tabBarButtonsContainer.data('sidebar', sidebar.id)
|
|
33021
|
+
tabBarButtonsContainer.data('sidebar-position', position)
|
|
33022
|
+
tabBarButtonsContainer.sortable({
|
|
33023
|
+
distance: 10,
|
|
33024
|
+
cancel: false,
|
|
33025
|
+
items: "button:not(.red-ui-sidebar-tab-bar-overflow-button)",
|
|
33026
|
+
placeholder: "red-ui-sidebar-tab-bar-button-placeholder",
|
|
33027
|
+
connectWith: ".red-ui-sidebar-tab-bar-buttons",
|
|
33028
|
+
start: function(event, ui) {
|
|
33029
|
+
const tabId = ui.item.attr('data-tab-id');
|
|
33030
|
+
const options = knownTabs[tabId];
|
|
33031
|
+
options.tabButtonTooltip.delete()
|
|
33032
|
+
draggingTabButton = true
|
|
33033
|
+
tabBar.css('z-index','inherit')
|
|
33034
|
+
$(".red-ui-sidebar-tab-bar").addClass("red-ui-sidebar-dragging-tab");
|
|
33035
|
+
},
|
|
33036
|
+
stop: function(event, ui) {
|
|
33037
|
+
// Restore the tooltip
|
|
33038
|
+
const tabId = ui.item.attr('data-tab-id');
|
|
33039
|
+
const options = knownTabs[tabId];
|
|
33040
|
+
options.tabButtonTooltip.delete()
|
|
33041
|
+
options.tabButtonTooltip = RED.popover.tooltip(options.tabButton, options.name, options.action);
|
|
33042
|
+
// Save the sidebar state
|
|
33043
|
+
exportSidebarState()
|
|
33044
|
+
tabBar.css('z-index','')
|
|
33045
|
+
$(".red-ui-sidebar-tab-bar").removeClass("red-ui-sidebar-dragging-tab");
|
|
33046
|
+
},
|
|
33047
|
+
receive: function(event, ui) {
|
|
33048
|
+
// Tab has been moved from one sidebar to another
|
|
33049
|
+
const src = sidebars[ui.sender.data('sidebar')]
|
|
33050
|
+
const dest = sidebars[$(this).data('sidebar')]
|
|
33051
|
+
const srcPosition = ui.sender.data('sidebar-position')
|
|
33052
|
+
const destPosition = $(this).data('sidebar-position')
|
|
33053
|
+
const tabId = ui.item.attr('data-tab-id');
|
|
33054
|
+
moveTab(tabId, src, srcPosition, dest, destPosition);
|
|
33055
|
+
if (ui.item.hasClass('selected')) {
|
|
33056
|
+
const firstTab = src.tabBars[srcPosition].container.find('button').first().attr('data-tab-id');
|
|
33057
|
+
if (firstTab) {
|
|
33058
|
+
RED.sidebar.show(firstTab);
|
|
31639
33059
|
}
|
|
33060
|
+
}
|
|
33061
|
+
src.resizeSidebarTabBar();
|
|
33062
|
+
dest.resizeSidebarTabBar();
|
|
31640
33063
|
|
|
31641
|
-
|
|
31642
|
-
|
|
31643
|
-
|
|
31644
|
-
$("#red-ui-sidebar").width(newSidebarWidth);
|
|
33064
|
+
RED.sidebar.show(tabId)
|
|
33065
|
+
}
|
|
33066
|
+
})
|
|
31645
33067
|
|
|
31646
|
-
|
|
31647
|
-
|
|
31648
|
-
|
|
31649
|
-
|
|
31650
|
-
|
|
31651
|
-
|
|
31652
|
-
|
|
31653
|
-
|
|
31654
|
-
|
|
31655
|
-
|
|
31656
|
-
|
|
31657
|
-
|
|
31658
|
-
|
|
31659
|
-
|
|
31660
|
-
|
|
31661
|
-
|
|
33068
|
+
let hasHidden = false
|
|
33069
|
+
const resizeSidebarTabBar = function () {
|
|
33070
|
+
let tabBarButtonsBottom = tabBarButtonsContainer.position().top + tabBarButtonsContainer.outerHeight();
|
|
33071
|
+
const buttonHeight = tabOverflowButton.outerHeight()
|
|
33072
|
+
// Find the last visible button
|
|
33073
|
+
let bottomButton = tabBarButtonsContainer.children(":visible").last()
|
|
33074
|
+
if (bottomButton.length === 0) {
|
|
33075
|
+
// Nothing visible - bail out
|
|
33076
|
+
return
|
|
33077
|
+
}
|
|
33078
|
+
if (tabBarButtonsBottom < bottomButton.position().top + buttonHeight * 1.5) {
|
|
33079
|
+
tabOverflowButton.show()
|
|
33080
|
+
let tabOverflowButtonBottom = tabOverflowButton.position().top + buttonHeight * 1.5;
|
|
33081
|
+
while (tabBarButtonsBottom < tabOverflowButtonBottom) {
|
|
33082
|
+
const lastVisible = tabBarButtonsContainer.children(':not(".red-ui-sidebar-tab-bar-overflow-button"):visible').last()
|
|
33083
|
+
if (lastVisible.length === 0) {
|
|
33084
|
+
// Nothing left to hide
|
|
33085
|
+
break
|
|
33086
|
+
}
|
|
33087
|
+
lastVisible.hide()
|
|
33088
|
+
tabOverflowButtonBottom = tabOverflowButton.position().top + buttonHeight * 1.5;
|
|
33089
|
+
}
|
|
33090
|
+
} else {
|
|
33091
|
+
const hiddenChildren = tabBarButtonsContainer.children(':not(".red-ui-sidebar-tab-bar-overflow-button"):hidden')
|
|
33092
|
+
if (hiddenChildren.length > 0) {
|
|
33093
|
+
// We may be able to show some more buttons
|
|
33094
|
+
let tabOverflowButtonBottom = tabOverflowButton.position().top + buttonHeight * 2;
|
|
33095
|
+
let shownCount = 0
|
|
33096
|
+
while (tabBarButtonsBottom > tabOverflowButtonBottom + buttonHeight) {
|
|
33097
|
+
const firstHidden = tabBarButtonsContainer.children(':not(".red-ui-sidebar-tab-bar-overflow-button"):hidden').first()
|
|
33098
|
+
if (firstHidden.length === 0) {
|
|
33099
|
+
// Nothing left to show
|
|
33100
|
+
break
|
|
33101
|
+
}
|
|
33102
|
+
firstHidden.show()
|
|
33103
|
+
shownCount++
|
|
33104
|
+
tabOverflowButtonBottom = tabOverflowButton.position().top + buttonHeight * 2;
|
|
33105
|
+
}
|
|
33106
|
+
if (hiddenChildren.length - shownCount <= 0) {
|
|
33107
|
+
// We were able to show all of the hidden buttons
|
|
33108
|
+
// so hide the overflow button again
|
|
33109
|
+
tabOverflowButton.hide()
|
|
33110
|
+
}
|
|
33111
|
+
}
|
|
33112
|
+
}
|
|
33113
|
+
}
|
|
33114
|
+
return {
|
|
33115
|
+
container: tabBarButtonsContainer,
|
|
33116
|
+
addButton: function(button, position) {
|
|
33117
|
+
if (position === undefined || position >= tabBarButtonsContainer.children().length) {
|
|
33118
|
+
button.insertBefore(tabOverflowButton);
|
|
33119
|
+
} else {
|
|
33120
|
+
button.insertBefore(tabBarButtonsContainer.children().eq(position));
|
|
33121
|
+
}
|
|
33122
|
+
},
|
|
33123
|
+
clearSelected: function() {
|
|
33124
|
+
tabBarButtonsContainer.children('button').removeClass('selected')
|
|
33125
|
+
},
|
|
33126
|
+
resizeSidebarTabBar
|
|
33127
|
+
}
|
|
33128
|
+
}
|
|
33129
|
+
function setupSidebarSeparator(sidebar) {
|
|
33130
|
+
const separator = $('<div class="red-ui-sidebar-separator"></div>');
|
|
33131
|
+
separator.attr('id', sidebar.container.attr('id') + '-separator')
|
|
33132
|
+
$('<div class="red-ui-sidebar-shade hide"></div>').appendTo(separator);
|
|
33133
|
+
$('<div class="red-ui-sidebar-separator-handle"></div>').appendTo(separator);
|
|
33134
|
+
let scaleFactor = 1;
|
|
33135
|
+
if (sidebar.direction === 'right') {
|
|
33136
|
+
separator.insertBefore(sidebar.container);
|
|
33137
|
+
} else if (sidebar.direction === 'left') {
|
|
33138
|
+
scaleFactor = -1;
|
|
33139
|
+
separator.insertAfter(sidebar.container);
|
|
33140
|
+
}
|
|
33141
|
+
// Track sidebar state whilst dragging
|
|
33142
|
+
const sidebarSeparator = {}
|
|
33143
|
+
separator.draggable({
|
|
33144
|
+
axis: "x",
|
|
33145
|
+
start:function(event,ui) {
|
|
33146
|
+
sidebarSeparator.closing = false;
|
|
33147
|
+
sidebarSeparator.opening = false;
|
|
33148
|
+
// var winWidth = $("#red-ui-editor").width();
|
|
33149
|
+
sidebarSeparator.start = ui.position.left;
|
|
33150
|
+
sidebarSeparator.width = sidebar.container.width();
|
|
33151
|
+
sidebarSeparator.chartWidth = $("#red-ui-workspace").width();
|
|
33152
|
+
sidebarSeparator.dragging = true;
|
|
33153
|
+
|
|
33154
|
+
if (!RED.menu.isSelected(sidebar.menuToggle)) {
|
|
33155
|
+
sidebarSeparator.opening = true;
|
|
33156
|
+
sidebar.container.width(0);
|
|
33157
|
+
RED.menu.setSelected(sidebar.menuToggle,true);
|
|
31662
33158
|
RED.events.emit("sidebar:resize");
|
|
31663
33159
|
}
|
|
31664
|
-
|
|
33160
|
+
sidebarSeparator.width = sidebar.container.width();
|
|
33161
|
+
},
|
|
33162
|
+
drag: function(event,ui) {
|
|
33163
|
+
var d = scaleFactor * (ui.position.left-sidebarSeparator.start);
|
|
31665
33164
|
|
|
31666
|
-
|
|
31667
|
-
|
|
31668
|
-
|
|
31669
|
-
|
|
31670
|
-
|
|
31671
|
-
|
|
31672
|
-
|
|
31673
|
-
if (
|
|
31674
|
-
|
|
33165
|
+
var newSidebarWidth = sidebarSeparator.width - d;
|
|
33166
|
+
if (newSidebarWidth > sidebar.maximumWidth) {
|
|
33167
|
+
newSidebarWidth = sidebar.maximumWidth;
|
|
33168
|
+
d = sidebarSeparator.width - sidebar.maximumWidth;
|
|
33169
|
+
ui.position.left = sidebarSeparator.start + scaleFactor * d;
|
|
33170
|
+
}
|
|
33171
|
+
|
|
33172
|
+
if (newSidebarWidth > sidebar.minimumWidth) {
|
|
33173
|
+
if (sidebarSeparator.chartWidth + d < 200) {
|
|
33174
|
+
// Chart is now too small, but we have room to resize the sidebar
|
|
33175
|
+
d += (200 - (sidebarSeparator.chartWidth + d));
|
|
33176
|
+
newSidebarWidth = sidebarSeparator.width - d;
|
|
33177
|
+
ui.position.left = sidebarSeparator.start + scaleFactor * d;
|
|
33178
|
+
}
|
|
33179
|
+
} else if (newSidebarWidth < sidebar.minimumWidth) {
|
|
33180
|
+
if (newSidebarWidth > 100) {
|
|
33181
|
+
newSidebarWidth = sidebar.minimumWidth
|
|
33182
|
+
sidebarSeparator.closing = false
|
|
33183
|
+
} else {
|
|
33184
|
+
newSidebarWidth = 0
|
|
33185
|
+
sidebarSeparator.closing = true
|
|
33186
|
+
}
|
|
31675
33187
|
} else {
|
|
31676
|
-
|
|
33188
|
+
sidebarSeparator.closing = false
|
|
31677
33189
|
}
|
|
31678
|
-
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
33190
|
+
sidebar.container.width(newSidebarWidth);
|
|
33191
|
+
ui.position.left -= scaleFactor * d
|
|
33192
|
+
|
|
33193
|
+
// sidebar.tabs.resize();
|
|
33194
|
+
RED.events.emit("sidebar:resize");
|
|
33195
|
+
},
|
|
33196
|
+
stop:function(event,ui) {
|
|
33197
|
+
sidebarSeparator.dragging = false;
|
|
33198
|
+
if (sidebarSeparator.closing) {
|
|
33199
|
+
sidebar.container.removeClass("closing");
|
|
33200
|
+
if (sidebar.menuToggle) {
|
|
33201
|
+
RED.menu.setSelected(sidebar.menuToggle,false);
|
|
33202
|
+
}
|
|
33203
|
+
sidebar.container.hide()
|
|
33204
|
+
sidebar.separator.hide()
|
|
33205
|
+
if (sidebar.container.width() < sidebar.minimumWidth) {
|
|
33206
|
+
sidebar.container.width(sidebar.defaultWidth);
|
|
33207
|
+
}
|
|
33208
|
+
}
|
|
33209
|
+
RED.events.emit("sidebar:resize");
|
|
31685
33210
|
}
|
|
31686
33211
|
});
|
|
33212
|
+
return separator
|
|
31687
33213
|
}
|
|
31688
33214
|
|
|
31689
|
-
function toggleSidebar(state) {
|
|
33215
|
+
function toggleSidebar(sidebar, state) {
|
|
31690
33216
|
if (!state) {
|
|
31691
|
-
|
|
33217
|
+
sidebar.container.hide()
|
|
33218
|
+
sidebar.separator.hide()
|
|
33219
|
+
sidebar.tabBars.top.clearSelected()
|
|
33220
|
+
sidebar.tabBars.bottom.clearSelected()
|
|
31692
33221
|
} else {
|
|
31693
|
-
|
|
31694
|
-
|
|
33222
|
+
sidebar.container.show()
|
|
33223
|
+
sidebar.separator.show()
|
|
33224
|
+
if (sidebar.tabBars.top.active) {
|
|
33225
|
+
sidebar.tabBars.top.container.find('button[data-tab-id="'+sidebar.tabBars.top.active+'"]').addClass('selected')
|
|
33226
|
+
}
|
|
33227
|
+
if (sidebar.tabBars.bottom.active) {
|
|
33228
|
+
sidebar.tabBars.bottom.container.find('button[data-tab-id="'+sidebar.tabBars.bottom.active+'"]').addClass('selected')
|
|
33229
|
+
}
|
|
31695
33230
|
}
|
|
31696
33231
|
RED.events.emit("sidebar:resize");
|
|
31697
33232
|
}
|
|
31698
33233
|
|
|
31699
33234
|
function showSidebar(id, skipShowSidebar) {
|
|
31700
33235
|
if (id === ":first") {
|
|
31701
|
-
|
|
33236
|
+
// Show the last selected tab for each sidebar
|
|
33237
|
+
Object.keys(sidebars).forEach(function(sidebarKey) {
|
|
33238
|
+
const sidebar = sidebars[sidebarKey];
|
|
33239
|
+
let lastTabId = lastSessionSelectedTabs[sidebarKey];
|
|
33240
|
+
if (!lastTabId) {
|
|
33241
|
+
lastTabId = sidebar.tabBars.top.container.children('button').first().attr('data-tab-id');
|
|
33242
|
+
}
|
|
33243
|
+
showSidebar(lastTabId, true)
|
|
33244
|
+
})
|
|
33245
|
+
return
|
|
31702
33246
|
}
|
|
31703
33247
|
if (id) {
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
33248
|
+
const tabOptions = knownTabs[id];
|
|
33249
|
+
if (tabOptions) {
|
|
33250
|
+
const targetSidebar = tabOptions.target === 'secondary' ? sidebars.secondary : sidebars.primary;
|
|
33251
|
+
const targetSection = tabOptions.targetSection || 'top'
|
|
33252
|
+
targetSidebar.sections[targetSection].content.children().hide();
|
|
33253
|
+
targetSidebar.sections[targetSection].footer.children().hide();
|
|
33254
|
+
if (tabOptions.onchange) {
|
|
33255
|
+
tabOptions.onchange.call(tabOptions);
|
|
33256
|
+
}
|
|
33257
|
+
$(tabOptions.wrapper).show();
|
|
33258
|
+
if (tabOptions.toolbar) {
|
|
33259
|
+
$(tabOptions.toolbar).show();
|
|
33260
|
+
}
|
|
33261
|
+
RED.settings.setLocal("last-sidebar-tab-" + targetSidebar.id, tabOptions.id)
|
|
33262
|
+
// TODO: find which tabBar the button is in
|
|
33263
|
+
targetSidebar.tabBars[targetSection].clearSelected()
|
|
33264
|
+
targetSidebar.tabBars[targetSection].container.find('button[data-tab-id="'+id+'"]').addClass('selected')
|
|
33265
|
+
targetSidebar.tabBars[targetSection].active = id
|
|
33266
|
+
|
|
33267
|
+
if (!skipShowSidebar && !RED.menu.isSelected(targetSidebar.menuToggle)) {
|
|
33268
|
+
RED.menu.setSelected(targetSidebar.menuToggle,true);
|
|
33269
|
+
}
|
|
31710
33270
|
}
|
|
31711
33271
|
}
|
|
31712
33272
|
}
|
|
31713
33273
|
|
|
31714
33274
|
function containsTab(id) {
|
|
31715
|
-
return
|
|
31716
|
-
}
|
|
31717
|
-
|
|
31718
|
-
function
|
|
31719
|
-
|
|
31720
|
-
|
|
31721
|
-
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
|
|
31725
|
-
|
|
31726
|
-
|
|
31727
|
-
|
|
31728
|
-
|
|
31729
|
-
|
|
31730
|
-
|
|
31731
|
-
|
|
31732
|
-
|
|
33275
|
+
return sidebars.primary.tabs.contains(id);
|
|
33276
|
+
}
|
|
33277
|
+
|
|
33278
|
+
function setupSidebar(sidebar) {
|
|
33279
|
+
// Get the appropriate height for the sidebar - as the sidebar will be hidden at this point in time, we need to use
|
|
33280
|
+
// the main-container height as a decent proxy
|
|
33281
|
+
const sidebarHeight = $("#red-ui-main-container").height();
|
|
33282
|
+
sidebar.container.addClass("red-ui-sidebar").addClass('red-ui-sidebar-' + sidebar.direction);
|
|
33283
|
+
sidebar.container.width(sidebar.defaultWidth);
|
|
33284
|
+
if (sidebar.direction === 'right') {
|
|
33285
|
+
$('<div>',{class:"red-ui-sidebar-shade hide"}).css("z-index", 0).appendTo(sidebar.container);
|
|
33286
|
+
}
|
|
33287
|
+
sidebar.sections = {};
|
|
33288
|
+
sidebar.sections.top = {}
|
|
33289
|
+
sidebar.sections.top.container = $('<div class="red-ui-sidebar-section red-ui-sidebar-section-top"></div>').appendTo(sidebar.container);
|
|
33290
|
+
sidebar.sections.top.content = $('<div class="red-ui-sidebar-content"></div>').appendTo(sidebar.sections.top.container);
|
|
33291
|
+
sidebar.sections.top.footer = $('<div class="red-ui-sidebar-footer"></div>').appendTo(sidebar.sections.top.container);
|
|
33292
|
+
sidebar.sectionsSeparator = $('<div class="red-ui-sidebar-tab-bar-separator"><div class="red-ui-sidebar-separator-handle"></div></div>').appendTo(sidebar.container);
|
|
33293
|
+
sidebar.sections.bottom = {}
|
|
33294
|
+
sidebar.sections.bottom.container = $('<div class="red-ui-sidebar-section red-ui-sidebar-section-bottom"></div>').appendTo(sidebar.container);
|
|
33295
|
+
sidebar.sections.bottom.content = $('<div class="red-ui-sidebar-content"></div>').appendTo(sidebar.sections.bottom.container);
|
|
33296
|
+
sidebar.sections.bottom.footer = $('<div class="red-ui-sidebar-footer"></div>').appendTo(sidebar.sections.bottom.container);
|
|
33297
|
+
|
|
33298
|
+
let startPosition
|
|
33299
|
+
let startTopSectionHeight
|
|
33300
|
+
let startTopTabSectionHeight
|
|
33301
|
+
let startSidebarHeight
|
|
33302
|
+
sidebar.sectionsSeparator.draggable({
|
|
33303
|
+
axis: "y",
|
|
33304
|
+
containment: sidebar.container,
|
|
33305
|
+
scroll: false,
|
|
33306
|
+
start:function(event,ui) {
|
|
33307
|
+
startPosition = ui.position.top
|
|
33308
|
+
startTopSectionHeight = sidebar.sections.top.container.outerHeight()
|
|
33309
|
+
startTopTabSectionHeight = sidebar.tabBars.top.container.outerHeight()
|
|
33310
|
+
startSidebarHeight = sidebar.container.height()
|
|
31733
33311
|
},
|
|
31734
|
-
|
|
31735
|
-
|
|
31736
|
-
|
|
31737
|
-
|
|
33312
|
+
drag: function(event,ui) {
|
|
33313
|
+
const delta = ui.position.top - startPosition
|
|
33314
|
+
const newTopHeight = startTopSectionHeight + delta
|
|
33315
|
+
const newBottomHeight = startSidebarHeight - newTopHeight
|
|
33316
|
+
if (newTopHeight < 100 || newBottomHeight < 100) {
|
|
33317
|
+
ui.position.top += delta
|
|
33318
|
+
return
|
|
31738
33319
|
}
|
|
33320
|
+
sidebar.sections.top.container.outerHeight(startTopSectionHeight + delta)
|
|
33321
|
+
sidebar.tabBars.top.container.outerHeight(startTopTabSectionHeight + delta)
|
|
33322
|
+
ui.position.top -= delta
|
|
33323
|
+
sidebar.resizeSidebar()
|
|
31739
33324
|
},
|
|
31740
|
-
|
|
31741
|
-
|
|
31742
|
-
onreorder: function(order) {
|
|
31743
|
-
RED.settings.set("editor.sidebar.order",order);
|
|
31744
|
-
},
|
|
31745
|
-
order: RED.settings.get("editor.sidebar.order",["info", "help", "version-control", "debug"])
|
|
31746
|
-
// scrollable: true
|
|
33325
|
+
stop:function(event,ui) {
|
|
33326
|
+
}
|
|
31747
33327
|
});
|
|
31748
33328
|
|
|
31749
|
-
$('<div
|
|
31750
|
-
|
|
31751
|
-
|
|
33329
|
+
// sidebar.shade = $('<div class="red-ui-sidebar-shade hide"></div>').appendTo(sidebar.container);
|
|
33330
|
+
|
|
33331
|
+
sidebar.separator = setupSidebarSeparator(sidebar);
|
|
33332
|
+
setupSidebarTabs(sidebar)
|
|
33333
|
+
sidebar.resizeSidebar = function () {
|
|
33334
|
+
// Resize sidebar sections as needed
|
|
33335
|
+
const topSectionHeight = sidebar.sections.top.container.outerHeight()
|
|
33336
|
+
if (!sidebar.sections.bottom.hidden) {
|
|
33337
|
+
const bottomSectionHeight = sidebar.sections.bottom.container.outerHeight()
|
|
33338
|
+
|
|
33339
|
+
// Shrink the top section if the bottom section is too small
|
|
33340
|
+
if (bottomSectionHeight < 90 && topSectionHeight > 90) {
|
|
33341
|
+
sidebar.sections.top.container.outerHeight(topSectionHeight - (90 - bottomSectionHeight));
|
|
33342
|
+
}
|
|
33343
|
+
sidebar.tabBars.top.container.height(sidebar.sections.top.container.outerHeight())
|
|
33344
|
+
// } else {
|
|
33345
|
+
// sidebar.tabBars.top.container.height(sidebar.sections.top.container.outerHeight() - 60)
|
|
33346
|
+
}
|
|
33347
|
+
// Trigger a resize of the tab bars to handle overflow
|
|
33348
|
+
sidebar.resizeSidebarTabBar()
|
|
33349
|
+
RED.events.emit("sidebar:resize");
|
|
33350
|
+
|
|
33351
|
+
}
|
|
33352
|
+
$(window).on("resize", sidebar.resizeSidebar)
|
|
33353
|
+
if (sidebar.defaultTopHeight > 0) {
|
|
33354
|
+
if (sidebar.defaultTopHeight === 1) {
|
|
33355
|
+
sidebar.hideSection('bottom')
|
|
33356
|
+
} else {
|
|
33357
|
+
sidebar.sections.top.container.outerHeight(sidebarHeight * sidebar.defaultTopHeight);
|
|
33358
|
+
}
|
|
33359
|
+
}
|
|
33360
|
+
sidebar.resizeSidebar()
|
|
33361
|
+
|
|
33362
|
+
}
|
|
33363
|
+
function init () {
|
|
33364
|
+
sidebars.primary.container = $("#red-ui-sidebar");
|
|
33365
|
+
setupSidebar(sidebars.primary)
|
|
33366
|
+
sidebars.secondary.container = $("#red-ui-sidebar-left");
|
|
33367
|
+
setupSidebar(sidebars.secondary)
|
|
31752
33368
|
|
|
31753
33369
|
RED.actions.add("core:toggle-sidebar",function(state){
|
|
31754
33370
|
if (state === undefined) {
|
|
31755
|
-
RED.menu.toggleSelected(
|
|
33371
|
+
RED.menu.toggleSelected(sidebars.primary.menuToggle);
|
|
31756
33372
|
} else {
|
|
31757
|
-
toggleSidebar(state);
|
|
33373
|
+
toggleSidebar(sidebars.primary, state);
|
|
33374
|
+
}
|
|
33375
|
+
});
|
|
33376
|
+
RED.actions.add("core:toggle-palette", function(state) {
|
|
33377
|
+
if (state === undefined) {
|
|
33378
|
+
RED.menu.toggleSelected(sidebars.secondary.menuToggle);
|
|
33379
|
+
} else {
|
|
33380
|
+
toggleSidebar(sidebars.secondary, state);
|
|
31758
33381
|
}
|
|
31759
33382
|
});
|
|
31760
|
-
RED.popover.tooltip($("#red-ui-sidebar-separator").find(".red-ui-sidebar-control-right"),RED._("keyboard.toggleSidebar"),"core:toggle-sidebar");
|
|
31761
|
-
|
|
31762
|
-
lastSessionSelectedTab = RED.settings.getLocal("last-sidebar-tab")
|
|
31763
33383
|
|
|
31764
|
-
|
|
31765
|
-
|
|
31766
|
-
|
|
31767
|
-
|
|
31768
|
-
|
|
31769
|
-
if ($("#red-ui-editor").width() < 600) { RED.menu.setSelected("menu-item-sidebar",false); }
|
|
33384
|
+
// Remember the last selected tab for each sidebar before
|
|
33385
|
+
// the tabs are readded causing the state to get updated
|
|
33386
|
+
Object.keys(sidebars).forEach(function(sidebarKey) {
|
|
33387
|
+
lastSessionSelectedTabs[sidebarKey] = RED.settings.getLocal("last-sidebar-tab-" + sidebarKey)
|
|
33388
|
+
})
|
|
31770
33389
|
}
|
|
31771
33390
|
|
|
31772
33391
|
return {
|
|
@@ -31814,7 +33433,6 @@ RED.palette = (function() {
|
|
|
31814
33433
|
];
|
|
31815
33434
|
|
|
31816
33435
|
var categoryContainers = {};
|
|
31817
|
-
var sidebarControls;
|
|
31818
33436
|
|
|
31819
33437
|
let paletteState = { filter: "", collapsed: [] };
|
|
31820
33438
|
|
|
@@ -32091,6 +33709,7 @@ RED.palette = (function() {
|
|
|
32091
33709
|
width: "300px",
|
|
32092
33710
|
content: "hi",
|
|
32093
33711
|
delay: { show: 750, hide: 50 }
|
|
33712
|
+
// direction: "left"
|
|
32094
33713
|
});
|
|
32095
33714
|
|
|
32096
33715
|
d.data('popover',popover);
|
|
@@ -32107,13 +33726,15 @@ RED.palette = (function() {
|
|
|
32107
33726
|
var paletteWidth;
|
|
32108
33727
|
var paletteTop;
|
|
32109
33728
|
var dropEnabled;
|
|
33729
|
+
let chartOffset;
|
|
32110
33730
|
$(d).draggable({
|
|
32111
33731
|
helper: 'clone',
|
|
32112
33732
|
appendTo: '#red-ui-editor',
|
|
32113
33733
|
revert: 'invalid',
|
|
32114
33734
|
revertDuration: 200,
|
|
32115
33735
|
containment:'#red-ui-main-container',
|
|
32116
|
-
start: function() {
|
|
33736
|
+
start: function(e, ui) {
|
|
33737
|
+
ui.helper.css('z-index', 1000);
|
|
32117
33738
|
dropEnabled = !(RED.nodes.workspace(RED.workspaces.active())?.locked);
|
|
32118
33739
|
paletteWidth = $("#red-ui-palette").width();
|
|
32119
33740
|
paletteTop = $("#red-ui-palette").parent().position().top + $("#red-ui-palette-container").position().top;
|
|
@@ -32122,6 +33743,7 @@ RED.palette = (function() {
|
|
|
32122
33743
|
if (activeGroup) {
|
|
32123
33744
|
document.getElementById("group_select_"+activeGroup.id).classList.add("red-ui-flow-group-active-hovered");
|
|
32124
33745
|
}
|
|
33746
|
+
chartOffset = chart.offset()
|
|
32125
33747
|
RED.view.focus();
|
|
32126
33748
|
},
|
|
32127
33749
|
stop: function() {
|
|
@@ -32139,10 +33761,13 @@ RED.palette = (function() {
|
|
|
32139
33761
|
},
|
|
32140
33762
|
drag: function(e,ui) {
|
|
32141
33763
|
var paletteNode = getPaletteNode(nt);
|
|
32142
|
-
ui.originalPosition.left
|
|
33764
|
+
// console.log(ui.originalPosition.left, paletteNode.offset().left)
|
|
33765
|
+
// ui.originalPosition.left = paletteNode.offset().left;
|
|
33766
|
+
// console.log(paletteNode.offset())
|
|
32143
33767
|
if (dropEnabled) {
|
|
32144
|
-
mouseX = ui.
|
|
32145
|
-
mouseY = ui.
|
|
33768
|
+
mouseX = ui.offset.left - chartOffset.left + (ui.helper.width()/2) + chart.scrollLeft();
|
|
33769
|
+
mouseY = ui.offset.top - chartOffset.top + (ui.helper.height()/2) + chart.scrollTop() + 10;
|
|
33770
|
+
// console.log(mouseX, mouseY)
|
|
32146
33771
|
if (!groupTimer) {
|
|
32147
33772
|
groupTimer = setTimeout(function() {
|
|
32148
33773
|
var mx = mouseX / RED.view.scale();
|
|
@@ -32388,11 +34013,24 @@ RED.palette = (function() {
|
|
|
32388
34013
|
|
|
32389
34014
|
function init() {
|
|
32390
34015
|
|
|
34016
|
+
const content = $('<div id="red-ui-palette" class="red-ui-sidebar-tab-content">')
|
|
34017
|
+
const toolbar = $('<div></div>');
|
|
34018
|
+
RED.sidebar.addTab({
|
|
34019
|
+
target: 'secondary',
|
|
34020
|
+
id: "palette",
|
|
34021
|
+
label: "Palette",
|
|
34022
|
+
name: "Palette",
|
|
34023
|
+
icon: "red/images/subflow_tab.svg",
|
|
34024
|
+
content,
|
|
34025
|
+
toolbar,
|
|
34026
|
+
pinned: true,
|
|
34027
|
+
enableOnEdit: false
|
|
34028
|
+
});
|
|
34029
|
+
|
|
32391
34030
|
$('<img src="red/images/spin.svg" class="red-ui-palette-spinner hide"/>').appendTo("#red-ui-palette");
|
|
32392
34031
|
$('<div id="red-ui-palette-search" class="red-ui-palette-search hide"><input type="text" data-i18n="[placeholder]palette.filter"></input></div>').appendTo("#red-ui-palette");
|
|
32393
34032
|
$('<div id="red-ui-palette-container" class="red-ui-palette-scroll hide"></div>').appendTo("#red-ui-palette");
|
|
32394
|
-
$('<div
|
|
32395
|
-
$('<div id="red-ui-palette-shade" class="hide"></div>').appendTo("#red-ui-palette");
|
|
34033
|
+
// $('<div id="red-ui-palette-shade" class="hide"></div>').appendTo("#red-ui-palette");
|
|
32396
34034
|
|
|
32397
34035
|
$("#red-ui-palette > .red-ui-palette-spinner").show();
|
|
32398
34036
|
|
|
@@ -32451,19 +34089,6 @@ RED.palette = (function() {
|
|
|
32451
34089
|
}
|
|
32452
34090
|
});
|
|
32453
34091
|
|
|
32454
|
-
sidebarControls = $('<div class="red-ui-sidebar-control-left"><i class="fa fa-chevron-left"></i></div>').appendTo($("#red-ui-palette"));
|
|
32455
|
-
RED.popover.tooltip(sidebarControls,RED._("keyboard.togglePalette"),"core:toggle-palette");
|
|
32456
|
-
|
|
32457
|
-
sidebarControls.on("click", function() {
|
|
32458
|
-
RED.menu.toggleSelected("menu-item-palette");
|
|
32459
|
-
})
|
|
32460
|
-
$("#red-ui-palette").on("mouseenter", function() {
|
|
32461
|
-
sidebarControls.toggle("slide", { direction: "left" }, 200);
|
|
32462
|
-
})
|
|
32463
|
-
$("#red-ui-palette").on("mouseleave", function() {
|
|
32464
|
-
sidebarControls.stop(false,true);
|
|
32465
|
-
sidebarControls.hide();
|
|
32466
|
-
})
|
|
32467
34092
|
var userCategories = [];
|
|
32468
34093
|
if (RED.settings.paletteCategories) {
|
|
32469
34094
|
userCategories = RED.settings.paletteCategories;
|
|
@@ -32485,7 +34110,7 @@ RED.palette = (function() {
|
|
|
32485
34110
|
}
|
|
32486
34111
|
});
|
|
32487
34112
|
|
|
32488
|
-
var paletteFooterButtons = $('<span class="button-group"></span>').appendTo(
|
|
34113
|
+
var paletteFooterButtons = $('<span class="button-group"></span>').appendTo(toolbar);
|
|
32489
34114
|
var paletteCollapseAll = $('<button type="button" class="red-ui-footer-button"><i class="fa fa-angle-double-up"></i></button>').appendTo(paletteFooterButtons);
|
|
32490
34115
|
paletteCollapseAll.on("click", function(e) {
|
|
32491
34116
|
e.preventDefault();
|
|
@@ -32508,13 +34133,7 @@ RED.palette = (function() {
|
|
|
32508
34133
|
});
|
|
32509
34134
|
RED.popover.tooltip(paletteExpandAll,RED._('palette.actions.expand-all'));
|
|
32510
34135
|
|
|
32511
|
-
|
|
32512
|
-
if (state === undefined) {
|
|
32513
|
-
RED.menu.toggleSelected("menu-item-palette");
|
|
32514
|
-
} else {
|
|
32515
|
-
togglePalette(state);
|
|
32516
|
-
}
|
|
32517
|
-
});
|
|
34136
|
+
|
|
32518
34137
|
|
|
32519
34138
|
try {
|
|
32520
34139
|
paletteState = JSON.parse(RED.settings.getLocal("palette-state") || '{"filter":"", "collapsed": []}');
|
|
@@ -32532,18 +34151,6 @@ RED.palette = (function() {
|
|
|
32532
34151
|
}, 10000)
|
|
32533
34152
|
}
|
|
32534
34153
|
|
|
32535
|
-
function togglePalette(state) {
|
|
32536
|
-
if (!state) {
|
|
32537
|
-
$("#red-ui-main-container").addClass("red-ui-palette-closed");
|
|
32538
|
-
sidebarControls.hide();
|
|
32539
|
-
sidebarControls.find("i").addClass("fa-chevron-right").removeClass("fa-chevron-left");
|
|
32540
|
-
} else {
|
|
32541
|
-
$("#red-ui-main-container").removeClass("red-ui-palette-closed");
|
|
32542
|
-
sidebarControls.find("i").removeClass("fa-chevron-right").addClass("fa-chevron-left");
|
|
32543
|
-
}
|
|
32544
|
-
setTimeout(function() { $(window).trigger("resize"); } ,200);
|
|
32545
|
-
}
|
|
32546
|
-
|
|
32547
34154
|
function getCategories() {
|
|
32548
34155
|
var categories = [];
|
|
32549
34156
|
$("#red-ui-palette-container .red-ui-palette-category").each(function(i,d) {
|
|
@@ -32624,10 +34231,6 @@ RED.sidebar.info = (function() {
|
|
|
32624
34231
|
|
|
32625
34232
|
var stackContainer = $("<div>",{class:"red-ui-sidebar-info-stack"}).appendTo(content);
|
|
32626
34233
|
|
|
32627
|
-
var outlinerPanel = $("<div>").css({
|
|
32628
|
-
"overflow": "hidden",
|
|
32629
|
-
"height": "calc(70%)"
|
|
32630
|
-
}).appendTo(stackContainer);
|
|
32631
34234
|
var propertiesPanel = $("<div>").css({
|
|
32632
34235
|
"overflow":"hidden",
|
|
32633
34236
|
"height":"100%",
|
|
@@ -32643,7 +34246,7 @@ RED.sidebar.info = (function() {
|
|
|
32643
34246
|
|
|
32644
34247
|
propertiesPanelHeaderCopyLink = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-link"></button>').css({
|
|
32645
34248
|
position: 'absolute',
|
|
32646
|
-
top: '
|
|
34249
|
+
top: '6px',
|
|
32647
34250
|
right: '32px'
|
|
32648
34251
|
}).on("click", function(evt) {
|
|
32649
34252
|
RED.actions.invoke('core:copy-item-url',selectedObject)
|
|
@@ -32652,7 +34255,7 @@ RED.sidebar.info = (function() {
|
|
|
32652
34255
|
|
|
32653
34256
|
propertiesPanelHeaderHelp = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-book"></button>').css({
|
|
32654
34257
|
position: 'absolute',
|
|
32655
|
-
top: '
|
|
34258
|
+
top: '6px',
|
|
32656
34259
|
right: '56px'
|
|
32657
34260
|
}).on("click", function(evt) {
|
|
32658
34261
|
evt.preventDefault();
|
|
@@ -32665,7 +34268,7 @@ RED.sidebar.info = (function() {
|
|
|
32665
34268
|
|
|
32666
34269
|
propertiesPanelHeaderReveal = $('<button type="button" class="red-ui-button red-ui-button-small"><i class="fa fa-search"></button>').css({
|
|
32667
34270
|
position: 'absolute',
|
|
32668
|
-
top: '
|
|
34271
|
+
top: '6px',
|
|
32669
34272
|
right: '8px'
|
|
32670
34273
|
}).on("click", function(evt) {
|
|
32671
34274
|
evt.preventDefault();
|
|
@@ -32684,13 +34287,13 @@ RED.sidebar.info = (function() {
|
|
|
32684
34287
|
}).appendTo(propertiesPanel);
|
|
32685
34288
|
|
|
32686
34289
|
|
|
32687
|
-
panels = RED.panels.create({container: stackContainer})
|
|
32688
|
-
panels.ratio(0.6);
|
|
32689
|
-
RED.sidebar.info.outliner.build().appendTo(outlinerPanel);
|
|
32690
|
-
|
|
34290
|
+
// panels = RED.panels.create({container: stackContainer})
|
|
34291
|
+
// panels.ratio(0.6);
|
|
34292
|
+
// RED.sidebar.info.outliner.build().appendTo(outlinerPanel);
|
|
32691
34293
|
|
|
32692
34294
|
RED.sidebar.addTab({
|
|
32693
34295
|
id: "info",
|
|
34296
|
+
// target: "secondary",
|
|
32694
34297
|
label: RED._("sidebar.info.label"),
|
|
32695
34298
|
name: RED._("sidebar.info.name"),
|
|
32696
34299
|
iconClass: "fa fa-info",
|
|
@@ -32729,6 +34332,8 @@ RED.sidebar.info = (function() {
|
|
|
32729
34332
|
tips.stop();
|
|
32730
34333
|
}
|
|
32731
34334
|
|
|
34335
|
+
resizeStack();
|
|
34336
|
+
|
|
32732
34337
|
}
|
|
32733
34338
|
|
|
32734
34339
|
function show() {
|
|
@@ -33866,9 +35471,30 @@ RED.sidebar.info = (function() {
|
|
|
33866
35471
|
function onSelectionChanged(selection) {
|
|
33867
35472
|
// treeList.treeList('clearSelection');
|
|
33868
35473
|
}
|
|
35474
|
+
function show() {
|
|
35475
|
+
RED.sidebar.show("explorer");
|
|
35476
|
+
}
|
|
35477
|
+
function init() {
|
|
35478
|
+
const content = build()
|
|
35479
|
+
|
|
35480
|
+
RED.actions.add("core:show-explorer-tab",show);
|
|
35481
|
+
|
|
35482
|
+
|
|
35483
|
+
RED.sidebar.addTab({
|
|
35484
|
+
id: "explorer",
|
|
35485
|
+
// target: "secondary",
|
|
35486
|
+
label: 'Explorer', // RED._("sidebar.info.label"),
|
|
35487
|
+
name: 'Explorer', //RED._("sidebar.info.name"),
|
|
35488
|
+
icon: "red/images/explorer.svg",
|
|
35489
|
+
action:"core:show-explorer-tab",
|
|
35490
|
+
content: content,
|
|
35491
|
+
pinned: true,
|
|
35492
|
+
enableOnEdit: true
|
|
35493
|
+
});
|
|
35494
|
+
}
|
|
33869
35495
|
|
|
33870
35496
|
return {
|
|
33871
|
-
|
|
35497
|
+
init,
|
|
33872
35498
|
search: function(val) {
|
|
33873
35499
|
searchInput.searchBox('value',val)
|
|
33874
35500
|
},
|
|
@@ -45508,14 +47134,14 @@ RED.eventLog = (function() {
|
|
|
45508
47134
|
}
|
|
45509
47135
|
function showTray(options) {
|
|
45510
47136
|
var el = $('<div class="red-ui-tray"></div>');
|
|
45511
|
-
// `editor-tray-header` is deprecated - use red-ui-tray-body instead
|
|
45512
|
-
var header = $('<div class="red-ui-tray-header editor-tray-header"></div>').appendTo(el);
|
|
45513
|
-
var bodyWrapper = $('<div class="red-ui-tray-body-wrapper"></div>').appendTo(el);
|
|
45514
|
-
// `editor-tray-body` is deprecated - use red-ui-tray-body instead
|
|
45515
|
-
var body = $('<div class="red-ui-tray-body editor-tray-body"></div>').appendTo(bodyWrapper);
|
|
45516
|
-
// `editor-tray-footer` is deprecated - use red-ui-tray-footer instead
|
|
45517
|
-
var footer = $('<div class="red-ui-tray-footer"></div>').appendTo(el);
|
|
45518
47137
|
var resizer = $('<div class="red-ui-tray-resize-handle"></div>').appendTo(el);
|
|
47138
|
+
var wrapper = $('<div class="red-ui-tray-wrapper"></div>').appendTo(el);
|
|
47139
|
+
|
|
47140
|
+
var header = $('<div class="red-ui-tray-header"></div>').appendTo(wrapper);
|
|
47141
|
+
var bodyWrapper = $('<div class="red-ui-tray-body-wrapper"></div>').appendTo(wrapper);
|
|
47142
|
+
var body = $('<div class="red-ui-tray-body"></div>').appendTo(bodyWrapper);
|
|
47143
|
+
// `editor-tray-footer` is deprecated - use red-ui-tray-footer instead
|
|
47144
|
+
var footer = $('<div class="red-ui-tray-footer"></div>').appendTo(wrapper);
|
|
45519
47145
|
// var growButton = $('<a class="red-ui-tray-resize-button" style="cursor: w-resize;"><i class="fa fa-angle-left"></i></a>').appendTo(resizer);
|
|
45520
47146
|
// var shrinkButton = $('<a class="red-ui-tray-resize-button" style="cursor: e-resize;"><i style="margin-left: 1px;" class="fa fa-angle-right"></i></a>').appendTo(resizer);
|
|
45521
47147
|
if (options.title) {
|
|
@@ -45677,6 +47303,8 @@ RED.eventLog = (function() {
|
|
|
45677
47303
|
}
|
|
45678
47304
|
|
|
45679
47305
|
function handleWindowResize() {
|
|
47306
|
+
let sidebarWidth = $("#red-ui-sidebar").is(":visible") ? $("#red-ui-sidebar").outerWidth() : 0;
|
|
47307
|
+
$("#red-ui-editor-stack").css('right', sidebarWidth + $("#red-ui-sidebar + .red-ui-sidebar-tab-bar").outerWidth() + 4);
|
|
45680
47308
|
if (stack.length > 0) {
|
|
45681
47309
|
var tray = stack[stack.length-1];
|
|
45682
47310
|
if (tray.options.maximized || tray.width > $("#red-ui-editor-stack").position().left-8) {
|
|
@@ -45701,11 +47329,11 @@ RED.eventLog = (function() {
|
|
|
45701
47329
|
function raiseTrayZ() {
|
|
45702
47330
|
setTimeout(function(){
|
|
45703
47331
|
$('#red-ui-editor-stack').css("zIndex","13");
|
|
45704
|
-
},
|
|
47332
|
+
},100);
|
|
45705
47333
|
}
|
|
45706
47334
|
//lower tray z-index back to original place for correct slide animation (related to fix for editor context menu clipped by sidebar)
|
|
45707
47335
|
function lowerTrayZ(){
|
|
45708
|
-
$('#red-ui-editor-stack').css("zIndex","
|
|
47336
|
+
$('#red-ui-editor-stack').css("zIndex","11");
|
|
45709
47337
|
}
|
|
45710
47338
|
|
|
45711
47339
|
return {
|
|
@@ -48838,6 +50466,12 @@ RED.search = (function() {
|
|
|
48838
50466
|
$('<div>',{class:"red-ui-search-result-node-type"}).text(node.type).appendTo(contentDiv);
|
|
48839
50467
|
$('<div>',{class:"red-ui-search-result-node-id"}).text(node.id).appendTo(contentDiv);
|
|
48840
50468
|
|
|
50469
|
+
div.on("mouseover", function(evt) {
|
|
50470
|
+
if ( node.z == RED.workspaces.active() ) {
|
|
50471
|
+
RED.view.reveal(node.id)
|
|
50472
|
+
}
|
|
50473
|
+
});
|
|
50474
|
+
|
|
48841
50475
|
div.on("click", function(evt) {
|
|
48842
50476
|
evt.preventDefault();
|
|
48843
50477
|
currentIndex = i;
|
|
@@ -48916,8 +50550,7 @@ RED.search = (function() {
|
|
|
48916
50550
|
$("#red-ui-header-shade").show();
|
|
48917
50551
|
$("#red-ui-editor-shade").show();
|
|
48918
50552
|
$("#red-ui-palette-shade").show();
|
|
48919
|
-
$("
|
|
48920
|
-
$("#red-ui-sidebar-separator").hide();
|
|
50553
|
+
$(".red-ui-sidebar-shade").show();
|
|
48921
50554
|
|
|
48922
50555
|
if (dialog === null) {
|
|
48923
50556
|
createDialog();
|
|
@@ -48941,8 +50574,7 @@ RED.search = (function() {
|
|
|
48941
50574
|
$("#red-ui-header-shade").hide();
|
|
48942
50575
|
$("#red-ui-editor-shade").hide();
|
|
48943
50576
|
$("#red-ui-palette-shade").hide();
|
|
48944
|
-
$("
|
|
48945
|
-
$("#red-ui-sidebar-separator").show();
|
|
50577
|
+
$(".red-ui-sidebar-shade").hide();
|
|
48946
50578
|
if (dialog !== null) {
|
|
48947
50579
|
dialog.slideUp(200,function() {
|
|
48948
50580
|
searchInput.searchBox('value','');
|
|
@@ -49042,7 +50674,7 @@ RED.search = (function() {
|
|
|
49042
50674
|
$("#red-ui-header-shade").on('mousedown',hide);
|
|
49043
50675
|
$("#red-ui-editor-shade").on('mousedown',hide);
|
|
49044
50676
|
$("#red-ui-palette-shade").on('mousedown',hide);
|
|
49045
|
-
$("
|
|
50677
|
+
$(".red-ui-sidebar-shade").on('mousedown',hide);
|
|
49046
50678
|
|
|
49047
50679
|
$("#red-ui-view-searchtools-close").on("click", function close() {
|
|
49048
50680
|
clearActiveSearch();
|
|
@@ -49533,8 +51165,7 @@ RED.actionList = (function() {
|
|
|
49533
51165
|
$("#red-ui-header-shade").show();
|
|
49534
51166
|
$("#red-ui-editor-shade").show();
|
|
49535
51167
|
$("#red-ui-palette-shade").show();
|
|
49536
|
-
$("
|
|
49537
|
-
$("#red-ui-sidebar-separator").hide();
|
|
51168
|
+
$(".red-ui-sidebar-shade").show();
|
|
49538
51169
|
if (dialog === null) {
|
|
49539
51170
|
createDialog();
|
|
49540
51171
|
}
|
|
@@ -49568,8 +51199,7 @@ RED.actionList = (function() {
|
|
|
49568
51199
|
$("#red-ui-header-shade").hide();
|
|
49569
51200
|
$("#red-ui-editor-shade").hide();
|
|
49570
51201
|
$("#red-ui-palette-shade").hide();
|
|
49571
|
-
$("
|
|
49572
|
-
$("#red-ui-sidebar-separator").show();
|
|
51202
|
+
$(".red-ui-sidebar-shade").hide();
|
|
49573
51203
|
if (dialog !== null) {
|
|
49574
51204
|
dialog.slideUp(200,function() {
|
|
49575
51205
|
searchInput.searchBox('value','');
|
|
@@ -49601,7 +51231,7 @@ RED.actionList = (function() {
|
|
|
49601
51231
|
$("#red-ui-header-shade").on('mousedown',hide);
|
|
49602
51232
|
$("#red-ui-editor-shade").on('mousedown',hide);
|
|
49603
51233
|
$("#red-ui-palette-shade").on('mousedown',hide);
|
|
49604
|
-
$("
|
|
51234
|
+
$(".red-ui-sidebar-shade").on('mousedown',hide);
|
|
49605
51235
|
}
|
|
49606
51236
|
|
|
49607
51237
|
return {
|
|
@@ -52423,7 +54053,7 @@ RED.userSettings = (function() {
|
|
|
52423
54053
|
});
|
|
52424
54054
|
settingsContent.i18n();
|
|
52425
54055
|
settingsTabs.activateTab("red-ui-settings-tab-"+(initialTab||'view'))
|
|
52426
|
-
$("
|
|
54056
|
+
$(".red-ui-sidebar-shade").show();
|
|
52427
54057
|
},
|
|
52428
54058
|
close: function() {
|
|
52429
54059
|
settingsVisible = false;
|
|
@@ -52432,7 +54062,7 @@ RED.userSettings = (function() {
|
|
|
52432
54062
|
pane.close();
|
|
52433
54063
|
}
|
|
52434
54064
|
});
|
|
52435
|
-
$("
|
|
54065
|
+
$(".red-ui-sidebar-shade").hide();
|
|
52436
54066
|
|
|
52437
54067
|
},
|
|
52438
54068
|
show: function() {}
|
|
@@ -55225,7 +56855,7 @@ RED.projects.settings = (function() {
|
|
|
55225
56855
|
});
|
|
55226
56856
|
settingsContent.i18n();
|
|
55227
56857
|
settingsTabs.activateTab("red-ui-project-settings-tab-"+(initialTab||'main'))
|
|
55228
|
-
$("
|
|
56858
|
+
$(".red-ui-sidebar-shade").show();
|
|
55229
56859
|
},
|
|
55230
56860
|
close: function() {
|
|
55231
56861
|
settingsVisible = false;
|
|
@@ -55234,7 +56864,7 @@ RED.projects.settings = (function() {
|
|
|
55234
56864
|
pane.close();
|
|
55235
56865
|
}
|
|
55236
56866
|
});
|
|
55237
|
-
$("
|
|
56867
|
+
$(".red-ui-sidebar-shade").hide();
|
|
55238
56868
|
|
|
55239
56869
|
},
|
|
55240
56870
|
show: function() {}
|
|
@@ -59248,10 +60878,15 @@ RED.touch.radialMenu = (function() {
|
|
|
59248
60878
|
|
|
59249
60879
|
function listTour() {
|
|
59250
60880
|
return [
|
|
60881
|
+
{
|
|
60882
|
+
id: "5_0",
|
|
60883
|
+
label: "5.0",
|
|
60884
|
+
path: "./tours/welcome.js"
|
|
60885
|
+
},
|
|
59251
60886
|
{
|
|
59252
60887
|
id: "4_1",
|
|
59253
60888
|
label: "4.1",
|
|
59254
|
-
path: "./tours/welcome.js"
|
|
60889
|
+
path: "./tours/4.1/welcome.js"
|
|
59255
60890
|
},
|
|
59256
60891
|
{
|
|
59257
60892
|
id: "4_0",
|