senangwebs-aframe-editor 1.6.5

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 (104) hide show
  1. package/.babelrc +3 -0
  2. package/.editorconfig +12 -0
  3. package/.eslintignore +2 -0
  4. package/.eslintrc +40 -0
  5. package/.github/workflows/ci.yml +39 -0
  6. package/.husky/pre-commit +4 -0
  7. package/.prettierignore +1 -0
  8. package/.prettierrc.json +5 -0
  9. package/.stylelintrc +12 -0
  10. package/LICENSE +21 -0
  11. package/README.md +75 -0
  12. package/assets/gltf.svg +49 -0
  13. package/dist/aframe-inspector.js +106250 -0
  14. package/dist/aframe-inspector.js.map +1 -0
  15. package/dist/aframe-inspector.min.js +29040 -0
  16. package/dist/aframe-inspector.min.js.LICENSE.txt +56 -0
  17. package/dist/aframe-inspector.min.js.map +1 -0
  18. package/examples/360video.html +48 -0
  19. package/examples/colors.html +18 -0
  20. package/examples/controllers.html +60 -0
  21. package/examples/embedded-zoom.html +78 -0
  22. package/examples/embedded.html +79 -0
  23. package/examples/empty.html +13 -0
  24. package/examples/index-aframe.html +66 -0
  25. package/examples/index.html +71 -0
  26. package/examples/supercraft.html +6 -0
  27. package/index.html +8 -0
  28. package/package.json +84 -0
  29. package/senangwebs-webverse-editor.png +0 -0
  30. package/src/components/AwesomeIcon.js +53 -0
  31. package/src/components/Collapsible.js +57 -0
  32. package/src/components/EntityRepresentation.js +83 -0
  33. package/src/components/Main.js +222 -0
  34. package/src/components/__tests__/Collapsible.test.js +30 -0
  35. package/src/components/components/AddComponent.js +104 -0
  36. package/src/components/components/CommonComponents.js +160 -0
  37. package/src/components/components/Component.js +151 -0
  38. package/src/components/components/ComponentsContainer.js +52 -0
  39. package/src/components/components/DefaultComponents.js +1 -0
  40. package/src/components/components/Mixins.js +83 -0
  41. package/src/components/components/PropertyRow.js +145 -0
  42. package/src/components/components/Sidebar.js +51 -0
  43. package/src/components/icons/BackViewIcon.js +27 -0
  44. package/src/components/icons/BottomViewIcon.js +26 -0
  45. package/src/components/icons/FrontViewIcon.js +23 -0
  46. package/src/components/icons/LeftViewIcon.js +24 -0
  47. package/src/components/icons/PerspectiveIcon.js +23 -0
  48. package/src/components/icons/PrimitiveBoxIcon.js +143 -0
  49. package/src/components/icons/PrimitiveConeIcon.js +44 -0
  50. package/src/components/icons/PrimitiveCylinderIcon.js +51 -0
  51. package/src/components/icons/PrimitiveEmptyEntityIcon.js +78 -0
  52. package/src/components/icons/PrimitiveImageIcon.js +86 -0
  53. package/src/components/icons/PrimitiveLightIcon.js +107 -0
  54. package/src/components/icons/PrimitivePlaneIcon.js +87 -0
  55. package/src/components/icons/PrimitiveSphereIcon.js +39 -0
  56. package/src/components/icons/PrimitiveTextIcon.js +89 -0
  57. package/src/components/icons/PrimitiveTorusIcon.js +31 -0
  58. package/src/components/icons/RightViewIcon.js +24 -0
  59. package/src/components/icons/TopViewIcon.js +24 -0
  60. package/src/components/modals/Modal.js +107 -0
  61. package/src/components/modals/ModalHelp.js +97 -0
  62. package/src/components/modals/ModalPrimitive.js +114 -0
  63. package/src/components/modals/ModalTextures.js +430 -0
  64. package/src/components/scenegraph/Entity.js +142 -0
  65. package/src/components/scenegraph/SceneGraph.js +337 -0
  66. package/src/components/scenegraph/Toolbar.js +147 -0
  67. package/src/components/viewport/CameraToolbar.js +122 -0
  68. package/src/components/viewport/TransformToolbar.js +102 -0
  69. package/src/components/viewport/ViewportHUD.js +33 -0
  70. package/src/components/widgets/BooleanWidget.js +49 -0
  71. package/src/components/widgets/ColorWidget.js +89 -0
  72. package/src/components/widgets/InputWidget.js +42 -0
  73. package/src/components/widgets/NumberWidget.js +179 -0
  74. package/src/components/widgets/SelectWidget.js +58 -0
  75. package/src/components/widgets/TextureWidget.js +252 -0
  76. package/src/components/widgets/Vec2Widget.js +55 -0
  77. package/src/components/widgets/Vec3Widget.js +58 -0
  78. package/src/components/widgets/Vec4Widget.js +61 -0
  79. package/src/components/widgets/index.js +9 -0
  80. package/src/index.js +301 -0
  81. package/src/lib/EditorControls.js +336 -0
  82. package/src/lib/Events.js +6 -0
  83. package/src/lib/TransformControls.js +1365 -0
  84. package/src/lib/assetsLoader.js +43 -0
  85. package/src/lib/assetsUtils.js +30 -0
  86. package/src/lib/cameras.js +121 -0
  87. package/src/lib/entity.js +556 -0
  88. package/src/lib/history.js +30 -0
  89. package/src/lib/raycaster.js +129 -0
  90. package/src/lib/shortcuts.js +211 -0
  91. package/src/lib/utils.js +118 -0
  92. package/src/lib/viewport.js +268 -0
  93. package/src/style/components.styl +275 -0
  94. package/src/style/entity.styl +22 -0
  95. package/src/style/help.styl +40 -0
  96. package/src/style/index.styl +358 -0
  97. package/src/style/lib.styl +41 -0
  98. package/src/style/primitiveModal.styl +90 -0
  99. package/src/style/scenegraph.styl +173 -0
  100. package/src/style/select.styl +71 -0
  101. package/src/style/textureModal.styl +220 -0
  102. package/src/style/viewport.styl +168 -0
  103. package/src/style/widgets.styl +71 -0
  104. package/webpack.config.js +65 -0
