<commit_before>'use strict';

function draw_logic(){
    for(var object in objects){
        // Draw rectangles if not in lines mode.
        if(canvas_mode != 1){
            canvas_buffer.fillStyle = objects[object]['color'];

            var height = objects[object]['x'] - core_mouse['x'];
            var width = objects[object]['y'] - core_mouse['y'];
            if(core_storage_data['fixed-length'] !== 0){
                height = core_storage_data['fixed-length'];
                width = core_storage_data['fixed-length'];

            }else{
                if(core_storage_data['length-multiplier'] !== 1){
                    height *= core_storage_data['length-multiplier'];
                    width *= core_storage_data['length-multiplier'];
                }

                if(core_storage_data['extra-length'] !== 0){
                    height *= core_storage_data['extra-length'];
                    width *= core_storage_data['extra-length'];
                }
            }

            canvas_buffer.fillRect(
              objects[object]['x'],
              objects[object]['y'],
              height,
              width
            );
        }

        // Draw lines if not in rectangles mode.
        if(canvas_mode != 2){
            var extra_x = 0;
            var extra_y = 0;
            var target_x = core_mouse['x'] - objects[object]['x'];
            var target_y = core_mouse['y'] - objects[object]['y'];

            if(core_storage_data['fixed-length'] !== 0){
                var length = Math.sqrt(
                  target_x * target_x + target_y * target_y
                );

                target_x /= length;
                target_x *= core_storage_data['fixed-length'];
                target_y /= length;
                target_y *= core_storage_data['fixed-length'];
            }

            if(core_storage_data['length-multiplier'] !== 1){
                target_x *= core_storage_data['length-multiplier'];
                target_y *= core_storage_data['length-multiplier'];
            }

            if(core_storage_data['extra-length'] !== 0){
                extra_x = core_mouse['x'] - objects[object]['x'];
                extra_y = core_mouse['y'] - objects[object]['y'];

                var length = Math.sqrt(
                  extra_x * extra_x + extra_y * extra_y
                );

                extra_x /= length;
                extra_x *= core_storage_data['extra-length'];
                extra_y /= length;
                extra_y *= core_storage_data['extra-length'];
            }

            canvas_draw_path({
              'properties': {
                'strokeStyle': objects[object]['color'],
              },
              'style': 'stroke',
              'vertices': [
                {
                  'type': 'moveTo',
                  'x': objects[object]['x'],
                  'y': objects[object]['y'],
                },
                {
                  'x': objects[object]['x'] + target_x + extra_x,
                  'y': objects[object]['y'] + target_y + extra_y,
                },
              ],
            });
        }
    }
}

function randomize_objects(){
    objects.length = 0;

    var loop_counter = core_storage_data['number-of-objects'] - 1;
    do{
        // Create randomized object.
        objects.push({
          'color': '#' + core_random_hex(),
          'x': core_random_integer({
            'max': canvas_width,
          }),
          'y': core_random_integer({
            'max': canvas_height,
          }),
        });
    }while(loop_counter--);

    canvas_draw();
}

function repo_init(){
    core_repo_init({
      'keybinds': {
        72: {
          'todo': function(){
              canvas_setmode({
                'mode': 1,
              });
          },
        },
        81: {
          'todo': canvas_menu_quit,
        },
      },
      'mousebinds': {
        'mousedown': {
          'todo': function(){
              if(canvas_mode <= 0
                || core_menu_open){
                  return;
              }

              canvas_draw();
          },
        },
        'mousemove': {
          'todo': function(){
              if(canvas_mode <= 0
                || (!core_storage_data['mouse-lock'] && !core_mouse['down'])
                || core_menu_open){
                  return;
              }

              canvas_draw();
          },
        },
      },
      'storage': {
        'extra-length': 0,
        'fixed-length': 0,
        'length-multiplier': 1,
        'line-width': 1,
        'mouse-lock': true,
        'number-of-objects': 100,
      },
      'title': 'Warped.htm',
    });
    canvas_init();
}

function resize_logic(){
    canvas_buffer.lineWidth = core_storage_data['line-width'];
    randomize_objects();
}

function setmode_logic(){
    // Main menu mode.
    if(canvas_mode === 0){
        document.getElementById('wrap').innerHTML = '

Both
'
          + 'Lines
Rectangles

'
          + '

<input disabled value=ESC>Main Menu

'
          + '
<input id=extra-length>Extra Length
'
          + '<input id=fixed-length>Fixed Length
'
          + '<input id=length-multiplier>Length Multiplier
'
          + '<input id=line-width>Line Width
'
          + '<input id=mouse-lock type=checkbox>Mouse Lock
'
          + '<input id=number-of-objects>Objects
'
          + 'Reset Settings

';
        core_storage_update();

    // Visualization mode.
    }else{
        core_storage_save();
    }
}

var objects = [];
<commit_msg>added core menu usage<commit_after>'use strict';

function draw_logic(){
    canvas_buffer.lineWidth = core_storage_data['line-width'];

    for(var object in objects){
        // Draw rectangles if not in only lines mode.
        if(canvas_mode != 1){
            canvas_buffer.fillStyle = objects[object]['color'];

            var height = objects[object]['x'] - core_mouse['x'];
            var width = objects[object]['y'] - core_mouse['y'];
            if(core_storage_data['fixed-length'] !== 0){
                height = core_storage_data['fixed-length'];
                width = core_storage_data['fixed-length'];

            }else{
                if(core_storage_data['length-multiplier'] !== 1){
                    height *= core_storage_data['length-multiplier'];
                    width *= core_storage_data['length-multiplier'];
                }

                if(core_storage_data['extra-length'] !== 0){
                    height *= core_storage_data['extra-length'];
                    width *= core_storage_data['extra-length'];
                }
            }

            canvas_buffer.fillRect(
              objects[object]['x'],
              objects[object]['y'],
              height,
              width
            );
        }

        // Draw lines if not in only rectangles mode.
        if(canvas_mode != 2){
            var extra_x = 0;
            var extra_y = 0;
            var target_x = core_mouse['x'] - objects[object]['x'];
            var target_y = core_mouse['y'] - objects[object]['y'];

            if(core_storage_data['fixed-length'] !== 0){
                var length = Math.sqrt(
                  target_x * target_x + target_y * target_y
                );

                target_x /= length;
                target_x *= core_storage_data['fixed-length'];
                target_y /= length;
                target_y *= core_storage_data['fixed-length'];
            }

            if(core_storage_data['length-multiplier'] !== 1){
                target_x *= core_storage_data['length-multiplier'];
                target_y *= core_storage_data['length-multiplier'];
            }

            if(core_storage_data['extra-length'] !== 0){
                extra_x = core_mouse['x'] - objects[object]['x'];
                extra_y = core_mouse['y'] - objects[object]['y'];

                var length = Math.sqrt(
                  extra_x * extra_x + extra_y * extra_y
                );

                extra_x /= length;
                extra_x *= core_storage_data['extra-length'];
                extra_y /= length;
                extra_y *= core_storage_data['extra-length'];
            }

            canvas_draw_path({
              'properties': {
                'strokeStyle': objects[object]['color'],
              },
              'style': 'stroke',
              'vertices': [
                {
                  'type': 'moveTo',
                  'x': objects[object]['x'],
                  'y': objects[object]['y'],
                },
                {
                  'x': objects[object]['x'] + target_x + extra_x,
                  'y': objects[object]['y'] + target_y + extra_y,
                },
              ],
            });
        }
    }
}

