janusweb 1.5.30 → 1.5.34

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 (84) hide show
  1. package/CHANGELOG +39 -0
  2. package/LICENSE +1 -1
  3. package/media/assets/janus-avatar-animations.glb +0 -0
  4. package/media/assets/janus-avatar-base.glb +0 -0
  5. package/media/assets/webui/apps/avatar/avatar.css +6 -2
  6. package/media/assets/webui/apps/avatar/avatar.js +99 -11
  7. package/media/assets/webui/apps/buttons/buttons.html +2 -2
  8. package/media/assets/webui/apps/buttons/buttons.js +3 -3
  9. package/media/assets/webui/apps/comms/comms.js +7 -4
  10. package/media/assets/webui/apps/comms/voip.js +133 -57
  11. package/media/assets/webui/apps/editor/codemirror/addon/hint/javascript-hint.js +162 -0
  12. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.css +36 -0
  13. package/media/assets/webui/apps/editor/codemirror/addon/hint/show-hint.js +529 -0
  14. package/media/assets/webui/apps/editor/codemirror/addon/hint/xml-hint.js +140 -0
  15. package/media/assets/webui/apps/editor/codemirror/keymap/vim.js +5734 -0
  16. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.css +350 -0
  17. package/media/assets/webui/apps/editor/codemirror/lib/codemirror.js +9800 -0
  18. package/media/assets/webui/apps/editor/codemirror/lib/jmldark.css +33 -0
  19. package/media/assets/webui/apps/editor/codemirror/mode/css/css.js +864 -0
  20. package/media/assets/webui/apps/editor/codemirror/mode/htmlmixed/htmlmixed.js +153 -0
  21. package/media/assets/webui/apps/editor/codemirror/mode/javascript/javascript.js +942 -0
  22. package/media/assets/webui/apps/editor/codemirror/mode/xml/xml.js +413 -0
  23. package/media/assets/webui/apps/editor/editor.css +3 -0
  24. package/media/assets/webui/apps/editor/editor.js +409 -9
  25. package/media/assets/webui/apps/editor/editor.json +6 -3
  26. package/media/assets/webui/apps/inventory/inventory.html +7 -4
  27. package/media/assets/webui/apps/inventory/inventory.js +2 -2
  28. package/media/assets/webui/apps/locomotion/teleporter.js +10 -9
  29. package/media/assets/webui/apps/navigation/navigation.js +12 -12
  30. package/media/assets/webui/apps/settings/settings.html +1 -1
  31. package/media/assets/webui/apps/vfs/browserfs/browserfs.js +8 -0
  32. package/media/assets/webui/apps/vfs/vfs.css +52 -0
  33. package/media/assets/webui/apps/vfs/vfs.js +452 -0
  34. package/media/assets/webui/apps/vfs/vfs.json +10 -0
  35. package/media/assets/webui/apps/virtualgamepad/virtualgamepad.js +9 -7
  36. package/media/assets/webui/apps/xrmenu/xrmenu.js +13 -4
  37. package/media/assets/webui/default.json +2 -1
  38. package/media/assets/webui/none.json +1 -0
  39. package/media/assets/webui/themes/default.css +29 -2
  40. package/media/assets.json +2 -1
  41. package/media/index.html +1 -1
  42. package/media/lib/draco/README.md +32 -0
  43. package/media/lib/draco/draco_decoder.js +52 -0
  44. package/media/lib/draco/draco_decoder.wasm +0 -0
  45. package/media/lib/draco/draco_encoder.js +33 -0
  46. package/media/lib/draco/draco_wasm_wrapper.js +104 -0
  47. package/media/lib/draco/gltf/draco_decoder.js +48 -0
  48. package/media/lib/draco/gltf/draco_decoder.wasm +0 -0
  49. package/media/lib/draco/gltf/draco_encoder.js +33 -0
  50. package/media/lib/draco/gltf/draco_wasm_wrapper.js +104 -0
  51. package/media/lib/physx/physx.release.js +21 -0
  52. package/media/lib/physx/physx.release.wasm +0 -0
  53. package/media/manifest.json +5 -5
  54. package/media/service-worker.js +7 -0
  55. package/package.json +2 -9
  56. package/scripts/client.js +67 -46
  57. package/scripts/config.js +2 -0
  58. package/scripts/elements/linesegments.js +10 -3
  59. package/scripts/external/JanusFireboxParser.js +1 -0
  60. package/scripts/image.js +7 -1
  61. package/scripts/janusbase.js +104 -43
  62. package/scripts/janusghost.js +314 -16
  63. package/scripts/januslight.js +11 -2
  64. package/scripts/janusparagraph.js +8 -6
  65. package/scripts/janusparticle.js +26 -16
  66. package/scripts/janusplayer.js +126 -37
  67. package/scripts/janusweb.js +26 -2
  68. package/scripts/janusxrplayer.js +5 -3
  69. package/scripts/multiplayermanager.js +6 -4
  70. package/scripts/object.js +246 -88
  71. package/scripts/portal.js +23 -14
  72. package/scripts/remoteplayer.js +1 -25
  73. package/scripts/room.js +199 -77
  74. package/scripts/sound.js +8 -6
  75. package/scripts/text.js +13 -5
  76. package/scripts/translators/janusvfs.js +20 -0
  77. package/scripts/video.js +1 -1
  78. package/scripts/websurface.js +17 -3
  79. package/utils/build.sh +3 -0
  80. package/utils/init.sh +2 -2
  81. package/scripts/chat.js +0 -80
  82. package/scripts/ui.js +0 -265
  83. package/scripts/urlbar.js +0 -93
  84. package/templates/janusweb.tpl +0 -3