@@ -0,0 +1,556 @@
1
+ import Events from './Events';
2
+ import { equal } from './utils';
3
+
4
+ /**
5
+ * Update a component.
6
+ *
7
+ * @param {Element} entity - Entity to modify.
8
+ * @param {string} component - component name
9
+ * @param {string} property - property name, use empty string if component is single property or if value is an object
10
+ * @param {string|number|object} value - New value.
11
+ */
12
+ export function updateEntity(entity, component, property, value) {
13
+ if (property) {
14
+ if (value === null || value === undefined) {
15
+ // Remove property.
16
+ entity.removeAttribute(component, property);
17
+ } else {
18
+ // Set property.
19
+ entity.setAttribute(component, property, value);
20
+ }
21
+ } else {
22
+ if (value === null || value === undefined) {
23
+ // Remove component.
24
+ entity.removeAttribute(component);
25
+ } else {
26
+ // Set component.
27
+ entity.setAttribute(component, value);
28
+ }
29
+ }
30
+
31
+ Events.emit('entityupdate', { entity, component, property, value });
32
+ }
33
+
34
+ /**
35
+ * Remove an entity.
36
+ *
37
+ * @param {Element} entity Entity to remove.
38
+ * @param {boolean} force (Optional) If true it won't ask for confirmation.
39
+ */
40
+ export function removeEntity(entity, force) {
41
+ if (entity) {
42
+ if (
43
+ force === true ||
44
+ confirm(
45
+ 'Do you really want to remove entity `' +
46
+ (entity.id || entity.tagName) +
47
+ '`?'
48
+ )
49
+ ) {
50
+ var closest = findClosestEntity(entity);
51
+ entity.parentNode.removeChild(entity);
52
+ AFRAME.INSPECTOR.selectEntity(closest);
53
+ }
54
+ }
55
+ }
56
+
57
+ function findClosestEntity(entity) {
58
+ // First we try to find the after the entity
59
+ var nextEntity = entity.nextElementSibling;
60
+ while (nextEntity && (!nextEntity.isEntity || nextEntity.isInspector)) {
61
+ nextEntity = nextEntity.nextElementSibling;
62
+ }
63
+
64
+ // Return if we found it
65
+ if (nextEntity && nextEntity.isEntity && !nextEntity.isInspector) {
66
+ return nextEntity;
67
+ }
68
+ // Otherwise try to find before the entity
69
+ var prevEntity = entity.previousElementSibling;
70
+ while (prevEntity && (!prevEntity.isEntity || prevEntity.isInspector)) {
71
+ prevEntity = prevEntity.previousElementSibling;
72
+ }
73
+
74
+ // Return if we found it
75
+ if (prevEntity && prevEntity.isEntity && !prevEntity.isInspector) {
76
+ return prevEntity;
77
+ }
78
+
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Remove the selected entity
84
+ * @param {boolean} force (Optional) If true it won't ask for confirmation
85
+ */
86
+ export function removeSelectedEntity(force) {
87
+ if (AFRAME.INSPECTOR.selectedEntity) {
88
+ removeEntity(AFRAME.INSPECTOR.selectedEntity, force);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Insert an node after a referenced node.
94
+ * @param {Element} newNode Node to insert.
95
+ * @param {Element} referenceNode Node used as reference to insert after it.
96
+ */
97
+ function insertAfter(newNode, referenceNode) {
98
+ if (!referenceNode.parentNode) {
99
+ referenceNode = AFRAME.INSPECTOR.selectedEntity;
100
+ }
101
+
102
+ if (!referenceNode) {
103
+ AFRAME.INSPECTOR.sceneEl.appendChild(newNode);
104
+ } else {
105
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Clone an entity, inserting it after the cloned one.
111
+ * @param {Element} entity Entity to clone
112
+ */
113
+ export function cloneEntity(entity) {
114
+ entity.flushToDOM();
115
+
116
+ const clone = prepareForSerialization(entity);
117
+ clone.addEventListener(
118
+ 'loaded',
119
+ function () {
120
+ AFRAME.INSPECTOR.selectEntity(clone);
121
+ Events.emit('entityclone', clone);
122
+ },
123
+ { once: true }
124
+ );
125
+
126
+ // Get a valid unique ID for the entity
127
+ if (entity.id) {
128
+ clone.id = getUniqueId(entity.id);
129
+ }
130
+ insertAfter(clone, entity);
131
+ }
132
+
133
+ /**
134
+ * Clone the selected entity
135
+ */
136
+ export function cloneSelectedEntity() {
137
+ if (AFRAME.INSPECTOR.selectedEntity) {
138
+ cloneEntity(AFRAME.INSPECTOR.selectedEntity);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Return the clipboard representation to be used to copy to the clipboard
144
+ * @param {Element} entity Entity to copy to clipboard
145
+ * @return {string} Entity clipboard representation
146
+ */
147
+ export function getEntityClipboardRepresentation(entity) {
148
+ var clone = prepareForSerialization(entity);
149
+ return clone.outerHTML;
150
+ }
151
+
152
+ /**
153
+ * Returns a copy of the DOM hierarchy prepared for serialization.
154
+ * The process optimises component representation to avoid values coming from
155
+ * primitive attributes, mixins and defaults.
156
+ *
157
+ * @param {Element} entity Root of the DOM hierarchy.
158
+ * @return {Element} Copy of the DOM hierarchy ready for serialization.
159
+ */
160
+ function prepareForSerialization(entity) {
161
+ var clone = entity.cloneNode(false);
162
+ var children = entity.childNodes;
163
+ for (var i = 0, l = children.length; i < l; i++) {
164
+ var child = children[i];
165
+ if (
166
+ child.nodeType !== Node.ELEMENT_NODE ||
167
+ (!child.hasAttribute('aframe-injected') &&
168
+ !child.hasAttribute('data-aframe-inspector') &&
169
+ !child.hasAttribute('data-aframe-canvas'))
170
+ ) {
171
+ clone.appendChild(prepareForSerialization(children[i]));
172
+ }
173
+ }
174
+ optimizeComponents(clone, entity);
175
+ return clone;
176
+ }
177
+
178
+ /**
179
+ * Removes from copy those components or components' properties that comes from
180
+ * primitive attributes, mixins, injected default components or schema defaults.
181
+ *
182
+ * @param {Element} copy Destinatary element for the optimization.
183
+ * @param {Element} source Element to be optimized.
184
+ */
185
+ function optimizeComponents(copy, source) {
186
+ var removeAttribute = HTMLElement.prototype.removeAttribute;
187
+ var setAttribute = HTMLElement.prototype.setAttribute;
188
+ var components = source.components || {};
189
+ Object.keys(components).forEach(function (name) {
190
+ var component = components[name];
191
+ var result = getImplicitValue(component, source);
192
+ var isInherited = result[1];
193
+ var implicitValue = result[0];
194
+ var currentValue = source.getAttribute(name);
195
+ var optimalUpdate = getOptimalUpdate(
196
+ component,
197
+ implicitValue,
198
+ currentValue
199
+ );
200
+ var doesNotNeedUpdate = optimalUpdate === null;
201
+ if (isInherited && doesNotNeedUpdate) {
202
+ removeAttribute.call(copy, name);
203
+ } else {
204
+ var schema = component.schema;
205
+ var value = stringifyComponentValue(schema, optimalUpdate);
206
+ setAttribute.call(copy, name, value);
207
+ }
208
+
209
+ // Remove special components if they use the default value
210
+ if (
211
+ value === '' &&
212
+ (name === 'visible' ||
213
+ name === 'position' ||
214
+ name === 'rotation' ||
215
+ name === 'scale')
216
+ ) {
217
+ removeAttribute.call(copy, name);
218
+ }
219
+ });
220
+ }
221
+
222
+ /**
223
+ * @param {Schema} schema The component schema.
224
+ * @param {any} data The component value.
225
+ * @return {string} The string representation of data according to the
226
+ * passed component's schema.
227
+ */
228
+ function stringifyComponentValue(schema, data) {
229
+ data = typeof data === 'undefined' ? {} : data;
230
+ if (data === null) {
231
+ return '';
232
+ }
233
+ return (isSingleProperty(schema) ? _single : _multi)();
234
+
235
+ function _single() {
236
+ return schema.stringify(data);
237
+ }
238
+
239
+ function _multi() {
240
+ var propertyBag = {};
241
+ Object.keys(data).forEach(function (name) {
242
+ if (schema[name]) {
243
+ propertyBag[name] = schema[name].stringify(data[name]);
244
+ }
245
+ });
246
+ return AFRAME.utils.styleParser.stringify(propertyBag);
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Computes the value for a component coming from primitive attributes,
252
+ * mixins, primitive defaults, a-frame default components and schema defaults.
253
+ * In this specific order.
254
+ *
255
+ * In other words, it is the value of the component if the author would have not
256
+ * overridden it explicitly.
257
+ *
258
+ * @param {Component} component Component to calculate the value of.
259
+ * @param {Element} source Element owning the component.
260
+ * @return A pair with the computed value for the component of source and a flag indicating if the component is completely inherited from other sources (`true`) or genuinely owned by the source entity (`false`).
261
+ */
262
+ function getImplicitValue(component, source) {
263
+ var isInherited = false;
264
+ var value = (isSingleProperty(component.schema) ? _single : _multi)();
265
+ return [value, isInherited];
266
+
267
+ function _single() {
268
+ var value = getMixedValue(component, null, source);
269
+ if (value === undefined) {
270
+ value = getInjectedValue(component, null, source);
271
+ }
272
+ if (value !== undefined) {
273
+ isInherited = true;
274
+ } else {
275
+ value = getDefaultValue(component, null, source);
276
+ }
277
+ if (value !== undefined) {
278
+ // XXX: This assumes parse is idempotent
279
+ return component.schema.parse(value);
280
+ }
281
+ return value;
282
+ }
283
+
284
+ function _multi() {
285
+ var value;
286
+
287
+ Object.keys(component.schema).forEach(function (propertyName) {
288
+ var propertyValue = getFromAttribute(component, propertyName, source);
289
+ if (propertyValue === undefined) {
290
+ propertyValue = getMixedValue(component, propertyName, source);
291
+ }
292
+ if (propertyValue === undefined) {
293
+ propertyValue = getInjectedValue(component, propertyName, source);
294
+ }
295
+ if (propertyValue !== undefined) {
296
+ isInherited = isInherited || true;
297
+ } else {
298
+ propertyValue = getDefaultValue(component, propertyName, source);
299
+ }
300
+ if (propertyValue !== undefined) {
301
+ var parse = component.schema[propertyName].parse;
302
+ value = value || {};
303
+ // XXX: This assumes parse is idempotent
304
+ value[propertyName] = parse(propertyValue);
305
+ }
306
+ });
307
+
308
+ return value;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * Gets the value for the component's property coming from a primitive
314
+ * attribute.
315
+ *
316
+ * Primitives have mappings from attributes to component's properties.
317
+ * The function looks for a present attribute in the source element which
318
+ * maps to the specified component's property.
319
+ *
320
+ * @param {Component} component Component to be found.
321
+ * @param {string} propertyName Component's property to be found.
322
+ * @param {Element} source Element owning the component.
323
+ * @return {any} The value of the component's property coming
324
+ * from the primitive's attribute if any or
325
+ * `undefined`, otherwise.
326
+ */
327
+ function getFromAttribute(component, propertyName, source) {
328
+ var value;
329
+ var mappings = source.mappings || {};
330
+ var route = component.name + '.' + propertyName;
331
+ var primitiveAttribute = findAttribute(mappings, route);
332
+ if (primitiveAttribute && source.hasAttribute(primitiveAttribute)) {
333
+ value = source.getAttribute(primitiveAttribute);
334
+ }
335
+ return value;
336
+
337
+ function findAttribute(mappings, route) {
338
+ var attributes = Object.keys(mappings);
339
+ for (var i = 0, l = attributes.length; i < l; i++) {
340
+ var attribute = attributes[i];
341
+ if (mappings[attribute] === route) {
342
+ return attribute;
343
+ }
344
+ }
345
+ return undefined;
346
+ }
347
+ }
348
+
349
+ /**
350
+ * Gets the value for a component or component's property coming from mixins of
351
+ * an element.
352
+ *
353
+ * If the component or component's property is not provided by mixins, the
354
+ * functions will return `undefined`.
355
+ *
356
+ * @param {Component} component Component to be found.
357
+ * @param {string} [propertyName] If provided, component's property to be
358
+ * found.
359
+ * @param {Element} source Element owning the component.
360
+ * @return The value of the component or components'
361
+ * property coming from mixins of the source.
362
+ */
363
+ function getMixedValue(component, propertyName, source) {
364
+ var value;
365
+ var reversedMixins = source.mixinEls.reverse();
366
+ for (var i = 0; value === undefined && i < reversedMixins.length; i++) {
367
+ var mixin = reversedMixins[i];
368
+ /* eslint-disable-next-line no-prototype-builtins */
369
+ if (mixin.attributes.hasOwnProperty(component.name)) {
370
+ if (!propertyName) {
371
+ value = mixin.getAttribute(component.name);
372
+ } else {
373
+ value = mixin.getAttribute(component.name)[propertyName];
374
+ }
375
+ }
376
+ }
377
+ return value;
378
+ }
379
+
380
+ /**
381
+ * Gets the value for a component or component's property coming from primitive
382
+ * defaults or a-frame defaults. In this specific order.
383
+ *
384
+ * @param {Component} component Component to be found.
385
+ * @param {string} [propertyName] If provided, component's property to be
386
+ * found.
387
+ * @param {Element} source Element owning the component.
388
+ * @return The component value coming from the
389
+ * injected default components of source.
390
+ */
391
+ function getInjectedValue(component, propertyName, source) {
392
+ var value;
393
+ var primitiveDefaults = source.defaultComponentsFromPrimitive || {};
394
+ var aFrameDefaults = source.defaultComponents || {};
395
+ var defaultSources = [primitiveDefaults, aFrameDefaults];
396
+ for (var i = 0; value === undefined && i < defaultSources.length; i++) {
397
+ var defaults = defaultSources[i];
398
+ /* eslint-disable-next-line no-prototype-builtins */
399
+ if (defaults.hasOwnProperty(component.name)) {
400
+ if (!propertyName) {
401
+ value = defaults[component.name];
402
+ } else {
403
+ value = defaults[component.name][propertyName];
404
+ }
405
+ }
406
+ }
407
+ return value;
408
+ }
409
+
410
+ /**
411
+ * Gets the value for a component or component's property coming from schema
412
+ * defaults.
413
+ *
414
+ * @param {Component} component Component to be found.
415
+ * @param {string} [propertyName] If provided, component's property to be
416
+ * found.
417
+ * @param {Element} source Element owning the component.
418
+ * @return The component value coming from the schema
419
+ * default.
420
+ */
421
+ function getDefaultValue(component, propertyName, source) {
422
+ if (!propertyName) {
423
+ return component.schema.default;
424
+ }
425
+ return component.schema[propertyName].default;
426
+ }
427
+
428
+ /**
429
+ * Returns the minimum value for a component with an implicit value to equal a
430
+ * reference value. A `null` optimal value means that there is no need for an
431
+ * update since the implicit value and the reference are equal.
432
+ *
433
+ * @param {Component} component Component of the computed value.
434
+ * @param {any} implicit The implicit value of the component.
435
+ * @param {any} reference The reference value for the component.
436
+ * @return the minimum value making the component to equal
437
+ * the reference value.
438
+ */
439
+ function getOptimalUpdate(component, implicit, reference) {
440
+ if (equal(implicit, reference)) {
441
+ return null;
442
+ }
443
+ if (isSingleProperty(component.schema)) {
444
+ return reference;
445
+ }
446
+ var optimal = {};
447
+ Object.keys(reference).forEach(function (key) {
448
+ var needsUpdate = !equal(reference[key], implicit[key]);
449
+ if (needsUpdate) {
450
+ optimal[key] = reference[key];
451
+ }
452
+ });
453
+ return optimal;
454
+ }
455
+
456
+ /**
457
+ * @param {Schema} schema Component's schema to test if it is single property.
458
+ * @return `true` if component is single property.
459
+ */
460
+ function isSingleProperty(schema) {
461
+ return AFRAME.schema.isSingleProperty(schema);
462
+ }
463
+
464
+ /**
465
+ * Detect element's Id collision and returns a valid one
466
+ * @param {string} baseId Proposed Id
467
+ * @return {string} Valid Id based on the proposed Id
468
+ */
469
+ function getUniqueId(baseId) {
470
+ if (!document.getElementById(baseId)) {
471
+ return baseId;
472
+ }
473
+
474
+ var i = 2;
475
+ // If the baseId ends with _#, it extracts the baseId removing the suffix
476
+ var groups = baseId.match(/(\w+)-(\d+)/);
477
+ if (groups) {
478
+ baseId = groups[1];
479
+ i = groups[2];
480
+ }
481
+
482
+ while (document.getElementById(baseId + '-' + i)) {
483
+ i++;
484
+ }
485
+
486
+ return baseId + '-' + i;
487
+ }
488
+
489
+ export function getComponentClipboardRepresentation(entity, componentName) {
490
+ /**
491
+ * Get the list of modified properties
492
+ * @param {Element} entity Entity where the component belongs
493
+ * @param {string} componentName Component name
494
+ * @return {object} List of modified properties with their value
495
+ */
496
+ function getModifiedProperties(entity, componentName) {
497
+ var data = entity.components[componentName].data;
498
+ var defaultData = entity.components[componentName].schema;
499
+ var diff = {};
500
+ for (var key in data) {
501
+ // Prevent adding unknown attributes
502
+ if (!defaultData[key]) {
503
+ continue;
504
+ }
505
+
506
+ var defaultValue = defaultData[key].default;
507
+ var currentValue = data[key];
508
+
509
+ // Some parameters could be null and '' like mergeTo
510
+ if ((currentValue || defaultValue) && currentValue !== defaultValue) {
511
+ diff[key] = data[key];
512
+ }
513
+ }
514
+ return diff;
515
+ }
516
+
517
+ const diff = getModifiedProperties(entity, componentName);
518
+ const attributes = AFRAME.utils.styleParser.stringify(diff);
519
+ return `${componentName}="${attributes}"`;
520
+ }
521
+
522
+ /**
523
+ * Helper function to add a new entity with a list of components
524
+ * @param {object} definition Entity definition to add, only components is required:
525
+ * {element: 'a-entity', id: "hbiuSdYL2", class: "box", components: {geometry: 'primitive:box'}}
526
+ * @return {Element} Entity created
527
+ */
528
+ export function createEntity(definition, cb) {
529
+ const entity = document.createElement(definition.element || 'a-entity');
530
+ if (definition.id) {
531
+ entity.id = definition.id;
532
+ }
533
+
534
+ if (definition.class) {
535
+ entity.setAttribute('class', definition.class);
536
+ }
537
+
538
+ // load default attributes
539
+ for (let attr in definition.components) {
540
+ entity.setAttribute(attr, definition.components[attr]);
541
+ }
542
+
543
+ // Ensure the components are loaded before update the UI
544
+ entity.addEventListener(
545
+ 'loaded',
546
+ () => {
547
+ Events.emit('entitycreated', entity);
548
+ cb(entity);
549
+ },
550
+ { once: true }
551
+ );
552
+
553
+ AFRAME.scenes[0].appendChild(entity);
554
+
555
+ return entity;
556
+ }
@@ -0,0 +1,30 @@
1
+ import Events from './Events';
2
+
3
+ export const updates = {};
4
+
5
+ /**
6
+ * Store change to export.
7
+ *
8
+ * payload: entity, component, property, value.
9
+ */
10
+ Events.on('entityupdate', (payload) => {
11
+ let value = payload.value;
12
+
13
+ const entity = payload.entity;
14
+ updates[entity.id] = updates[entity.id] || {};
15
+
16
+ const component = AFRAME.components[payload.component];
17
+ if (component) {
18
+ if (payload.property) {
19
+ updates[entity.id][payload.component] =
20
+ updates[entity.id][payload.component] || {};
21
+ if (component.schema[payload.property]) {
22
+ value = component.schema[payload.property].stringify(payload.value);
23
+ }
24
+ updates[entity.id][payload.component][payload.property] = value;
25
+ } else {
26
+ value = component.schema.stringify(payload.value);
27
+ updates[entity.id][payload.component] = value;
28
+ }
29
+ }
30
+ });
@@ -0,0 +1,129 @@
1
+ import Events from './Events';
2
+
3
+ export function initRaycaster(inspector) {
4
+ // Use cursor="rayOrigin: mouse".
5
+ const mouseCursor = document.createElement('a-entity');
6
+ mouseCursor.setAttribute('id', 'aframeInspectorMouseCursor');
7
+ mouseCursor.setAttribute('raycaster', {
8
+ interval: 100,
9
+ objects: 'a-scene :not([data-aframe-inspector])'
10
+ });
11
+ mouseCursor.setAttribute('cursor', 'rayOrigin', 'mouse');
12
+ mouseCursor.setAttribute('data-aframe-inspector', 'true');
13
+
14
+ // Only visible objects.
15
+ const raycaster = mouseCursor.components.raycaster;
16
+ const refreshObjects = raycaster.refreshObjects;
17
+ const overrideRefresh = () => {
18
+ refreshObjects.call(raycaster);
19
+ const objects = raycaster.objects;
20
+ raycaster.objects = objects.filter((node) => {
21
+ while (node) {
22
+ if (!node.visible) {
23
+ return false;
24
+ }
25
+ node = node.parent;
26
+ }
27
+ return true;
28
+ });
29
+ };
30
+ raycaster.refreshObjects = overrideRefresh;
31
+
32
+ inspector.sceneEl.appendChild(mouseCursor);
33
+ inspector.cursor = mouseCursor;
34
+
35
+ mouseCursor.addEventListener('click', handleClick);
36
+ mouseCursor.addEventListener('mouseenter', onMouseEnter);
37
+ mouseCursor.addEventListener('mouseleave', onMouseLeave);
38
+ inspector.container.addEventListener('mousedown', onMouseDown);
39
+ inspector.container.addEventListener('mouseup', onMouseUp);
40
+ inspector.container.addEventListener('dblclick', onDoubleClick);
41
+
42
+ inspector.sceneEl.canvas.addEventListener('mouseleave', () => {
43
+ setTimeout(() => {
44
+ Events.emit('raycastermouseleave', null);
45
+ });
46
+ });
47
+
48
+ const onDownPosition = new THREE.Vector2();
49
+ const onUpPosition = new THREE.Vector2();
50
+
51
+ function onMouseEnter() {
52
+ Events.emit(
53
+ 'raycastermouseenter',
54
+ mouseCursor.components.cursor.intersectedEl
55
+ );
56
+ }
57
+
58
+ function onMouseLeave() {
59
+ Events.emit(
60
+ 'raycastermouseleave',
61
+ mouseCursor.components.cursor.intersectedEl
62
+ );
63
+ }
64
+
65
+ function handleClick(evt) {
66
+ // Check to make sure not dragging.
67
+ if (onDownPosition.distanceTo(onUpPosition) === 0) {
68
+ inspector.selectEntity(evt.detail.intersectedEl);
69
+ }
70
+ }
71
+
72
+ function onMouseDown(event) {
73
+ if (event instanceof CustomEvent) {
74
+ return;
75
+ }
76
+ event.preventDefault();
77
+ const array = getMousePosition(
78
+ inspector.container,
79
+ event.clientX,
80
+ event.clientY
81
+ );
82
+ onDownPosition.fromArray(array);
83
+ }
84
+
85
+ function onMouseUp(event) {
86
+ if (event instanceof CustomEvent) {
87
+ return;
88
+ }
89
+ event.preventDefault();
90
+ const array = getMousePosition(
91
+ inspector.container,
92
+ event.clientX,
93
+ event.clientY
94
+ );
95
+ onUpPosition.fromArray(array);
96
+ }
97
+
98
+ /**
99
+ * Focus on double click.
100
+ */
101
+ function onDoubleClick(event) {
102
+ const intersectedEl = mouseCursor.components.cursor.intersectedEl;
103
+ if (!intersectedEl) {
104
+ return;
105
+ }
106
+ Events.emit('objectfocus', intersectedEl.object3D);
107
+ }
108
+
109
+ return {
110
+ el: mouseCursor,
111
+ enable: () => {
112
+ mouseCursor.setAttribute('raycaster', 'enabled', true);
113
+ inspector.container.addEventListener('mousedown', onMouseDown);
114
+ inspector.container.addEventListener('mouseup', onMouseUp);
115
+ inspector.container.addEventListener('dblclick', onDoubleClick);
116
+ },
117
+ disable: () => {
118
+ mouseCursor.setAttribute('raycaster', 'enabled', false);
119
+ inspector.container.removeEventListener('mousedown', onMouseDown);
120
+ inspector.container.removeEventListener('mouseup', onMouseUp);
121
+ inspector.container.removeEventListener('dblclick', onDoubleClick);
122
+ }
123
+ };
124
+ }
125
+
126
+ function getMousePosition(dom, x, y) {
127
+ const rect = dom.getBoundingClientRect();
128
+ return [(x - rect.left) / rect.width, (y - rect.top) / rect.height];
129
+ }