function randomize_objects(){
    objects.length = 0;

    var loop_counter = core_storage_data['number-of-objects'] - 1;
    do{
        // Create randomized object.
        objects.push({
          'color': '#' + core_random_hex(),
          'x': core_random_integer({
            'max': canvas_width,
          }),
          'y': core_random_integer({
            'max': canvas_height,
          }),
        });
    }while(loop_counter--);

    canvas_draw();
}

function repo_init(){
    core_repo_init({
      'info': 'Both
Lines
Rectangles',
      'keybinds': {
        72: {
          'todo': function(){
              canvas_setmode({
                'mode': 1,
              });
          },
        },
        81: {
          'todo': canvas_menu_quit,
        },
      },
      'menu': true,
      'mousebinds': {
        'mousedown': {},
        'mousemove': {},
      },
      'storage': {
        'extra-length': 0,
        'fixed-length': 0,
        'length-multiplier': 1,
        'line-width': 1,
        'mouse-lock': true,
        'number-of-objects': 100,
      },
      'storage-menu': '<input id=extra-length>Extra Length
'
          + '<input id=fixed-length>Fixed Length
'
          + '<input id=length-multiplier>Length Multiplier
'
          + '<input id=line-width>Line Width
'
          + '<input id=mouse-lock type=checkbox>Mouse Lock
'
          + '<input id=number-of-objects>Objects',
      'title': 'Warped.htm',
    });
    canvas_init();
}

function resize_logic(){
    canvas_buffer.lineWidth = core_storage_data['line-width'];
    randomize_objects();
}

var objects = [];
<|endoftext|>


<commit_before>/**
 * Copyright reelyActive 2017-2020
 * We believe in an open Internet of Things
 */

// Constant definitions
const SCAN_OPTIONS = {
    acceptAllAdvertisements: true,
    keepRepeatedDevices: true
};

// DOM elements
let eventJson = document.querySelector('#eventJson');
let scanStatus = document.querySelector('#scanStatus');

// Attempt to run the experimental requestLEScan function
async function scanForAdvertisements() {
  try {
    const scan = await navigator.bluetooth.requestLEScan(SCAN_OPTIONS);
    let numberOfEvents = 0;

    scanStatus.textContent = 'Scan started: active = ' + scan.active;

    navigator.bluetooth.addEventListener('advertisementreceived', event => {
      eventJson.textContent = JSON.stringify(event, null, 2);
      numberOfEvents++;
    });

    function stopScan() {
      scan.stop();
      scanStatus.textContent = 'Scan stopped.  ' + numberOfEvents +
                               ' events detected.';
      stopButton.removeEventListener('click', stopScan);
    }

    stopButton.addEventListener('click', stopScan);
  }
  catch(error)  {
    scanStatus.textContent = 'Scan unsuccessful: ' + error;
  }
}

// Handle scan button click
scanButton.addEventListener('click', scanForAdvertisements);<commit_msg>Updated scan results<commit_after>/**
 * Copyright reelyActive 2017-2020
 * We believe in an open Internet of Things
 */

// Constant definitions
const SCAN_OPTIONS = {
    acceptAllAdvertisements: true,
    keepRepeatedDevices: true
};

// DOM elements
let eventJson = document.querySelector('#eventJson');
let scanStatus = document.querySelector('#scanStatus');

// Other variables
let devices = {};

// Attempt to run the experimental requestLEScan function
async function scanForAdvertisements() {
  devices = {};
  eventJson.textContent = '';
  scanStatus.textContent = 'Initiating scan';

  try {
    const scan = await navigator.bluetooth.requestLEScan(SCAN_OPTIONS);
    let numberOfEvents = 0;

    scanStatus.textContent = 'Scan started: active = ' + scan.active;

    navigator.bluetooth.addEventListener('advertisementreceived', event => {
      devices[event.device.id] = { rssi: event.rssi };
      eventJson.textContent = JSON.stringify(event, null, 2);
      numberOfEvents++;
    });

    function stopScan() {
      scan.stop();
      eventJson.textContent = JSON.stringify(devices, null, 2);
      scanStatus.textContent = 'Scan stopped.  ' + numberOfEvents +
                               ' events detected.';
      stopButton.removeEventListener('click', stopScan);
    }

    stopButton.addEventListener('click', stopScan);
  }
  catch(error)  {
    scanStatus.textContent = 'Scan unsuccessful: ' + error;
  }
}

// Handle scan button click
scanButton.addEventListener('click', scanForAdvertisements);<|endoftext|>


