sunrize 1.7.37 → 1.7.39

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sunrize",
3
3
  "productName": "Sunrize X3D Editor",
4
- "version": "1.7.37",
4
+ "version": "1.7.39",
5
5
  "description": "A Multi-Platform X3D Editor",
6
6
  "main": "src/main.js",
7
7
  "bin": {
@@ -109,7 +109,7 @@
109
109
  "string-similarity": "^4.0.4",
110
110
  "tweakpane": "^3.1.10",
111
111
  "update-electron-app": "^3.1.0",
112
- "x_ite": "^11.0.1",
113
- "x3d-traverse": "^1.0.7"
112
+ "x_ite": "^11.0.2",
113
+ "x3d-traverse": "^1.0.8"
114
114
  }
115
115
  }
@@ -22,6 +22,8 @@ const
22
22
 
23
23
  module .exports = class Document extends Interface
24
24
  {
25
+ #replaceWorld;
26
+
25
27
  constructor ()
26
28
  {
27
29
  super ("Sunrize.Document.");
@@ -129,6 +131,13 @@ module .exports = class Document extends Interface
129
131
 
130
132
  UndoManager .shared .addInterest (this, () => this .undoManager ());
131
133
 
134
+ // Override replaceWorld and loadURL.
135
+
136
+ this .#replaceWorld = X3D .X3DBrowser .prototype .replaceWorld;
137
+
138
+ X3D .X3DBrowser .prototype .loadURL = () => Promise .resolve ();
139
+ X3D .X3DBrowser .prototype .replaceWorld = () => Promise .resolve ();
140
+
132
141
  // Connect browser options.
133
142
 
134
143
  const browserOptions = [
@@ -379,10 +388,11 @@ Viewpoint {
379
388
  {
380
389
  try
381
390
  {
382
- if (fileURL)
383
- await this .browser .loadURL (new X3D .MFString (fileURL));
384
- else
385
- await this .browser .replaceWorld (null);
391
+ const scene = fileURL
392
+ ? await this .browser .createX3DFromURL (new X3D .MFString (fileURL))
393
+ : null;
394
+
395
+ await this .#replaceWorld .call (this .browser, scene);
386
396
 
387
397
  this .browser .currentScene .setSpecificationVersion (X3D .LATEST_VERSION);
388
398
  }
@@ -1884,7 +1884,7 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1884
1884
 
1885
1885
  const
1886
1886
  target = $(event .target),
1887
- element = target .closest (".node", this .sceneGraph),
1887
+ element = target .closest (".node, .exported-node", this .sceneGraph),
1888
1888
  layerNode = this .getNode (element),
1889
1889
  layerSet = this .browser .getWorld () .getLayerSet (),
1890
1890
  index = layerSet ._layers .findIndex (node => node ?.getValue () .valueOf () === layerNode);
@@ -1898,6 +1898,57 @@ module .exports = class OutlineEditor extends OutlineRouteGraph
1898
1898
  Editor .setFieldValue (this .browser .currentScene, layerSet, layerSet ._activeLayer, index + 1);
1899
1899
  }
1900
1900
 
1901
+ playNode (event)
1902
+ {
1903
+ const
1904
+ target = $(event .target),
1905
+ element = target .closest (".node, .exported-node", this .sceneGraph),
1906
+ node = this .getNode (element);
1907
+
1908
+ event .preventDefault ();
1909
+ event .stopImmediatePropagation ();
1910
+
1911
+ if (node ._isActive .getValue ())
1912
+ {
1913
+ if (node ._isPaused .getValue ())
1914
+ Editor .setFieldValue (this .browser .currentScene, node, node ._resumeTime, Date .now () / 1000);
1915
+ else
1916
+ Editor .setFieldValue (this .browser .currentScene, node, node ._pauseTime, Date .now () / 1000);
1917
+ }
1918
+ else
1919
+ {
1920
+ node ._isEvenLive = true;
1921
+
1922
+ Editor .setFieldValue (this .browser .currentScene, node, node ._startTime, Date .now () / 1000);
1923
+ }
1924
+ }
1925
+
1926
+ stopNode (event)
1927
+ {
1928
+ const
1929
+ target = $(event .target),
1930
+ element = target .closest (".node, .exported-node", this .sceneGraph),
1931
+ node = this .getNode (element);
1932
+
1933
+ event .preventDefault ();
1934
+ event .stopImmediatePropagation ();
1935
+
1936
+ Editor .setFieldValue (this .browser .currentScene, node, node ._stopTime, Date .now () / 1000);
1937
+ }
1938
+
1939
+ loopNode (event)
1940
+ {
1941
+ const
1942
+ target = $(event .target),
1943
+ element = target .closest (".node, .exported-node", this .sceneGraph),
1944
+ node = this .getNode (element);
1945
+
1946
+ event .preventDefault ();
1947
+ event .stopImmediatePropagation ();
1948
+
1949
+ Editor .setFieldValue (this .browser .currentScene, node, node ._loop, !node ._loop .getValue ());
1950
+ }
1951
+
1901
1952
  addBooleanField (button)
1902
1953
  {
1903
1954
  const
@@ -248,6 +248,22 @@ module .exports = class OutlineView extends Interface
248
248
  .wrapInner ("<div class=\"item no-select\"/>")
249
249
  .find (".item") .append ("<div class=\"route-curves-wrapper\"><canvas class=\"route-curves\"></canvas></div>");
250
250
 
251
+ // Connect actions.
252
+
253
+ this .connectNodeActions (parent, child);
254
+
255
+ // Expand children.
256
+
257
+ const
258
+ specialElements = child .find (".externproto, .proto, .imported-node, .exported-node"),
259
+ elements = child .find (".node");
260
+
261
+ child .show ();
262
+ this .expandSceneSubtreeComplete (specialElements, elements);
263
+ }
264
+
265
+ connectNodeActions (parent, child)
266
+ {
251
267
  if (this .isEditable (parent))
252
268
  {
253
269
  child .find (".externproto > .item")
@@ -281,20 +297,39 @@ module .exports = class OutlineView extends Interface
281
297
  child .find (".bind-node")
282
298
  .on ("click", this .bindNode .bind (this));
283
299
 
300
+ child .find (".play-node")
301
+ .on ("click", this .playNode .bind (this));
302
+
303
+ child .find (".stop-node")
304
+ .on ("click", this .stopNode .bind (this));
305
+
306
+ child .find (".loop-node")
307
+ .on ("click", this .loopNode .bind (this));
308
+
284
309
  child .find (".reload-node")
285
310
  .on ("click", this .reloadNode .bind (this));
286
311
 
287
312
  child .find (".show-preview")
288
313
  .on ("click", this .showPreview .bind (this));
314
+ }
289
315
 
290
- // Expand children.
316
+ connectFieldActions (child)
317
+ {
318
+ child .find ("area.input-selector")
319
+ .on ("mouseenter", this .hoverInSingleConnector .bind (this, "input"))
320
+ .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "input"))
321
+ .on ("click", this .selectSingleConnector .bind (this, "input"));
291
322
 
292
- const
293
- specialElements = child .find (".externproto, .proto, .imported-node, .exported-node"),
294
- elements = child .find (".node");
323
+ child .find ("area.output-selector")
324
+ .on ("mouseenter", this .hoverInSingleConnector .bind (this, "output"))
325
+ .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "output"))
326
+ .on ("click", this .selectSingleConnector .bind (this, "output"));
295
327
 
296
- child .show ();
297
- this .expandSceneSubtreeComplete (specialElements, elements);
328
+ child .find ("area.input-routes-selector")
329
+ .on ("click", this .selectSingleRoute .bind (this, "input"));
330
+
331
+ child .find ("area.output-routes-selector")
332
+ .on ("click", this .selectSingleRoute .bind (this, "output"));
298
333
  }
299
334
 
300
335
  expandSceneSubtreeComplete (specialElements, elements)
@@ -309,7 +344,7 @@ module .exports = class OutlineView extends Interface
309
344
  element = $(e),
310
345
  node = this .getNode (element);
311
346
 
312
- if (node && node .getUserData (_expanded) && element .jstree ("is_closed", element))
347
+ if (node ?.getUserData (_expanded) && element .jstree ("is_closed", element))
313
348
  {
314
349
  element .jstree ("open_node", element);
315
350
  }
@@ -323,7 +358,7 @@ module .exports = class OutlineView extends Interface
323
358
  element = $(e),
324
359
  node = this .getNode (element);
325
360
 
326
- if (node .getUserData (_expanded) && element .jstree ("is_closed", element))
361
+ if (node ?.getUserData (_expanded) && element .jstree ("is_closed", element))
327
362
  {
328
363
  element .data ("auto-expand", true);
329
364
  element .jstree ("open_node", element);
@@ -903,6 +938,7 @@ module .exports = class OutlineView extends Interface
903
938
  #nodeSymbol = Symbol ();
904
939
  #updateNodeBoundSymbol = Symbol ();
905
940
  #updateNodeLoadStateSymbol = Symbol ();
941
+ #updateNodePlaySymbol = Symbol ();
906
942
 
907
943
  createNodeElement (type, parent, node, index)
908
944
  {
@@ -1001,17 +1037,41 @@ module .exports = class OutlineView extends Interface
1001
1037
  .text (cloneCount > 1 ? `[${cloneCount}]` : "")
1002
1038
  .appendTo (name);
1003
1039
 
1004
- // Buttons
1040
+ // Add buttons to name.
1041
+
1042
+ this .addNodeButtons (node, name);
1005
1043
 
1006
- const buttons = [ ];
1044
+ // Append empty tree to enable expander.
1045
+
1046
+ if (!this .isInParents (parent, node))
1047
+ $("<ul><li></li></ul>") .appendTo (child);
1048
+ }
1049
+ else
1050
+ {
1051
+ $("<div></div>")
1052
+ .addClass ("name")
1053
+ .append ($("<span></span>") .addClass ("node-type-name") .text ("NULL"))
1054
+ .appendTo (child);
1055
+ }
1056
+
1057
+ return child;
1058
+ }
1059
+
1060
+ addNodeButtons (node, name)
1061
+ {
1062
+ // Add buttons to name.
1007
1063
 
1008
- if (node .setHidden && !(node .getExecutionContext () .getOuterNode () instanceof X3D .X3DProtoDeclaration))
1064
+ const buttons = [ ];
1065
+
1066
+ if (!(node .getExecutionContext () .getOuterNode () instanceof X3D .X3DProtoDeclaration))
1067
+ {
1068
+ if (node .setHidden)
1009
1069
  {
1010
1070
  buttons .push ($("<span></span>")
1011
1071
  .attr ("order", "0")
1072
+ .attr ("title", "Toggle visibility.")
1012
1073
  .addClass (["toggle-visibility", "button", "material-symbols-outlined"])
1013
1074
  .addClass (node .isHidden () ? "off" : "on")
1014
- .attr ("title", "Toggle visibility.")
1015
1075
  .text (node .isHidden () ? "visibility_off" : "visibility"));
1016
1076
  }
1017
1077
 
@@ -1019,99 +1079,124 @@ module .exports = class OutlineView extends Interface
1019
1079
  {
1020
1080
  buttons .push ($("<span></span>")
1021
1081
  .attr ("order", "1")
1082
+ .attr ("title", _("Toggle display tool."))
1022
1083
  .addClass (["toggle-tool", "button", "material-symbols-outlined"])
1023
1084
  .addClass (node .valueOf () === node ? "off" : "on")
1024
- .attr ("title", _("Toggle display tool."))
1025
1085
  .text ("build_circle"));
1026
1086
  }
1087
+ }
1027
1088
 
1028
- for (const type of node .getType ())
1089
+ for (const type of node .getType ())
1090
+ {
1091
+ switch (type)
1029
1092
  {
1030
- switch (type)
1093
+ case X3D .X3DConstants .X3DLayerNode:
1031
1094
  {
1032
- case X3D .X3DConstants .X3DLayerNode:
1033
- {
1034
- buttons .push ($("<span></span>")
1035
- .attr ("order", "2")
1036
- .addClass (["activate-layer", "button", "material-symbols-outlined"])
1037
- .addClass (this .browser .getActiveLayer () === node ? "green" : "off")
1038
- .attr ("title", _("Activate layer."))
1039
- .text ("check_circle"));
1095
+ buttons .push ($("<span></span>")
1096
+ .attr ("order", "2")
1097
+ .attr ("title", _("Activate layer."))
1098
+ .addClass (["activate-layer", "button", "material-symbols-outlined"])
1099
+ .addClass (this .browser .getActiveLayer () === node ? "green" : "off")
1100
+ .text ("check_circle"));
1040
1101
 
1102
+ continue;
1103
+ }
1104
+ case X3D .X3DConstants .X3DBindableNode:
1105
+ {
1106
+ if (node .getExecutionContext () .getOuterNode () instanceof X3D .X3DProtoDeclaration)
1041
1107
  continue;
1042
- }
1043
- case X3D .X3DConstants .X3DBindableNode:
1044
- {
1045
- node ._isBound .addFieldCallback (this .#updateNodeBoundSymbol, this .updateNodeBound .bind (this, node));
1046
1108
 
1047
- buttons .push ($("<span></span>")
1048
- .attr ("order", "3")
1049
- .addClass (["bind-node", "button", "material-symbols-outlined"])
1050
- .addClass (node ._isBound .getValue () ? "on" : "off")
1051
- .attr ("title", _("Bind node."))
1052
- .text (node ._isBound .getValue () ? "radio_button_checked" : "radio_button_unchecked"));
1109
+ node ._isBound .addFieldCallback (this .#updateNodeBoundSymbol, this .updateNodeBound .bind (this, node));
1053
1110
 
1111
+ buttons .push ($("<span></span>")
1112
+ .attr ("order", "3")
1113
+ .attr ("title", _("Bind node."))
1114
+ .addClass (["bind-node", "button", "material-symbols-outlined"])
1115
+ .addClass (node ._isBound .getValue () ? "on" : "off")
1116
+ .text (node ._isBound .getValue () ? "radio_button_checked" : "radio_button_unchecked"));
1117
+
1118
+ continue;
1119
+ }
1120
+ case X3D .X3DConstants .X3DTimeDependentNode:
1121
+ {
1122
+ if (node .getExecutionContext () !== this .executionContext)
1054
1123
  continue;
1055
- }
1056
- case X3D .X3DConstants .X3DUrlObject:
1057
- {
1058
- if (node .getExecutionContext () .getOuterNode () instanceof X3D .X3DProtoDeclaration)
1059
- {
1060
- if (!node .getType () .includes (X3D .X3DConstants .Inline))
1061
- continue;
1062
- }
1063
1124
 
1064
- const [className] = this .getLoadState (node .checkLoadState (), node .getTypeName ());
1125
+ node ._enabled .addFieldCallback (this .#updateNodePlaySymbol, this .updateNodePlay .bind (this, node));
1126
+ node ._isActive .addFieldCallback (this .#updateNodePlaySymbol, this .updateNodePlay .bind (this, node));
1127
+ node ._isPaused .addFieldCallback (this .#updateNodePlaySymbol, this .updateNodePlay .bind (this, node));
1128
+ node ._loop .addFieldCallback (this .#updateNodePlaySymbol, this .updateNodePlay .bind (this, node));
1129
+
1130
+ buttons .push ($("<span></span>")
1131
+ .attr ("order", "4")
1132
+ .attr ("title", node ._isActive .getValue () && !node ._isPaused .getValue () ? _("Pause timer.") : _("Start timer."))
1133
+ .addClass (["play-node", "button", "material-icons"])
1134
+ .addClass (node ._isPaused .getValue () ? "on" : "off")
1135
+ .text (node ._isActive .getValue () ? "pause" : "play_arrow"));
1136
+
1137
+ buttons .push ($("<span></span>")
1138
+ .attr ("order", "5")
1139
+ .attr ("title", _("Stop timer."))
1140
+ .addClass (["stop-node", "button", "material-icons"])
1141
+ .addClass (node ._isActive .getValue () ? "on" : "off")
1142
+ .text ("stop"));
1143
+
1144
+ buttons .push ($("<span></span>")
1145
+ .attr ("order", "6")
1146
+ .attr ("title", _("Toggle loop."))
1147
+ .addClass (["loop-node", "button", "material-icons"])
1148
+ .addClass (node ._loop .getValue () ? "on" : "off")
1149
+ .text ("repeat"));
1150
+
1151
+ if (!node ._enabled .getValue ())
1152
+ buttons .slice (-3) .forEach (button => button .hide ());
1065
1153
 
1066
- node .getLoadState () .addFieldCallback (this .#updateNodeLoadStateSymbol, this .updateNodeLoadState .bind (this, node));
1154
+ continue;
1155
+ }
1156
+ case X3D .X3DConstants .X3DUrlObject:
1157
+ {
1158
+ if (node .getExecutionContext () .getOuterNode () instanceof X3D .X3DProtoDeclaration)
1159
+ {
1160
+ if (!node .getType () .includes (X3D .X3DConstants .Inline))
1161
+ continue;
1162
+ }
1067
1163
 
1068
- buttons .push ($("<span></span>")
1069
- .attr ("order", "4")
1070
- .addClass (["reload-node", "button", "material-symbols-outlined", className])
1071
- .attr ("title", "Load now.")
1072
- .text ("autorenew"));
1164
+ const [className] = this .getLoadState (node .checkLoadState (), node .getTypeName ());
1073
1165
 
1074
- continue;
1075
- }
1076
- case X3D .X3DConstants .AudioClip:
1077
- case X3D .X3DConstants .BufferAudioSource:
1078
- case X3D .X3DConstants .X3DMaterialNode:
1079
- case X3D .X3DConstants .X3DSingleTextureNode:
1080
- {
1081
- buttons .push ($("<span></span>")
1082
- .attr ("order", "5")
1083
- .addClass (["show-preview", "button", "material-symbols-outlined", "off"])
1084
- .css ("top", "2px")
1085
- .attr ("title", _("Show preview."))
1086
- .text ("preview"));
1166
+ node .getLoadState () .addFieldCallback (this .#updateNodeLoadStateSymbol, this .updateNodeLoadState .bind (this, node));
1087
1167
 
1088
- continue;
1089
- }
1090
- }
1091
- }
1168
+ buttons .push ($("<span></span>")
1169
+ .attr ("order", "7")
1170
+ .attr ("title", "Load now.")
1171
+ .addClass (["reload-node", "button", "material-symbols-outlined", className])
1172
+ .text ("autorenew"));
1092
1173
 
1093
- buttons .sort ((a, b) => a .attr ("order") - b .attr ("order"))
1174
+ continue;
1175
+ }
1176
+ case X3D .X3DConstants .AudioClip:
1177
+ case X3D .X3DConstants .BufferAudioSource:
1178
+ case X3D .X3DConstants .X3DMaterialNode:
1179
+ case X3D .X3DConstants .X3DSingleTextureNode:
1180
+ {
1181
+ buttons .push ($("<span></span>")
1182
+ .attr ("order", "8")
1183
+ .attr ("title", _("Show preview."))
1184
+ .addClass (["show-preview", "button", "material-symbols-outlined", "off"])
1185
+ .css ("top", "2px")
1186
+ .text ("preview"));
1094
1187
 
1095
- for (const button of buttons)
1096
- {
1097
- name .append (document .createTextNode (" "));
1098
- name .append (button);
1188
+ continue;
1189
+ }
1099
1190
  }
1191
+ }
1100
1192
 
1101
- // Append empty tree to enable expander.
1193
+ buttons .sort ((a, b) => a .attr ("order") - b .attr ("order"))
1102
1194
 
1103
- if (!this .isInParents (parent, node))
1104
- $("<ul><li></li></ul>") .appendTo (child);
1105
- }
1106
- else
1195
+ for (const button of buttons)
1107
1196
  {
1108
- $("<div></div>")
1109
- .addClass ("name")
1110
- .append ($("<span></span>") .addClass ("node-type-name") .text ("NULL"))
1111
- .appendTo (child);
1197
+ name .append (document .createTextNode (" "));
1198
+ name .append (button);
1112
1199
  }
1113
-
1114
- return child;
1115
1200
  }
1116
1201
 
1117
1202
  updateNodeTypeName (node)
@@ -1162,7 +1247,7 @@ module .exports = class OutlineView extends Interface
1162
1247
  if (!this .browser .getActiveLayer ())
1163
1248
  return;
1164
1249
 
1165
- this .sceneGraph .find (`.node[node-id=${this .browser .getActiveLayer () .getId ()}]`)
1250
+ this .sceneGraph .find (`.node[node-id=${this .browser .getActiveLayer () .getId ()}], .exported-node[node-id=${this .browser .getActiveLayer () .getId ()}]`)
1166
1251
  .find ("> .item .activate-layer")
1167
1252
  .removeClass ("off")
1168
1253
  .addClass ("green");
@@ -1171,7 +1256,7 @@ module .exports = class OutlineView extends Interface
1171
1256
  updateNodeBound (node)
1172
1257
  {
1173
1258
  this .sceneGraph
1174
- .find (`.node[node-id=${node .getId ()}]`)
1259
+ .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
1175
1260
  .find ("> .item .bind-node")
1176
1261
  .removeClass (["on", "off"])
1177
1262
  .addClass (node ._isBound .getValue () ? "on" : "off")
@@ -1183,12 +1268,45 @@ module .exports = class OutlineView extends Interface
1183
1268
  const [className] = this .getLoadState (node .checkLoadState (), node .getTypeName ());
1184
1269
 
1185
1270
  this .sceneGraph
1186
- .find (`.node[node-id=${node .getId ()}],.externproto[node-id=${node .getId ()}]`)
1271
+ .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}], .externproto[node-id=${node .getId ()}]`)
1187
1272
  .find ("> .item .reload-node")
1188
1273
  .removeClass (["not-started-state", "in-progress-state", "complete-state", "failed-state"])
1189
1274
  .addClass (className);
1190
1275
  }
1191
1276
 
1277
+ updateNodePlay (node)
1278
+ {
1279
+ const buttons = [ ];
1280
+
1281
+ buttons .push (this .sceneGraph
1282
+ .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
1283
+ .find ("> .item .play-node")
1284
+ .removeClass (["on", "off"])
1285
+ .addClass (node ._isPaused .getValue () ? "on" : "off")
1286
+ .attr ("title", node ._isActive .getValue () && !node ._isPaused .getValue () ? _("Pause timer.") : _("Start timer."))
1287
+ .text (node ._isActive .getValue () ? "pause" : "play_arrow"));
1288
+
1289
+ buttons .push (this .sceneGraph
1290
+ .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
1291
+ .find ("> .item .stop-node")
1292
+ .removeClass (["on", "off"])
1293
+ .addClass (node ._isActive .getValue () ? "on" : "off"));
1294
+
1295
+ buttons .push (this .sceneGraph
1296
+ .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
1297
+ .find ("> .item .loop-node")
1298
+ .removeClass (["on", "off"])
1299
+ .addClass (node ._loop .getValue () ? "on" : "off"));
1300
+
1301
+ if (node ._enabled .getValue ())
1302
+ buttons .slice (-3) .forEach (button => button .show ());
1303
+ else
1304
+ buttons .slice (-3) .forEach (button => button .hide ());
1305
+
1306
+ if (!node ._isActive .getValue ())
1307
+ node ._isEvenLive = false;
1308
+ }
1309
+
1192
1310
  isInParents (parent, node)
1193
1311
  {
1194
1312
  return parent .closest (".node[node-id=" + node .getId () + "]", this .sceneGraph) .length;
@@ -1246,6 +1364,10 @@ module .exports = class OutlineView extends Interface
1246
1364
  .append ($("<span></span>") .addClass ("as-name") .text (importedNode .getImportedName ()));
1247
1365
  }
1248
1366
 
1367
+ // Add buttons to name.
1368
+
1369
+ this .addNodeButtons (importedNode .getExportedNode (), name);
1370
+
1249
1371
  // Append empty tree to enable expander.
1250
1372
 
1251
1373
  $("<ul><li></li></ul>") .appendTo (child);
@@ -1346,6 +1468,10 @@ module .exports = class OutlineView extends Interface
1346
1468
  if (exportedNode .getExportedName () === node .getName ())
1347
1469
  name .find (".node-name") .nextAll () .hide ();
1348
1470
 
1471
+ // Add buttons to name.
1472
+
1473
+ this .addNodeButtons (node, name);
1474
+
1349
1475
  // Append empty tree to enable expander.
1350
1476
 
1351
1477
  $("<ul><li></li></ul>") .appendTo (child);
@@ -1404,58 +1530,62 @@ module .exports = class OutlineView extends Interface
1404
1530
 
1405
1531
  // Color
1406
1532
 
1407
- switch (field .getType ())
1533
+ if (field .isInitializable ())
1408
1534
  {
1409
- case X3D .X3DConstants .SFBool:
1535
+ switch (field .getType ())
1410
1536
  {
1411
- $("<img></img>")
1412
- .addClass (["boolean-button", "field-button"])
1413
- .attr ("src", `../images/OutlineEditor/Values/${field .getValue () ? "TRUE" : "FALSE"}.svg`)
1414
- .attr ("title", _("Toggle value."))
1415
- .appendTo (child);
1416
-
1417
- field .addFieldCallback (this .#fieldButtonSymbol, this .updateBoolean .bind (this, parent, node, field));
1418
- break;
1419
- }
1420
- case X3D .X3DConstants .SFColor:
1421
- case X3D .X3DConstants .SFColorRGBA:
1422
- {
1423
- $("<div></div>")
1424
- .addClass (["color-button", "field-button"])
1425
- .attr ("title", _("Open color picker."))
1426
- .css ("background-color", this .getColorFromField (node, field))
1427
- .appendTo (child);
1537
+ case X3D .X3DConstants .SFBool:
1538
+ {
1539
+ $("<img></img>")
1540
+ .addClass (["boolean-button", "field-button", "button",])
1541
+ .attr ("src", `../images/OutlineEditor/Values/${field .getValue () ? "TRUE" : "FALSE"}.svg`)
1542
+ .attr ("title", _("Toggle value."))
1543
+ .appendTo (child);
1428
1544
 
1429
- field .addFieldCallback (this .#fieldButtonSymbol, this .updateColor .bind (this, parent, node, field));
1430
- break;
1431
- }
1432
- case X3D .X3DConstants .SFTime:
1433
- {
1434
- $("<img></img>")
1435
- .addClass (["time-button", "field-button"])
1436
- .attr ("src", `../images/OutlineEditor/Values/Bell.svg`)
1437
- .attr ("title", _("Set current time."))
1438
- .appendTo (child);
1545
+ field .addFieldCallback (this .#fieldButtonSymbol, this .updateBoolean .bind (this, parent, node, field));
1546
+ break;
1547
+ }
1548
+ case X3D .X3DConstants .SFColor:
1549
+ case X3D .X3DConstants .SFColorRGBA:
1550
+ {
1551
+ $("<div></div>")
1552
+ .addClass (["color-button", "field-button", "button",])
1553
+ .attr ("title", _("Open color picker."))
1554
+ .css ("background-color", this .getColorFromField (node, field))
1555
+ .appendTo (child);
1439
1556
 
1440
- break;
1441
- }
1442
- case X3D .X3DConstants .MFString:
1443
- {
1444
- if (OutlineView .urlFields .has (field .getName ()) && field .isInitializable ())
1557
+ field .addFieldCallback (this .#fieldButtonSymbol, this .updateColor .bind (this, parent, node, field));
1558
+ break;
1559
+ }
1560
+ case X3D .X3DConstants .SFTime:
1445
1561
  {
1446
- $("<span></span>")
1447
- .addClass (["url-button", "field-button", "material-symbols-outlined"])
1448
- .attr ("title", _("Add URLs."))
1449
- .text ("add_circle")
1562
+ $("<img></img>")
1563
+ .addClass (["time-button", "field-button", "button",])
1564
+ .attr ("src", `../images/OutlineEditor/Values/Bell.svg`)
1565
+ .attr ("title", _("Set current time."))
1450
1566
  .appendTo (child);
1567
+
1568
+ break;
1451
1569
  }
1570
+ case X3D .X3DConstants .MFString:
1571
+ {
1572
+ if (OutlineView .urlFields .has (field .getName ()) && field .isInitializable ())
1573
+ {
1574
+ $("<span></span>")
1575
+ .addClass (["url-button", "field-button", "button", "material-symbols-outlined"])
1576
+ .attr ("title", _("Add URLs."))
1577
+ .text ("add_circle")
1578
+ .appendTo (child);
1579
+ }
1452
1580
 
1453
- break;
1581
+ break;
1582
+ }
1583
+ default:
1584
+ break;
1454
1585
  }
1455
- default:
1456
- break;
1457
1586
  }
1458
1587
 
1588
+
1459
1589
  // Access type
1460
1590
 
1461
1591
  const accessType = $("<div></div>")
@@ -1985,52 +2115,10 @@ module .exports = class OutlineView extends Interface
1985
2115
  .wrapInner ("<div class=\"item no-select\"/>")
1986
2116
  .find (".item") .append ("<div class=\"route-curves-wrapper\"><canvas class=\"route-curves\"></canvas></div>");
1987
2117
 
1988
- if (this .isEditable (parent))
1989
- {
1990
- child .find (".node:not([node-id=NULL]) > .item")
1991
- .attr ("draggable", "true")
1992
- .on ("dragstart", this .onDragStartNode .bind (this));
1993
- }
1994
-
1995
- child .find (".node .name, .node .icon")
1996
- .on ("click", this .selectNode .bind (this));
2118
+ // Connect actions.
1997
2119
 
1998
- child .find (".node .name")
1999
- .on ("mouseenter", this .updateNodeTitle .bind (this));
2000
-
2001
- child .find (".toggle-visibility")
2002
- .on ("click", this .toggleVisibility .bind (this));
2003
-
2004
- child .find (".toggle-tool")
2005
- .on ("click", this .toggleTool .bind (this));
2006
-
2007
- child .find (".activate-layer")
2008
- .on ("click", this .activateLayer .bind (this));
2009
-
2010
- child .find (".bind-node")
2011
- .on ("click", this .bindNode .bind (this));
2012
-
2013
- child .find (".reload-node")
2014
- .on ("click", this .reloadNode .bind (this));
2015
-
2016
- child .find (".show-preview")
2017
- .on ("click", this .showPreview .bind (this));
2018
-
2019
- child .find ("area.input-selector")
2020
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "input"))
2021
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "input"))
2022
- .on ("click", this .selectSingleConnector .bind (this, "input"));
2023
-
2024
- child .find ("area.output-selector")
2025
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "output"))
2026
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "output"))
2027
- .on ("click", this .selectSingleConnector .bind (this, "output"));
2028
-
2029
- child .find ("area.input-routes-selector")
2030
- .on ("click", this .selectSingleRoute .bind (this, "input"));
2031
-
2032
- child .find ("area.output-routes-selector")
2033
- .on ("click", this .selectSingleRoute .bind (this, "output"));
2120
+ this .connectNodeActions (parent, child);
2121
+ this .connectFieldActions (child);
2034
2122
 
2035
2123
  // Expand children.
2036
2124
 
@@ -2108,52 +2196,10 @@ module .exports = class OutlineView extends Interface
2108
2196
  .wrapInner ("<div class=\"item no-select\"/>")
2109
2197
  .find (".item") .append ("<div class=\"route-curves-wrapper\"><canvas class=\"route-curves\"></canvas></div>");
2110
2198
 
2111
- if (this .isEditable (parent))
2112
- {
2113
- child .find (".node:not([node-id=NULL]) > .item")
2114
- .attr ("draggable", "true")
2115
- .on ("dragstart", this .onDragStartNode .bind (this));
2116
- }
2117
-
2118
- child .find (".node .name, .node .icon")
2119
- .on ("click", this .selectNode .bind (this));
2199
+ // Connect actions.
2120
2200
 
2121
- child .find (".node .name")
2122
- .on ("mouseenter", this .updateNodeTitle .bind (this));
2123
-
2124
- child .find (".toggle-visibility")
2125
- .on ("click", this .toggleVisibility .bind (this));
2126
-
2127
- child .find (".toggle-tool")
2128
- .on ("click", this .toggleTool .bind (this));
2129
-
2130
- child .find (".activate-layer")
2131
- .on ("click", this .activateLayer .bind (this));
2132
-
2133
- child .find (".bind-node")
2134
- .on ("click", this .bindNode .bind (this))
2135
-
2136
- child .find (".reload-node")
2137
- .on ("click", this .reloadNode .bind (this));
2138
-
2139
- child .find (".show-preview")
2140
- .on ("click", this .showPreview .bind (this));
2141
-
2142
- child .find ("area.input-selector")
2143
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "input"))
2144
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "input"))
2145
- .on ("click", this .selectSingleConnector .bind (this, "input"));
2146
-
2147
- child .find ("area.output-selector")
2148
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "output"))
2149
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "output"))
2150
- .on ("click", this .selectSingleConnector .bind (this, "output"));
2151
-
2152
- child .find ("area.input-routes-selector")
2153
- .on ("click", this .selectSingleRoute .bind (this, "input"));
2154
-
2155
- child .find ("area.output-routes-selector")
2156
- .on ("click", this .selectSingleRoute .bind (this, "output"));
2201
+ this .connectNodeActions (parent, child);
2202
+ this .connectFieldActions (child);
2157
2203
 
2158
2204
  // Expand children.
2159
2205
 
@@ -2246,21 +2292,7 @@ module .exports = class OutlineView extends Interface
2246
2292
  .wrapInner ("<div class=\"item no-select\"/>")
2247
2293
  .find (".item") .append ("<div class=\"route-curves-wrapper\"><canvas class=\"route-curves\"></canvas></div>");
2248
2294
 
2249
- child .find ("area.input-selector")
2250
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "input"))
2251
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "input"))
2252
- .on ("click", this .selectSingleConnector .bind (this, "input"));
2253
-
2254
- child .find ("area.output-selector")
2255
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "output"))
2256
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "output"))
2257
- .on ("click", this .selectSingleConnector .bind (this, "output"));
2258
-
2259
- child .find ("area.input-routes-selector")
2260
- .on ("click", this .selectSingleRoute .bind (this, "input"));
2261
-
2262
- child .find ("area.output-routes-selector")
2263
- .on ("click", this .selectSingleRoute .bind (this, "output"));
2295
+ this .connectFieldActions (child);
2264
2296
 
2265
2297
  // Input
2266
2298
 
@@ -2368,21 +2400,7 @@ module .exports = class OutlineView extends Interface
2368
2400
  .wrapInner ("<div class=\"item no-select\"/>")
2369
2401
  .find (".item") .append ("<div class=\"route-curves-wrapper\"><canvas class=\"route-curves\"></canvas></div>")
2370
2402
 
2371
- child .find ("area.input-selector")
2372
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "input"))
2373
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "input"))
2374
- .on ("click", this .selectSingleConnector .bind (this, "input"))
2375
-
2376
- child .find ("area.output-selector")
2377
- .on ("mouseenter", this .hoverInSingleConnector .bind (this, "output"))
2378
- .on ("mouseleave", this .hoverOutSingleConnector .bind (this, "output"))
2379
- .on ("click", this .selectSingleConnector .bind (this, "output"))
2380
-
2381
- child .find ("area.input-routes-selector")
2382
- .on ("click", this .selectSingleRoute .bind (this, "input"))
2383
-
2384
- child .find ("area.output-routes-selector")
2385
- .on ("click", this .selectSingleRoute .bind (this, "output"))
2403
+ this .connectFieldActions (child);
2386
2404
 
2387
2405
  // Textarea
2388
2406
 
@@ -2904,6 +2922,14 @@ module .exports = class OutlineView extends Interface
2904
2922
  {
2905
2923
  switch (type)
2906
2924
  {
2925
+ case X3D .X3DConstants .X3DTimeDependentNode:
2926
+ {
2927
+ node ._enabled .removeFieldCallback (this .#updateNodePlaySymbol);
2928
+ node ._isActive .removeFieldCallback (this .#updateNodePlaySymbol);
2929
+ node ._isPaused .removeFieldCallback (this .#updateNodePlaySymbol);
2930
+ node ._loop .removeFieldCallback (this .#updateNodePlaySymbol);
2931
+ continue;
2932
+ }
2907
2933
  case X3D .X3DConstants .X3DBindableNode:
2908
2934
  {
2909
2935
  node ._isBound .removeFieldCallback (this .#updateNodeBoundSymbol);
@@ -2987,7 +3013,7 @@ module .exports = class OutlineView extends Interface
2987
3013
  const
2988
3014
  icon = $(event .currentTarget) ,
2989
3015
  item = icon .closest (".item", this .sceneGraph),
2990
- element = icon .closest (".node", this .sceneGraph),
3016
+ element = icon .closest (".node, .exported-node", this .sceneGraph),
2991
3017
  node = this .objects .get (parseInt (element .attr ("node-id"))),
2992
3018
  on = !!item .attr ("data-hasqtip");
2993
3019
 
@@ -3058,7 +3084,7 @@ module .exports = class OutlineView extends Interface
3058
3084
  {
3059
3085
  const
3060
3086
  target = $(event .target),
3061
- element = target .closest (".node", this .sceneGraph),
3087
+ element = target .closest (".node, .exported-node", this .sceneGraph),
3062
3088
  node = this .getNode (element),
3063
3089
  hidden = !node .isHidden ();
3064
3090
 
@@ -3067,7 +3093,7 @@ module .exports = class OutlineView extends Interface
3067
3093
 
3068
3094
  node .setHidden (hidden);
3069
3095
 
3070
- this .sceneGraph .find (`.node[node-id=${node .getId ()}]`)
3096
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
3071
3097
  .find ("> .item .toggle-visibility")
3072
3098
  .removeClass (["on", "off"])
3073
3099
  .addClass (hidden ? "off" : "on")
@@ -3078,7 +3104,7 @@ module .exports = class OutlineView extends Interface
3078
3104
  {
3079
3105
  const
3080
3106
  target = $(event .target),
3081
- element = target .closest (".node", this .sceneGraph),
3107
+ element = target .closest (".node, .exported-node", this .sceneGraph),
3082
3108
  node = this .getNode (element),
3083
3109
  tool = node .getTool ();
3084
3110
 
@@ -3097,7 +3123,7 @@ module .exports = class OutlineView extends Interface
3097
3123
 
3098
3124
  node .setUserData (_changing, true);
3099
3125
 
3100
- this .sceneGraph .find (`.node[node-id=${node .getId ()}] > .item .toggle-tool`)
3126
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}] > .item .toggle-tool, .exported-node[node-id=${node .getId ()}] > .item .toggle-tool`)
3101
3127
  .removeClass (["on", "off"])
3102
3128
  .addClass (tool ? "off" : "on");
3103
3129
  }
@@ -3108,7 +3134,7 @@ module .exports = class OutlineView extends Interface
3108
3134
  {
3109
3135
  const
3110
3136
  target = $(event .target),
3111
- element = target .closest (".node", this .sceneGraph),
3137
+ element = target .closest (".node, .exported-node", this .sceneGraph),
3112
3138
  node = this .getNode (element);
3113
3139
 
3114
3140
  event .preventDefault ();
@@ -3117,11 +3143,17 @@ module .exports = class OutlineView extends Interface
3117
3143
  node ._set_bind = true;
3118
3144
  }
3119
3145
 
3146
+ playNode (event) { }
3147
+
3148
+ stopNode (event) { }
3149
+
3150
+ loopNode (event) { }
3151
+
3120
3152
  reloadNode (event)
3121
3153
  {
3122
3154
  const
3123
3155
  target = $(event .target),
3124
- element = target .closest (".node, .externproto", this .sceneGraph),
3156
+ element = target .closest (".node, .exported-node, .externproto", this .sceneGraph),
3125
3157
  item = target .closest (".item"),
3126
3158
  node = this .getNode (element);
3127
3159
 
@@ -3156,7 +3188,7 @@ module .exports = class OutlineView extends Interface
3156
3188
 
3157
3189
  node .setHidden (node .getType () .includes (X3D .X3DConstants .X3DShapeNode));
3158
3190
 
3159
- this .sceneGraph .find (`.node[node-id=${node .getId ()}]`)
3191
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
3160
3192
  .find ("> .item .toggle-visibility")
3161
3193
  .removeClass (["on", "off"])
3162
3194
  .addClass (node .isHidden () ? "off" : "on")
@@ -3182,7 +3214,7 @@ module .exports = class OutlineView extends Interface
3182
3214
 
3183
3215
  node .setHidden (false);
3184
3216
 
3185
- this .sceneGraph .find (`.node[node-id=${node .getId ()}]`)
3217
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
3186
3218
  .find ("> .item .toggle-visibility")
3187
3219
  .removeClass ("off")
3188
3220
  .addClass ("on")
@@ -3206,7 +3238,7 @@ module .exports = class OutlineView extends Interface
3206
3238
 
3207
3239
  node .setHidden (false);
3208
3240
 
3209
- this .sceneGraph .find (`.node[node-id=${node .getId ()}]`)
3241
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
3210
3242
  .find ("> .item .toggle-visibility")
3211
3243
  .removeClass ("off")
3212
3244
  .addClass ("on")
@@ -3228,7 +3260,7 @@ module .exports = class OutlineView extends Interface
3228
3260
 
3229
3261
  node .setHidden (false);
3230
3262
 
3231
- this .sceneGraph .find (`.node[node-id=${node .getId ()}]`)
3263
+ this .sceneGraph .find (`.node[node-id=${node .getId ()}], .exported-node[node-id=${node .getId ()}]`)
3232
3264
  .find ("> .item .toggle-visibility")
3233
3265
  .removeClass ("off")
3234
3266
  .addClass ("on")
@@ -1035,17 +1035,6 @@ body.dark .ui-widget .library-list .component {
1035
1035
  width: min-content;
1036
1036
  }
1037
1037
 
1038
- .scene-graph .material-symbols-outlined {
1039
- font-size: var(--font-size);
1040
- position: relative;
1041
- top: 2.5px;
1042
- color: var(--system-blue);
1043
- }
1044
-
1045
- .scene-graph .material-symbols-outlined:hover {
1046
- color: var(--system-gray0);
1047
- }
1048
-
1049
1038
  .scene-graph .hidden {
1050
1039
  display: none;
1051
1040
  }
@@ -1343,6 +1332,18 @@ body.dark .ui-widget .library-list .component {
1343
1332
 
1344
1333
  .scene-graph .button {
1345
1334
  cursor: pointer;
1335
+ font-size: var(--font-size);
1336
+ position: relative;
1337
+ top: 2.5px;
1338
+ color: var(--system-blue);
1339
+ }
1340
+
1341
+ .scene-graph .button:hover {
1342
+ color: var(--system-gray0);
1343
+ }
1344
+
1345
+ .scene-graph .button.on:hover {
1346
+ color: var(--system-cyan);
1346
1347
  }
1347
1348
 
1348
1349
  .scene-graph .button.off {
@@ -1353,6 +1354,10 @@ body.dark .ui-widget .library-list .component {
1353
1354
  color: var(--system-gray0);
1354
1355
  }
1355
1356
 
1357
+ .scene-graph .field-button {
1358
+ top: 4px;
1359
+ }
1360
+
1356
1361
  /* Spectrum Color Picker */
1357
1362
 
1358
1363
  .sp-container {
@@ -1035,17 +1035,6 @@ body.dark .ui-widget .library-list .component {
1035
1035
  width: min-content;
1036
1036
  }
1037
1037
 
1038
- .scene-graph .material-symbols-outlined {
1039
- font-size: var(--font-size);
1040
- position: relative;
1041
- top: 2.5px;
1042
- color: var(--system-blue);
1043
- }
1044
-
1045
- .scene-graph .material-symbols-outlined:hover {
1046
- color: var(--system-gray0);
1047
- }
1048
-
1049
1038
  .scene-graph .hidden {
1050
1039
  display: none;
1051
1040
  }
@@ -1343,6 +1332,18 @@ body.dark .ui-widget .library-list .component {
1343
1332
 
1344
1333
  .scene-graph .button {
1345
1334
  cursor: pointer;
1335
+ font-size: var(--font-size);
1336
+ position: relative;
1337
+ top: 2.5px;
1338
+ color: var(--system-blue);
1339
+ }
1340
+
1341
+ .scene-graph .button:hover {
1342
+ color: var(--system-gray0);
1343
+ }
1344
+
1345
+ .scene-graph .button.on:hover {
1346
+ color: var(--system-cyan);
1346
1347
  }
1347
1348
 
1348
1349
  .scene-graph .button.off {
@@ -1353,6 +1354,10 @@ body.dark .ui-widget .library-list .component {
1353
1354
  color: var(--system-gray0);
1354
1355
  }
1355
1356
 
1357
+ .scene-graph .field-button {
1358
+ top: 4px;
1359
+ }
1360
+
1356
1361
  /* Spectrum Color Picker */
1357
1362
 
1358
1363
  .sp-container {