janusweb 1.5.33 → 1.5.37

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 (47) hide show
  1. package/CHANGELOG +39 -0
  2. package/media/assets/webui/apps/avatar/avatar.js +2 -1
  3. package/media/assets/webui/apps/comms/voip.js +2 -4
  4. package/media/assets/webui/apps/editor/codemirror/addon/hint/javascript-hint.js +162 -0
  5. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.css +36 -0
  6. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.js +529 -0
  7. package/media/assets/webui/apps/editor/codemirror/addon/hint/xml-hint.js +140 -0
  8. package/media/assets/webui/apps/editor/codemirror/keymap/vim.js +5734 -0
  9. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.css +350 -0
  10. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.js +9800 -0
  11. package/media/assets/webui/apps/editor/codemirror/lib/jmldark.css +33 -0
  12. package/media/assets/webui/apps/editor/codemirror/mode/css/css.js +864 -0
  13. package/media/assets/webui/apps/editor/codemirror/mode/htmlmixed/htmlmixed.js +153 -0
  14. package/media/assets/webui/apps/editor/codemirror/mode/javascript/javascript.js +942 -0
  15. package/media/assets/webui/apps/editor/codemirror/mode/xml/xml.js +413 -0
  16. package/media/assets/webui/apps/editor/editor.css +3 -0
  17. package/media/assets/webui/apps/editor/editor.js +407 -9
  18. package/media/assets/webui/apps/editor/editor.json +6 -3
  19. package/media/assets/webui/apps/inventory/inventory.js +1 -1
  20. package/media/assets/webui/apps/locomotion/teleporter.js +1 -1
  21. package/media/assets/webui/apps/navigation/navigation.js +12 -12
  22. package/media/assets/webui/apps/vfs/browserfs/browserfs.js +8 -0
  23. package/media/assets/webui/apps/vfs/vfs.css +52 -0
  24. package/media/assets/webui/apps/vfs/vfs.js +452 -0
  25. package/media/assets/webui/apps/vfs/vfs.json +10 -0
  26. package/media/assets/webui/default.json +2 -1
  27. package/media/assets/webui/themes/default.css +29 -2
  28. package/package.json +2 -9
  29. package/scripts/client.js +64 -44
  30. package/scripts/elements/linesegments.js +5 -2
  31. package/scripts/janusbase.js +21 -9
  32. package/scripts/janusghost.js +0 -1
  33. package/scripts/janusparagraph.js +5 -3
  34. package/scripts/janusparticle.js +11 -12
  35. package/scripts/janusplayer.js +4 -4
  36. package/scripts/janusweb.js +21 -2
  37. package/scripts/janusxrplayer.js +3 -0
  38. package/scripts/multiplayermanager.js +3 -3
  39. package/scripts/object.js +159 -82
  40. package/scripts/portal.js +3 -5
  41. package/scripts/room.js +86 -44
  42. package/scripts/sound.js +0 -1
  43. package/scripts/text.js +10 -2
  44. package/scripts/translators/janusvfs.js +20 -0
  45. package/scripts/video.js +1 -1
  46. package/scripts/websurface.js +11 -2
  47. package/utils/init.sh +1 -1