<commit_before>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			$('#app-content').attr('class', '');
			$('#app-content').addClass('participants-' + currentUsersNo);

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('speaking', function(id) {
			console.log('received speaking event from ', id);
			OCA.SpreedMe.speakers.add(id);
		});
		messageEventSource.listen('speaking', function(id) {
			console.log('received stoppedSpeaking event from ', id);
			OCA.SpreedMe.speakers.remove(id);
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: false,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			supportDataChannel: false,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		OCA.SpreedMe.speakers = {
			listOfSpeaker: {},
			add: function(id) {
				OCA.SpreedMe.speakers.listOfSpeakers[id] = (new Date()).getTime();
				var currentContainer = $('#container_' + id);
				if (currentContainer.hasClass('speaking') ) {
					console.log('latest speaker is already promoted');
					return;
				}
				console.log('change promoted speaker after speaking');
				$appContent.find('.speaking').removeClass('speaking');
				currentContainer.addClass('speaking');
			},
			remove: function(id) {
				OCA.SpreedMe.speakers.listOfSpeakers[id] = -1;
				var currentContainer = $('#container_' + id);
				if (!currentContainer.hasClass('speaking') ) {
					console.log('stopped speaker is not promoted');
					return;
				}
				console.log('change promoted speaker after speakingStopped');
				currentContainer.removeClass('speaking');

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in OCA.SpreedMe.speakers.listOfSpeakers) {
					// skip loop if the property is from prototype
					if (!OCA.SpreedMe.speakers.listOfSpeakers.hasOwnProperty(currentId)) continue;

					var currentTime = OCA.SpreedMe.speakers.listOfSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId
					}
				}

				if (mostRecentId !== null) {
					console.log('promoted new speaker');
					$('#container_' + id).addClass('speaking');
				} else {
					console.log('no recent speaker to promote');
				}

			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(userIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<commit_msg>do not update participants class if the same<commit_after>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			var appContentElement = $('#app-content'),
				participantsClass = 'participants-' + currentUsersNo;
			if (!appContentElement.hasClass(participantsClass)) {
				appContentElement.attr('class', '').addClass(participantsClass);
			}

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('speaking', function(id) {
			console.log('received speaking event from ', id);
			OCA.SpreedMe.speakers.add(id);
		});
		messageEventSource.listen('speaking', function(id) {
			console.log('received stoppedSpeaking event from ', id);
			OCA.SpreedMe.speakers.remove(id);
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: false,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			supportDataChannel: false,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		OCA.SpreedMe.speakers = {
			listOfSpeaker: {},
			add: function(id) {
				OCA.SpreedMe.speakers.listOfSpeakers[id] = (new Date()).getTime();
				var currentContainer = $('#container_' + id);
				if (currentContainer.hasClass('speaking') ) {
					console.log('latest speaker is already promoted');
					return;
				}
				console.log('change promoted speaker after speaking');
				$appContent.find('.speaking').removeClass('speaking');
				currentContainer.addClass('speaking');
			},
			remove: function(id) {
				OCA.SpreedMe.speakers.listOfSpeakers[id] = -1;
				var currentContainer = $('#container_' + id);
				if (!currentContainer.hasClass('speaking') ) {
					console.log('stopped speaker is not promoted');
					return;
				}
				console.log('change promoted speaker after speakingStopped');
				currentContainer.removeClass('speaking');

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in OCA.SpreedMe.speakers.listOfSpeakers) {
					// skip loop if the property is from prototype
					if (!OCA.SpreedMe.speakers.listOfSpeakers.hasOwnProperty(currentId)) continue;

					var currentTime = OCA.SpreedMe.speakers.listOfSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId
					}
				}

				if (mostRecentId !== null) {
					console.log('promoted new speaker');
					$('#container_' + id).addClass('speaking');
				} else {
					console.log('no recent speaker to promote');
				}

			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(userIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<|endoftext|>


<commit_before>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;
var spreedMappingTable = [];

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
				spreedMappingTable[user['sessionId']] = user['userId'];
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			var appContentElement = $('#app-content'),
				participantsClass = 'participants-' + currentUsersNo;
			if (!appContentElement.hasClass(participantsClass)) {
				appContentElement.attr('class', '').addClass(participantsClass);
			}

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: false,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if(message.type === 'speaking') {
				console.log('received speaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.add(message.payload);
			} else if(message.type === 'stoppedSpeaking') {
				console.log('received stoppedSpeaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.remove(message.payload);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			supportDataChannel: false,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		var spreedListofSpeakers = {};
		var latestSpeakerId = null;
		OCA.SpreedMe.speakers = {
			showStatus: function() {
				var data = [];
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					var currentTime = spreedListofSpeakers[currentId];
					var id = currentId.replace('\\', '');
					data.push([spreedMappingTable[id], currentTime]);
				}
				console.table(data);
			},
			getContainerId: function(id) {
				var sanitizedId = id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
				return '#container_' + sanitizedId + '_type_incoming';
			},
			switchVideoToId: function(id) {
				var videoSpeakingElement = $('#video-speaking');

				if (latestSpeakerId !== null) {
					console.log('move existing promoted user back');
					// move old video to new location
					var oldContainer = $(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
					oldContainer.removeClass('speaking');
					oldContainer.parent().find('.videoContainer-dummy').remove();
				}

				console.log('change promoted speaker after speaking');

				// add new user to it
				var newContainer = $(OCA.SpreedMe.speakers.getContainerId(id));
				newContainer.addClass('speaking');
				newContainer.after('

');

				latestSpeakerId = id;
			},
			add: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = (new Date()).getTime();

				if (latestSpeakerId === id) {
					console.log('latest speaker is already promoted');
					return;
				}

				console.log('change promoted speaker after speaking');
				OCA.SpreedMe.speakers.switchVideoToId(id);
			},
			remove: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = -1;

				if (latestSpeakerId !== id) {
					console.log('stopped speaker is not promoted');
					return;
				}

				console.log('change promoted speaker after speakingStopped');

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					// skip non-string ids
					if (!(typeof currentId === 'string' || currentId instanceof String))  continue;

					var currentTime = spreedListofSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId;
					}
				}

				if (mostRecentId !== null) {
					console.log('promoted new speaker');
					OCA.SpreedMe.speakers.switchVideoToId(mostRecentId);
				} else {
					console.log('no recent speaker to promote');
				}
			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Avatar for username
				var avatar = document.createElement('div');
				avatar.className = 'avatar';

				var avatarContainer = document.createElement('div');
				avatarContainer.className = 'avatar-container hidden';
				avatarContainer.appendChild(avatar);

				// Mute indicator
				var muteIndicator = document.createElement('div');
				muteIndicator.className = 'muteIndicator hidden';
				muteIndicator.textContent = 'muted';

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(avatarContainer);
				container.appendChild(userIndicator);
				container.appendChild(muteIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}

			var otherSpeakerPromoted = false;
			for (var key in spreedListofSpeakers) {
				if (spreedListofSpeakers.hasOwnProperty(key)) {
					otherSpeakerPromoted = true;
					break;
				}
			}
			if (!otherSpeakerPromoted) {
				OCA.SpreedMe.speakers.add(peer.id);
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			// a removed peer can't speak anymore ;)
			OCA.SpreedMe.speakers.remove(peer);

			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

		// Peer is muted
		OCA.SpreedMe.webrtc.on('mute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				var avatar = $el.find('.avatar');
				avatar.avatar(spreedMappingTable[data.id], 128);

				var avatarContainer = $el.find('.avatar-container');
				avatarContainer.removeClass('hidden');
				avatarContainer.show();
				//$el.find('video').hide();
			} else {
				var muteIndicator = $el.find('.muteIndicator');
				muteIndicator.removeClass('hidden');
				muteIndicator.show();
			}
		});

		// Peer is umuted
		OCA.SpreedMe.webrtc.on('unmute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				$el.find('.avatar-container').hide();
				//avaa$el.find('video').show();
			} else {
				$el.find('.muteIndicator').hide();
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<commit_msg>Enable data channels<commit_after>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;
var spreedMappingTable = [];

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
				spreedMappingTable[user['sessionId']] = user['userId'];
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			var appContentElement = $('#app-content'),
				participantsClass = 'participants-' + currentUsersNo;
			if (!appContentElement.hasClass(participantsClass)) {
				appContentElement.attr('class', '').addClass(participantsClass);
			}

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: true,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if(message.type === 'speaking') {
				console.log('received speaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.add(message.payload);
			} else if(message.type === 'stoppedSpeaking') {
				console.log('received stoppedSpeaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.remove(message.payload);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			enableDataChannels: true,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		var spreedListofSpeakers = {};
		var latestSpeakerId = null;
		OCA.SpreedMe.speakers = {
			showStatus: function() {
				var data = [];
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					var currentTime = spreedListofSpeakers[currentId];
					var id = currentId.replace('\\', '');
					data.push([spreedMappingTable[id], currentTime]);
				}
				console.table(data);
			},
			getContainerId: function(id) {
				var sanitizedId = id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
				return '#container_' + sanitizedId + '_type_incoming';
			},
			switchVideoToId: function(id) {
				var videoSpeakingElement = $('#video-speaking');

				if (latestSpeakerId !== null) {
					console.log('move existing promoted user back');
					// move old video to new location
					var oldContainer = $(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
					oldContainer.removeClass('speaking');
					oldContainer.parent().find('.videoContainer-dummy').remove();
				}

				console.log('change promoted speaker after speaking');

				// add new user to it
				var newContainer = $(OCA.SpreedMe.speakers.getContainerId(id));
				newContainer.addClass('speaking');
				newContainer.after('

');

				latestSpeakerId = id;
			},
			add: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = (new Date()).getTime();

				if (latestSpeakerId === id) {
					console.log('latest speaker is already promoted');
					return;
				}

				console.log('change promoted speaker after speaking');
				OCA.SpreedMe.speakers.switchVideoToId(id);
			},
			remove: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = -1;

				if (latestSpeakerId !== id) {
					console.log('stopped speaker is not promoted');
					return;
				}

				console.log('change promoted speaker after speakingStopped');

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					// skip non-string ids
					if (!(typeof currentId === 'string' || currentId instanceof String))  continue;

					var currentTime = spreedListofSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId;
					}
				}

				if (mostRecentId !== null) {
					console.log('promoted new speaker');
					OCA.SpreedMe.speakers.switchVideoToId(mostRecentId);
				} else {
					console.log('no recent speaker to promote');
				}
			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Avatar for username
				var avatar = document.createElement('div');
				avatar.className = 'avatar';

				var avatarContainer = document.createElement('div');
				avatarContainer.className = 'avatar-container hidden';
				avatarContainer.appendChild(avatar);

				// Mute indicator
				var muteIndicator = document.createElement('div');
				muteIndicator.className = 'muteIndicator hidden';
				muteIndicator.textContent = 'muted';

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(avatarContainer);
				container.appendChild(userIndicator);
				container.appendChild(muteIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}

			var otherSpeakerPromoted = false;
			for (var key in spreedListofSpeakers) {
				if (spreedListofSpeakers.hasOwnProperty(key)) {
					otherSpeakerPromoted = true;
					break;
				}
			}
			if (!otherSpeakerPromoted) {
				OCA.SpreedMe.speakers.add(peer.id);
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			// a removed peer can't speak anymore ;)
			OCA.SpreedMe.speakers.remove(peer);

			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

		// Peer is muted
		OCA.SpreedMe.webrtc.on('mute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				var avatar = $el.find('.avatar');
				avatar.avatar(spreedMappingTable[data.id], 128);

				var avatarContainer = $el.find('.avatar-container');
				avatarContainer.removeClass('hidden');
				avatarContainer.show();
				//$el.find('video').hide();
			} else {
				var muteIndicator = $el.find('.muteIndicator');
				muteIndicator.removeClass('hidden');
				muteIndicator.show();
			}
		});

		// Peer is umuted
		OCA.SpreedMe.webrtc.on('unmute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				$el.find('.avatar-container').hide();
				//avaa$el.find('video').show();
			} else {
				$el.find('.muteIndicator').hide();
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<|endoftext|>


<commit_before>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;
var spreedMappingTable = [];

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
				spreedMappingTable[user['sessionId']] = user['userId'];
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			var appContentElement = $('#app-content'),
				participantsClass = 'participants-' + currentUsersNo;
			if (!appContentElement.hasClass(participantsClass)) {
				appContentElement.attr('class', '').addClass(participantsClass);
			}

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: true,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if(message.type === 'speaking') {
				console.log('received speaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.add(message.payload);
			} else if(message.type === 'stoppedSpeaking') {
				console.log('received stoppedSpeaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.remove(message.payload);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			enableDataChannels: true,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		var spreedListofSpeakers = {};
		var latestSpeakerId = null;
		OCA.SpreedMe.speakers = {
			showStatus: function() {
				var data = [];
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					var currentTime = spreedListofSpeakers[currentId];
					var id = currentId.replace('\\', '');
					data.push([spreedMappingTable[id], id, currentTime]);
				}
				console.table(data);
				console.table(spreedMappingTable);
			},
			getContainerId: function(id) {
				var sanitizedId = id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
				return '#container_' + sanitizedId + '_type_incoming';
			},
			switchVideoToId: function(id) {
				var videoSpeakingElement = $('#video-speaking');

				if (latestSpeakerId !== null) {
					console.log('move existing promoted user back');
					// move old video to new location
					var oldContainer = $(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
					oldContainer.removeClass('speaking');
					oldContainer.parent().find('.videoContainer-dummy').remove();
				}

				console.log('change promoted speaker after speaking');

				// add new user to it
				var newContainer = $(OCA.SpreedMe.speakers.getContainerId(id));
				newContainer.addClass('speaking');
				newContainer.after('

');

				latestSpeakerId = id;
			},
			add: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = (new Date()).getTime();

				if (latestSpeakerId === id) {
					console.log('latest speaker is already promoted ', spreedMappingTable[id]);
					return;
				}

				console.log('change promoted speaker after speaking ', spreedMappingTable[id]);
				OCA.SpreedMe.speakers.switchVideoToId(id);
			},
			remove: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = -1;

				if (latestSpeakerId !== id) {
					console.log('stopped speaker is not promoted ', spreedMappingTable[id]);
					return;
				}

				console.log('change promoted speaker after speakingStopped ', spreedMappingTable[id]);

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					// skip non-string ids
					if (!(typeof currentId === 'string' || currentId instanceof String))  continue;

					var currentTime = spreedListofSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId;
					}
				}

				if (mostRecentId !== null) {
					console.log('promoted new speaker ', spreedMappingTable[id]);
					OCA.SpreedMe.speakers.switchVideoToId(mostRecentId);
				} else {
					console.log('no recent speaker to promote');
				}
			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Avatar for username
				var avatar = document.createElement('div');
				avatar.className = 'avatar';

				var avatarContainer = document.createElement('div');
				avatarContainer.className = 'avatar-container hidden';
				avatarContainer.appendChild(avatar);

				// Mute indicator
				var muteIndicator = document.createElement('div');
				muteIndicator.className = 'muteIndicator icon-sound-white hidden';
				muteIndicator.textContent = '';

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(avatarContainer);
				container.appendChild(userIndicator);
				container.appendChild(muteIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}

			var otherSpeakerPromoted = false;
			for (var key in spreedListofSpeakers) {
				if (spreedListofSpeakers.hasOwnProperty(key)) {
					otherSpeakerPromoted = true;
					break;
				}
			}
			if (!otherSpeakerPromoted) {
				OCA.SpreedMe.speakers.add(peer.id);
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			// a removed peer can't speak anymore ;)
			OCA.SpreedMe.speakers.remove(peer);

			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

		// Peer is muted
		OCA.SpreedMe.webrtc.on('mute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				var avatar = $el.find('.avatar');
				avatar.avatar(spreedMappingTable[data.id], 128);

				var avatarContainer = $el.find('.avatar-container');
				avatarContainer.removeClass('hidden');
				avatarContainer.show();
				//$el.find('video').hide();
			} else {
				var muteIndicator = $el.find('.muteIndicator');
				muteIndicator.removeClass('hidden');
				muteIndicator.show();
			}
		});

		// Peer is umuted
		OCA.SpreedMe.webrtc.on('unmute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				$el.find('.avatar-container').hide();
				//avaa$el.find('video').show();
			} else {
				$el.find('.muteIndicator').hide();
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<commit_msg>better debug logs<commit_after>// TODO(fancycode): Should load through AMD if possible.
/* global SimpleWebRTC, OC, OCA: false */

var webrtc;
var spreedMappingTable = [];

(function(OCA, OC) {
	'use strict';

	OCA.SpreedMe = OCA.SpreedMe || {};

	/**
	 * @private
	 */
	function openEventSource() {
		// Connect to the messages endpoint and pull for new messages
		var messageEventSource = new OC.EventSource(OC.generateUrl('/apps/spreed/messages'));
		var previousUsersInRoom = [];
		Array.prototype.diff = function(a) {
			return this.filter(function(i) {
				return a.indexOf(i) < 0;
			});
		};
		messageEventSource.listen('usersInRoom', function(users) {
			var currentUsersInRoom = [];
			users.forEach(function(user) {
				currentUsersInRoom.push(user['sessionId']);
				spreedMappingTable[user['sessionId']] = user['userId'];
			});

			var currentUsersNo = currentUsersInRoom.length;
			if(currentUsersNo === 0) {
				currentUsersNo = 1;
			}

			var appContentElement = $('#app-content'),
				participantsClass = 'participants-' + currentUsersNo;
			if (!appContentElement.hasClass(participantsClass)) {
				appContentElement.attr('class', '').addClass(participantsClass);
			}

			var disconnectedUsers = previousUsersInRoom.diff(currentUsersInRoom);
			disconnectedUsers.forEach(function(user) {
				console.log('XXX Remove peer', user);
				OCA.SpreedMe.webrtc.removePeers(user);
			});
			previousUsersInRoom = currentUsersInRoom;
		});

		messageEventSource.listen('message', function(message) {
			message = JSON.parse(message);
			var peers = self.webrtc.getPeers(message.from, message.roomType);
			var peer;

			if (message.type === 'offer') {
				if (peers.length) {
					peers.forEach(function(p) {
						if (p.sid === message.sid) {
							peer = p;
						}
					});
				}
				if (!peer) {
					peer = self.webrtc.createPeer({
						id: message.from,
						sid: message.sid,
						type: message.roomType,
						enableDataChannels: true,
						sharemyscreen: message.roomType === 'screen' && !message.broadcaster,
						broadcaster: message.roomType === 'screen' && !message.broadcaster ? self.connection.getSessionid() : null
					});
					OCA.SpreedMe.webrtc.emit('createdPeer', peer);
				}
				peer.handleMessage(message);
			} else if(message.type === 'speaking') {
				console.log('received speaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.add(message.payload);
			} else if(message.type === 'stoppedSpeaking') {
				console.log('received stoppedSpeaking event from ', spreedMappingTable[message.payload]);
				OCA.SpreedMe.speakers.remove(message.payload);
			} else if (peers.length) {
				peers.forEach(function(peer) {
					if (message.sid) {
						if (peer.sid === message.sid) {
							peer.handleMessage(message);
						}
					} else {
						peer.handleMessage(message);
					}
				});
			}
		});
		messageEventSource.listen('__internal__', function(data) {
			if (data === 'close') {
				console.log('signaling connection closed - will reopen');
				setTimeout(openEventSource, 0);
			}
		});
	}

	function initWebRTC() {
		'use strict';
		openEventSource();

		webrtc = new SimpleWebRTC({
			localVideoEl: 'localVideo',
			remoteVideosEl: '',
			autoRequestMedia: true,
			debug: false,
			media: {
				audio: true,
				video: {
					width: { max: 1280 },
					height: { max: 720 }
				}
			},
			autoAdjustMic: false,
			detectSpeakingEvents: true,
			connection: OCA.SpreedMe.XhrConnection,
			enableDataChannels: true,
			nick: OC.getCurrentUser()['displayName']
	});
		OCA.SpreedMe.webrtc = webrtc;

		var $appContent = $('#app-content');
		var spreedListofSpeakers = {};
		var latestSpeakerId = null;
		OCA.SpreedMe.speakers = {
			showStatus: function() {
				var data = [];
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					var currentTime = spreedListofSpeakers[currentId];
					var id = currentId.replace('\\', '');
					data.push([spreedMappingTable[id], id, currentTime]);
				}
				console.log('spreedListofSpeakers');
				console.table(data);
				console.log('spreedMappingTable');
				console.table(spreedMappingTable);
			},
			getContainerId: function(id) {
				var sanitizedId = id.replace(/[!"#$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, "\\$&");
				return '#container_' + sanitizedId + '_type_incoming';
			},
			switchVideoToId: function(id) {
				var videoSpeakingElement = $('#video-speaking');

				if (latestSpeakerId !== null) {
					console.log('promote: unpromote speaker "' + spreedMappingTable[latestSpeakerId] + '"');
					// move old video to new location
					var oldContainer = $(OCA.SpreedMe.speakers.getContainerId(latestSpeakerId));
					oldContainer.removeClass('speaking');
					oldContainer.parent().find('.videoContainer-dummy').remove();
				}

				// add new user to it
				var newContainer = $(OCA.SpreedMe.speakers.getContainerId(id));
				newContainer.addClass('speaking');
				newContainer.after('

');

				latestSpeakerId = id;
			},
			add: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = (new Date()).getTime();

				if (latestSpeakerId === id) {
					console.log('promoting: latest speaker "' + spreedMappingTable[id] + '" is already promoted');
					return;
				}

				console.log('promoting: change promoted speaker to "' + spreedMappingTable[id] + '" after speaking');
				OCA.SpreedMe.speakers.switchVideoToId(id);
			},
			remove: function(id) {
				if (!(typeof id === 'string' || id instanceof String)) {
					return;
				}

				var sanitizedId = OCA.SpreedMe.speakers.getContainerId(id);
				spreedListofSpeakers[sanitizedId] = -1;

				if (latestSpeakerId !== id) {
					console.log('promoting: stopped speaker "' + spreedMappingTable[id] + '" is not promoted');
					return;
				}

				console.log('promoting: try to find better promoted speaker for "' + spreedMappingTable[id] + '"');

				var mostRecentTime = 0,
					mostRecentId = null;
				for (var currentId in spreedListofSpeakers) {
					// skip loop if the property is from prototype
					if (!spreedListofSpeakers.hasOwnProperty(currentId)) {
						continue;
					}

					// skip non-string ids
					if (!(typeof currentId === 'string' || currentId instanceof String))  continue;

					var currentTime = spreedListofSpeakers[currentId];
					if (currentTime > mostRecentTime) {
						mostRecentTime = currentTime;
						mostRecentId = currentId;
					}
				}

				if (mostRecentId !== null) {
					console.log('promoting: change promoted speaker from "' + spreedMappingTable[id] + '" to "' + spreedMappingTable[mostRecentId] + '" after speakingStopped');
					OCA.SpreedMe.speakers.switchVideoToId(mostRecentId);
				} else {
					console.log('promoting: no recent speaker to promote - keep "' + spreedMappingTable[id] + '"');
				}
			}
		};

		OCA.SpreedMe.webrtc.on('createdPeer', function (peer) {
			peer.pc.on('PeerConnectionTrace', function (event) {
				console.log('trace', event);
			});
		});

		OCA.SpreedMe.webrtc.on('localMediaError', function(error) {
			console.log('Access to microphone & camera failed', error);
			var message, messageAdditional;
			if (error.name === "NotAllowedError") {
				if (error.message && error.message.indexOf("Only secure origins") !== -1) {
					message = t('spreed', 'Access to microphone & camera is only possible with HTTPS');
					messageAdditional = t('spreed', 'Please adjust your configuration');
				} else {
					message = t('spreed', 'Access to microphone & camera was denied');
					$('#emptycontent p').hide();
				}
			} else {
				message = t('spreed', 'Error while accessing microphone & camera: {error}', {error: error.message || error.name});
				$('#emptycontent p').hide();
			}
			$('#emptycontent h2').text(message);
			$('#emptycontent p').text(messageAdditional);
		});

		OCA.SpreedMe.webrtc.on('joinedRoom', function(name) {
			$('#app-content').removeClass('icon-loading');
			$('.videoView').removeClass('hidden');
			OCA.SpreedMe.app.syncAndSetActiveRoom(name);
		});

		OCA.SpreedMe.webrtc.on('videoAdded', function(video, peer) {
			console.log('video added', peer);
			var remotes = document.getElementById('videos');
			if (remotes) {
				// Indicator for username
				var userIndicator = document.createElement('div');
				userIndicator.className = 'nameIndicator';
				userIndicator.textContent = peer.nick;

				// Avatar for username
				var avatar = document.createElement('div');
				avatar.className = 'avatar';

				var avatarContainer = document.createElement('div');
				avatarContainer.className = 'avatar-container hidden';
				avatarContainer.appendChild(avatar);

				// Mute indicator
				var muteIndicator = document.createElement('div');
				muteIndicator.className = 'muteIndicator icon-sound-white hidden';
				muteIndicator.textContent = '';

				// Generic container
				var container = document.createElement('div');
				container.className = 'videoContainer';
				container.id = 'container_' + OCA.SpreedMe.webrtc.getDomId(peer);
				container.appendChild(video);
				container.appendChild(avatarContainer);
				container.appendChild(userIndicator);
				container.appendChild(muteIndicator);
				video.oncontextmenu = function() {
					return false;
				};

				// show the ice connection state
				if (peer && peer.pc) {
					peer.pc.on('iceConnectionStateChange', function () {
						switch (peer.pc.iceConnectionState) {
							case 'checking':
								console.log('Connecting to peer...');
								break;
							case 'connected':
							case 'completed': // on caller side
								console.log('Connection established.');
								break;
							case 'disconnected':
								// If the peer is still disconnected after 5 seconds
								// we close the video connection.
								setTimeout(function() {
									if(peer.pc.iceConnectionState === 'disconnected') {
										OCA.SpreedMe.webrtc.removePeers(peer.id);
									}
								}, 5000);
								console.log('Disconnected.');
								break;
							case 'failed':
								console.log('Connection failed.');
								break;
							case 'closed':
								console.log('Connection closed.');
								break;
						}
					});
				}

				$(container).prependTo($('#videos'));
			}

			var otherSpeakerPromoted = false;
			for (var key in spreedListofSpeakers) {
				if (spreedListofSpeakers.hasOwnProperty(key)) {
					otherSpeakerPromoted = true;
					break;
				}
			}
			if (!otherSpeakerPromoted) {
				OCA.SpreedMe.speakers.add(peer.id);
			}
		});

		OCA.SpreedMe.webrtc.on('speaking', function(){
			console.log('local speaking');
			OCA.SpreedMe.webrtc.sendToAll('speaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});
		OCA.SpreedMe.webrtc.on('stoppedSpeaking', function(){
			console.log('local stoppedSpeaking');
			OCA.SpreedMe.webrtc.sendToAll('stoppedSpeaking', OCA.SpreedMe.XhrConnection.getSessionid());
		});

		// a peer was removed
		OCA.SpreedMe.webrtc.on('videoRemoved', function(video, peer) {
			// a removed peer can't speak anymore ;)
			OCA.SpreedMe.speakers.remove(peer);

			var remotes = document.getElementById('videos');
			var el = document.getElementById(peer ? 'container_' + OCA.SpreedMe.webrtc.getDomId(peer) : 'localScreenContainer');
			if (remotes && el) {
				remotes.removeChild(el);
			}
		});

		// Peer is muted
		OCA.SpreedMe.webrtc.on('mute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				var avatar = $el.find('.avatar');
				avatar.avatar(spreedMappingTable[data.id], 128);

				var avatarContainer = $el.find('.avatar-container');
				avatarContainer.removeClass('hidden');
				avatarContainer.show();
				//$el.find('video').hide();
			} else {
				var muteIndicator = $el.find('.muteIndicator');
				muteIndicator.removeClass('hidden');
				muteIndicator.show();
			}
		});

		// Peer is umuted
		OCA.SpreedMe.webrtc.on('unmute', function(data) {
			var el = document.getElementById('container_' + OCA.SpreedMe.webrtc.getDomId({
					id: data.id,
					type: 'type',
					broadcaster: false
				}));
			var $el = $(el);

			if (data.name === 'video') {
				$el.find('.avatar-container').hide();
				//avaa$el.find('video').show();
			} else {
				$el.find('.muteIndicator').hide();
			}
		});

	}

	OCA.SpreedMe.initWebRTC = initWebRTC;

})(OCA, OC);
<|endoftext|>


<commit_before>(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jumpstate = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (process){
"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,a=Array(t.length);e<t.length;e++)a[e]=t[e];return a}return Array.from(t)}function attachDispatcher(t,e){var a=[e];"object"===("undefined"==typeof e?"undefined":_typeof(e))&&(e.length?a=e:(a=[],Object.keys(e).forEach(function(t){a.push(e[t])}))),a.forEach(function(e){e._isJumpstate&&(e._dispatch=t.dispatch)})}function shortID(){return("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)}Object.defineProperty(exports,"__esModule",{value:!0});var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};exports.default=function(){var t=arguments.length>1,e=t?arguments.length<=0?void 0:arguments[0]:{},a=t?arguments.length<=1?void 0:arguments[1]:arguments.length<=0?void 0:arguments[0];"string"==typeof e&&(e={name:n});var n=Object.assign(jumpstateDefaults,{name:shortID()},e);if("string"==typeof n.name&&!n.name.length)throw new Error("Jumpstate requires a name if defined in config");var r=a.initial||null;delete a.initial;var o={},i=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:r,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e||!o[e.type])return r;var a=e._jumpstate.multiPayload?e.payload:[e.payload],i=o[e.type].apply(o,[t].concat(_toConsumableArray(a)));return r=n.autoAssign?Object.assign({},t,i):i};return Object.keys(a).forEach(function(t){var e=n.name+"_"+t;o[e]=a[t],i[t]=function(){for(var a=arguments.length,o=Array(a),s=0;s<a;s++)o[s]=arguments[s];var u=o.length>1,c={_jumpstate:{stateName:n.name,actionName:t,multiPayload:u},type:e,payload:u?o:o[0]};return n.actionsCreator?c:n.detached?i(r,c):i._dispatch(c)},"testing"===process.env.NODE_ENV&&(i["_"+t]=a[t])}),Object.assign(i,{_config:n,_isJumpstate:!0}),i._config=n,i},exports.attachDispatcher=attachDispatcher;var jumpstateDefaults=exports.jumpstateDefaults={autoAssign:!0,detached:!1,actionCreator:!1};
}).call(this,require('_process'))
},{"_process":2}],2:[function(require,module,exports){
function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(e){if(cachedSetTimeout===setTimeout)return setTimeout(e,0);if((cachedSetTimeout===defaultSetTimout||!cachedSetTimeout)&&setTimeout)return cachedSetTimeout=setTimeout,setTimeout(e,0);try{return cachedSetTimeout(e,0)}catch(t){try{return cachedSetTimeout.call(null,e,0)}catch(t){return cachedSetTimeout.call(this,e,0)}}}function runClearTimeout(e){if(cachedClearTimeout===clearTimeout)return clearTimeout(e);if((cachedClearTimeout===defaultClearTimeout||!cachedClearTimeout)&&clearTimeout)return cachedClearTimeout=clearTimeout,clearTimeout(e);try{return cachedClearTimeout(e)}catch(t){try{return cachedClearTimeout.call(null,e)}catch(t){return cachedClearTimeout.call(this,e)}}}function cleanUpNextTick(){draining&&currentQueue&&(draining=!1,currentQueue.length?queue=currentQueue.concat(queue):queueIndex=-1,queue.length&&drainQueue())}function drainQueue(){if(!draining){var e=runTimeout(cleanUpNextTick);draining=!0;for(var t=queue.length;t;){for(currentQueue=queue,queue=[];++queueIndex<t;)currentQueue&&currentQueue[queueIndex].run();queueIndex=-1,t=queue.length}currentQueue=null,draining=!1,runClearTimeout(e)}}function Item(e,t){this.fun=e,this.array=t}function noop(){}var process=module.exports={},cachedSetTimeout,cachedClearTimeout;!function(){try{cachedSetTimeout="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(e){cachedSetTimeout=defaultSetTimout}try{cachedClearTimeout="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(e){cachedClearTimeout=defaultClearTimeout}}();var queue=[],draining=!1,currentQueue,queueIndex=-1;process.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var u=1;u<arguments.length;u++)t[u-1]=arguments[u];queue.push(new Item(e,t)),1!==queue.length||draining||runTimeout(drainQueue)},Item.prototype.run=function(){this.fun.apply(null,this.array)},process.title="browser",process.browser=!0,process.env={},process.argv=[],process.version="",process.versions={},process.on=noop,process.addListener=noop,process.once=noop,process.off=noop,process.removeListener=noop,process.removeAllListeners=noop,process.emit=noop,process.binding=function(e){throw new Error("process.binding is not supported")},process.cwd=function(){return"/"},process.chdir=function(e){throw new Error("process.chdir is not supported")},process.umask=function(){return 0};
},{}]},{},[1])(1)
});<commit_msg>built<commit_after>(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.jumpstate = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (process){
"use strict";function _toConsumableArray(t){if(Array.isArray(t)){for(var e=0,a=Array(t.length);e<t.length;e++)a[e]=t[e];return a}return Array.from(t)}function attachDispatcher(t,e){var a=[e];"object"===("undefined"==typeof e?"undefined":_typeof(e))&&(e.length?a=e:(a=[],Object.keys(e).forEach(function(t){a.push(e[t])}))),a.forEach(function(e){e._isJumpstate&&(e._dispatch=t.dispatch)})}function shortID(){return("0000"+(Math.random()*Math.pow(36,4)<<0).toString(36)).slice(-4)}Object.defineProperty(exports,"__esModule",{value:!0});var _typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t};exports.default=function(){var t=arguments.length>1,e=t?arguments.length<=0?void 0:arguments[0]:{},a=t?arguments.length<=1?void 0:arguments[1]:arguments.length<=0?void 0:arguments[0];"string"==typeof e&&(e={name:n});var n=Object.assign(jumpstateDefaults,{name:shortID()},e);if("string"==typeof n.name&&!n.name.length)throw new Error("Jumpstate requires a name if defined in config");var r=a.initial||null;delete a.initial;var o={},i=function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:r,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!e||!o[e.type])return r;var a=e._jumpstate.multiPayload?e.payload:[e.payload],i=o[e.type].apply(o,[t].concat(_toConsumableArray(a)));return r=n.autoAssign?Object.assign({},t,i):i};return Object.keys(a).forEach(function(t){var e=n.name+"_"+t;o[e]=a[t],i[t]=function(){for(var a=arguments.length,o=Array(a),s=0;s<a;s++)o[s]=arguments[s];var u=o.length>1,c={_jumpstate:{stateName:n.name,actionName:t,multiPayload:u},type:e,payload:u?o:o[0]};return n.actionCreator?c:n.detached?i(r,c):i._dispatch(c)},"testing"===process.env.NODE_ENV&&(i["_"+t]=a[t])}),Object.assign(i,{_config:n,_isJumpstate:!0}),i._config=n,i},exports.attachDispatcher=attachDispatcher;var jumpstateDefaults=exports.jumpstateDefaults={autoAssign:!0,detached:!1,actionCreator:!1};
}).call(this,require('_process'))
},{"_process":2}],2:[function(require,module,exports){
function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(e){if(cachedSetTimeout===setTimeout)return setTimeout(e,0);if((cachedSetTimeout===defaultSetTimout||!cachedSetTimeout)&&setTimeout)return cachedSetTimeout=setTimeout,setTimeout(e,0);try{return cachedSetTimeout(e,0)}catch(t){try{return cachedSetTimeout.call(null,e,0)}catch(t){return cachedSetTimeout.call(this,e,0)}}}function runClearTimeout(e){if(cachedClearTimeout===clearTimeout)return clearTimeout(e);if((cachedClearTimeout===defaultClearTimeout||!cachedClearTimeout)&&clearTimeout)return cachedClearTimeout=clearTimeout,clearTimeout(e);try{return cachedClearTimeout(e)}catch(t){try{return cachedClearTimeout.call(null,e)}catch(t){return cachedClearTimeout.call(this,e)}}}function cleanUpNextTick(){draining&&currentQueue&&(draining=!1,currentQueue.length?queue=currentQueue.concat(queue):queueIndex=-1,queue.length&&drainQueue())}function drainQueue(){if(!draining){var e=runTimeout(cleanUpNextTick);draining=!0;for(var t=queue.length;t;){for(currentQueue=queue,queue=[];++queueIndex<t;)currentQueue&&currentQueue[queueIndex].run();queueIndex=-1,t=queue.length}currentQueue=null,draining=!1,runClearTimeout(e)}}function Item(e,t){this.fun=e,this.array=t}function noop(){}var process=module.exports={},cachedSetTimeout,cachedClearTimeout;!function(){try{cachedSetTimeout="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(e){cachedSetTimeout=defaultSetTimout}try{cachedClearTimeout="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(e){cachedClearTimeout=defaultClearTimeout}}();var queue=[],draining=!1,currentQueue,queueIndex=-1;process.nextTick=function(e){var t=new Array(arguments.length-1);if(arguments.length>1)for(var u=1;u<arguments.length;u++)t[u-1]=arguments[u];queue.push(new Item(e,t)),1!==queue.length||draining||runTimeout(drainQueue)},Item.prototype.run=function(){this.fun.apply(null,this.array)},process.title="browser",process.browser=!0,process.env={},process.argv=[],process.version="",process.versions={},process.on=noop,process.addListener=noop,process.once=noop,process.off=noop,process.removeListener=noop,process.removeAllListeners=noop,process.emit=noop,process.binding=function(e){throw new Error("process.binding is not supported")},process.cwd=function(){return"/"},process.chdir=function(e){throw new Error("process.chdir is not supported")},process.umask=function(){return 0};
},{}]},{},[1])(1)
});<|endoftext|>