@@ -202,21 +202,19 @@ elation.elements.define('janus-voip-client-hifi', class extends elation.elements
202
202
  let localuser = this.parentNode.localuser;
203
203
  this.remoteusers[localuser.userid] = localuser;
204
204
 
205
- console.log('create hifi client', this);
206
205
  elation.file.get('js', 'https://hifi-spatial-audio-api.s3.amazonaws.com/releases/latest/HighFidelityAudio-latest.js', (ev) => {
207
206
  this.connect();
208
- console.log('FIRE HIFI INIT');
209
207
  elation.events.fire({type: 'init', element: this});
210
208
  });
211
209
  this.roomid = room.voipid || '2bfa2bd1-9338-46c3-b740-e339303a1225';
212
210
  }
213
211
  async connect() {
214
212
  let currentPosition = new HighFidelityAudio.Point3D({ "x": 0, "y": 0, "z": 0 });
215
- let currentOrientation = new HighFidelityAudio.OrientationQuat3D({ x: 0, y: 0, z: 0, w: 1});
213
+ let currentOrientation = new HighFidelityAudio.Quaternion({ x: 0, y: 0, z: 0, w: 1});
216
214
 
217
215
  let initialHiFiAudioAPIData = new HighFidelityAudio.HiFiAudioAPIData({
218
216
  position: currentPosition,
219
- orientationQuat: currentOrientation,
217
+ orientation: currentOrientation,
220
218
  providedUserID: player.getNetworkUsername(),
221
219
  });
222
220
  let hifiCommunicator = new HighFidelityAudio.HiFiCommunicator({ initialHiFiAudioAPIData });
@@ -236,7 +234,6 @@ console.log('voip picker select?', ev);
236
234
  });
237
235
  */
238
236
 
239
- console.log('new hifi client has existing input stream?', this.inputstream);
240
237
  if (this.inputstream) {
241
238
  hifiCommunicator.setInputAudioMediaStream(this.inputstream);
242
239
  }
@@ -262,12 +259,12 @@ console.log('new hifi client has existing input stream?', this.inputstream);
262
259
  }
263
260
  });
264
261
 
265
- elation.events.add(janus.engine, 'engine_frame', ev => this.handleFrameUpdate(ev))
262
+ elation.events.add(janus.engine, 'engine_frame', ev => this.handleFrameUpdate(ev));
266
263
 