@@ -155,13 +155,13 @@ elation.elements.define('janus.ui.editor.panel', class extends elation.elements.
155
155
  if (typeof room == 'undefined') return;
156
156
  if (!this.manipulator) {
157
157
  let view = janus.engine.client.view;
158
- this.manipulator = new THREE.TransformControls(view.actualcamera, view.container);
158
+ this.manipulator = new THREE.TransformControls(view.actualcamera, view.canvas);
159
159
 
160
160
  elation.events.add(this.manipulator, 'mouseDown', (ev) => {
161
161
  this.roomedit.transforming = true;
162
162
  this.editObjectShowInfo(this.roomedit.object);
163
163
  if (this.manipulator.object && this.manipulator.object.userData.thing) {
164
- //elation.events.fire({type: 'admin_edit_start', element: this, data: this.manipulator.object.userData.thing});
164
+ //elation.events.fire({type: 'admin_edit_start', element: this, data: this.manipulator.object.userData.thing});
165
165
  //player.disable();
166
166
  }
167
167
  });
@@ -271,7 +271,16 @@ console.log('set translation snap', ev.data, ev);
271
271
  janus.engine.systems.render.setdirty();
272
272
  }
273
273
  handleViewSourceClick(ev) {
274
- room.showDebug();
274
+ if (!this.sourcewindow) {
275
+ let win = elation.elements.create('ui-window', { append: document.body, center: true });
276
+ let editor = elation.elements.create('janus-ui-editor-source', { room: room });
277
+
278
+ win.setcontent(editor);
279
+ setTimeout(() => { win.center = false; }, 1500);
280
+ this.sourcewindow = win;
281
+ } else {
282
+ this.sourcewindow.show();
283
+ }
275
284
  }
276
285
  handleExportClick(ev) {
277
286
  let exporter = new THREE.GLTFExporter();
@@ -281,7 +290,7 @@ console.log('set translation snap', ev.data, ev);
281
290
  var url = window.URL.createObjectURL(filedata);
282
291
 
283
292
  let d = new Date(),
284
- ts = d.getFullYear() + (d.getMonth() + 1).toString().padStart(2, '0') + d.getDate().toString().padStart(2, '0') + d.getHours().toString().padStart(2, '0') + d.getMinutes().toString().padStart(2, '0') + d.getSeconds().toString().padStart(2, '0')
293
+ ts = d.getFullYear() + (d.getMonth() + 1).toString().padStart(2, '0') + d.getDate().toString().padStart(2, '0') + d.getHours().toString().padStart(2, '0') + d.getMinutes().toString().padStart(2, '0') + d.getSeconds().toString().padStart(2, '0');
285
294
 
286
295
  var a = document.createElement('A');
287
296
  a.style.display = 'none';
@@ -433,7 +442,7 @@ console.log('set translation snap', ev.data, ev);
433
442
  editObjectCloneWireframe(obj, parent, material, constrain) {
434
443
  let newobj = null;
435
444
  if (obj instanceof THREE.Mesh) {
436
- newobj = new THREE.Mesh(obj.geometry, material)
445
+ newobj = new THREE.Mesh(obj.geometry, material);
437
446
  newobj.matrixAutoUpdate = false;
438
447
  newobj.matrix.copy(obj.matrix);
439
448
  //newobj.matrixWorld.copy(obj.matrix);
@@ -694,7 +703,7 @@ obj.opacity = obj.opacity;
694
703
  if ('min' in attrdef && 'max' in attrdef) {
695
704
  min = attrdef.min;
696
705
  max = attrdef.max;
697
- amount *= (max - min) / divisor;
706
+ amount *= (max - min) / divisor;
698
707
  } else {
699
708
  amount /= divisor;
700
709
  }
@@ -1111,6 +1120,7 @@ console.log('change color', obj.col, vec);
1111
1120
  */
1112
1121
  }
1113
1122
  handleThingAdd(ev) {
1123
+ console.log('[editor] scene added a thing', ev);
1114
1124
  setTimeout(() => {
1115
1125
  this.scenetree.refreshList();
1116
1126
  }, 0);
@@ -1136,7 +1146,7 @@ console.log('change color', obj.col, vec);
1136
1146
  sync: true,
1137
1147
  pos: new elation.physics.vector3().copy(player.vectors.cursor_pos),
1138
1148
  persist: true
1139
- }
1149
+ };
1140
1150
  if (trimmedurl.length > 0) {
1141
1151
  var schemeidx = trimmedurl.indexOf(':');
1142
1152
  if (schemeidx != -1) {
@@ -1219,14 +1229,26 @@ console.log('change color', obj.col, vec);
1219
1229
  // handle them (eg, video). However, CORS is a nightmare, and most servers don't whitelist Range headers.
1220
1230
 
1221
1231
  // If mime type detection fails, we could also check for known signatures in the first 1k of data, but we don't do that here yet
1232
+ if (false) {
1222
1233
  let proxiedurl = url;
1223
- if (elation.engine.assets.corsproxy && !url.match(/^https?:\/\/localhost/) && !url.match(/^data:/)) {
1234
+ if (elation.engine.assets.corsproxy && !url.match(/^https?:\/\/localhost/) && !url.match(/^data:/) && !url.match(/^janus-vfs:/)) {
1224
1235
  proxiedurl = elation.engine.assets.corsproxy + url;
1225
1236
  }
1226
1237
  fetch(proxiedurl, { headers: { /*'Range': 'bytes=0-1023'}*/ }}).then(res => resolve(res.headers.get('content-type')))
1227
1238
  .catch((e) => {
1228
1239
  reject();
1229
1240
  });
1241
+ } else {
1242
+ elation.engine.assetdownloader.fetchURL(url).then(ev => {
1243
+ let res = ev.target;
1244
+ if (res.headers) {
1245
+ resolve(res.headers.get('content-type'));
1246
+ } else {
1247
+ // FIXME - no headers, unknown type
1248
+ resolve();
1249
+ }
1250
+ });
1251
+ }
1230
1252
  });
1231
1253
  }
1232
1254
  loadObjectFromJML(jml) {
@@ -1241,7 +1263,8 @@ console.log('change color', obj.col, vec);
1241
1263
  args = {
1242
1264
  id: name,
1243
1265
  pos: player.vectors.cursor_pos.clone(),
1244
- sync: true
1266
+ sync: true,
1267
+ persist: true,
1245
1268
  },
1246
1269
  assetargs = {
1247
1270
  id: name,
@@ -1510,6 +1533,381 @@ elation.elements.define('janus.ui.editor.scenetree', class extends elation.eleme
1510
1533
  elation.events.fire({type: 'select', element: this, data: ev.data.value.getProxyObject()});
1511
1534
  }
1512
1535
  });
1536
+ elation.elements.define('janus.ui.editor.source', class extends elation.elements.base {
1537
+ async create() {
1538
+ await janus.ui.apps.default.apps.editor.loadScriptsAndCSS([
1539
+ './codemirror/addon/hint/show-hint.js',
1540
+ './codemirror/addon/hint/xml-hint.js',
1541
+ './codemirror/addon/hint/javascript-hint.js',
1542
+ './codemirror/keymap/vim.js',
1543
+ ], [
1544
+ './codemirror/addon/hint/show-hint.css',
1545
+ ]);
1546
+ await room.loadComponentList();
1547
+ this.jmlhints = this.buildJMLMap();
1548
+
1549
+ this.tabs = elation.elements.create('ui-tabs', { append: this });
1550
+ //let roomtab = elation.elements.create('ui-tab', { append: this.tabs, label: 'Room Markup' });
1551
+ let roomedit = elation.elements.create('janus-ui-editor-source-file', { append: this.tabs, label: 'Room Markup', source: room.getRoomSource(), hints: this.jmlhints });
1552
+
1553
+ if (room.roomassets.script) {
1554
+ for (let k in room.roomassets.script) {
1555
+ let scriptasset = room.roomassets.script[k];
1556
+ //let scripttab = elation.elements.create('ui-tab', { append: this.tabs, label: k });
1557
+ let scriptedit = elation.elements.create('janus-ui-editor-source-file', { append: this.tabs, label: k, source: scriptasset.code, mode: 'javascript' });
1558
+ }
1559
+ }
1560
+ }
1561
+ buildJMLMap() {
1562
+ let tagMap = {};
1563
+ for (let tag in janus.customElements) tagMap[tag] = janus.customElements[tag];
1564
+ for (let tag in room.customElements) tagMap[tag] = room.customElements[tag];
1565
+ let allTags = Object.keys(tagMap).sort();
1566
+
1567
+ let hints = {
1568
+ "!top": ["janus-viewer"],
1569
+ "fireboxroom": {
1570
+ children: ['assets', 'room'],
1571
+ },
1572
+ "janus-viewer": {
1573
+ children: ['assets', 'room'],
1574
+ },
1575
+ 'assets': {
1576
+ children: ['assetimage', 'assetobject', 'assetfont', 'assetwebsurface', 'assetscript', 'assetsound', 'assetvideo', 'assetshader', 'assetghost'],
1577
+ },
1578
+ 'room': {
1579
+ attrs: {},
1580
+ children: allTags,
1581
+ },
1582
+ 'assetimage': {
1583
+ attrs: this.getAssetAttributes('image'),
1584
+ },
1585
+ 'assetobject': {
1586
+ attrs: this.getAssetAttributes('model'),
1587
+ },
1588
+ 'assetfont': {
1589
+ attrs: this.getAssetAttributes('font'),
1590
+ },
1591
+ 'assetwebsurface': {
1592
+ attrs: {
1593
+ id: null,
1594
+ src: null,
1595
+ },
1596
+ },
1597
+ 'assetscript': {
1598
+ attrs: this.getAssetAttributes('script'),
1599
+ },
1600
+ 'assetsound': {
1601
+ attrs: this.getAssetAttributes('sound'),
1602
+ },
1603
+ 'assetvideo': {
1604
+ attrs: this.getAssetAttributes('video'),
1605
+ },
1606
+ 'assetshader': {
1607
+ attrs: this.getAssetAttributes('shader'),
1608
+ },
1609
+ 'assetghost': {
1610
+ attrs: {
1611
+ id: null,
1612
+ src: null,
1613
+ },
1614
+ }
1615
+ };
1616
+ hints.Room = hints.room;
1617
+ hints.FireBoxRoom = hints.fireboxroom;
1618
+ hints.Assets = hints.assets;
1619
+ hints.AssetImage = hints.assetimage;
1620
+ hints.AssetObject = hints.assetobject;
1621
+ hints.AssetFont = hints.assetfont;
1622
+ hints.AssetWebsurface = hints.assetwebsurface;
1623
+ hints.AssetScript = hints.assetscript;
1624
+ hints.AssetSound = hints.assetsound;
1625
+ hints.AssetVideo = hints.assetvideo;
1626
+ hints.AssetShader = hints.assetshader;
1627
+ hints.AssetGhost = hints.assetghost;
1628
+
1629
+ let roomattrs = this.getAttributes({classname: 'janusroom'}, room);
1630
+ let roomattrkeys = Object.keys(roomattrs).sort();
1631
+ roomattrkeys.forEach(attrname => {
1632
+ if (attrname != 'requires') { // We'll handle <room require="..."> as a special case, partly because of the name mismatch and partly so we can autosuggest components from the repository
1633
+ let attr = roomattrs[attrname];
1634
+ hints['room'].attrs[attrname] = (attr.type == 'boolean' || attr.types == 'bool' ? ['true', 'false'] : null);
1635
+ } else {
1636
+ hints['room'].attrs.require = Object.keys(room.componentrepository).sort();;
1637
+ }
1638
+ });
1639
+
1640
+ hints['room'].attrDelims = { require: ' ' };
1641
+
1642
+ for (let i = 0; i < allTags.length; i++) {
1643
+ let tag = allTags[i],
1644
+ tagdef = tagMap[tag];
1645
+ let attrs = this.getAttributes(tagdef);
1646
+
1647
+ hints[tag] = {
1648
+ attrs: {},
1649
+ children: allTags,
1650
+ };
1651
+ hints[elation.utils.camelize('.' + tag)] = hints[tag]; // capitalize first letter of tag, mapped to the all-lowercase tag definition
1652
+
1653
+ let attrkeys = Object.keys(attrs).sort();
1654
+ attrkeys.forEach(attrname => {
1655
+ let attr = attrs[attrname];
1656
+ hints[tag].attrs[attrname] = (attr.type == 'boolean' || attr.types == 'bool' ? ['true', 'false'] : null);
1657
+ });
1658
+ }
1659
+
1660
+ // Autocomplete asset ids of various types
1661
+ hints.object.attrs.id =
1662
+ hints.object.attrs.collision_id =
1663
+ hints.particle.attrs.emitter_id =
1664
+ cm => this.getAssetSuggestions('model');
1665
+
1666
+ hints.object.attrs.image_id =
1667
+ hints.object.attrs.normalmap_id =
1668
+ hints.object.attrs.roughness_id =
1669
+ hints.object.attrs.metalness_id =
1670
+ hints.object.attrs.displacementmap_id =
1671
+ hints.object.attrs.emissive_id =
1672
+ hints.object.attrs.alphamap_id =
1673
+ hints.object.attrs.envmap_id =
1674
+ hints.object.attrs.lmap_id =
1675
+ hints.object.attrs.lmap_id =
1676
+ hints.image.attrs.id =
1677
+ hints.image3d.attrs.id =
1678
+ hints.particle.attrs.image_id =
1679
+ hints.link.attrs.thumb_id =
1680
+ hints.room.attrs.skybox_up_id =
1681
+ hints.room.attrs.skybox_down_id =
1682
+ hints.room.attrs.skybox_left_id =
1683
+ hints.room.attrs.skybox_right_id =
1684
+ hints.room.attrs.skybox_front_id =
1685
+ hints.room.attrs.skybox_back_id =
1686
+ hints.room.attrs.skybox_equi =
1687
+ cm => this.getAssetSuggestions('image');
1688
+
1689
+ hints.object.attrs.video_id =
1690
+ hints.video.attrs.video_id =
1691
+ cm => this.getAssetSuggestions('video');
1692
+
1693
+ hints.object.attrs.shader_id =
1694
+ hints.link.attrs.shader_id =
1695
+ cm => this.getAssetSuggestions('shader');
1696
+
1697
+ hints.text.attrs.font = cm => this.getAssetSuggestions('font');
1698
+ hints.sound.attrs.id = cm => this.getAssetSuggestions('sound');
1699
+
1700
+ hints.object.attrs.cull_face =
1701
+ hints.paragraph.attrs.cull_face =
1702
+ ['back', 'front', 'none'];
1703
+
1704
+ hints.object.blend_src =
1705
+ hints.object.blend_dest =
1706
+ ['zero', 'one', 'src_color', 'dst_color', 'src_alpha', 'dst_alpha', 'one_minus_src_color', 'one_minus_src_alpha', 'one_minus_dst_color', 'one_minus_dst_alpha'];
1707
+
1708
+ hints.room.attrs.fog_mode = ['linear', 'exp', 'exp2'];
1709
+ hints.room.attrs.tonemapping_type = Object.keys(room.toneMappingTypes);
1710
+
1711
+ return hints;
1712
+ }
1713
+ getAssetSuggestions(assettype) {
1714
+ let hints = [];
1715
+ if (janus.assetpack.assetmap[assettype]) Array.prototype.push.apply(hints, Object.keys(janus.assetpack.assetmap[assettype]));
1716
+ if (room.assetpack && room.assetpack.assetmap[assettype]) Array.prototype.push.apply(hints, Object.keys(room.assetpack.assetmap[assettype]));
1717
+
1718
+ return hints.sort();
1719
+ }
1720
+ getAttributes(objdef, bindobj) {
1721
+ console.log('parse the object', objdef);
1722
+ let elationThing = elation.engine.things[objdef.classname];
1723
+ let allProperties = this.parseThingProperties(elationThing, null, bindobj);
1724
+
1725
+ if (!(objdef.class instanceof Function)) {
1726
+ for (let attrname in objdef.class) {
1727
+ let attr = objdef.class[attrname];
1728
+ let attrtype = (attr === true || attr === false ? 'boolean' : 'whatever');
1729
+ allProperties.properties[attrname] = { type: attrtype };
1730
+ allProperties.proxies[attrname] = ['property', attrname];
1731
+ }
1732
+ }
1733
+
1734
+ let attrs = {};
1735
+ if (allProperties) {
1736
+ for (let k in allProperties.proxies) {
1737
+ let p = allProperties.proxies[k];
1738
+ if (p[0] == 'property' && allProperties.properties[p[1]]) {
1739
+ let attr = allProperties.properties[p[1]];
1740
+ if (attr.type != 'object') { // Skip object types, since they're generally only used internally
1741
+ attrs[k] = attr;
1742
+ }
1743
+ } else if (p[0] == 'callback') {
1744
+ attrs[k] = { type: 'callback' };
1745
+ }
1746
+ }
1747
+ }
1748
+ return attrs;
1749
+ }
1750
+ getAssetAttributes(assetname) {
1751
+ let attrs = {},
1752
+ props = this.parseAssetProperties(assetname);
1753
+ let propkeys = Object.keys(props).sort();
1754
+ for (let i = 0; i < propkeys.length; i++) {
1755
+ attrs[propkeys[i]] = (props[propkeys[i]].type == 'boolean' ? ['true', 'false'] : null);
1756
+ }
1757
+ return attrs;
1758
+ }
1759
+ parseAssetProperties(assetname) {
1760
+ let props = {};
1761
+ if (elation.engine.assets[assetname]) {
1762
+ let prot = elation.engine.assets[assetname].prototype;
1763
+ let exclude = ['assettype', 'assetpack', 'instances', 'baseurl', 'preview', 'loaded', 'size', 'proxy', 'sourceurl', 'author', 'license', 'description', 'frames', 'canvas', 'rawimage', 'texture', 'video'];
1764
+ for (let k in prot) {
1765
+ if (typeof prot[k] != 'function' && exclude.indexOf(k) == -1) {
1766
+ let attrtype = (prot[k] === true || prot[k] === false ? 'boolean' : 'whatever');
1767
+ let propname = (k == 'name' ? 'id' : k);
1768
+ props[propname] = { type: attrtype };
1769
+ }
1770
+ }
1771
+ }
1772
+ return props;
1773
+ }
1774
+ parseThingProperties(thing, props, bindobj) {
1775
+ if (!props) props = {
1776
+ properties: {},
1777
+ proxies: {}
1778
+ };
1779
+
1780
+ if (thing.extendclassdef) {
1781
+ let parentThing = thing.extendclassdef;
1782
+ this.parseThingProperties(parentThing, props);
1783
+ }
1784
+
1785
+ let mProps = thing.classdef.toString().match(/defineProperties\(({.*?})\)/ms);
1786
+ let mProxies = thing.classdef.toString().match(/(new elation\.proxy\(this, |_proxydefs = )({.*?})[;|\)]/ms);
1787
+ (elation.bind(bindobj || this, function() {
1788
+ if (mProps) {
1789
+ let propmap;
1790
+ eval('propmap = ' + mProps[1]);
1791
+ for (let k in propmap) props.properties[k] = propmap[k];
1792
+ }
1793
+ if (mProxies) {
1794
+ let proxymap;
1795
+ eval('proxymap = ' + mProxies[2]);
1796
+ for (let k in proxymap) props.proxies[k] = proxymap[k];
1797
+ }
1798
+ }))();
1799
+ return props;
1800
+ }
1801
+ });
1802
+ elation.elements.define('janus.ui.editor.source.file', class extends elation.elements.ui.tab {
1803
+ init() {
1804
+ super.init();
1805
+ this.defineAttributes({
1806
+ source: { type: 'string' },
1807
+ filename: { type: 'string', default: 'New File' },
1808
+ mode: { type: 'string', default: 'xml' },
1809
+ hints: { type: 'object' },
1810
+ });
1811
+ }
1812
+ create() {
1813
+ this.filename = this.label;
1814
+
1815
+ this.initCodemirror();
1816
+ elation.events.add(this.parentNode, 'tabselect', ev => { this.codemirror.refresh(); setTimeout(() => { this.codemirror.focus(); }, 0); });
1817
+ }
1818
+ async initCodemirror() {
1819
+ if (!CodeMirror.modes[this.mode]) {
1820
+ await janus.ui.apps.default.apps.editor.loadScriptsAndCSS([`./codemirror/mode/${this.mode}/${this.mode}.js`]);
1821
+ }
1822
+
1823
+ this.codemirror = CodeMirror(this, {
1824
+ value: this.source,
1825
+ mode: this.mode,
1826
+ lineNumbers: true,
1827
+ extraKeys: {
1828
+ 'Ctrl-S': () => this.handleSave(),
1829
+ 'Ctrl-Space':(cm) => { CodeMirror.showHint(cm, CodeMirror.hint[this.mode]); },
1830
+ 'Ctrl-Alt-V':(cm) => {
1831
+ if (cm.state.keyMaps.indexOf(CodeMirror.keyMap.vim) != -1) {
1832
+ // FIXME - removing the keymap doesn't actually work, the bindings stay active
1833
+ cm.removeKeyMap(CodeMirror.keyMap.vim);
1834
+ } else {
1835
+ cm.addKeyMap(CodeMirror.keyMap.vim);
1836
+ }
1837
+ cm.refresh();
1838
+ },
1839
+ },
1840
+ hintOptions: {
1841
+ autohint: true,
1842
+ schemaInfo: this.hints,
1843
+ matchInMiddle: true,
1844
+ },
1845
+ });
1846
+ console.log('editor hints', this.hints);
1847
+ this.codemirror.on('change', (ev) => this.handleEditContentChange(ev));
1848
+ this.codemirror.on('change', (ev) => this.handleEditContentChange(ev));
1849
+
1850
+ let skipKeyCodes = [
1851
+ 8, // backspace
1852
+ 9, // tab
1853
+ 13, // enter
1854
+ 16, // shift
1855
+ 17, // ctrl
1856
+ 18, // alt
1857
+ 27, // esc
1858
+ //32, // space
1859
+ 33, // pgup
1860
+ 34, // pgdn
1861
+ 35, // home
1862
+ 36, // end
1863
+ 37, // left
1864
+ 38, // up
1865
+ 39, // right
1866
+ 40, // down
1867
+ 46, // delete
1868
+
1869
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // 0-9
1870
+ 186, // ;
1871
+ 189, // -
1872
+ 219, // {
1873
+ 221, // }
1874
+ ];
1875
+ if (this.mode == 'javascript') {
1876
+ skipKeyCodes.push(32); // space
1877
+ skipKeyCodes.push(187); // =
1878
+ } else if (this.mode == 'xml') {
1879
+ skipKeyCodes.push(190); // . and >
1880
+ }
1881
+ this.codemirror.on("keyup", function (cm, event) {
1882
+ if ((!cm.state.completionActive && /*Enables keyboard navigation in autocomplete list*/
1883
+ !event.ctrlKey && !event.altKey &&
1884
+ skipKeyCodes.indexOf(event.keyCode) == -1)) { /*Enter - do not open autocomplete list just after item has been selected in it*/
1885
+ CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});
1886
+ }
1887
+ });
1888
+ }
1889
+ handleSave() {
1890
+ this.source = this.codemirror.getValue();
1891
+ this.label = this.filename;
1892
+ }
1893
+ handleEditContentChange(ev) {
1894
+ //console.log('it changed', ev);
1895
+ let newsource = this.codemirror.getValue();
1896
+ this.dirty = (newsource != this.source);
1897
+ if (this.dirty) {
1898
+ this.label = '*' + this.filename;
1899
+ } else {
1900
+ this.label = this.filename;
1901
+ }
1902
+ if (this.updatetimer) {
1903
+ clearTimeout(this.updatetimer);
1904
+ }
1905
+ this.updatetimer = setTimeout(() => {
1906
+ room.updateSource(newsource);
1907
+ this.updatetimer = false;
1908
+ }, 500);
1909
+ }
1910
+ });
1513
1911
 