<commit_before>(function (root, smoothScroll) {
  'use strict';

  // Support RequireJS and CommonJS/NodeJS module formats.
  // Attach smoothScroll to the `window` when executed as a <script>.

  // RequireJS
  if (typeof define === 'function' && define.amd) {
    define(smoothScroll);

    // CommonJS
  } else if (typeof exports === 'object' && typeof module === 'object') {
    module.exports = smoothScroll();

  } else {
    root.smoothScroll = smoothScroll();
  }
})(this, function () {
  'use strict';

  // Do not initialize smoothScroll when running server side, handle it in client:
  if (typeof window !== 'object') {
    return;
  }

  // We do not want this script to be applied in browsers that do not support those
  // That means no smoothscroll on IE9 and below.
  if (document.querySelectorAll === void 0 || window.pageYOffset === void 0 || history.pushState === void 0) {
    return;
  }

  // Get the top position of an element in the document
  var getTop = function (element) {
    // return value of html.getBoundingClientRect().top ... IE : 0, other browsers : -pageYOffset
    if (element.nodeName === 'HTML') {
      return -window.pageYOffset
    }
    return element.getBoundingClientRect().top + window.pageYOffset;
  };
  var getTopWhenCentered = function (element, offset) {
    var viewportHeight = window.innerHeight;
    var elementHeight = element.clientHeight;
    var elementTop = getTop(element);
    if (elementHeight >= viewportHeight) {
      return elementTop;
    }
    return elementTop - (viewportHeight - elementHeight - offset) / 2;
  };

  // ease in out function thanks to:
  // http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
  var easeInOutCubic = function (t) {
    return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
  };

  // calculate the scroll position we should be in
  // given the start and end point of the scroll
  // the time elapsed from the beginning of the scroll
  // and the total duration of the scroll (default 500ms)
  var position = function (start, end, elapsed, duration) {
    if (elapsed > duration) {
      return end;
    }
    return start + (end - start) * easeInOutCubic(elapsed / duration); // <-- you can change the easing funtion there
    // return start + (end - start) * (elapsed / duration); // <-- this would give a linear scroll
  };

  // we use requestAnimationFrame to be called by the browser before every repaint
  // if the first argument is an element then scroll to the top of this element
  // if the first argument is numeric then scroll to this location
  // if the callback exist, it is called when the scrolling is finished
  // if context is set then scroll that element, else scroll window
  return function (el, duration, offset, callback, center) {
    duration = duration || 500;
    var start = window.pageYOffset;
    var end;
    if (typeof el === 'number') {
      end = parseInt(el);
    } else {
      if (typeof el === 'string' && el.substr(0, 1) === '#') { // shortcut for links to ids (e.g. the anchor for top in #top is an element with the ID "top")
        var hash = el;
        var id = hash.substr(1);
        el = document.getElementById(id);
        location.hash = hash;
      }
      end = center ? getTopWhenCentered(el, offset) : getTop(el);
    }
    end = end - offset;

    var clock = Date.now();
    var requestAnimationFrame = window.requestAnimationFrame ||
      window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
      function (fn) {
        window.setTimeout(fn, 15);
      };

    var step = function () {
      var elapsed = Date.now() - clock;
      window.scroll(0, position(start, end, elapsed, duration));

      if (elapsed > duration) {
        if (typeof callback === 'function') {
          callback(el);
        }
      } else {
        requestAnimationFrame(step);
      }
    };
    step();
  };
});
<commit_msg>Reverse behavior: center scroll by default<commit_after>(function (root, smoothScroll) {
  'use strict';

  // Support RequireJS and CommonJS/NodeJS module formats.
  // Attach smoothScroll to the `window` when executed as a <script>.

  // RequireJS
  if (typeof define === 'function' && define.amd) {
    define(smoothScroll);

    // CommonJS
  } else if (typeof exports === 'object' && typeof module === 'object') {
    module.exports = smoothScroll();

  } else {
    root.smoothScroll = smoothScroll();
  }
})(this, function () {
  'use strict';

  // Do not initialize smoothScroll when running server side, handle it in client:
  if (typeof window !== 'object') {
    return;
  }

  // We do not want this script to be applied in browsers that do not support those
  // That means no smoothscroll on IE9 and below.
  if (document.querySelectorAll === void 0 || window.pageYOffset === void 0 || history.pushState === void 0) {
    return;
  }

  // Get the top position of an element in the document
  var getTop = function (element) {
    // return value of html.getBoundingClientRect().top ... IE : 0, other browsers : -pageYOffset
    if (element.nodeName === 'HTML') {
      return -window.pageYOffset
    }
    return element.getBoundingClientRect().top + window.pageYOffset;
  };
  var getTopWhenCentered = function (element, offset) {
    var viewportHeight = window.innerHeight;
    var elementHeight = element.clientHeight;
    var elementTop = getTop(element);
    if (elementHeight >= viewportHeight) {
      return elementTop;
    }
    return elementTop - (viewportHeight - elementHeight - offset) / 2;
  };

  // ease in out function thanks to:
  // http://blog.greweb.fr/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/
  var easeInOutCubic = function (t) {
    return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1
  };

  // calculate the scroll position we should be in
  // given the start and end point of the scroll
  // the time elapsed from the beginning of the scroll
  // and the total duration of the scroll (default 500ms)
  var position = function (start, end, elapsed, duration) {
    if (elapsed > duration) {
      return end;
    }
    return start + (end - start) * easeInOutCubic(elapsed / duration); // <-- you can change the easing funtion there
    // return start + (end - start) * (elapsed / duration); // <-- this would give a linear scroll
  };

  // we use requestAnimationFrame to be called by the browser before every repaint
  // if the first argument is an element then scroll to the top of this element
  // if the first argument is numeric then scroll to this location
  // if the callback exist, it is called when the scrolling is finished
  // if context is set then scroll that element, else scroll window
  return function (el, duration, offset, callback, goToTop) {
    duration = duration || 500;
    var start = window.pageYOffset;
    var end;
    if (typeof el === 'number') {
      end = parseInt(el);
    } else {
      if (typeof el === 'string' && el.substr(0, 1) === '#') { // shortcut for links to ids (e.g. the anchor for top in #top is an element with the ID "top")
        var hash = el;
        var id = hash.substr(1);
        el = document.getElementById(id);
        location.hash = hash;
      }
      end = goToTop ? getTop(el) : getTopWhenCentered(el, offset);
    }
    end = end - offset;

    var clock = Date.now();
    var requestAnimationFrame = window.requestAnimationFrame ||
      window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame ||
      function (fn) {
        window.setTimeout(fn, 15);
      };

    var step = function () {
      var elapsed = Date.now() - clock;
      window.scroll(0, position(start, end, elapsed, duration));

      if (elapsed > duration) {
        if (typeof callback === 'function') {
          callback(el);
        }
      } else {
        requestAnimationFrame(step);
      }
    };
    step();
  };
});
<|endoftext|>