sunrize 1.0.11

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.
Files changed (200) hide show
  1. package/.vscode/launch.json +17 -0
  2. package/.vscode/settings.json +21 -0
  3. package/.vscode/tasks.json +25 -0
  4. package/LICENSE.md +627 -0
  5. package/Makefile +4 -0
  6. package/README.md +15 -0
  7. package/bin/sunrise.js +13 -0
  8. package/package.json +103 -0
  9. package/src/audio/Tink.mp3 +0 -0
  10. package/src/html/application.html +62 -0
  11. package/src/html/application.js +6 -0
  12. package/src/html/document.html +36 -0
  13. package/src/html/document.js +6 -0
  14. package/src/images/OutlineEditor/AccessTypes/AccessTypes.xcf +0 -0
  15. package/src/images/OutlineEditor/AccessTypes/initializeOnly.png +0 -0
  16. package/src/images/OutlineEditor/AccessTypes/inputOnly.0.png +0 -0
  17. package/src/images/OutlineEditor/AccessTypes/inputOnly.1.png +0 -0
  18. package/src/images/OutlineEditor/AccessTypes/inputOnly.2.png +0 -0
  19. package/src/images/OutlineEditor/AccessTypes/inputOnly.active.png +0 -0
  20. package/src/images/OutlineEditor/AccessTypes/inputOnly.png +0 -0
  21. package/src/images/OutlineEditor/AccessTypes/inputOutput.0.0.png +0 -0
  22. package/src/images/OutlineEditor/AccessTypes/inputOutput.0.1.png +0 -0
  23. package/src/images/OutlineEditor/AccessTypes/inputOutput.0.2.png +0 -0
  24. package/src/images/OutlineEditor/AccessTypes/inputOutput.1.0.png +0 -0
  25. package/src/images/OutlineEditor/AccessTypes/inputOutput.1.1.png +0 -0
  26. package/src/images/OutlineEditor/AccessTypes/inputOutput.1.2.png +0 -0
  27. package/src/images/OutlineEditor/AccessTypes/inputOutput.2.0.png +0 -0
  28. package/src/images/OutlineEditor/AccessTypes/inputOutput.2.1.png +0 -0
  29. package/src/images/OutlineEditor/AccessTypes/inputOutput.2.2.png +0 -0
  30. package/src/images/OutlineEditor/AccessTypes/inputOutput.png +0 -0
  31. package/src/images/OutlineEditor/AccessTypes/outputOnly.0.png +0 -0
  32. package/src/images/OutlineEditor/AccessTypes/outputOnly.1.png +0 -0
  33. package/src/images/OutlineEditor/AccessTypes/outputOnly.2.png +0 -0
  34. package/src/images/OutlineEditor/AccessTypes/outputOnly.active.png +0 -0
  35. package/src/images/OutlineEditor/AccessTypes/outputOnly.png +0 -0
  36. package/src/images/OutlineEditor/Fields/MFBool.svg +177 -0
  37. package/src/images/OutlineEditor/Fields/MFColor.svg +150 -0
  38. package/src/images/OutlineEditor/Fields/MFColorRGBA.svg +220 -0
  39. package/src/images/OutlineEditor/Fields/MFDouble.svg +162 -0
  40. package/src/images/OutlineEditor/Fields/MFFloat.svg +173 -0
  41. package/src/images/OutlineEditor/Fields/MFImage.svg +234 -0
  42. package/src/images/OutlineEditor/Fields/MFInt32.svg +168 -0
  43. package/src/images/OutlineEditor/Fields/MFMatrix3d.svg +219 -0
  44. package/src/images/OutlineEditor/Fields/MFMatrix3f.svg +212 -0
  45. package/src/images/OutlineEditor/Fields/MFMatrix4d.svg +212 -0
  46. package/src/images/OutlineEditor/Fields/MFMatrix4f.svg +205 -0
  47. package/src/images/OutlineEditor/Fields/MFNode.svg +180 -0
  48. package/src/images/OutlineEditor/Fields/MFRotation.svg +175 -0
  49. package/src/images/OutlineEditor/Fields/MFString.svg +152 -0
  50. package/src/images/OutlineEditor/Fields/MFTime.svg +164 -0
  51. package/src/images/OutlineEditor/Fields/MFVec2d.svg +172 -0
  52. package/src/images/OutlineEditor/Fields/MFVec2f.svg +165 -0
  53. package/src/images/OutlineEditor/Fields/MFVec3d.svg +179 -0
  54. package/src/images/OutlineEditor/Fields/MFVec3f.svg +173 -0
  55. package/src/images/OutlineEditor/Fields/MFVec4d.svg +166 -0
  56. package/src/images/OutlineEditor/Fields/MFVec4f.svg +159 -0
  57. package/src/images/OutlineEditor/Fields/SFBool.svg +137 -0
  58. package/src/images/OutlineEditor/Fields/SFColor.svg +143 -0
  59. package/src/images/OutlineEditor/Fields/SFColorRGBA.svg +213 -0
  60. package/src/images/OutlineEditor/Fields/SFDouble.svg +155 -0
  61. package/src/images/OutlineEditor/Fields/SFFloat.svg +166 -0
  62. package/src/images/OutlineEditor/Fields/SFImage.svg +234 -0
  63. package/src/images/OutlineEditor/Fields/SFInt32.svg +163 -0
  64. package/src/images/OutlineEditor/Fields/SFMatrix3d.svg +219 -0
  65. package/src/images/OutlineEditor/Fields/SFMatrix3f.svg +206 -0
  66. package/src/images/OutlineEditor/Fields/SFMatrix4d.svg +212 -0
  67. package/src/images/OutlineEditor/Fields/SFMatrix4f.svg +199 -0
  68. package/src/images/OutlineEditor/Fields/SFNode.svg +174 -0
  69. package/src/images/OutlineEditor/Fields/SFRotation.svg +169 -0
  70. package/src/images/OutlineEditor/Fields/SFString.svg +147 -0
  71. package/src/images/OutlineEditor/Fields/SFTime.svg +158 -0
  72. package/src/images/OutlineEditor/Fields/SFVec2d.svg +166 -0
  73. package/src/images/OutlineEditor/Fields/SFVec2f.svg +159 -0
  74. package/src/images/OutlineEditor/Fields/SFVec3d.svg +173 -0
  75. package/src/images/OutlineEditor/Fields/SFVec3f.svg +167 -0
  76. package/src/images/OutlineEditor/Fields/SFVec4d.svg +166 -0
  77. package/src/images/OutlineEditor/Fields/SFVec4f.svg +153 -0
  78. package/src/images/OutlineEditor/Fields/Unkown.svg +137 -0
  79. package/src/images/OutlineEditor/Fields/none.png +0 -0
  80. package/src/images/OutlineEditor/Node/ExportedNode.svg +212 -0
  81. package/src/images/OutlineEditor/Node/ExternProto.svg +237 -0
  82. package/src/images/OutlineEditor/Node/ImportedNode.svg +230 -0
  83. package/src/images/OutlineEditor/Node/NULL.svg +160 -0
  84. package/src/images/OutlineEditor/Node/Prototype.svg +263 -0
  85. package/src/images/OutlineEditor/Node/Route.svg +207 -0
  86. package/src/images/OutlineEditor/Node/SharedNode.svg +245 -0
  87. package/src/images/OutlineEditor/Node/X3DBaseNode.svg +167 -0
  88. package/src/images/OutlineEditor/Node/X3DExecutionContext.svg +207 -0
  89. package/src/images/OutlineEditor/Values/Bell.svg +79 -0
  90. package/src/images/OutlineEditor/Values/FALSE.svg +76 -0
  91. package/src/images/OutlineEditor/Values/TRUE.svg +76 -0
  92. package/src/images/icon.icns +0 -0
  93. package/src/images/icon.ico +0 -0
  94. package/src/images/icon.png +0 -0
  95. package/src/images/icons/arrow.svg +113 -0
  96. package/src/images/icons/hand.svg +129 -0
  97. package/src/main.js +9 -0
  98. package/src/sunrize/Application/ActionKeys.js +80 -0
  99. package/src/sunrize/Application/Application.js +660 -0
  100. package/src/sunrize/Application/DataStorage.js +179 -0
  101. package/src/sunrize/Application/Document.js +453 -0
  102. package/src/sunrize/Application/Footer.js +46 -0
  103. package/src/sunrize/Application/GetText.js +10 -0
  104. package/src/sunrize/Application/Interface.js +148 -0
  105. package/src/sunrize/Application/SecondaryToolbar.js +156 -0
  106. package/src/sunrize/Application/Selection.js +49 -0
  107. package/src/sunrize/Application/Sidebar.js +51 -0
  108. package/src/sunrize/Application/Tabs.js +329 -0
  109. package/src/sunrize/Application/Traverse.js +341 -0
  110. package/src/sunrize/Bits/Algorithm.js +5 -0
  111. package/src/sunrize/Bits/Beep.js +13 -0
  112. package/src/sunrize/Bits/Highlight.js +16 -0
  113. package/src/sunrize/Bits/Validate.js +59 -0
  114. package/src/sunrize/Controls/AddPrototypePopover.js +111 -0
  115. package/src/sunrize/Controls/Dialog.js +74 -0
  116. package/src/sunrize/Controls/EditUserDefinedFieldPopover.js +223 -0
  117. package/src/sunrize/Controls/Popover.js +32 -0
  118. package/src/sunrize/Controls/RenameNodeInput.js +94 -0
  119. package/src/sunrize/Controls/RenameNodePopover.js +40 -0
  120. package/src/sunrize/Controls/Splitter.js +102 -0
  121. package/src/sunrize/Controls/Tabs.js +96 -0
  122. package/src/sunrize/Editors/AnimationEditor.js +18 -0
  123. package/src/sunrize/Editors/BrowserSize.js +101 -0
  124. package/src/sunrize/Editors/Console.js +239 -0
  125. package/src/sunrize/Editors/FileManager.js +18 -0
  126. package/src/sunrize/Editors/Library.js +268 -0
  127. package/src/sunrize/Editors/NodeInspector.js +18 -0
  128. package/src/sunrize/Editors/NodeList.js +122 -0
  129. package/src/sunrize/Editors/OutlineEditor.js +2007 -0
  130. package/src/sunrize/Editors/OutlineRouteGraph.js +557 -0
  131. package/src/sunrize/Editors/OutlineView.js +3017 -0
  132. package/src/sunrize/Editors/Primitives.js +452 -0
  133. package/src/sunrize/Editors/SceneProperties.js +477 -0
  134. package/src/sunrize/Editors/ScriptEditor.js +594 -0
  135. package/src/sunrize/Editors/Units.js +146 -0
  136. package/src/sunrize/Fields/MFStringTextArea.js +32 -0
  137. package/src/sunrize/Fields/SFStringInput.js +32 -0
  138. package/src/sunrize/Fields.js +2 -0
  139. package/src/sunrize/Tools/CADGeometry/CADAssemblyTool.js +20 -0
  140. package/src/sunrize/Tools/CADGeometry/CADFaceTool.js +20 -0
  141. package/src/sunrize/Tools/CADGeometry/CADLayerTool.js +20 -0
  142. package/src/sunrize/Tools/CADGeometry/CADPartTool.js +20 -0
  143. package/src/sunrize/Tools/CADGeometry.js +6 -0
  144. package/src/sunrize/Tools/Core/ToolColors.js +33 -0
  145. package/src/sunrize/Tools/Core/X3DNodeTool.js +132 -0
  146. package/src/sunrize/Tools/Core/X3DPrototypeInstanceTool.js +31 -0
  147. package/src/sunrize/Tools/Core.js +3 -0
  148. package/src/sunrize/Tools/Geospatial/GeoLODTool.js +40 -0
  149. package/src/sunrize/Tools/Geospatial/GeoLocationTool.js +20 -0
  150. package/src/sunrize/Tools/Geospatial/GeoTransformTool.js +20 -0
  151. package/src/sunrize/Tools/Geospatial.js +5 -0
  152. package/src/sunrize/Tools/Grouping/GroupTool.js +20 -0
  153. package/src/sunrize/Tools/Grouping/StaticGroupTool.js +20 -0
  154. package/src/sunrize/Tools/Grouping/SwitchTool.js +20 -0
  155. package/src/sunrize/Tools/Grouping/TransformTool.js +20 -0
  156. package/src/sunrize/Tools/Grouping/TransformTool.x3d +37 -0
  157. package/src/sunrize/Tools/Grouping/Vector.x3d +98 -0
  158. package/src/sunrize/Tools/Grouping/X3DBoundedObjectTool.js +44 -0
  159. package/src/sunrize/Tools/Grouping/X3DBoundedObjectTool.x3d +330 -0
  160. package/src/sunrize/Tools/Grouping/X3DTransformNodeTool.js +44 -0
  161. package/src/sunrize/Tools/Grouping.js +6 -0
  162. package/src/sunrize/Tools/HAnim/HAnimHumanoidTool.js +20 -0
  163. package/src/sunrize/Tools/HAnim/HAnimJointTool.js +20 -0
  164. package/src/sunrize/Tools/HAnim/HAnimSegmentTool.js +20 -0
  165. package/src/sunrize/Tools/HAnim/HAnimSiteTool.js +20 -0
  166. package/src/sunrize/Tools/HAnim.js +6 -0
  167. package/src/sunrize/Tools/Layering/LayerTool.js +16 -0
  168. package/src/sunrize/Tools/Layering/X3DLayerNodeTool.js +22 -0
  169. package/src/sunrize/Tools/Layering.js +3 -0
  170. package/src/sunrize/Tools/Layout/LayoutGroupTool.js +78 -0
  171. package/src/sunrize/Tools/Layout/LayoutLayerTool.js +16 -0
  172. package/src/sunrize/Tools/Layout/ScreenGroupTool.js +20 -0
  173. package/src/sunrize/Tools/Layout.js +5 -0
  174. package/src/sunrize/Tools/Navigation/BillboardTool.js +37 -0
  175. package/src/sunrize/Tools/Navigation/CollisionTool.js +37 -0
  176. package/src/sunrize/Tools/Navigation/LODTool.js +37 -0
  177. package/src/sunrize/Tools/Navigation.js +5 -0
  178. package/src/sunrize/Tools/Networking/AnchorTool.js +20 -0
  179. package/src/sunrize/Tools/Networking/InlineTool.js +20 -0
  180. package/src/sunrize/Tools/Networking.js +4 -0
  181. package/src/sunrize/Tools/ParticleSystems/ParticleSystemTool.js +20 -0
  182. package/src/sunrize/Tools/ParticleSystems.js +3 -0
  183. package/src/sunrize/Tools/Picking/PickableGroupTool.js +20 -0
  184. package/src/sunrize/Tools/Picking.js +3 -0
  185. package/src/sunrize/Tools/RigidBodyPhysics/CollidableOffsetTool.js +37 -0
  186. package/src/sunrize/Tools/RigidBodyPhysics/CollidableShapeTool.js +37 -0
  187. package/src/sunrize/Tools/RigidBodyPhysics.js +4 -0
  188. package/src/sunrize/Tools/Shaders/ToolShader.x3d +86 -0
  189. package/src/sunrize/Tools/Shape/ShapeTool.js +20 -0
  190. package/src/sunrize/Tools/Shape.js +3 -0
  191. package/src/sunrize/Tools/VolumeRendering/IsoSurfaceVolumeDataTool.js +16 -0
  192. package/src/sunrize/Tools/VolumeRendering/SegmentedVolumeDataTool.js +16 -0
  193. package/src/sunrize/Tools/VolumeRendering/VolumeDataTool.js +16 -0
  194. package/src/sunrize/Tools/VolumeRendering/X3DVolumeDataNodeTool.js +12 -0
  195. package/src/sunrize/Tools/VolumeRendering.js +5 -0
  196. package/src/sunrize/Tools.js +15 -0
  197. package/src/sunrize/Undo/Editor.js +2146 -0
  198. package/src/sunrize/Undo/UndoManager.js +204 -0
  199. package/src/sunrize/X3D.js +8 -0
  200. package/src/themes/dark.css +1291 -0