1514
1912
  class ColorTransform extends THREE.Object3D {
1515
1913
 
@@ -3,10 +3,13 @@
3
3
  "janus.ui.editor.panel": "./editor-panel.html",
4
4
  "janus.ui.editor.object.info": "./editor-object-info.html"
5
5
  },
6
- "scripts": ["./editor.js", "./editor-properties.js"],
7
- "css": ["./editor.css"],
6
+ "scripts": ["./editor.js", "./editor-properties.js", "./codemirror/lib/codemirror.js"],
7
+ "css": ["./editor.css", "./codemirror/lib/codemirror.css", "./codemirror/lib/jmldark.css"],
8
8
  "components": [
9
- "janus-ui-editor-panel"
9
+ "janus-ui-editor-panel",
10
+ "janus-ui-editor-objectinfo",
11
+ "janus-ui-editor-scenetree",
12
+ "janus-ui-editor-source"
10
13
  ]
11
14
  }
12
15
 
@@ -68,7 +68,7 @@ elation.elements.define('janus.ui.inventory.roomassets.list', class extends elat
68
68
  this.list.setItems(this.getAssetList());
69
69
  }
70
70
  getAssetList() {
71
- if (room.assetpack.assetmap[this.assettype]) {
71
+ if (room.assetpack && room.assetpack.assetmap[this.assettype]) {
72
72
  return room.assetpack.assetmap[this.assettype];
73
73
  }
74
74
  return {};
@@ -154,7 +154,7 @@ janus.registerElement('locomotion_teleporter', {
154
154
  let laserpoints = this.laser.positions;
155
155
  // Trace our arc in steps, performing a raytrace at each step
156
156
  for (i = 0; i < segments; i++) {
157
- let t = (i + 1) * duration / segments
157
+ let t = (i + 1) * duration / segments;
158
158
  dir.copy(v0);
159
159
  speed = dir.length();
160
160
  dir.divideScalar(speed);
@@ -16,12 +16,12 @@ elation.elements.define('janus.ui.navigation', class extends elation.elements.ui
16
16
  getClient() {
17
17
  var node = this;
18
18
  while (node && node !== document) {
19
- if (node.dataset['elationComponent'] == 'janusweb.client') {
20
- return elation.component.fetch(node);
19
+ if (node instanceof elation.elements.janusweb.client) {
20
+ return node;
21
21
  }
22
- node = node.parentNode
22
+ node = node.parentNode;
23
23
  }
24
- return elation.component.fetch(document.querySelector('[data-elation-component="janusweb.client"]'));
24
+ return document.querySelector('janusweb-client');
25
25
  }
26
26
  updateCurrentURL() {
27
27
  var room = this.janusweb.currentroom;
@@ -86,12 +86,12 @@ elation.elements.define('janus.ui.statusindicator', class extends elation.elemen
86
86
  getClient() {
87
87
  var node = this;
88
88
  while (node && node !== document) {
89
- if (node.dataset['elationComponent'] == 'janusweb.client') {
90
- return elation.component.fetch(node);
89
+ if (node instanceof elation.elements.janusweb.client) {
90
+ return node;
91
91
  }
92
- node = node.parentNode
92
+ node = node.parentNode;
93
93
  }
94
- return elation.component.fetch(document.querySelector('[data-elation-component="janusweb.client"]'));
94
+ return document.querySelector('janusweb-client');
95
95
  }
96
96
  updateCurrentURL(room) {
97
97
  if (!room && this.janusweb.currentroom) {
@@ -334,12 +334,12 @@ elation.elements.define('janus.ui.urlbar', class extends elation.elements.ui.pan
334
334
  getClient() {
335
335
  var node = this;
336
336
  while (node && node !== document) {
337
- if (node.dataset['elationComponent'] == 'janusweb.client') {
338
- return elation.component.fetch(node);
337
+ if (node instanceof elation.elements.janusweb.client) {
338
+ return node;
339
339
  }
340
- node = node.parentNode
340
+ node = node.parentNode;
341
341
  }
342
- return elation.component.fetch(document.querySelector('[data-elation-component="janusweb.client"]'));
342
+ return document.querySelector('janusweb-client');
343
343
  }
344
344
  updateRoom() {
345
345
  // Remove listeners from previous room