267
264
  let newUserDataSubscription = new HighFidelityAudio.UserDataSubscription({
268
265
  components: [
269
266
  HighFidelityAudio.AvailableUserDataSubscriptionComponents.Position,
270
- HighFidelityAudio.AvailableUserDataSubscriptionComponents.OrientationQuat,
267
+ HighFidelityAudio.AvailableUserDataSubscriptionComponents.Orientation,
271
268
  HighFidelityAudio.AvailableUserDataSubscriptionComponents.VolumeDecibels
272
269
  ],
273
270
  callback: audioDataArray => this.handleUserDataSubscription(audioDataArray)
@@ -278,7 +275,7 @@ console.log('new hifi client has existing input stream?', this.inputstream);
278
275
  /*
279
276
  this.userData = {
280
277
  position: currentPosition,
281
- orientationQuat: currentOrientation,
278
+ orientation: currentOrientation,
282
279
  providedUserID: player.getNetworkUsername(),
283
280
  };
284
281
  */
@@ -313,10 +310,10 @@ console.log('new hifi client has existing input stream?', this.inputstream);
313
310
  this.userData.position.y = pos.y * scale;
314
311
  this.userData.position.z = pos.z * scale;
315
312
 
316
- this.userData.orientationQuat.x = orientation.x;
317
- this.userData.orientationQuat.y = orientation.y;
318
- this.userData.orientationQuat.z = orientation.z;
319
- this.userData.orientationQuat.w = orientation.w;
313
+ this.userData.orientation.x = orientation.x;
314
+ this.userData.orientation.y = orientation.y;
315
+ this.userData.orientation.z = orientation.z;
316
+ this.userData.orientation.w = orientation.w;
320
317
 
321
318
  if (this.hifiCommunicator._currentHiFiAudioAPIData) {
322
319
  this.hifiCommunicator.updateUserDataAndTransmit(this.userData);
@@ -371,7 +368,7 @@ elation.elements.define('janus-voip-localuser', class extends elation.elements.b
371
368
  this.defineAttribute('userid', { type: 'string', });
372
369
  this.defineAttribute('muted', { type: 'boolean', default: true, });
373
370
  this.defineAttribute('speaking', { type: 'boolean', default: false });
374
-
371
+ this.defineAttribute('threshold', { type: 'float', default: .2 });
375
372
  }
376
373
  create() {
377
374
  this.userid = player.getNetworkUsername();
@@ -433,6 +430,30 @@ elation.elements.define('janus-voip-localuser', class extends elation.elements.b
433
430
  */
434
431
  this.stream = data;
435
432
 
433
+ let listener = player.engine.systems.sound.getRealListener();
434
+ let context = listener.context;
435
+ let analyser = context.createAnalyser();
436
+ let source = context.createMediaStreamSource(this.stream);
437
+ source.connect(analyser);
438
+
439
+ analyser.fftSize = 32;
440
+ this.analyser = analyser;
441
+ this.audiobuffer = new Uint8Array(analyser.frequencyBinCount);
442
+
443
+ if (this.updateVolumeInterval) {
444
+ clearInterval(this.updateVolumeInterval);
445
+ this.updateVolumeInterval = false;
446
+ }
447
+ this.updateVolumeInterval = setInterval(() => {
448
+ analyser.getByteFrequencyData(this.audiobuffer);
449
+ let sum = 0;
450
+ for (let i = 0; i < this.audiobuffer.length; i++) {
451
+ sum += this.audiobuffer[i];
452
+ }
453
+ let avg = sum / this.audiobuffer.length;
454
+ this.updateVolume(avg / 100);
455
+ }, 20);
456
+
436
457
  /*
437
458
  player.createObject('object', {
438
459
  id: 'cone',
@@ -462,15 +483,24 @@ elation.elements.define('janus-voip-localuser', class extends elation.elements.b
462
483
  this.averagevolume = (percent * .6 + this.averagevolume * .4)
463
484
  if (isNaN(this.averagevolume)) this.averagevolume = 0;
464
485
  //this.style.opacity = percent;
465
- if (this.averagevolume > .1) {
486
+ if (this.averagevolume > this.threshold) {
466
487
  this.speaking = true;
488
+ player.defaultanimation = 'speak';
467
489
  if (this.colorreset) {
468
490
  clearTimeout(this.colorreset);
469
491
  }
470
492
  this.colorreset = setTimeout(() => {
471
493
  this.speaking = false;
472
494
  this.colorreset = false;
495
+ player.defaultanimation = 'idle';
473
496
  }, 200);
497
+ if (player.ghost) {
498
+ player.ghost.setSpeakingVolume(this.averagevolume);
499
+ }
500
+ } else {
501
+ if (player.ghost) {
502
+ player.ghost.setSpeakingVolume(0);
503
+ }
474
504
  }
475
505
  }
476
506
 
@@ -481,9 +511,9 @@ elation.elements.define('janus-voip-remoteuser', class extends elation.elements.
481
511
  'muted': { type: 'boolean', default: false },
482
512
  'hasaudio': { type: 'boolean', default: false },
483
513
  'hasvideo': { type: 'boolean', default: false },
484
- 'hasvideo': { type: 'boolean', default: false },
485
514
  'averagevolume': { type: 'float', default: 0 },
486
515
  'speaking': { type: 'boolean', default: false },
516
+ 'threshold': { type: 'float', default: .2 },
487
517
  });
488
518
  this.lastposition = V();
489
519
  setTimeout(() => this.setAttribute('active', true), 100);
@@ -525,6 +555,30 @@ elation.elements.define('janus-voip-remoteuser', class extends elation.elements.
525
555
  this.audio = audio;
526
556
  this.appendChild(audio);
527
557
  this.hasaudio = true;
558
+
559
+ let listener = player.engine.systems.sound.getRealListener();
560
+ let context = listener.context;
561
+ let analyser = context.createAnalyser();
562
+ let source = context.createMediaStreamSource(audio.srcObject);
563
+ source.connect(analyser);
564
+
565
+ analyser.fftSize = 32;
566
+ this.analyser = analyser;
567
+ this.audiobuffer = new Uint8Array(analyser.frequencyBinCount);
568
+
569
+ if (this.updateVolumeInterval) {
570
+ clearInterval(this.updateVolumeInterval);
571
+ this.updateVolumeInterval = false;
572
+ }
573
+ this.updateVolumeInterval = setInterval(() => {
574
+ analyser.getByteFrequencyData(this.audiobuffer);
575
+ let sum = 0;
576
+ for (let i = 0; i < this.audiobuffer.length; i++) {
577
+ sum += this.audiobuffer[i];
578
+ }
579
+ let avg = sum / this.audiobuffer.length;
580
+ this.updateVolume(avg / 100);
581
+ }, 20);
528
582
  }
529
583
 
530
584
  let remoteuser = janus.network.remoteplayers[this.id];
@@ -603,7 +657,9 @@ elation.elements.define('janus-voip-remoteuser', class extends elation.elements.
603
657
 
604
658
  elation.events.add(this.remoteuser, 'update', ev => {
605
659
  // Store user's last known position so we know where they were once they leave
606
- this.remoteuser.localToWorld(this.lastposition.set(0,0,0));
660
+ if (this.lastposition) {
661
+ this.remoteuser.localToWorld(this.lastposition.set(0,0,0));
662
+ }
607
663
  });
608
664
  }
609
665
 
@@ -658,7 +714,7 @@ elation.elements.define('janus-voip-remoteuser', class extends elation.elements.
658
714
  if (isNaN(this.averagevolume)) this.averagevolume = 0;
659
715
  //this.style.opacity = percent;
660
716
  //console.log('update volume', percent, this);
661
- if (this.averagevolume > .1) {
717
+ if (this.averagevolume > this.threshold) {
662
718
  this.speaking = true;
663
719
  if (this.colorreset) {
664
720
  clearTimeout(this.colorreset);
@@ -669,10 +725,9 @@ elation.elements.define('janus-voip-remoteuser', class extends elation.elements.
669
725
  }, 200);
670
726
  }
671
727
 
672
- let scale = 1 + this.averagevolume / 2.5;
673
728
  if (this.label3d) {
674
729
  this.label3d.setAudioVolume(this.averagevolume);
675
- this.label3d.scale.set(scale, scale, scale);
730
+ this.remoteuser.setSpeakingVolume(this.averagevolume);
676
731
  }
677
732
  }
678
733
  });
@@ -685,6 +740,9 @@ elation.elements.define('janus-voip-picker', class extends elation.elements.base
685
740
  <!-- li><ui-togglebutton name="video" disabled="1">With my webcam</ui-togglebutton></li -->
686
741
  </ul>
687
742
  `, this);
743
+ this.defineAttributes({
744
+ 'showvideo': { type: 'boolean', default: true },
745
+ });
688
746
 
689
747
  elation.events.add(this.elements.none, 'click', (ev) => this.handleSelectNone());
690
748
  elation.events.add(this.elements.audio, 'click', (ev) => this.handleSelectAudio(ev));
@@ -694,7 +752,7 @@ elation.elements.define('janus-voip-picker', class extends elation.elements.base
694
752
  if (this.voipconfig) {
695
753
  // FIXME - starting the audio without explicitly asking the user within this session is bad, but we want to resume across refreshes
696
754
  // We should use a session cookie-based timeout in addition to the localStorage settings
697
- //this.handleSelectAudio({detail: 1});
755
+ this.handleSelectAudio({detail: 1});
698
756
  }
699
757
  console.log('voip picker loaded config', this.voipconfig);
700
758
  }
@@ -715,7 +773,7 @@ elation.elements.define('janus-voip-picker', class extends elation.elements.base
715
773
  console.log('selected audio', ev.detail, this.voipconfig);
716
774
  //janus.engine.systems.sound.enableSound();
717
775
  if (!this.subpicker) {
718
- this.subpicker = elation.elements.create('janus-voip-picker-audio', { config: (this.voipconfig ? JSON.stringify(this.voipconfig) : false) });
776
+ this.subpicker = elation.elements.create('janus-voip-picker-audio', { config: (this.voipconfig ? JSON.stringify(this.voipconfig) : false), showvideo: this.showvideo });
719
777
  elation.events.add(this.subpicker, 'select', ev => {
720
778
  console.log('SELECTED', ev.detail);
721
779
  document.dispatchEvent(new CustomEvent('voip-picker-select', { detail: ev.detail }));
@@ -739,8 +797,9 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
739
797
  create() {
740
798
  this.defineAttributes({
741
799
  'config': { type: 'object', default: false },
800
+ 'showvideo': { type: 'boolean', default: true },
742
801
  });
743
- this.elements = elation.elements.fromString(`
802
+ let tplstr = `
744
803
  <div class="permissions" name="permissions">
745
804
  <ui-spinner></ui-spinner>
746
805
  Waiting for permission to be granted
@@ -752,20 +811,27 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
752
811
  <janus-voip-picker-mictest name="mictest" width="75" height="25"></janus-voip-picker-mictest>
753
812
  </li>
754
813
  <!-- li><ui-select name="outputDevice" label="Output"></ui-select></li -->
755
- <li><ui-toggle name="echoCancellation" label="Echo Cancellation"></ui-toggle></li>
756
- <li><ui-toggle name="noiseSuppression" label="Noise Reduction"></ui-toggle></li>
757
- </ul>
758
- <h3>Video</h3>
759
- <ul>
760
- <li><ui-toggle name="webcamEnabled" label="Webcam"></ui-toggle></li>
761
- <li>
762
- <ui-select name="videoDevice" label="Webcam" hidden="1"></ui-select>
763
- <janus-voip-picker-videotest name="videotest" width="150" height="150"></janus-voip-picker-videotest>
764
- </li>
814
+ <li><ui-toggle name="echoCancellation" label="Echo Cancellation" checked></ui-toggle></li>
815
+ <li><ui-toggle name="noiseSuppression" label="Noise Reduction" checked></ui-toggle></li>
765
816
  </ul>
817
+ `;
818
+
819
+ if (this.showvideo && this.showvideo != 'false') {
820
+ tplstr += `<h3>Video</h3>
821
+ <ul>
822
+ <li><ui-toggle name="webcamEnabled" label="Webcam"></ui-toggle></li>
823
+ <li>
824
+ <ui-select name="videoDevice" label="Webcam" hidden="1"></ui-select>
825
+ <janus-voip-picker-videotest name="videotest" width="150" height="150"></janus-voip-picker-videotest>
826
+ </li>
827
+ </ul>
828
+ `;
829
+ }
830
+ tplstr += `
766
831
  <ui-label name="error" hidden></ui-label>
767
832
  <ui-button name="submit">Continue</ui-button>
768
- `, this);
833
+ `;
834
+ this.elements = elation.elements.fromString(tplstr, this);
769
835
 
770
836
  if (this.config) {
771
837
  console.log('I have a config!', this.config);
@@ -776,7 +842,9 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
776
842
  //this.getUserMedia();
777
843
 
778
844
  this.elements.inputDevice.addEventListener('change', (ev) => this.getUserMedia());
779
- this.elements.videoDevice.addEventListener('change', (ev) => this.getUserMedia());
845
+ if (this.elements.videoDevice) {
846
+ this.elements.videoDevice.addEventListener('change', (ev) => this.getUserMedia());
847
+ }
780
848
  //this.elements.outputDevice.addEventListener('change', (ev) => this.getUserMedia());
781
849
  elation.events.add(this.elements.echoCancellation, 'toggle', (ev) => this.getUserMedia());
782
850
  elation.events.add(this.elements.noiseSuppression, 'toggle', (ev) => this.getUserMedia());
@@ -808,11 +876,13 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
808
876
 
809
877
  // FIXME - I'm not really using these UI elements properly here, I shouldn't be mucking with the HTML elements directly
810
878
  inputs.select.innerHTML = '';
811
- cameras.select.innerHTML = '';
812
- //outputs.select.innerHTML = '';
813
-
814
879
  let hasWebcamPermission = true;
815
- let webcamDisabled = elation.elements.create('option', {innerHTML: 'Disabled', value: 'none', append: cameras.select});
880
+ if (cameras) {
881
+ cameras.select.innerHTML = '';
882
+ //outputs.select.innerHTML = '';
883
+
884
+ this.webcamDisabledElement = elation.elements.create('option', {innerHTML: 'Disabled', value: 'none', append: cameras.select});
885
+ }
816
886
  devices.forEach(d => {
817
887
  if (d.kind == 'audioinput') {
818
888
  elation.elements.create('option', {innerHTML: d.label, value: d.deviceId, append: inputs.select});
@@ -826,16 +896,18 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
826
896
  elation.elements.create('option', {innerHTML: d.label, value: d.deviceId, append: outputs.select});
827
897
  }
828
898
  });
829
- if (hasWebcamPermission && this.elements.videoDevice.hidden ) {
830
- this.elements.webcamEnabled.hide();
831
- this.elements.videoDevice.show();
832
- let option = this.elements.videoDevice.select.childNodes[0];
833
- option.selected = true;
834
- this.elements.videoDevice.select.value = option.value;
835
- } else {
836
- console.log('NO WEBCAM PERMISSION');
837
- this.elements.webcamEnabled.show();
838
- this.elements.videoDevice.hide();
899
+ if (this.elements.videoDevice) {
900
+ if (hasWebcamPermission && this.elements.videoDevice.hidden ) {
901
+ this.elements.webcamEnabled.hide();
902
+ this.elements.videoDevice.show();
903
+ let option = this.elements.videoDevice.select.childNodes[0];
904
+ option.selected = true;
905
+ this.elements.videoDevice.select.value = option.value;
906
+ } else {
907
+ console.log('NO WEBCAM PERMISSION');
908
+ this.elements.webcamEnabled.show();
909
+ this.elements.videoDevice.hide();
910
+ }
839
911
  }
840
912
  this.elements.permissions.style.display = 'none';
841
913
 
@@ -845,7 +917,7 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
845
917
  //this.elements.inputDevice.select.value = this.voipconfig.inputDevice;
846
918
  this.elements.inputDevice.setSelected(voipconfig.inputDevice);
847
919
  }
848
- if (voipconfig.videoDevice) {
920
+ if (voipconfig.videoDevice && this.elements.videoDevice) {
849
921
  this.elements.videoDevice.setSelected(voipconfig.videoDevice);
850
922
  }
851
923
  //this.dispatchEvent(new CustomEvent('select', {detail: this.stream}));
@@ -867,7 +939,7 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
867
939
  console.log('input!', this.elements.inputDevice.value);
868
940
  constraints.audio.deviceId = { exact: this.elements.inputDevice.value };
869
941
  }
870
- if (this.elements.videoDevice.value && this.elements.videoDevice.value != 'none') {
942
+ if (this.elements.videoDevice && this.elements.videoDevice.value && this.elements.videoDevice.value != 'none') {
871
943
  console.log('video!', this.elements.videoDevice.value);
872
944
  constraints.video = {
873
945
  deviceId: { exact: this.elements.videoDevice.value },
@@ -891,7 +963,9 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
891
963
  .then(stream => {
892
964
  this.elements.submit.disabled = false;
893
965
  this.elements.mictest.setStream(stream);
894
- this.elements.videotest.setStream(stream);
966
+ if (this.elements.videotest) {
967
+ this.elements.videotest.setStream(stream);
968
+ }
895
969
  if (this.stream) {
896
970
  let tracks = this.stream.getTracks();
897
971
  tracks.forEach(t => {
@@ -928,10 +1002,12 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
928
1002
  ];
929
1003
  keys.forEach(k => {
930
1004
  let el = this.elements[k];
931
- if (el instanceof elation.elements.ui.toggle) {
932
- settings[k] = this.elements[k].checked;
933
- } else {
934
- settings[k] = this.elements[k].value;
1005
+ if (this.elements[k]) {
1006
+ if (el instanceof elation.elements.ui.toggle) {
1007
+ settings[k] = this.elements[k].checked;
1008
+ } else {
1009
+ settings[k] = this.elements[k].value;
1010
+ }
935
1011
  }
936
1012
  });
937
1013
  return settings;
@@ -954,8 +1030,8 @@ elation.elements.define('janus-voip-picker-audio', class extends elation.element
954
1030
  if ('inputDevice' in settings) this.elements['inputDevice'].setSelected(settings['inputDevice']);
955
1031
  if ('echoCancellation' in settings) this.elements['echoCancellation'].checked = settings['echoCancellation'];
956
1032
  if ('noiseSuppression' in settings) this.elements['noiseSuppression'].checked = settings['noiseSuppression'];
957
- if ('webcamEnabled' in settings) this.elements['webcamEnabled'].checked = settings['webcamEnabled'];
958
- if ('videoDevice' in settings) this.elements['videoDevice'].setSelected(settings['videoDevice']);
1033
+ if ('webcamEnabled' in settings && this.elements['webcamEnabled']) this.elements['webcamEnabled'].checked = settings['webcamEnabled'];
1034
+ if ('videoDevice' in settings && this.elements['videoDevice']) this.elements['videoDevice'].setSelected(settings['videoDevice']);
959
1035
 
960
1036
  this.currentsettings = settings;
961
1037
  }
@@ -0,0 +1,162 @@
1
+ // CodeMirror, copyright (c) by Marijn Haverbeke and others
2
+ // Distributed under an MIT license: https://codemirror.net/LICENSE
3
+
4
+ (function(mod) {
5
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
6
+ mod(require("../../lib/codemirror"));
7
+ else if (typeof define == "function" && define.amd) // AMD
8
+ define(["../../lib/codemirror"], mod);
9
+ else // Plain browser env
10
+ mod(CodeMirror);
11
+ })(function(CodeMirror) {
12
+ var Pos = CodeMirror.Pos;
13
+
14
+ function forEach(arr, f) {
15
+ for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
16
+ }
17
+
18
+ function arrayContains(arr, item) {
19
+ if (!Array.prototype.indexOf) {
20
+ var i = arr.length;
21
+ while (i--) {
22
+ if (arr[i] === item) {
23
+ return true;
24
+ }
25
+ }
26
+ return false;
27
+ }
28
+ return arr.indexOf(item) != -1;
29
+ }
30
+
31
+ function scriptHint(editor, keywords, getToken, options) {
32
+ // Find the token at the cursor
33
+ var cur = editor.getCursor(), token = getToken(editor, cur);
34
+ if (/\b(?:string|comment)\b/.test(token.type)) return;
35
+ var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
36
+ if (innerMode.mode.helperType === "json") return;
37
+ token.state = innerMode.state;
38
+
39
+ // If it's not a 'word-style' token, ignore the token.
40
+ if (!/^[\w$_]*$/.test(token.string)) {
41
+ token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
42
+ type: token.string == "." ? "property" : null};
43
+ } else if (token.end > cur.ch) {
44
+ token.end = cur.ch;
45
+ token.string = token.string.slice(0, cur.ch - token.start);
46
+ }
47
+
48
+ var tprop = token;
49
+ // If it is a property, find out what it is a property of.
50
+ while (tprop.type == "property") {
51
+ tprop = getToken(editor, Pos(cur.line, tprop.start));
52
+ if (tprop.string != ".") return;
53
+ tprop = getToken(editor, Pos(cur.line, tprop.start));
54
+ if (!context) var context = [];
55
+ context.push(tprop);
56
+ }
57
+ return {list: getCompletions(token, context, keywords, options),
58
+ from: Pos(cur.line, token.start),
59
+ to: Pos(cur.line, token.end)};
60
+ }
61
+
62
+ function javascriptHint(editor, options) {
63
+ return scriptHint(editor, javascriptKeywords,
64
+ function (e, cur) {return e.getTokenAt(cur);},
65
+ options);
66
+ };
67
+ CodeMirror.registerHelper("hint", "javascript", javascriptHint);
68
+
69
+ function getCoffeeScriptToken(editor, cur) {
70
+ // This getToken, it is for coffeescript, imitates the behavior of
71
+ // getTokenAt method in javascript.js, that is, returning "property"
72
+ // type and treat "." as independent token.
73
+ var token = editor.getTokenAt(cur);
74
+ if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
75
+ token.end = token.start;
76
+ token.string = '.';
77
+ token.type = "property";
78
+ }
79
+ else if (/^\.[\w$_]*$/.test(token.string)) {
80
+ token.type = "property";
81
+ token.start++;
82
+ token.string = token.string.replace(/\./, '');
83
+ }
84
+ return token;
85
+ }
86
+
87
+ function coffeescriptHint(editor, options) {
88
+ return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
89
+ }
90
+ CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
91
+
92
+ var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
93
+ "toUpperCase toLowerCase split concat match replace search").split(" ");
94
+ var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
95
+ "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
96
+ var funcProps = "prototype apply call bind".split(" ");
97
+ var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
98
+ "if in import instanceof new null return super switch this throw true try typeof var void while with yield").split(" ");
99
+ var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
100
+ "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
101
+
102
+ function forAllProps(obj, callback) {
103
+ if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
104
+ for (var name in obj) callback(name)
105
+ } else {
106
+ for (var o = obj; o; o = Object.getPrototypeOf(o))
107
+ Object.getOwnPropertyNames(o).forEach(callback)
108
+ }
109
+ }
110
+
111
+ function getCompletions(token, context, keywords, options) {
112
+ var found = [], start = token.string, global = options && options.globalScope || window;
113
+ function maybeAdd(str) {
114
+ if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
115
+ }
116
+ function gatherCompletions(obj) {
117
+ if (typeof obj == "string") forEach(stringProps, maybeAdd);
118
+ else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
119
+ else if (obj instanceof Function) forEach(funcProps, maybeAdd);
120
+ forAllProps(obj, maybeAdd)
121
+ }
122
+
123
+ if (context && context.length) {
124
+ // If this is a property, see if it belongs to some object we can
125
+ // find in the current environment.
126
+ var obj = context.pop(), base;
127
+ if (obj.type && obj.type.indexOf("variable") === 0) {
128
+ if (options && options.additionalContext)
129
+ base = options.additionalContext[obj.string];
130
+ if (!options || options.useGlobalScope !== false)
131
+ base = base || global[obj.string];
132
+ } else if (obj.type == "string") {
133
+ base = "";
134
+ } else if (obj.type == "atom") {
135
+ base = 1;
136
+ } else if (obj.type == "function") {
137
+ if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
138
+ (typeof global.jQuery == 'function'))
139
+ base = global.jQuery();
140
+ else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
141
+ base = global._();
142
+ }
143
+ while (base != null && context.length)
144
+ base = base[context.pop().string];
145
+ if (base != null) gatherCompletions(base);
146
+ } else {
147
+ // If not, just look in the global object, any local scope, and optional additional-context
148
+ // (reading into JS mode internals to get at the local and global variables)
149
+ for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
150
+ for (var c = token.state.context; c; c = c.prev)
151
+ for (var v = c.vars; v; v = v.next) maybeAdd(v.name)
152
+ for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
153
+ if (options && options.additionalContext != null)
154
+ for (var key in options.additionalContext)
155
+ maybeAdd(key);
156
+ if (!options || options.useGlobalScope !== false)
157
+ gatherCompletions(global);
158
+ forEach(keywords, maybeAdd);
159
+ }
160
+ return found;
161
+ }
162
+ });
@@ -0,0 +1,36 @@
1
+ .CodeMirror-hints {
2
+ position: absolute;
3
+ z-index: 10;
4
+ overflow: hidden;
5
+ list-style: none;
6
+
7
+ margin: 0;
8
+ padding: 2px;
9
+
10
+ -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
11
+ -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
12
+ box-shadow: 2px 3px 5px rgba(0,0,0,.2);
13
+ border-radius: 3px;
14
+ border: 1px solid silver;
15
+
16
+ background: white;
17
+ font-size: 90%;
18
+ font-family: monospace;
19
+
20
+ max-height: 20em;
21
+ overflow-y: auto;
22
+ }
23
+
24
+ .CodeMirror-hint {
25
+ margin: 0;
26
+ padding: 0 4px;
27
+ border-radius: 2px;
28
+ white-space: pre;
29
+ color: black;
30
+ cursor: pointer;
31
+ }
32
+
33
+ li.CodeMirror-hint-active {
34
+ background: #08f;
35
+ color: white;
36
+ }