@@ -0,0 +1,2007 @@
1
+ "use strict"
2
+
3
+ const
4
+ $ = require ("jquery"),
5
+ electron = require ("electron"),
6
+ X3D = require ("../X3D"),
7
+ OutlineRouteGraph = require ("./OutlineRouteGraph"),
8
+ Editor = require ("../Undo/Editor"),
9
+ UndoManager = require ("../Undo/UndoManager"),
10
+ _ = require ("../Application/GetText")
11
+
12
+ module .exports = class OutlineEditor extends OutlineRouteGraph
13
+ {
14
+ constructor (element)
15
+ {
16
+ super (element)
17
+
18
+ element .on ("contextmenu", (event) => this .showContextMenu (event))
19
+
20
+ electron .ipcRenderer .on ("outline-editor-menu", (event, key, ...args) => this [key] (...args))
21
+ electron .ipcRenderer .on ("remove-empty-groups", (event) => this .removeEmptyGroups ())
22
+
23
+ this .setup ()
24
+ }
25
+
26
+ configure ()
27
+ {
28
+ super .configure ()
29
+
30
+ this .X3DTransformMatrix3DNode = new Set ([
31
+ X3D .X3DConstants .Billboard,
32
+ X3D .X3DConstants .CADPart,
33
+ X3D .X3DConstants .GeoLocation,
34
+ X3D .X3DConstants .GeoTransform,
35
+ X3D .X3DConstants .HAnimJoint,
36
+ X3D .X3DConstants .HAnimSite,
37
+ X3D .X3DConstants .LayoutGroup,
38
+ X3D .X3DConstants .ScreenGroup,
39
+ X3D .X3DConstants .Transform,
40
+ X3D .X3DConstants .X3DNBodyCollidableNode,
41
+ ])
42
+ }
43
+
44
+ showContextMenu (event)
45
+ {
46
+ const element = $(document .elementFromPoint (event .pageX, event .pageY))
47
+ .closest ("li, #outline-editor", this .outlineEditor)
48
+
49
+ if (!this .isEditable (element))
50
+ return
51
+
52
+ if (element .is (".externproto, .proto, .proto-scene, .node, .field"))
53
+ this .selectPrimaryElement (element)
54
+
55
+ const
56
+ executionContextElement = element .closest (".scene"),
57
+ executionContext = this .getNode (executionContextElement) || this .executionContext,
58
+ node = this .getNode (element)
59
+
60
+ if (element .is (".field"))
61
+ {
62
+ const
63
+ outerNode = executionContext .getOuterNode (),
64
+ field = this .getField (element),
65
+ userDefined = node .getUserDefinedFields () .has (field .getName ())
66
+
67
+ const
68
+ addReferences = [ ],
69
+ removeReferences = [ ]
70
+
71
+ if (outerNode instanceof X3D .X3DProtoDeclaration && node .getType () .includes (X3D .X3DConstants .X3DNode))
72
+ {
73
+ const
74
+ proto = outerNode,
75
+ references = [ ]
76
+
77
+ for (const protoField of proto .getFields ())
78
+ {
79
+ if (protoField .getType () === field .getType () &&
80
+ protoField .isReference (field .getAccessType ()))
81
+ {
82
+ references .push (protoField)
83
+ }
84
+ }
85
+
86
+ if (references .length)
87
+ {
88
+ // Make menus.
89
+
90
+ for (const reference of references)
91
+ {
92
+ const menuItem = {
93
+ label: reference .getName (),
94
+ args: [proto .getId (), reference .getId (), node .getId (), field .getId ()],
95
+ }
96
+
97
+ if (field .getReferences () .has (reference))
98
+ {
99
+ menuItem .args .unshift ("removeReference")
100
+ removeReferences .push (menuItem)
101
+ }
102
+ else
103
+ {
104
+ menuItem .args .unshift ("addReference")
105
+ addReferences .push (menuItem)
106
+ }
107
+ }
108
+ }
109
+ }
110
+
111
+ var menu = [
112
+ {
113
+ label: "Add Field...",
114
+ visible: node .canUserDefinedFields (),
115
+ args: ["addUserDefinedField", element .attr ("id"), executionContext .getId (), node .getId ()],
116
+ },
117
+ {
118
+ label: "Edit Field...",
119
+ visible: node .canUserDefinedFields (),
120
+ enabled: userDefined,
121
+ args: ["editUserDefinedField", element .attr ("id"), executionContext .getId (), node .getId (), field .getId ()],
122
+ },
123
+ {
124
+ label: "Delete Field",
125
+ visible: node .canUserDefinedFields (),
126
+ enabled: userDefined,
127
+ args: ["deleteUserDefinedField", element .attr ("id"), executionContext .getId (), node .getId (), field .getId ()],
128
+ },
129
+ { type: "separator" },
130
+ {
131
+ label: "Add Reference To",
132
+ submenu: addReferences,
133
+ visible: !! addReferences .length,
134
+ },
135
+ {
136
+ label: "Remove Reference To",
137
+ submenu: removeReferences,
138
+ visible: !! removeReferences .length,
139
+ },
140
+ { type: "separator" },
141
+ {
142
+ label: "Reset to Default Value",
143
+ visible: field .getAccessType () !== X3D .X3DConstants .outputOnly,
144
+ args: ["resetToDefaultValue", element .attr ("id"), executionContext .getId (), node .getId (), field .getId ()],
145
+ },
146
+ {
147
+ label: "Add Event",
148
+ visible: field .getAccessType () !== X3D .X3DConstants .outputOnly,
149
+ args: ["addEvent", element .attr ("id"), node .getId (), field .getId ()],
150
+ },
151
+ ]
152
+ }
153
+
154
+ else if (element .is (".node"))
155
+ {
156
+ const
157
+ parentFieldElement = element .closest (".field, .scene", this .sceneGraph),
158
+ parentNodeElement = parentFieldElement .closest (".node, .proto, .scene", this .sceneGraph)
159
+
160
+ var menu = [
161
+ {
162
+ label: "Rename Node...",
163
+ args: ["renameNode", element .attr ("id"), executionContext .getId (), node .getId ()],
164
+ },
165
+ {
166
+ label: "Add Node...",
167
+ args: ["openLibrary", element .attr ("id"), executionContext .getId ()],
168
+ },
169
+ { type: "separator" },
170
+ {
171
+ label: "Cut",
172
+ args: ["cutNodes"],
173
+ },
174
+ {
175
+ label: "Copy",
176
+ args: ["copyNodes"],
177
+ },
178
+ {
179
+ label: "Paste",
180
+ args: ["pasteNodes", element .attr ("id"), executionContext .getId ()],
181
+ },
182
+ {
183
+ label: "Delete",
184
+ args: ["deleteNodes"],
185
+ },
186
+ {
187
+ label: "Unlink Clone",
188
+ enabled: node .getCloneCount () > 1,
189
+ args: ["unlinkClone", element .attr ("id"), executionContext .getId (), node .getId ()],
190
+ },
191
+ { type: "separator" },
192
+ {
193
+ label: "Add Field...",
194
+ visible: node .canUserDefinedFields (),
195
+ args: ["addUserDefinedField", element .attr ("id"), executionContext .getId (), node .getId ()],
196
+ },
197
+ { type: "separator" },
198
+ {
199
+ label: "Add Parent Group",
200
+ submenu: [
201
+ {
202
+ label: "Transform",
203
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Transform", "children"],
204
+ },
205
+ {
206
+ label: "Group",
207
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Group", "children"],
208
+ },
209
+ {
210
+ label: "StaticGroup",
211
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "StaticGroup", "children"],
212
+ },
213
+ {
214
+ label: "Switch",
215
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Switch", "children"],
216
+ },
217
+ { type: "separator" },
218
+ {
219
+ label: "Billboard",
220
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Billboard", "children"],
221
+ },
222
+ {
223
+ label: "Collision",
224
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Collision", "children"],
225
+ },
226
+ {
227
+ label: "LOD",
228
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "LOD", "children"],
229
+ },
230
+ {
231
+ label: "ViewpointGroup",
232
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "ViewpointGroup", "children"],
233
+ },
234
+ { type: "separator" },
235
+ {
236
+ label: "Anchor",
237
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Anchor", "children"],
238
+ },
239
+ { type: "separator" },
240
+ {
241
+ label: "LayoutLayer",
242
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "LayoutLayer", "children"],
243
+ },
244
+ {
245
+ label: "ScreenGroup",
246
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "ScreenGroup", "children"],
247
+ },
248
+ { type: "separator" },
249
+ {
250
+ label: "GeoTransform",
251
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "GeoTransform", "children"],
252
+ },
253
+ {
254
+ label: "GeoLocation",
255
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "GeoLocation", "children"],
256
+ },
257
+ { type: "separator" },
258
+ {
259
+ label: "CADAssembly",
260
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CADAssembly", "children"],
261
+ },
262
+ {
263
+ label: "CADFace",
264
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CADFace", "shape"],
265
+ },
266
+ {
267
+ label: "CADLayer",
268
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CADLayer", "children"],
269
+ },
270
+ {
271
+ label: "CADPart",
272
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CADPart", "children"],
273
+ },
274
+ { type: "separator" },
275
+ {
276
+ label: "LayerSet",
277
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "LayerSet", "layers"],
278
+ },
279
+ {
280
+ label: "Layer",
281
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Layer", "children"],
282
+ },
283
+ {
284
+ label: "Viewport",
285
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "Viewport", "children"],
286
+ },
287
+ { type: "separator" },
288
+ {
289
+ label: "PickableGroup",
290
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "PickableGroup", "children"],
291
+ },
292
+ { type: "separator" },
293
+ {
294
+ label: "CollidableShape",
295
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CollidableShape", "shape"],
296
+ },
297
+ {
298
+ label: "CollidableOffset",
299
+ args:[ "addParentGroup", element .attr ("id"), executionContext .getId (), node .getId (), "CollidableOffset", "collidable"],
300
+ },
301
+ ],
302
+ },
303
+ {
304
+ label: "Remove Parent",
305
+ enabled: parentNodeElement .hasClass ("node"),
306
+ args: ["removeParent", element .attr ("id"), executionContext .getId (), node .getId ()],
307
+ },
308
+ ]
309
+ }
310
+
311
+ else if (element .is (".externproto, .proto"))
312
+ {
313
+ const
314
+ protoNode = node,
315
+ used = Editor .isProtoNodeUsed (executionContext, protoNode),
316
+ available = Editor .getNextAvailableProtoNode (executionContext, protoNode),
317
+ proto = protoNode .isExternProto && executionContext .protos .get (protoNode .getName ())
318
+
319
+ var menu = [
320
+ {
321
+ label: "Add Prototype...",
322
+ args: ["addPrototype", element .attr ("id"), executionContext .getId ()],
323
+ },
324
+ {
325
+ label: "Rename Prototype...",
326
+ args: ["renamePrototype", element .attr ("id"), executionContext .getId (), protoNode .getId ()],
327
+ },
328
+ {
329
+ label: "Delete Prototype",
330
+ enabled: !used || !!available,
331
+ args: ["deletePrototype", element .attr ("id"), executionContext .getId (), protoNode .getId (), used, available ? available .getId () : undefined],
332
+ },
333
+ { type: "separator" },
334
+ {
335
+ label: "Add Field...",
336
+ visible: element .is (".proto"),
337
+ args: ["addUserDefinedField", element .attr ("id"), executionContext .getId (), protoNode .getId ()],
338
+ },
339
+ { type: "separator" },
340
+ {
341
+ label: "Load Now",
342
+ visible: element .is (".externproto"),
343
+ args: ["loadNow", element .attr ("id"), protoNode .getId ()],
344
+ },
345
+ {
346
+ label: "Turn Into Extern Prototype...",
347
+ visible: element .is (".proto"),
348
+ args: ["turnIntoExternPrototype", element .attr ("id"), executionContext .getId (), protoNode .getId ()],
349
+ },
350
+ {
351
+ label: "Turn Into Prototype",
352
+ visible: element .is (".externproto"),
353
+ enabled: element .is (".externproto") && protoNode .checkLoadState () === X3D .X3DConstants .COMPLETE_STATE,
354
+ args: ["turnIntoPrototype", element .attr ("id"), executionContext .getId (), protoNode .getId ()],
355
+ },
356
+ { type: "separator" },
357
+ {
358
+ label: "Add Instance",
359
+ enabled: !proto,
360
+ args: ["addInstance", element .attr ("id"), executionContext .getId (), protoNode .getId ()],
361
+ },
362
+ ]
363
+ }
364
+ else if (element .is ("#outline-editor, .proto-scene, .description.externprotos, .description.protos, .description.root-nodes, .description.empty-scene"))
365
+ {
366
+ var menu = [
367
+ {
368
+ label: "Add Node...",
369
+ args: ["openLibrary", element .attr ("id"), executionContext .getId ()],
370
+ },
371
+ {
372
+ label: "Add Prototype...",
373
+ args: ["addPrototype", element .attr ("id"), executionContext .getId ()],
374
+ },
375
+ {
376
+ label: "Paste",
377
+ args: ["pasteNodes", element .attr ("id"), executionContext .getId ()],
378
+ },
379
+ ]
380
+ }
381
+ else
382
+ {
383
+ return
384
+ }
385
+
386
+ electron .ipcRenderer .send ("context-menu", "outline-editor-menu", menu)
387
+ }
388
+
389
+ addUserDefinedField (id, executionContextId, nodeId)
390
+ {
391
+ require ("../Controls/EditUserDefinedFieldPopover")
392
+
393
+ const
394
+ element = $(`#${id}`),
395
+ executionContext = this .objects .get (executionContextId),
396
+ node = this .objects .get (nodeId)
397
+
398
+ element .find ("> .item") .editUserDefinedFieldPopover (executionContext, node)
399
+ }
400
+
401
+ editUserDefinedField (id, executionContextId, nodeId, fieldId)
402
+ {
403
+ require ("../Controls/EditUserDefinedFieldPopover")
404
+
405
+ const
406
+ element = $(`#${id}`),
407
+ node = this .objects .get (nodeId),
408
+ executionContext = this .objects .get (executionContextId),
409
+ field = this .objects .get (fieldId)
410
+
411
+ element .find ("> .item") .editUserDefinedFieldPopover (executionContext, node, field)
412
+ }
413
+
414
+ deleteUserDefinedField (id, executionContextId, nodeId, fieldId)
415
+ {
416
+ const
417
+ executionContext = this .objects .get (executionContextId),
418
+ node = this .objects .get (nodeId),
419
+ field = this .objects .get (fieldId)
420
+
421
+ Editor .removeUserDefinedField (executionContext, node, field)
422
+ }
423
+
424
+ addReference (protoId, protoFieldId, nodeId, fieldId)
425
+ {
426
+ const
427
+ proto = this .objects .get (protoId),
428
+ protoField = this .objects .get (protoFieldId),
429
+ node = this .objects .get (nodeId),
430
+ field = this .objects .get (fieldId)
431
+
432
+ Editor .addReference (proto, protoField, node, field)
433
+ }
434
+
435
+ removeReference (protoId, protoFieldId, nodeId, fieldId)
436
+ {
437
+ const
438
+ proto = this .objects .get (protoId),
439
+ protoField = this .objects .get (protoFieldId),
440
+ node = this .objects .get (nodeId),
441
+ field = this .objects .get (fieldId)
442
+
443
+ Editor .removeReference (proto, protoField, node, field)
444
+ }
445
+
446
+ resetToDefaultValue (id, executionContextId, nodeId, fieldId)
447
+ {
448
+ const
449
+ executionContext = this .objects .get (executionContextId),
450
+ node = this .objects .get (nodeId),
451
+ field = this .objects .get (fieldId),
452
+ fieldDefinition = node .getFieldDefinitions () .get (field .getName ())
453
+
454
+ if (node .canUserDefinedFields () && node .getUserDefinedFields () .has (field .getName ()))
455
+ Editor .setFieldValue (executionContext, node, field, field .create ())
456
+ else
457
+ Editor .setFieldValue (executionContext, node, field, fieldDefinition .value)
458
+ }
459
+
460
+ addEvent (id, nodeId, fieldId)
461
+ {
462
+ const field = this .objects .get (fieldId)
463
+
464
+ field .addEvent ()
465
+ }
466
+
467
+ renameNode (id, executionContextId, nodeId)
468
+ {
469
+ require ("../Controls/RenameNodePopover")
470
+
471
+ const
472
+ element = $(`#${id}`),
473
+ node = this .objects .get (nodeId)
474
+
475
+ element .find ("> .item .node-name") .renameNodePopover (node)
476
+ }
477
+
478
+ openLibrary (id, executionContextId)
479
+ {
480
+ const executionContext = this .objects .get (executionContextId)
481
+
482
+ require ("./Library") .open (executionContext)
483
+ }
484
+
485
+ cutNodes ()
486
+ {
487
+ UndoManager .shared .beginUndo (_ ("Cut Nodes"))
488
+
489
+ this .copyNodes ()
490
+ this .deleteNodes ()
491
+
492
+ UndoManager .shared .endUndo ()
493
+ }
494
+
495
+ copyNodes ()
496
+ {
497
+ const
498
+ primary = $(".node.primary, .proto.primary, .externproto.primary"),
499
+ selected = this .sceneGraph .find (".node.selected"),
500
+ selection = selected .filter (primary) .length ? selected : primary,
501
+ ids = selection .map (function () { return this .id }) .get (),
502
+ elements = ids .map (id => $(`#${id}`)),
503
+ nodes = elements .map (element => this .getNode (element)),
504
+ undoManager = new UndoManager ()
505
+
506
+ undoManager .beginUndo ()
507
+
508
+ for (const element of elements)
509
+ {
510
+ const node = this .getNode (element)
511
+
512
+ if (!node .getType () .includes (X3D .X3DConstants .X3DTransformNode))
513
+ continue
514
+
515
+ Editor .setMatrixWithCenter (node, this .getModelMatrix (element), undefined, undoManager)
516
+ }
517
+
518
+ undoManager .endUndo ()
519
+
520
+ const x3dSyntax = Editor .exportVRML (this .executionContext, nodes)
521
+
522
+ //console .log (x3dSyntax)
523
+
524
+ navigator .clipboard .writeText (x3dSyntax)
525
+
526
+ undoManager .undo ()
527
+ }
528
+
529
+ async pasteNodes (id, executionContextId)
530
+ {
531
+ try
532
+ {
533
+ const
534
+ primary = $(".node.primary"),
535
+ executionContextElement = primary .closest (".scene", this .sceneGraph),
536
+ executionContext = executionContextId ? this .objects .get (executionContextId) : this .getNode (executionContextElement) || this .executionContext,
537
+ x3dSyntax = await navigator .clipboard .readText ()
538
+
539
+ UndoManager .shared .beginUndo (_ ("Paste Nodes"))
540
+ Editor .importX3D (executionContext, x3dSyntax)
541
+ UndoManager .shared .endUndo ()
542
+ }
543
+ catch (error)
544
+ {
545
+ // Catch "Document is not focused." from navigator.clipboard.readText.
546
+ console .error (`Paste failed: ${error .message}`)
547
+ }
548
+ }
549
+
550
+ deleteNodes ()
551
+ {
552
+ const
553
+ primary = $(".node.primary"),
554
+ selected = this .sceneGraph .find (".node.selected"),
555
+ selection = selected .filter (primary) .length ? selected : primary,
556
+ ids = selection .map (function () { return this .id }) .get ()
557
+
558
+ if (ids .length > 1)
559
+ UndoManager .shared .beginUndo (_ ("Delete %s Nodes"), ids .length)
560
+ else if (ids .length === 1)
561
+ UndoManager .shared .beginUndo (_ ("Delete Node %s"), this .getNode ($(`#${ids [0]}`)) .getTypeName ())
562
+ else
563
+ return
564
+
565
+ const nodes = [ ]
566
+
567
+ for (const id of ids .reverse ())
568
+ {
569
+ const
570
+ element = $(`#${id}`),
571
+ node = this .getNode (element),
572
+ parentFieldElement = element .closest (".field, .scene", this .sceneGraph),
573
+ parentNodeElement = parentFieldElement .closest (".node, .scene, .proto", this .sceneGraph),
574
+ parentNode = this .getNode (parentNodeElement),
575
+ parentField = parentFieldElement .hasClass ("scene") ? parentNode .rootNodes : this .getField (parentFieldElement),
576
+ index = parseInt (element .attr ("index")),
577
+ executionContextElement = element .closest (".scene", this .sceneGraph),
578
+ executionContext = this .getNode (executionContextElement)
579
+
580
+ switch (parentField .getType ())
581
+ {
582
+ case X3D .X3DConstants .SFNode:
583
+ Editor .setFieldValue (executionContext, parentNode, parentField, null)
584
+ break
585
+ case X3D .X3DConstants .MFNode:
586
+ Editor .removeValueFromArray (executionContext, parentNode, parentField, index)
587
+ break
588
+ }
589
+
590
+ nodes .push (node)
591
+ }
592
+
593
+ Editor .removeNodesFromExecutionContextIfNecessary (this .executionContext, nodes)
594
+
595
+ UndoManager .shared .endUndo ()
596
+ }
597
+
598
+ unlinkClone (id, executionContextId, nodeId)
599
+ {
600
+ const
601
+ X3DBaseNode = X3D .require ("x_ite/Base/X3DBaseNode"),
602
+ element = $(`#${id}`),
603
+ executionContext = this .objects .get (executionContextId),
604
+ parentFieldElement = element .closest (".field, .scene", this .sceneGraph),
605
+ parentNodeElement = parentFieldElement .closest (".node, .proto, .scene", this .sceneGraph),
606
+ parentNode = this .getNode (parentNodeElement),
607
+ parentField = parentFieldElement .hasClass ("scene") ? parentNode .rootNodes : this .getField (parentFieldElement),
608
+ node = this .objects .get (nodeId),
609
+ copy = X3DBaseNode .prototype .copy .call (node, executionContext),
610
+ index = parseInt (element .attr ("index"))
611
+
612
+ UndoManager .shared .beginUndo (_ ("Unlink Clone"))
613
+
614
+ switch (parentField .getType ())
615
+ {
616
+ case X3D .X3DConstants .SFNode:
617
+ {
618
+ Editor .setFieldValue (executionContext, parentNode, parentField, copy)
619
+ break
620
+ }
621
+ case X3D .X3DConstants .MFNode:
622
+ {
623
+ Editor .removeValueFromArray (executionContext, parentNode, parentField, index)
624
+ Editor .insertValueIntoArray (executionContext, parentNode, parentField, index, copy)
625
+ break
626
+ }
627
+ }
628
+
629
+ UndoManager .shared .endUndo ()
630
+ }
631
+
632
+ addParentGroup (id, executionContextId, nodeId, typeName, fieldName)
633
+ {
634
+ const
635
+ element = $(`#${id}`),
636
+ executionContext = this .objects .get (executionContextId),
637
+ childNode = this .objects .get (nodeId),
638
+ childIndex = parseInt (element .attr ("index")),
639
+ parentFieldElement = element .closest (".field, .scene", this .sceneGraph),
640
+ parentNodeElement = parentFieldElement .closest (".node, .proto, .scene", this .sceneGraph),
641
+ parentNode = this .getNode (parentNodeElement),
642
+ parentField = parentFieldElement .hasClass ("scene") ? parentNode .rootNodes : this .getField (parentFieldElement),
643
+ node = executionContext .createNode (typeName) .getValue (),
644
+ field = node .getField (fieldName)
645
+
646
+ UndoManager .shared .beginUndo (_ ("Add Parent Group %s to Node %s"), typeName, childNode .getTypeName ())
647
+
648
+ if (field instanceof X3D .X3DArrayField)
649
+ Editor .insertValueIntoArray (executionContext, node, field, 0, childNode)
650
+ else
651
+ Editor .setFieldValue (executionContext, node, field, childNode)
652
+
653
+ if (parentField instanceof X3D .X3DArrayField)
654
+ {
655
+ Editor .insertValueIntoArray (executionContext, parentNode, parentField, childIndex, node)
656
+ Editor .removeValueFromArray (executionContext, parentNode, parentField, childIndex + 1)
657
+ }
658
+ else
659
+ {
660
+ Editor .setFieldValue (executionContext, parentNode, parentField, node)
661
+ }
662
+
663
+ UndoManager .shared .endUndo ()
664
+
665
+ requestAnimationFrame (() => this .expandTo (node))
666
+ }
667
+
668
+ removeParent (id, executionContextId, nodeId)
669
+ {
670
+ const
671
+ element = $(`#${id}`),
672
+ executionContext = this .objects .get (executionContextId),
673
+ childNode = this .objects .get (nodeId),
674
+ parentFieldElement = element .closest (".field, .scene", this .sceneGraph),
675
+ parentNodeElement = parentFieldElement .closest (".node, .proto, .scene", this .sceneGraph),
676
+ parentNode = this .getNode (parentNodeElement),
677
+ parentField = parentFieldElement .hasClass ("scene") ? parentNode .rootNodes : this .getField (parentFieldElement),
678
+ parentIndex = parseInt (parentNodeElement .attr ("index")),
679
+ parent2FieldElement = parentNodeElement .closest (".field, .scene", this .sceneGraph),
680
+ parent2NodeElement = parent2FieldElement .closest (".node, .proto, .scene", this .sceneGraph),
681
+ parent2Node = this .getNode (parent2NodeElement),
682
+ parent2Field = parent2FieldElement .hasClass ("scene") ? parent2Node .rootNodes : this .getField (parent2FieldElement)
683
+
684
+ UndoManager .shared .beginUndo (_ ("Remove Parent of %s"), childNode .getTypeName ())
685
+
686
+ if (parent2Field instanceof X3D .X3DArrayField)
687
+ {
688
+ if (parentField instanceof X3D .X3DArrayField)
689
+ {
690
+ const length = parentField .length
691
+
692
+ for (let i = 0; i < length; ++ i)
693
+ Editor .insertValueIntoArray (executionContext, parent2Node, parent2Field, parentIndex + i, parentField [i])
694
+
695
+ Editor .removeValueFromArray (executionContext, parent2Node, parent2Field, parentIndex + length)
696
+ }
697
+ else
698
+ {
699
+ Editor .insertValueIntoArray (executionContext, parent2Node, parent2Field, parentIndex, childNode)
700
+ Editor .removeValueFromArray (executionContext, parent2Node, parent2Field, parentIndex + 1)
701
+ }
702
+ }
703
+ else
704
+ {
705
+ Editor .setFieldValue (executionContext, parent2Node, parent2Field, childNode)
706
+ }
707
+
708
+ if (parentField instanceof X3D .X3DArrayField)
709
+ Editor .setFieldValue (executionContext, parentNode, parentField, new X3D .MFNode ())
710
+ else
711
+ Editor .setFieldValue (executionContext, parentNode, parentField, null)
712
+
713
+ UndoManager .shared .endUndo ()
714
+ }
715
+
716
+ addPrototype (id, executionContextId)
717
+ {
718
+ require ("../Controls/AddPrototypePopover")
719
+
720
+ let element = $(`#${id}`)
721
+
722
+ const executionContext = this .objects .get (executionContextId)
723
+
724
+ if (element .is ("#outline-editor"))
725
+ {
726
+ element = element .find (".scene-graph > div > ul > .externprotos, .scene-graph > div > ul > .protos, .scene-graph > div > ul > .root-nodes, .scene-graph > div > ul > .empty-scene") .first ()
727
+ }
728
+
729
+ if (element .is (".externprotos, .externproto"))
730
+ element .addPrototypePopover (executionContext, "externproto")
731
+ else if (element .is (".protos, .proto"))
732
+ element .addPrototypePopover (executionContext, "proto")
733
+ else
734
+ element .addPrototypePopover (executionContext)
735
+ }
736
+
737
+ renamePrototype (id, executionContextId, protoNodeId)
738
+ {
739
+ require ("../Controls/RenameNodePopover")
740
+
741
+ const
742
+ element = $(`#${id}`),
743
+ protoNode = this .objects .get (protoNodeId)
744
+
745
+ element .find ("> .item .node-name") .renameNodePopover (protoNode)
746
+ }
747
+
748
+ deletePrototype (id, executionContextId, protoNodeId, used, availableId)
749
+ {
750
+ const
751
+ executionContext = this .objects .get (executionContextId),
752
+ protoNode = this .objects .get (protoNodeId)
753
+
754
+ if (protoNode .isExternProto)
755
+ {
756
+ UndoManager .shared .beginUndo (_ ("Remove Extern Prototype Declaration »%s«"), protoNode .getName ())
757
+ Editor .removeExternProtoDeclaration (executionContext, protoNode .getName ())
758
+ }
759
+ else
760
+ {
761
+ UndoManager .shared .beginUndo (_ ("Remove Prototype Declaration »%s«"), protoNode .getName ())
762
+ Editor .removeProtoDeclaration (executionContext, protoNode .getName ())
763
+ }
764
+
765
+ if (used)
766
+ Editor .replaceProtoNodes (executionContext, protoNode, this .objects .get (availableId))
767
+
768
+ UndoManager .shared .endUndo ()
769
+ }
770
+
771
+ loadNow (id, protoNodeId)
772
+ {
773
+ const externproto = this .objects .get (protoNodeId)
774
+
775
+ externproto .loadNow ()
776
+ }
777
+
778
+ async turnIntoExternPrototype (id, executionContextId, protoNodeId)
779
+ {
780
+ const
781
+ executionContext = this .objects .get (executionContextId),
782
+ proto = this .objects .get (protoNodeId),
783
+ response = await electron .ipcRenderer .invoke ("file-path", proto .getName ())
784
+
785
+ if (response .canceled)
786
+ return
787
+
788
+ Editor .turnIntoExternProto (executionContext, proto, response .filePath)
789
+ }
790
+
791
+ turnIntoPrototype (id, executionContextId, protoNodeId)
792
+ {
793
+ const
794
+ executionContext = this .objects .get (executionContextId),
795
+ externproto = this .objects .get (protoNodeId)
796
+
797
+ Editor .turnIntoPrototype (executionContext, externproto)
798
+ }
799
+
800
+ addInstance (id, executionContextId, protoNodeId)
801
+ {
802
+ const
803
+ executionContext = this .objects .get (executionContextId),
804
+ protoNode = this .objects .get (protoNodeId),
805
+ instance = executionContext .createProto (protoNode .getName ())
806
+
807
+ UndoManager .shared .beginUndo (_ ("Add Instance of Type »%s«"), protoNode .getName ())
808
+
809
+ Editor .appendValueToArray (executionContext, executionContext, executionContext .rootNodes, instance)
810
+
811
+ UndoManager .shared .endUndo ()
812
+ }
813
+
814
+ addBooleanField (boolean)
815
+ {
816
+ const
817
+ element = boolean .closest (".field"),
818
+ node = this .getNode (element),
819
+ field = this .getField (element)
820
+
821
+ if (field .getAccessType () === X3D .X3DConstants .outputOnly)
822
+ return
823
+
824
+ boolean .addClass ("pointer") .on ("click", () =>
825
+ {
826
+ Editor .setFieldValue (node .getExecutionContext (), node, field, !field .getValue ())
827
+ })
828
+ }
829
+
830
+ addColorField (color)
831
+ {
832
+ //https://seballot.github.io/spectrum/#skinning-nonInput
833
+ require ("spectrum-colorpicker2")
834
+
835
+ const
836
+ element = color .closest (".field"),
837
+ node = this .getNode (element),
838
+ field = this .getField (element)
839
+
840
+ if (field .getAccessType () === X3D .X3DConstants .outputOnly)
841
+ return
842
+
843
+ color .addClass ("pointer") .spectrum ({
844
+ type: "color",
845
+ showAlpha: field .getType () === X3D .X3DConstants .SFColorRGBA,
846
+ showInitial: true,
847
+ showInput: false,
848
+ preferredFormat: "name",
849
+ showButtons: false,
850
+ allowEmpty: false
851
+ })
852
+ .on ("beforeShow.spectrum", (event) =>
853
+ {
854
+ color .spectrum ("set", color .css ("background-color"))
855
+ })
856
+ .on("move.spectrum", (event, tinyColor) =>
857
+ {
858
+ const
859
+ rgb = tinyColor .toRgb (),
860
+ value = field .copy ()
861
+
862
+ value .r = rgb .r / 255
863
+ value .g = rgb .g / 255
864
+ value .b = rgb .b / 255
865
+
866
+ if (value .getType () === X3D .X3DConstants .SFColorRGBA)
867
+ value .a = rgb .a
868
+
869
+ Editor .setFieldValue (node .getExecutionContext (), node, field, value)
870
+ })
871
+ .on ("dragstart.spectrum", (event, tinyColor) =>
872
+ {
873
+ UndoManager .shared .beginUndo (_ ("Change Field »%s.%s«"), node .getTypeName (), field .getName ())
874
+ })
875
+ .on ("dragstop.spectrum", (event, tinyColor) =>
876
+ {
877
+ UndoManager .shared .endUndo ()
878
+ })
879
+ }
880
+
881
+ removeColorField (element)
882
+ {
883
+ element .spectrum ("destroy")
884
+ }
885
+
886
+ addTimeField (time)
887
+ {
888
+ const
889
+ element = time .closest (".field"),
890
+ node = this .getNode (element),
891
+ field = this .getField (element)
892
+
893
+ if (field .getAccessType () === X3D .X3DConstants .outputOnly)
894
+ return
895
+
896
+ time .addClass ("pointer") .on ("click", () =>
897
+ {
898
+ Editor .setFieldValue (node .getExecutionContext (), node, field, Date .now () / 1000)
899
+ })
900
+ }
901
+
902
+ /*
903
+ * Change field value.
904
+ */
905
+
906
+ onFieldEdited (input, node, field)
907
+ {
908
+ try
909
+ {
910
+ if (field .getType () === X3D .X3DConstants .SFString)
911
+ Editor .setFieldValue (node .getExecutionContext (), node, field, input .val ())
912
+ else
913
+ Editor .setFieldFromString (node .getExecutionContext (), node, field, input .val ())
914
+ }
915
+ catch
916
+ {
917
+ $ .beep ()
918
+ input .highlight ()
919
+ }
920
+ }
921
+
922
+ onArrayFieldEdited (textarea, node, field)
923
+ {
924
+ try
925
+ {
926
+ Editor .setFieldFromString (node .getExecutionContext (), node, field, "[" + textarea .val () + "]")
927
+ }
928
+ catch
929
+ {
930
+ $ .beep ()
931
+ textarea .highlight ()
932
+ }
933
+ }
934
+
935
+ /*
936
+ * Routing
937
+ */
938
+
939
+ clearConnectors ()
940
+ {
941
+ if (!this .connector)
942
+ return
943
+
944
+ this .sceneGraph .find (".field .access-type img.active.activated")
945
+ .removeClass ("activated")
946
+
947
+ delete this .connector
948
+ }
949
+
950
+ hoverInConnector (type, event)
951
+ {
952
+ // Hover in connector.
953
+
954
+ const
955
+ element = $(event .currentTarget) .closest (".field", this .sceneGraph),
956
+ field = this .getField (element),
957
+ sceneElement = element .closest (".scene"),
958
+ executionContext = this .getNode (sceneElement)
959
+
960
+ if (!this .isEditable (element .parent ()))
961
+ return
962
+
963
+ if (this .connector)
964
+ {
965
+ if (this .connector .type === type)
966
+ return
967
+
968
+ if (this .connector .executionContext !== executionContext)
969
+ return
970
+
971
+ if (this .connector .field .getType () !== field .getType ())
972
+ return
973
+ }
974
+
975
+ element .find ("> .item .access-type img.active." + type)
976
+ .addClass ("activated")
977
+ }
978
+
979
+ hoverOutConnector (type, event)
980
+ {
981
+ // Hover out connector.
982
+
983
+ const
984
+ element = $(event .currentTarget) .closest (".field", this .sceneGraph),
985
+ field = this .getField (element)
986
+
987
+ if (!this .isEditable (element .parent ()))
988
+ return
989
+
990
+ if (this .connector)
991
+ {
992
+ if (this .connector .type === type)
993
+ {
994
+ if (this .connector .field === field)
995
+ return
996
+ }
997
+ }
998
+
999
+ element .find ("> .item .access-type img.active." + type)
1000
+ .removeClass ("activated")
1001
+ }
1002
+
1003
+ hoverInSingleConnector (type, event)
1004
+ {
1005
+ const element = $(event .currentTarget) .closest (".route", this .sceneGraph)
1006
+
1007
+ if (!this .isEditable (element .parent ()))
1008
+ return
1009
+
1010
+ element .find (".access-type img")
1011
+ .addClass ("activated")
1012
+ }
1013
+
1014
+ hoverOutSingleConnector (type, event)
1015
+ {
1016
+ const element = $(event .currentTarget) .closest (".route", this .sceneGraph)
1017
+
1018
+ if (!this .isEditable (element .parent ()))
1019
+ return
1020
+
1021
+ element .find (".access-type img")
1022
+ .removeClass ("activated")
1023
+ }
1024
+
1025
+ selectConnector (type, event)
1026
+ {
1027
+ // Click on connector.
1028
+
1029
+ const
1030
+ element = $(event .currentTarget) .closest (".field", this .sceneGraph),
1031
+ node = this .getNode (element),
1032
+ field = this .getField (element)
1033
+
1034
+ // Block default href.
1035
+ event .preventDefault ()
1036
+ event .stopImmediatePropagation ()
1037
+
1038
+ if (!this .isEditable (element .parent ()))
1039
+ return
1040
+
1041
+ if (event .ctrlKey || event .metaKey)
1042
+ {
1043
+ switch (type)
1044
+ {
1045
+ case "input":
1046
+ {
1047
+ const routes = field .getInputRoutes ()
1048
+
1049
+ switch (routes .size)
1050
+ {
1051
+ case 0:
1052
+ {
1053
+ break
1054
+ }
1055
+ case 1:
1056
+ {
1057
+ for (const route of routes)
1058
+ {
1059
+ // Delete route.
1060
+
1061
+ Editor .deleteRoute (route .getExecutionContext (), route .sourceNode, route .sourceField, route .destinationNode, route .destinationField)
1062
+ }
1063
+
1064
+ break
1065
+ }
1066
+ default:
1067
+ {
1068
+ element .data ("full-expanded", true)
1069
+ element .jstree ("open_node", element)
1070
+ break
1071
+ }
1072
+ }
1073
+
1074
+ break
1075
+ }
1076
+ case "output":
1077
+ {
1078
+ const routes = field .getOutputRoutes ()
1079
+
1080
+ switch (routes .size)
1081
+ {
1082
+ case 0:
1083
+ {
1084
+ break
1085
+ }
1086
+ case 1:
1087
+ {
1088
+ for (const route of routes)
1089
+ {
1090
+ // Delete route.
1091
+
1092
+ Editor .deleteRoute (route .getExecutionContext (), route .sourceNode, route .sourceField, route .destinationNode, route .destinationField)
1093
+ }
1094
+
1095
+ break
1096
+ }
1097
+ default:
1098
+ {
1099
+ element .data ("full-expanded", true)
1100
+ element .jstree ("open_node", element)
1101
+ break
1102
+ }
1103
+ }
1104
+
1105
+ break
1106
+ }
1107
+ }
1108
+ }
1109
+ else
1110
+ {
1111
+ const
1112
+ sceneElement = element .closest (".scene", this .sceneGraph),
1113
+ executionContext = this .getNode (sceneElement)
1114
+
1115
+ switch (type)
1116
+ {
1117
+ case "input":
1118
+ {
1119
+ if (this .connector)
1120
+ {
1121
+ if (this .connector .type === type)
1122
+ break
1123
+
1124
+ if (this .connector .executionContext !== executionContext)
1125
+ break
1126
+
1127
+ if (this .connector .field .getType () !== field .getType ())
1128
+ break
1129
+
1130
+ // Add route.
1131
+
1132
+ Editor .addRoute (executionContext, new X3D .SFNode (this .connector .node), this .connector .field .getName (), new X3D .SFNode (node), field .getName ())
1133
+
1134
+ if (event .shiftKey)
1135
+ break
1136
+
1137
+ this .connector .element .find (".access-type img.active.output.activated")
1138
+ .removeClass ("activated")
1139
+
1140
+ delete this .connector
1141
+ }
1142
+ else
1143
+ {
1144
+ this .connector = { type: type, executionContext: executionContext, node: node, field: field, element: element }
1145
+ }
1146
+
1147
+ break
1148
+ }
1149
+ case "output":
1150
+ {
1151
+ if (this .connector)
1152
+ {
1153
+ if (this .connector .type === type)
1154
+ break
1155
+
1156
+ if (this .connector .executionContext !== executionContext)
1157
+ break
1158
+
1159
+ if (this .connector .field .getType () !== field .getType ())
1160
+ break
1161
+
1162
+ // Add route.
1163
+
1164
+ Editor .addRoute (executionContext, new X3D .SFNode (node), field .getName (), new X3D .SFNode (this .connector .node), this .connector .field .getName ())
1165
+
1166
+ if (event .shiftKey)
1167
+ break
1168
+
1169
+ this .connector .element .find (".access-type img.active.input.activated")
1170
+ .removeClass ("activated")
1171
+
1172
+ delete this .connector
1173
+ }
1174
+ else
1175
+ {
1176
+ this .connector = { type: type, executionContext: executionContext, node: node, field: field, element: element }
1177
+ }
1178
+
1179
+ break
1180
+ }
1181
+ }
1182
+ }
1183
+ }
1184
+
1185
+ selectSingleConnector (type, event)
1186
+ {
1187
+ // Click on connector.
1188
+
1189
+ const
1190
+ element = $(event .currentTarget) .closest (".route", this .sceneGraph),
1191
+ field = this .getField (element)
1192
+
1193
+ // Block default href.
1194
+ event .preventDefault ()
1195
+ event .stopImmediatePropagation ()
1196
+
1197
+ if (!this .isEditable (element .parent ()))
1198
+ return
1199
+
1200
+ if (!(event .ctrlKey || event .metaKey))
1201
+ return
1202
+
1203
+ switch (type)
1204
+ {
1205
+ case "input":
1206
+ {
1207
+ const route = this .getRoute (element, field .getInputRoutes ())
1208
+
1209
+ element .hide ({
1210
+ duration: this .expandTime,
1211
+ complete: function ()
1212
+ {
1213
+ // Delete route.
1214
+
1215
+ Editor .deleteRoute (route .getExecutionContext (), route .sourceNode, route .sourceField, route .destinationNode, route .destinationField)
1216
+ }
1217
+ .bind (this),
1218
+ })
1219
+
1220
+ break
1221
+ }
1222
+ case "output":
1223
+ {
1224
+ const route = this .getRoute (element, field .getOutputRoutes ())
1225
+
1226
+ element .hide ({
1227
+ duration: this .expandTime,
1228
+ complete: function ()
1229
+ {
1230
+ // Delete route.
1231
+
1232
+ Editor .deleteRoute (route .getExecutionContext (), route .sourceNode, route .sourceField, route .destinationNode, route .destinationField)
1233
+ }
1234
+ .bind (this),
1235
+ })
1236
+
1237
+ break
1238
+ }
1239
+ }
1240
+ }
1241
+
1242
+ /*
1243
+ * Drag & Drop
1244
+ */
1245
+
1246
+ onDragStartExternProto (event)
1247
+ {
1248
+ const
1249
+ element = $(event .target) .closest (".externproto", this .sceneGraph),
1250
+ selected = this .sceneGraph .find (".externproto.selected"),
1251
+ selection = selected .filter (element) .length ? selected : element,
1252
+ ids = selection .map (function () { return this .id }) .get ()
1253
+
1254
+ this .selectPrimaryElement (element)
1255
+
1256
+ event .originalEvent .dataTransfer .setData ("sunrize/externproto", ids .join (","))
1257
+ }
1258
+
1259
+ onDragStartProto (event)
1260
+ {
1261
+ const
1262
+ element = $(event .target) .closest (".proto", this .sceneGraph),
1263
+ selected = this .sceneGraph .find (".proto.selected"),
1264
+ selection = selected .filter (element) .length ? selected : element,
1265
+ ids = selection .map (function () { return this .id }) .get ()
1266
+
1267
+ this .selectPrimaryElement (element)
1268
+
1269
+ event .originalEvent .dataTransfer .setData ("sunrize/proto", ids .join (","))
1270
+ }
1271
+
1272
+ onDragStartNode (event)
1273
+ {
1274
+ const
1275
+ element = $(event .target) .closest (".node", this .sceneGraph),
1276
+ selected = this .sceneGraph .find (".node.selected"),
1277
+ selection = selected .filter (element) .length ? selected : element,
1278
+ ids = selection .map (function () { return this .id }) .get ()
1279
+
1280
+ this .selectPrimaryElement (element)
1281
+
1282
+ event .originalEvent .dataTransfer .setData ("sunrize/nodes", ids .join (","))
1283
+ }
1284
+
1285
+ onDragStartField (event)
1286
+ {
1287
+ const
1288
+ element = $(event .target) .closest (".field", this .sceneGraph),
1289
+ node = this .getNode (element),
1290
+ field = this .getField (element)
1291
+
1292
+ if (node .canUserDefinedFields () && node .getUserDefinedFields () .has (field .getName ()))
1293
+ {
1294
+ this .selectPrimaryElement (element)
1295
+
1296
+ event .originalEvent .dataTransfer .setData ("sunrize/field", element .attr ("id"))
1297
+ }
1298
+ else
1299
+ {
1300
+ event .preventDefault ()
1301
+ }
1302
+ }
1303
+
1304
+ onDragEnter (event)
1305
+ {
1306
+ event .preventDefault ()
1307
+ event .stopPropagation ()
1308
+
1309
+ event .originalEvent .dataTransfer .dropEffect = "none"
1310
+
1311
+ // Show drop indicator.
1312
+
1313
+ const destinationElement = $(event .target) .closest ("li, .scene", this .sceneGraph)
1314
+ .removeClass (["drag-before", "drag-into", "drag-after"])
1315
+
1316
+ if (this .isEditable (destinationElement))
1317
+ {
1318
+ if (event .originalEvent .dataTransfer .types .includes ("sunrize/externproto"))
1319
+ {
1320
+ const
1321
+ sourceElement = this .sceneGraph .find (".primary"),
1322
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1323
+ destinationExecutionContextElement = destinationElement .closest (".scene", this .sceneGraph),
1324
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement),
1325
+ destinationExecutionContext = this .getNode (destinationExecutionContextElement)
1326
+
1327
+ if (sourceExecutionContext === destinationExecutionContext)
1328
+ {
1329
+ if (event .altKey)
1330
+ event .originalEvent .dataTransfer .dropEffect = "copy"
1331
+ else
1332
+ event .originalEvent .dataTransfer .dropEffect = "move"
1333
+ }
1334
+ else
1335
+ {
1336
+ event .originalEvent .dataTransfer .dropEffect = "copy"
1337
+ }
1338
+
1339
+ if (destinationElement .is (".scene-graph"))
1340
+ destinationElement .addClass ("drag-after")
1341
+ else if (destinationElement .is (".scene, .externprotos"))
1342
+ destinationElement .addClass ("drag-into")
1343
+ else if (destinationElement .is (".externproto"))
1344
+ {
1345
+ const
1346
+ item = destinationElement .find ("> .item"),
1347
+ y = event .pageY - destinationElement .offset () .top
1348
+
1349
+ if (y < item .height () * 0.5)
1350
+ destinationElement .data ("drag-type", "drag-before")
1351
+ else if (y > destinationElement .height () - item .height () * 0.5)
1352
+ destinationElement .data ("drag-type", "drag-after")
1353
+ else
1354
+ {
1355
+ destinationElement .data ("drag-type", "")
1356
+ event .originalEvent .dataTransfer .dropEffect = "none"
1357
+ }
1358
+
1359
+ item .addClass (destinationElement .data ("drag-type"))
1360
+ }
1361
+ else
1362
+ event .originalEvent .dataTransfer .dropEffect = "none"
1363
+ }
1364
+ else if (event .originalEvent .dataTransfer .types .includes ("sunrize/proto"))
1365
+ {
1366
+ const
1367
+ sourceElement = this .sceneGraph .find (".primary"),
1368
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1369
+ destinationExecutionContextElement = destinationElement .closest (".scene", this .sceneGraph),
1370
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement),
1371
+ destinationExecutionContext = this .getNode (destinationExecutionContextElement)
1372
+
1373
+ if (sourceExecutionContext === destinationExecutionContext)
1374
+ {
1375
+ if (event .altKey)
1376
+ {
1377
+ event .originalEvent .dataTransfer .dropEffect = "copy"
1378
+ }
1379
+ else
1380
+ {
1381
+ const
1382
+ sourceIndex = parseInt (sourceElement .attr ("index")),
1383
+ destinationIndex = destinationElement .hasClass ("proto") ? parseInt (destinationElement .attr ("index")) : destinationExecutionContext .protos .length,
1384
+ sourceProto = this .getNode (sourceElement),
1385
+ destinationProtos = destinationExecutionContext .protos
1386
+
1387
+ event .originalEvent .dataTransfer .dropEffect = "move"
1388
+
1389
+ if (sourceIndex == destinationIndex || sourceIndex + 1 == destinationIndex)
1390
+ { }
1391
+ else if (sourceIndex < destinationIndex)
1392
+ {
1393
+ for (let i = sourceIndex + 1; i < destinationIndex; ++ i)
1394
+ {
1395
+ if (Editor .protoIsUsedInProto (sourceProto, destinationProtos [i]))
1396
+ {
1397
+ event .originalEvent .dataTransfer .dropEffect = "none"
1398
+ break
1399
+ }
1400
+ }
1401
+ }
1402
+ else if (sourceIndex > destinationIndex)
1403
+ {
1404
+ for (let i = destinationIndex; i < sourceIndex; ++ i)
1405
+ {
1406
+ if (Editor .protoIsUsedInProto (destinationProtos [i], sourceProto))
1407
+ {
1408
+ event .originalEvent .dataTransfer .dropEffect = "none"
1409
+ break
1410
+ }
1411
+ }
1412
+ }
1413
+ }
1414
+ }
1415
+ else
1416
+ {
1417
+ event .originalEvent .dataTransfer .dropEffect = "copy"
1418
+ }
1419
+
1420
+ if (event .originalEvent .dataTransfer .dropEffect !== "none")
1421
+ {
1422
+ if (destinationElement .is (".scene-graph"))
1423
+ destinationElement .addClass ("drag-after")
1424
+ else if (destinationElement .is (".scene, .protos"))
1425
+ destinationElement .addClass ("drag-into")
1426
+ else if (destinationElement .is (".proto"))
1427
+ {
1428
+ const
1429
+ item = destinationElement .find ("> .item"),
1430
+ y = event .pageY - destinationElement .offset () .top
1431
+
1432
+ if (y < item .height () * 0.5)
1433
+ destinationElement .data ("drag-type", "drag-before")
1434
+ else if (y > destinationElement .height () - item .height () * 0.5)
1435
+ destinationElement .data ("drag-type", "drag-after")
1436
+ else
1437
+ {
1438
+ destinationElement .data ("drag-type", "")
1439
+ event .originalEvent .dataTransfer .dropEffect = "none"
1440
+ }
1441
+
1442
+ item .addClass (destinationElement .data ("drag-type"))
1443
+ }
1444
+ else
1445
+ event .originalEvent .dataTransfer .dropEffect = "none"
1446
+ }
1447
+ }
1448
+ else if (event .originalEvent .dataTransfer .types .includes ("sunrize/nodes"))
1449
+ {
1450
+ if (event .altKey)
1451
+ event .originalEvent .dataTransfer .dropEffect = "copy"
1452
+ else if (event .ctrlKey)
1453
+ event .originalEvent .dataTransfer .dropEffect = "link"
1454
+ else
1455
+ event .originalEvent .dataTransfer .dropEffect = "move"
1456
+
1457
+ if (destinationElement .is (".scene-graph"))
1458
+ destinationElement .addClass ("drag-after")
1459
+ else if (destinationElement .is (".scene, .root-nodes, .field[type-name*=Node]"))
1460
+ destinationElement .addClass ("drag-into")
1461
+ else if (destinationElement .is (".node"))
1462
+ {
1463
+ const
1464
+ item = destinationElement .find ("> .item"),
1465
+ y = event .pageY - destinationElement .offset () .top
1466
+
1467
+ if (y < item .height () * 0.25)
1468
+ {
1469
+ destinationElement .data ("drag-type", "drag-before")
1470
+ destinationElement .addClass ("drag-before")
1471
+ }
1472
+ else if (y > destinationElement .height () - item .height () * 0.25)
1473
+ {
1474
+ destinationElement .data ("drag-type", "drag-after")
1475
+ destinationElement .addClass ("drag-after")
1476
+ }
1477
+ else
1478
+ {
1479
+ destinationElement .data ("drag-type", "drag-into")
1480
+ destinationElement .addClass ("drag-into")
1481
+ }
1482
+ }
1483
+ else
1484
+ event .originalEvent .dataTransfer .dropEffect = "none"
1485
+ }
1486
+ else if (event .originalEvent .dataTransfer .types .includes ("sunrize/field"))
1487
+ {
1488
+ if (destinationElement .hasClass ("field"))
1489
+ {
1490
+ const
1491
+ sourceElement = this .sceneGraph .find (".primary"),
1492
+ sourceNode = this .getNode (sourceElement)
1493
+
1494
+ const
1495
+ destinationNode = this .getNode (destinationElement),
1496
+ destinationField = this .getField (destinationElement),
1497
+ userDefined = destinationNode .getUserDefinedFields () .has (destinationField .getName ())
1498
+
1499
+ if (destinationNode === sourceNode && userDefined)
1500
+ {
1501
+ event .originalEvent .dataTransfer .dropEffect = "move"
1502
+
1503
+ const
1504
+ item = destinationElement .find ("> .item"),
1505
+ y = event .pageY - destinationElement .offset () .top
1506
+
1507
+ if (y < item .height () * 0.5)
1508
+ {
1509
+ destinationElement .data ("drag-type", "drag-before")
1510
+ destinationElement .addClass ("drag-before")
1511
+ }
1512
+ else if (y > destinationElement .height () - item .height () * 0.5)
1513
+ {
1514
+ destinationElement .data ("drag-type", "drag-after")
1515
+ destinationElement .addClass ("drag-after")
1516
+ }
1517
+ else
1518
+ {
1519
+ event .originalEvent .dataTransfer .dropEffect = "none"
1520
+ }
1521
+ }
1522
+ }
1523
+ }
1524
+ }
1525
+
1526
+ destinationElement .data ("dropEffect", event .originalEvent .dataTransfer .dropEffect)
1527
+ }
1528
+
1529
+ onDragLeave (event)
1530
+ {
1531
+ event .preventDefault ()
1532
+ event .stopPropagation ()
1533
+
1534
+ // Hide drop indicator.
1535
+
1536
+ const element = $(event .target)
1537
+ .closest ("li, .scene-graph", this .sceneGraph)
1538
+
1539
+ element .removeClass (["drag-before", "drag-into", "drag-after"])
1540
+ }
1541
+
1542
+ onDrop (event)
1543
+ {
1544
+ // console .log ("onDrop")
1545
+
1546
+ event .preventDefault ()
1547
+ event .stopPropagation ()
1548
+
1549
+ if (event .originalEvent .dataTransfer .types .includes ("sunrize/externproto"))
1550
+ {
1551
+ const
1552
+ sourceElementId = event .originalEvent .dataTransfer .getData ("sunrize/externproto"),
1553
+ sourceElement = $("#" + sourceElementId),
1554
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1555
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement),
1556
+ sourceIndex = parseInt (sourceElement .attr ("index"))
1557
+
1558
+ let sourceExternProto = this .getNode (sourceElement)
1559
+
1560
+ const
1561
+ destinationElement = $(event .target) .closest (".externproto, .externprotos, .scene", this .sceneGraph),
1562
+ destinationExecutionContextElement = destinationElement .closest (".scene", this .sceneGraph),
1563
+ destinationExecutionContext = this .getNode (destinationExecutionContextElement)
1564
+
1565
+ let destinationIndex = destinationElement .hasClass ("externproto") ? parseInt (destinationElement .attr ("index")) : destinationExecutionContext .externprotos .length
1566
+
1567
+ if (destinationElement .hasClass ("externproto") && destinationElement .data ("drag-type") ==="drag-after")
1568
+ ++ destinationIndex
1569
+
1570
+ switch (destinationElement .data ("dropEffect"))
1571
+ {
1572
+ case "copy":
1573
+ {
1574
+ UndoManager .shared .beginUndo (_ ("Copy Extern Proto »%s«"), sourceExternProto .getName ())
1575
+ Editor .importX3D (destinationExecutionContext, Editor .exportVRML (sourceExecutionContext, [sourceExternProto]))
1576
+
1577
+ const
1578
+ externprotos = Array .from (destinationExecutionContext .externprotos),
1579
+ externproto = externprotos .pop ()
1580
+
1581
+ externprotos .splice (destinationIndex, 0, externproto)
1582
+
1583
+ Editor .setExternProtoDeclarations (destinationExecutionContext, externprotos)
1584
+
1585
+ if (Editor .isParentContext (sourceExecutionContext, destinationExecutionContext))
1586
+ {
1587
+ if (!destinationExecutionContext .protos .get (externproto .getName ()))
1588
+ {
1589
+ const available = Editor .getNextAvailableProtoNode (destinationExecutionContext, externproto)
1590
+
1591
+ Editor .replaceProtoNodes (destinationExecutionContext, available, externproto)
1592
+ }
1593
+ }
1594
+
1595
+ UndoManager .shared .endUndo ()
1596
+ break
1597
+ }
1598
+ case "move":
1599
+ {
1600
+ if (sourceExecutionContext !== destinationExecutionContext)
1601
+ break
1602
+
1603
+ if (sourceIndex === destinationIndex || sourceIndex + 1 === destinationIndex)
1604
+ break
1605
+
1606
+ UndoManager .shared .beginUndo (_ ("Move Extern Proto »%s«"), sourceExternProto .getName ())
1607
+
1608
+ const externprotos = Array .from (destinationExecutionContext .externprotos)
1609
+
1610
+ if (sourceIndex < destinationIndex)
1611
+ -- destinationIndex
1612
+
1613
+ externprotos .splice (sourceIndex, 1)
1614
+ externprotos .splice (destinationIndex, 0, sourceExternProto)
1615
+
1616
+ Editor .setExternProtoDeclarations (destinationExecutionContext, externprotos)
1617
+ UndoManager .shared .endUndo ()
1618
+ break
1619
+ }
1620
+ }
1621
+ }
1622
+ else if (event .originalEvent .dataTransfer .types .includes ("sunrize/proto"))
1623
+ {
1624
+ const
1625
+ sourceElementId = event .originalEvent .dataTransfer .getData ("sunrize/proto"),
1626
+ sourceElement = $("#" + sourceElementId),
1627
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1628
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement),
1629
+ sourceIndex = parseInt (sourceElement .attr ("index"))
1630
+
1631
+ let sourceProto = this .getNode (sourceElement)
1632
+
1633
+ const
1634
+ destinationElement = $(event .target) .closest (".proto, .protos, .scene", this .sceneGraph),
1635
+ destinationExecutionContextElement = destinationElement .closest (".scene", this .sceneGraph),
1636
+ destinationExecutionContext = this .getNode (destinationExecutionContextElement)
1637
+
1638
+ let destinationIndex = destinationElement .hasClass ("proto") ? parseInt (destinationElement .attr ("index")) : destinationExecutionContext .protos .length
1639
+
1640
+ if (destinationElement .hasClass ("proto") && destinationElement .data ("drag-type") ==="drag-after")
1641
+ ++ destinationIndex
1642
+
1643
+ switch (destinationElement .data ("dropEffect"))
1644
+ {
1645
+ case "copy":
1646
+ {
1647
+ UndoManager .shared .beginUndo (_ ("Copy Prototype »%s«"), sourceProto .getName ())
1648
+ Editor .importX3D (destinationExecutionContext, Editor .exportVRML (sourceExecutionContext, [sourceProto]))
1649
+
1650
+ const
1651
+ protos = Array .from (destinationExecutionContext .protos),
1652
+ proto = protos .pop ()
1653
+
1654
+ protos .splice (destinationIndex, 0, proto)
1655
+
1656
+ Editor .setProtoDeclarations (destinationExecutionContext, protos)
1657
+
1658
+ if (Editor .isParentContext (sourceExecutionContext, destinationExecutionContext))
1659
+ {
1660
+ const available = Editor .getNextAvailableProtoNode (destinationExecutionContext, proto)
1661
+
1662
+ Editor .replaceProtoNodes (destinationExecutionContext, available, proto)
1663
+ }
1664
+
1665
+ UndoManager .shared .endUndo ()
1666
+ break
1667
+ }
1668
+ case "move":
1669
+ {
1670
+ if (sourceExecutionContext !== destinationExecutionContext)
1671
+ break
1672
+
1673
+ if (sourceIndex === destinationIndex || sourceIndex + 1 === destinationIndex)
1674
+ break
1675
+
1676
+ UndoManager .shared .beginUndo (_ ("Move Prototype »%s«"), sourceProto .getName ())
1677
+
1678
+ const protos = Array .from (destinationExecutionContext .protos)
1679
+
1680
+ if (sourceIndex < destinationIndex)
1681
+ -- destinationIndex
1682
+
1683
+ protos .splice (sourceIndex, 1)
1684
+ protos .splice (destinationIndex, 0, sourceProto)
1685
+
1686
+ Editor .setProtoDeclarations (destinationExecutionContext, protos)
1687
+ UndoManager .shared .endUndo ()
1688
+ break
1689
+ }
1690
+ }
1691
+ }
1692
+ else if (event .originalEvent .dataTransfer .types .includes ("sunrize/nodes"))
1693
+ {
1694
+ const sourceElementsIds = event .originalEvent .dataTransfer .getData ("sunrize/nodes") .split (",")
1695
+
1696
+ const
1697
+ destinationElement = $(event .target) .closest ("li, .scene", this .sceneGraph),
1698
+ destinationParentFieldElement = destinationElement .closest (".field, .scene", this .sceneGraph),
1699
+ destinationParentNodeElement = destinationParentFieldElement .closest (".node, .proto, .scene", this .sceneGraph),
1700
+ destinationExecutionContextElement = destinationElement .closest (".scene", this .sceneGraph),
1701
+ destinationExecutionContext = this .getNode (destinationExecutionContextElement)
1702
+
1703
+ let
1704
+ destinationParentNode = this .getNode (destinationParentNodeElement),
1705
+ destinationParentField = destinationParentFieldElement .hasClass ("scene") ? destinationParentNode .rootNodes : this .getField (destinationParentFieldElement),
1706
+ destinationIndex = parseInt (destinationElement .attr ("index"))
1707
+
1708
+ if (destinationElement .hasClass ("node") && destinationElement .data ("drag-type") ==="drag-after")
1709
+ ++ destinationIndex
1710
+
1711
+ if (destinationElement .attr ("node-id") !== "NULL")
1712
+ {
1713
+ if (destinationElement .hasClass ("node") && destinationElement .data ("drag-type") ==="drag-into")
1714
+ {
1715
+ destinationParentNode = this .getNode (destinationElement)
1716
+ }
1717
+ }
1718
+
1719
+ // Begin undo.
1720
+
1721
+ if (sourceElementsIds .length === 1)
1722
+ {
1723
+ const
1724
+ sourceElement = $("#" + sourceElementsIds [0]),
1725
+ sourceNode = this .getNode (sourceElement)
1726
+
1727
+ UndoManager .shared .beginUndo (this .getUndoDescriptionForNode (destinationElement .data ("dropEffect"), sourceNode), sourceNode .getTypeName (), sourceNode .getDisplayName ())
1728
+ }
1729
+ else
1730
+ {
1731
+ UndoManager .shared .beginUndo (this .getUndoDescriptionForNode (destinationElement .data ("dropEffect"), sourceElementsIds), sourceElementsIds .length)
1732
+ }
1733
+
1734
+ // Copy source nodes if needed.
1735
+
1736
+ const sourceNodes = [ ]
1737
+
1738
+ for (const sourceElementId of sourceElementsIds)
1739
+ {
1740
+ const
1741
+ sourceElement = $("#" + sourceElementId),
1742
+ sourceNode = this .getNode (sourceElement),
1743
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1744
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement)
1745
+
1746
+ if (destinationElement .data ("dropEffect") === "copy" || sourceExecutionContext !== destinationExecutionContext)
1747
+ {
1748
+ sourceNodes .push (sourceNode)
1749
+ }
1750
+ }
1751
+
1752
+ const copiedNodes = sourceNodes .length
1753
+ ? Editor .importX3D (destinationExecutionContext, Editor .exportVRML (this .executionContext, sourceNodes))
1754
+ : [ ]
1755
+
1756
+ if (copiedNodes .length)
1757
+ destinationExecutionContext .rootNodes .length -= copiedNodes .length
1758
+
1759
+ // Move, copy, link nodes.
1760
+
1761
+ const sourceIndexOffsets = new Map ()
1762
+
1763
+ for (const sourceElementId of sourceElementsIds)
1764
+ {
1765
+ const
1766
+ sourceElement = $("#" + sourceElementId),
1767
+ sourceParentFieldElement = sourceElement .closest (".field, .scene", this .sceneGraph),
1768
+ sourceParentNodeElement = sourceParentFieldElement .closest (".node, .proto, .scene", this .sceneGraph),
1769
+ sourceParentNode = this .getNode (sourceParentNodeElement),
1770
+ sourceParentField = sourceParentFieldElement .hasClass ("scene") ? sourceParentNode .rootNodes : this .getField (sourceParentFieldElement),
1771
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1772
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement)
1773
+
1774
+ let
1775
+ sourceNode = this .getNode (sourceElement),
1776
+ sourceIndex = parseInt (sourceElement .attr ("index"))
1777
+
1778
+ if (destinationElement .attr ("node-id") !== "NULL")
1779
+ {
1780
+ if (destinationElement .hasClass ("node") && destinationElement .data ("drag-type") ==="drag-into")
1781
+ {
1782
+ try
1783
+ {
1784
+ if (destinationParentNode === sourceNode)
1785
+ continue
1786
+
1787
+ destinationParentField = destinationParentNode .getField (sourceNode .getContainerField ())
1788
+ }
1789
+ catch
1790
+ {
1791
+ for (const field of Array .from (destinationParentNode .getFields ()) .reverse ())
1792
+ {
1793
+ if (!field .isInitializable ())
1794
+ continue
1795
+
1796
+ if (field .getType () !== X3D .X3DConstants .SFNode && field .getType () !== X3D .X3DConstants .MFNode)
1797
+ continue
1798
+
1799
+ destinationParentField = field
1800
+ break
1801
+ }
1802
+ }
1803
+ }
1804
+ }
1805
+
1806
+ // Adjust source index.
1807
+
1808
+ if (sourceIndexOffsets .has (sourceParentField))
1809
+ {
1810
+ if (sourceParentField !== destinationParentField || destinationIndex > sourceIndex)
1811
+ sourceIndexOffsets .set (sourceParentField, sourceIndexOffsets .get (sourceParentField) - 1)
1812
+ }
1813
+ else
1814
+ {
1815
+ sourceIndexOffsets .set (sourceParentField, 0)
1816
+ }
1817
+
1818
+ sourceIndex += sourceIndexOffsets .get (sourceParentField)
1819
+
1820
+ // If source equal destination, continue.
1821
+
1822
+ if (sourceParentField === destinationParentField && (sourceIndex === destinationIndex || sourceIndex + 1 === destinationIndex || isNaN (sourceIndex) && isNaN (destinationIndex)) && destinationElement .data ("dropEffect") === "move")
1823
+ {
1824
+ continue
1825
+ }
1826
+
1827
+ // Remove source node.
1828
+
1829
+ if (destinationElement .data ("dropEffect") === "move")
1830
+ {
1831
+ switch (sourceParentField .getType ())
1832
+ {
1833
+ case X3D .X3DConstants .SFNode:
1834
+ {
1835
+ Editor .setFieldValue (sourceExecutionContext, sourceParentNode, sourceParentField, null)
1836
+ break
1837
+ }
1838
+ case X3D .X3DConstants .MFNode:
1839
+ {
1840
+ if (sourceParentField === destinationParentField && destinationIndex >= sourceIndex)
1841
+ -- destinationIndex
1842
+
1843
+ Editor .removeValueFromArray (sourceExecutionContext, sourceParentNode, sourceParentField, sourceIndex)
1844
+ break
1845
+ }
1846
+ }
1847
+ }
1848
+
1849
+ // Copy source node if needed.
1850
+
1851
+ if (destinationElement .data ("dropEffect") === "copy" || sourceExecutionContext !== destinationExecutionContext)
1852
+ {
1853
+ sourceNode = copiedNodes .shift ()
1854
+ }
1855
+
1856
+ // Adjust matrix.
1857
+
1858
+ if (destinationElement .data ("dropEffect") .match (/copy|move/))
1859
+ {
1860
+ if (sourceNode .getType () .includes (X3D .X3DConstants .X3DTransformNode))
1861
+ {
1862
+ const
1863
+ sourceModelMatrix = this .getModelMatrix (sourceElement),
1864
+ destinationModelMatrix = this .getModelMatrix (destinationParentNodeElement)
1865
+
1866
+ destinationModelMatrix .inverse () .multLeft (sourceModelMatrix)
1867
+ Editor .setMatrixWithCenter (sourceNode, destinationModelMatrix)
1868
+ }
1869
+ }
1870
+
1871
+ // Insert source node.
1872
+
1873
+ switch (destinationParentField .getType ())
1874
+ {
1875
+ case X3D .X3DConstants .SFNode:
1876
+ {
1877
+ Editor .setFieldValue (destinationExecutionContext, destinationParentNode, destinationParentField, sourceNode)
1878
+ break
1879
+ }
1880
+ case X3D .X3DConstants .MFNode:
1881
+ {
1882
+ Editor .insertValueIntoArray (destinationExecutionContext, destinationParentNode, destinationParentField, isNaN (destinationIndex) ? destinationParentField .length : destinationIndex, sourceNode)
1883
+ break
1884
+ }
1885
+ }
1886
+
1887
+ // Update node name and clone count.
1888
+
1889
+ if (destinationElement .data ("dropEffect") === "link")
1890
+ {
1891
+ this .registerUpdateSceneGraph ()
1892
+ }
1893
+
1894
+ // End.
1895
+
1896
+ ++ destinationIndex
1897
+ }
1898
+
1899
+ // End undo.
1900
+
1901
+ UndoManager .shared .endUndo ()
1902
+ }
1903
+ if (event .originalEvent .dataTransfer .types .includes ("sunrize/field"))
1904
+ {
1905
+ const
1906
+ sourceElementId = event .originalEvent .dataTransfer .getData ("sunrize/field"),
1907
+ sourceElement = $(`#${sourceElementId}`),
1908
+ sourceNode = this .getNode (sourceElement),
1909
+ sourceFields = Array .from (sourceNode .getUserDefinedFields ()),
1910
+ sourceField = this .getField (sourceElement),
1911
+ sourceIndex = sourceFields .indexOf (sourceField),
1912
+ sourceExecutionContextElement = sourceElement .closest (".scene", this .sceneGraph),
1913
+ sourceExecutionContext = this .getNode (sourceExecutionContextElement)
1914
+
1915
+ const
1916
+ destinationElement = $(event .target) .closest (".field", this .sceneGraph),
1917
+ destinationField = this .getField (destinationElement)
1918
+
1919
+ let destinationIndex = sourceFields .indexOf (destinationField)
1920
+
1921
+ if (destinationElement .data ("drag-type") ==="drag-after")
1922
+ ++ destinationIndex
1923
+
1924
+ if (sourceIndex === destinationIndex || sourceIndex + 1 === destinationIndex)
1925
+ return
1926
+
1927
+ sourceFields .splice (sourceIndex, 1)
1928
+ sourceFields .splice (sourceIndex < destinationIndex ? destinationIndex - 1 : destinationIndex, 0, sourceField)
1929
+
1930
+ UndoManager .shared .beginUndo (_ ("Move Field »%s«"), sourceField .getName ())
1931
+ Editor .setUserDefinedFields (sourceExecutionContext, sourceNode, sourceFields)
1932
+ UndoManager .shared .endUndo ()
1933
+ }
1934
+ }
1935
+
1936
+ getUndoDescriptionForNode (dropEffect, node)
1937
+ {
1938
+ if (Array .isArray (node))
1939
+ {
1940
+ switch (dropEffect)
1941
+ {
1942
+ case "copy": return _ ("Copy %s Nodes", node .length)
1943
+ case "link": return _ ("Link %s Nodes", node .length)
1944
+ case "move": return _ ("Move %s Nodes", node .length)
1945
+ }
1946
+ }
1947
+ else
1948
+ {
1949
+ if (node .getDisplayName ())
1950
+ {
1951
+ switch (dropEffect)
1952
+ {
1953
+ case "copy": return _ ("Copy Node %s »%s«")
1954
+ case "link": return _ ("Link Node %s »%s«")
1955
+ case "move": return _ ("Move Node %s »%s«")
1956
+ }
1957
+ }
1958
+ else
1959
+ {
1960
+ switch (dropEffect)
1961
+ {
1962
+ case "copy": return _ ("Copy Node %s")
1963
+ case "link": return _ ("Link Node %s")
1964
+ case "move": return _ ("Move Node %s")
1965
+ }
1966
+ }
1967
+ }
1968
+ }
1969
+
1970
+ getModelMatrix (nodeElement, self = true)
1971
+ {
1972
+ if (!nodeElement .length)
1973
+ return new (X3D .require ("standard/Math/Numbers/Matrix4")) ()
1974
+
1975
+ const
1976
+ node = this .getNode (nodeElement),
1977
+ modelMatrix = this .getModelMatrix (nodeElement .parent () .closest (".node", this .sceneGraph))
1978
+
1979
+ if (self && node .getType () .some (Set .prototype .has, this .X3DTransformMatrix3DNode))
1980
+ modelMatrix .multLeft (node .getMatrix ())
1981
+
1982
+ return modelMatrix
1983
+ }
1984
+
1985
+ // Call update.
1986
+
1987
+ registerUpdateSceneGraph (undoManager = UndoManager .shared)
1988
+ {
1989
+ this .updateSceneGraph ()
1990
+
1991
+ undoManager .registerUndo (() =>
1992
+ {
1993
+ this .registerUpdateSceneGraph (undoManager)
1994
+ },
1995
+ true)
1996
+ }
1997
+
1998
+ removeEmptyGroups ()
1999
+ {
2000
+ const
2001
+ selection = this .sceneGraph .find (".node.primary, .node.selected"),
2002
+ ids = selection .map (function () { return this .id }) .get (),
2003
+ nodes = ids .length ? ids .map (id => this .getNode ($(`#${id}`))) : this .executionContext .rootNodes
2004
+
2005
+ Editor .removeEmptyGroups (this .executionContext, nodes)
2006
+ }
2007
+ }