0 &&
- obj.values.animationCountOnSteps &&
- obj.values.animationCountOnSteps[step] <= animation_step) {
- animation_step = obj.values.animationCountOnSteps[step];
- }
- // jump to animation steps by calling flyToNextStep()
- function doAnimationSteps() {
- if (obj.values.isMoving == true) {
- setTimeout(doAnimationSteps, 100); // wait until the flight ends
- return;
- }
- while (animation_step-- > 0) {
- obj.flyToNextStep(); // do the animation steps
- }
- }
- setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time
- // jump to the step
- return this.sendMessage({
- 'action': 'present',
- 'data': ['moveToStep', step]
- });
- };
-
- PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
- PreziPlayer.prototype.flyToObject = function(objectId) {
- return this.sendMessage({
- 'action': 'present',
- 'data': ['moveToObject', objectId]
- });
- };
-
- PreziPlayer.prototype.play = function(defaultDelay) {
- return this.sendMessage({
- 'action': 'present',
- 'data': ['startAutoPlay', defaultDelay]
- });
- };
-
- PreziPlayer.prototype.stop = function() {
- return this.sendMessage({
- 'action': 'present',
- 'data': ['stopAutoPlay']
- });
- };
-
- PreziPlayer.prototype.pause = function(defaultDelay) {
- return this.sendMessage({
- 'action': 'present',
- 'data': ['pauseAutoPlay', defaultDelay]
- });
- };
-
- PreziPlayer.prototype.getCurrentStep = function() {
- return this.values.currentStep;
- };
-
- PreziPlayer.prototype.getCurrentAnimationStep = function() {
- return this.values.currentAnimationStep;
- };
-
- PreziPlayer.prototype.getCurrentObject = function() {
- return this.values.currentObject;
- };
-
- PreziPlayer.prototype.getStatus = function() {
- return this.values.status;
- };
-
- PreziPlayer.prototype.isPlaying = function() {
- return this.values.isAutoPlaying;
- };
-
- PreziPlayer.prototype.getStepCount = function() {
- return this.values.stepCount;
- };
-
- PreziPlayer.prototype.getAnimationCountOnSteps = function() {
- return this.values.animationCountOnSteps;
- };
-
- PreziPlayer.prototype.getTitle = function() {
- return this.values.title;
- };
-
- PreziPlayer.prototype.setDimensions = function(dims) {
- for (var parameter in dims) {
- this.iframe[parameter] = dims[parameter];
- }
- }
-
- PreziPlayer.prototype.getDimensions = function() {
- return {
- width: parseInt(this.iframe.width, 10),
- height: parseInt(this.iframe.height, 10)
- }
- }
-
- PreziPlayer.prototype.on = function(event, callback) {
- this.callbacks.push({
- event: event,
- callback: callback
- });
- };
-
- PreziPlayer.prototype.off = function(event, callback) {
- var j, item;
- if (event === undefined) {
- this.callbacks = [];
- }
- j = this.callbacks.length;
- while (j--) {
- item = this.callbacks[j];
- if (item && item.event === event && (callback === undefined || item.callback === callback)){
- this.callbacks.splice(j, 1);
- }
- }
- };
-
- if (window.addEventListener) {
- window.addEventListener('message', PreziPlayer.messageReceived, false);
- } else {
- window.attachEvent('onmessage', PreziPlayer.messageReceived);
- }
-
- return PreziPlayer;
-
- })();
-
-})();
-
-module.exports = PreziPlayer;
+ if(Resolutions[resolution])
+ {
+ constraints.video.mandatory.minWidth = Resolutions[resolution].width;
+ constraints.video.mandatory.minHeight = Resolutions[resolution].height;
+ }
+ else
+ {
+ if (isAndroid) {
+ constraints.video.mandatory.minWidth = 320;
+ constraints.video.mandatory.minHeight = 240;
+ constraints.video.mandatory.maxFrameRate = 15;
+ }
+ }
-},{}],22:[function(require,module,exports){
-var Chat = require("./chat/Chat");
-var ContactList = require("./contactlist/ContactList");
-var Settings = require("./../../settings/Settings");
-var SettingsMenu = require("./settings/SettingsMenu");
-var VideoLayout = require("../videolayout/VideoLayout");
-var ToolbarToggler = require("../toolbars/ToolbarToggler");
-var UIUtil = require("../util/UIUtil");
-var LargeVideo = require("../videolayout/LargeVideo");
-
-/**
- * Toggler for the chat, contact list, settings menu, etc..
- */
-var PanelToggler = (function(my) {
-
- var currentlyOpen = null;
- var buttons = {
- '#chatspace': '#chatBottomButton',
- '#contactlist': '#contactListButton',
- '#settingsmenu': '#settingsButton'
- };
-
- /**
- * Toggles the windows in the side panel
- * @param object the window that should be shown
- * @param selector the selector for the element containing the panel
- * @param onOpenComplete function to be called when the panel is opened
- * @param onOpen function to be called if the window is going to be opened
- * @param onClose function to be called if the window is going to be closed
- */
- var toggle = function(object, selector, onOpenComplete, onOpen, onClose) {
- UIUtil.buttonClick(buttons[selector], "active");
-
- if (object.isVisible()) {
- $("#toast-container").animate({
- right: '5px'
- },
- {
- queue: false,
- duration: 500
- });
- $(selector).hide("slide", {
- direction: "right",
- queue: false,
- duration: 500
- });
- if(typeof onClose === "function") {
- onClose();
- }
-
- currentlyOpen = null;
- }
- else {
- // Undock the toolbar when the chat is shown and if we're in a
- // video mode.
- if (LargeVideo.isLargeVideoVisible()) {
- ToolbarToggler.dockToolbar(false);
- }
-
- if(currentlyOpen) {
- var current = $(currentlyOpen);
- UIUtil.buttonClick(buttons[currentlyOpen], "active");
- current.css('z-index', 4);
- setTimeout(function () {
- current.css('display', 'none');
- current.css('z-index', 5);
- }, 500);
- }
-
- $("#toast-container").animate({
- right: (PanelToggler.getPanelSize()[0] + 5) + 'px'
- },
- {
- queue: false,
- duration: 500
- });
- $(selector).show("slide", {
- direction: "right",
- queue: false,
- duration: 500,
- complete: onOpenComplete
- });
- if(typeof onOpen === "function") {
- onOpen();
- }
-
- currentlyOpen = selector;
- }
- };
-
- /**
- * Opens / closes the chat area.
- */
- my.toggleChat = function() {
- var chatCompleteFunction = Chat.isVisible() ?
- function() {} : function () {
- Chat.scrollChatToBottom();
- $('#chatspace').trigger('shown');
- };
-
- VideoLayout.resizeVideoArea(!Chat.isVisible(), chatCompleteFunction);
-
- toggle(Chat,
- '#chatspace',
- function () {
- // Request the focus in the nickname field or the chat input field.
- if ($('#nickname').css('visibility') === 'visible') {
- $('#nickinput').focus();
- } else {
- $('#usermsg').focus();
- }
- },
- null,
- Chat.resizeChat,
- null);
- };
-
- /**
- * Opens / closes the contact list area.
- */
- my.toggleContactList = function () {
- var completeFunction = ContactList.isVisible() ?
- function() {} : function () { $('#contactlist').trigger('shown');};
- VideoLayout.resizeVideoArea(!ContactList.isVisible(), completeFunction);
-
- toggle(ContactList,
- '#contactlist',
- null,
- function() {
- ContactList.setVisualNotification(false);
- },
- null);
- };
-
- /**
- * Opens / closes the settings menu
- */
- my.toggleSettingsMenu = function() {
- VideoLayout.resizeVideoArea(!SettingsMenu.isVisible(), function (){});
- toggle(SettingsMenu,
- '#settingsmenu',
- null,
- function() {
- var settings = Settings.getSettings();
- $('#setDisplayName').get(0).value = settings.displayName;
- $('#setEmail').get(0).value = settings.email;
- },
- null);
- };
-
- /**
- * Returns the size of the side panel.
- */
- my.getPanelSize = function () {
- var availableHeight = window.innerHeight;
- var availableWidth = window.innerWidth;
-
- var panelWidth = 200;
- if (availableWidth * 0.2 < 200) {
- panelWidth = availableWidth * 0.2;
- }
-
- return [panelWidth, availableHeight];
- };
-
- my.isVisible = function() {
- return (Chat.isVisible() || ContactList.isVisible() || SettingsMenu.isVisible());
- };
-
- return my;
-
-}(PanelToggler || {}));
-
-module.exports = PanelToggler;
-},{"../toolbars/ToolbarToggler":31,"../util/UIUtil":35,"../videolayout/LargeVideo":37,"../videolayout/VideoLayout":41,"./../../settings/Settings":49,"./chat/Chat":23,"./contactlist/ContactList":27,"./settings/SettingsMenu":28}],23:[function(require,module,exports){
-/* global $, Util, nickname:true */
-var Replacement = require("./Replacement");
-var CommandsProcessor = require("./Commands");
-var ToolbarToggler = require("../../toolbars/ToolbarToggler");
-var smileys = require("./smileys.json").smileys;
-var NicknameHandler = require("../../util/NicknameHandler");
-var UIUtil = require("../../util/UIUtil");
-var UIEvents = require("../../../../service/UI/UIEvents");
-
-var notificationInterval = false;
-var unreadMessages = 0;
-
-
-/**
- * Shows/hides a visual notification, indicating that a message has arrived.
- */
-function setVisualNotification(show) {
- var unreadMsgElement = document.getElementById('unreadMessages');
- var unreadMsgBottomElement
- = document.getElementById('bottomUnreadMessages');
-
- var glower = $('#chatButton');
- var bottomGlower = $('#chatBottomButton');
-
- if (unreadMessages) {
- unreadMsgElement.innerHTML = unreadMessages.toString();
- unreadMsgBottomElement.innerHTML = unreadMessages.toString();
-
- ToolbarToggler.dockToolbar(true);
-
- var chatButtonElement
- = document.getElementById('chatButton').parentNode;
- var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
- UIUtil.getTextWidth(unreadMsgElement)) / 2;
- var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
- UIUtil.getTextHeight(unreadMsgElement)) / 2 - 3;
-
- unreadMsgElement.setAttribute(
- 'style',
- 'top:' + topIndent +
- '; left:' + leftIndent + ';');
-
- var chatBottomButtonElement
- = document.getElementById('chatBottomButton').parentNode;
- var bottomLeftIndent = (UIUtil.getTextWidth(chatBottomButtonElement) -
- UIUtil.getTextWidth(unreadMsgBottomElement)) / 2;
- var bottomTopIndent = (UIUtil.getTextHeight(chatBottomButtonElement) -
- UIUtil.getTextHeight(unreadMsgBottomElement)) / 2 - 2;
-
- unreadMsgBottomElement.setAttribute(
- 'style',
- 'top:' + bottomTopIndent +
- '; left:' + bottomLeftIndent + ';');
-
-
- if (!glower.hasClass('icon-chat-simple')) {
- glower.removeClass('icon-chat');
- glower.addClass('icon-chat-simple');
- }
- }
- else {
- unreadMsgElement.innerHTML = '';
- unreadMsgBottomElement.innerHTML = '';
- glower.removeClass('icon-chat-simple');
- glower.addClass('icon-chat');
- }
-
- if (show && !notificationInterval) {
- notificationInterval = window.setInterval(function () {
- glower.toggleClass('active');
- bottomGlower.toggleClass('active glowing');
- }, 800);
- }
- else if (!show && notificationInterval) {
- window.clearInterval(notificationInterval);
- notificationInterval = false;
- glower.removeClass('active');
- bottomGlower.removeClass('glowing');
- bottomGlower.addClass('active');
- }
-}
-
-
-/**
- * Returns the current time in the format it is shown to the user
- * @returns {string}
- */
-function getCurrentTime(stamp) {
- var now = (stamp? new Date(stamp): new Date());
- var hour = now.getHours();
- var minute = now.getMinutes();
- var second = now.getSeconds();
- if(hour.toString().length === 1) {
- hour = '0'+hour;
- }
- if(minute.toString().length === 1) {
- minute = '0'+minute;
- }
- if(second.toString().length === 1) {
- second = '0'+second;
- }
- return hour+':'+minute+':'+second;
-}
-
-function toggleSmileys()
-{
- var smileys = $('#smileysContainer');
- if(!smileys.is(':visible')) {
- smileys.show("slide", { direction: "down", duration: 300});
- } else {
- smileys.hide("slide", { direction: "down", duration: 300});
- }
- $('#usermsg').focus();
-}
-
-function addClickFunction(smiley, number) {
- smiley.onclick = function addSmileyToMessage() {
- var usermsg = $('#usermsg');
- var message = usermsg.val();
- message += smileys['smiley' + number];
- usermsg.val(message);
- usermsg.get(0).setSelectionRange(message.length, message.length);
- toggleSmileys();
- usermsg.focus();
- };
-}
-
-/**
- * Adds the smileys container to the chat
- */
-function addSmileys() {
- var smileysContainer = document.createElement('div');
- smileysContainer.id = 'smileysContainer';
- for(var i = 1; i <= 21; i++) {
- var smileyContainer = document.createElement('div');
- smileyContainer.id = 'smiley' + i;
- smileyContainer.className = 'smileyContainer';
- var smiley = document.createElement('img');
- smiley.src = 'images/smileys/smiley' + i + '.svg';
- smiley.className = 'smiley';
- addClickFunction(smiley, i);
- smileyContainer.appendChild(smiley);
- smileysContainer.appendChild(smileyContainer);
- }
-
- $("#chatspace").append(smileysContainer);
-}
-
-/**
- * Resizes the chat conversation.
- */
-function resizeChatConversation() {
- var msgareaHeight = $('#usermsg').outerHeight();
- var chatspace = $('#chatspace');
- var width = chatspace.width();
- var chat = $('#chatconversation');
- var smileys = $('#smileysarea');
-
- smileys.height(msgareaHeight);
- $("#smileys").css('bottom', (msgareaHeight - 26) / 2);
- $('#smileysContainer').css('bottom', msgareaHeight);
- chat.width(width - 10);
- chat.height(window.innerHeight - 15 - msgareaHeight);
-}
-
-/**
- * Chat related user interface.
- */
-var Chat = (function (my) {
- /**
- * Initializes chat related interface.
- */
- my.init = function () {
- if(NicknameHandler.getNickname())
- Chat.setChatConversationMode(true);
- NicknameHandler.addListener(UIEvents.NICKNAME_CHANGED,
- function (nickname) {
- Chat.setChatConversationMode(true);
- });
-
- $('#nickinput').keydown(function (event) {
- if (event.keyCode === 13) {
- event.preventDefault();
- var val = UIUtil.escapeHtml(this.value);
- this.value = '';
- if (!NicknameHandler.getNickname()) {
- NicknameHandler.setNickname(val);
-
- return;
- }
- }
- });
-
- $('#usermsg').keydown(function (event) {
- if (event.keyCode === 13) {
- event.preventDefault();
- var value = this.value;
- $('#usermsg').val('').trigger('autosize.resize');
- this.focus();
- var command = new CommandsProcessor(value);
- if(command.isCommand())
- {
- command.processCommand();
- }
- else
- {
- var message = UIUtil.escapeHtml(value);
- APP.xmpp.sendChatMessage(message, NicknameHandler.getNickname());
- }
- }
- });
-
- var onTextAreaResize = function () {
- resizeChatConversation();
- Chat.scrollChatToBottom();
- };
- $('#usermsg').autosize({callback: onTextAreaResize});
-
- $("#chatspace").bind("shown",
- function () {
- unreadMessages = 0;
- setVisualNotification(false);
- });
-
- addSmileys();
- };
-
- /**
- * Appends the given message to the chat conversation.
- */
- my.updateChatConversation = function (from, displayName, message, myjid, stamp) {
- var divClassName = '';
-
- if (APP.xmpp.myJid() === from) {
- divClassName = "localuser";
- }
- else {
- divClassName = "remoteuser";
-
- if (!Chat.isVisible()) {
- unreadMessages++;
- UIUtil.playSoundNotification('chatNotification');
- setVisualNotification(true);
- }
- }
-
- // replace links and smileys
- // Strophe already escapes special symbols on sending,
- // so we escape here only tags to avoid double &
- var escMessage = message.replace(//g, '>').replace(/\n/g, '
');
- var escDisplayName = UIUtil.escapeHtml(displayName);
- message = Replacement.processReplacements(escMessage);
-
- var messageContainer =
- ''+
- '
' +
- '
' + escDisplayName +
- '
' + '
' + getCurrentTime(stamp) +
- '
' + '
' + message + '
' +
- '
';
-
- $('#chatconversation').append(messageContainer);
- $('#chatconversation').animate(
- { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
- };
-
- /**
- * Appends error message to the conversation
- * @param errorMessage the received error message.
- * @param originalText the original message.
- */
- my.chatAddError = function(errorMessage, originalText)
- {
- errorMessage = UIUtil.escapeHtml(errorMessage);
- originalText = UIUtil.escapeHtml(originalText);
-
- $('#chatconversation').append(
- 'Error: ' + 'Your message' +
- (originalText? (' \"'+ originalText + '\"') : "") +
- ' was not sent.' +
- (errorMessage? (' Reason: ' + errorMessage) : '') + '
');
- $('#chatconversation').animate(
- { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
- };
-
- /**
- * Sets the subject to the UI
- * @param subject the subject
- */
- my.chatSetSubject = function(subject)
- {
- if(subject)
- subject = subject.trim();
- $('#subject').html(Replacement.linkify(UIUtil.escapeHtml(subject)));
- if(subject === "")
- {
- $("#subject").css({display: "none"});
- }
- else
- {
- $("#subject").css({display: "block"});
- }
- };
-
-
-
- /**
- * Sets the chat conversation mode.
- */
- my.setChatConversationMode = function (isConversationMode) {
- if (isConversationMode) {
- $('#nickname').css({visibility: 'hidden'});
- $('#chatconversation').css({visibility: 'visible'});
- $('#usermsg').css({visibility: 'visible'});
- $('#smileysarea').css({visibility: 'visible'});
- $('#usermsg').focus();
- }
- };
-
- /**
- * Resizes the chat area.
- */
- my.resizeChat = function () {
- var chatSize = require("../SidePanelToggler").getPanelSize();
-
- $('#chatspace').width(chatSize[0]);
- $('#chatspace').height(chatSize[1]);
-
- resizeChatConversation();
- };
-
- /**
- * Indicates if the chat is currently visible.
- */
- my.isVisible = function () {
- return $('#chatspace').is(":visible");
- };
- /**
- * Shows and hides the window with the smileys
- */
- my.toggleSmileys = toggleSmileys;
-
- /**
- * Scrolls chat to the bottom.
- */
- my.scrollChatToBottom = function() {
- setTimeout(function () {
- $('#chatconversation').scrollTop(
- $('#chatconversation')[0].scrollHeight);
- }, 5);
- };
-
-
- return my;
-}(Chat || {}));
-module.exports = Chat;
-},{"../../../../service/UI/UIEvents":104,"../../toolbars/ToolbarToggler":31,"../../util/NicknameHandler":34,"../../util/UIUtil":35,"../SidePanelToggler":22,"./Commands":24,"./Replacement":25,"./smileys.json":26}],24:[function(require,module,exports){
-var UIUtil = require("../../util/UIUtil");
-
-/**
- * List with supported commands. The keys are the names of the commands and
- * the value is the function that processes the message.
- * @type {{String: function}}
- */
-var commands = {
- "topic" : processTopic
-};
-
-/**
- * Extracts the command from the message.
- * @param message the received message
- * @returns {string} the command
- */
-function getCommand(message)
-{
- if(message)
- {
- for(var command in commands)
- {
- if(message.indexOf("/" + command) == 0)
- return command;
- }
- }
- return "";
-};
-
-/**
- * Processes the data for topic command.
- * @param commandArguments the arguments of the topic command.
- */
-function processTopic(commandArguments)
-{
- var topic = UIUtil.escapeHtml(commandArguments);
- APP.xmpp.setSubject(topic);
-}
-
-/**
- * Constructs new CommandProccessor instance from a message that
- * handles commands received via chat messages.
- * @param message the message
- * @constructor
- */
-function CommandsProcessor(message)
-{
-
-
- var command = getCommand(message);
-
- /**
- * Returns the name of the command.
- * @returns {String} the command
- */
- this.getCommand = function()
- {
- return command;
- };
-
-
- var messageArgument = message.substr(command.length + 2);
-
- /**
- * Returns the arguments of the command.
- * @returns {string}
- */
- this.getArgument = function()
- {
- return messageArgument;
- };
-}
-
-/**
- * Checks whether this instance is valid command or not.
- * @returns {boolean}
- */
-CommandsProcessor.prototype.isCommand = function()
-{
- if(this.getCommand())
- return true;
- return false;
-};
-
-/**
- * Processes the command.
- */
-CommandsProcessor.prototype.processCommand = function()
-{
- if(!this.isCommand())
- return;
-
- commands[this.getCommand()](this.getArgument());
-
-};
-
-module.exports = CommandsProcessor;
-},{"../../util/UIUtil":35}],25:[function(require,module,exports){
-var Smileys = require("./smileys.json");
-/**
- * Processes links and smileys in "body"
- */
-function processReplacements(body)
-{
- //make links clickable
- body = linkify(body);
-
- //add smileys
- body = smilify(body);
-
- return body;
-}
-
-/**
- * Finds and replaces all links in the links in "body"
- * with their
- */
-function linkify(inputText)
-{
- var replacedText, replacePattern1, replacePattern2, replacePattern3;
-
- //URLs starting with http://, https://, or ftp://
- replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
- replacedText = inputText.replace(replacePattern1, '$1');
-
- //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
- replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
- replacedText = replacedText.replace(replacePattern2, '$1$2');
-
- //Change email addresses to mailto:: links.
- replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
- replacedText = replacedText.replace(replacePattern3, '$1');
-
- return replacedText;
-}
-
-/**
- * Replaces common smiley strings with images
- */
-function smilify(body)
-{
- if(!body) {
- return body;
- }
-
- var regexs = Smileys["regexs"];
- for(var smiley in regexs) {
- if(regexs.hasOwnProperty(smiley)) {
- body = body.replace(regexs[smiley],
- '');
- }
- }
-
- return body;
-}
-
-module.exports = {
- processReplacements: processReplacements,
- linkify: linkify
-};
+ if (constraints.video.mandatory.minWidth)
+ constraints.video.mandatory.maxWidth = constraints.video.mandatory.minWidth;
+ if (constraints.video.mandatory.minHeight)
+ constraints.video.mandatory.maxHeight = constraints.video.mandatory.minHeight;
+}
-},{"./smileys.json":26}],26:[function(require,module,exports){
-module.exports={
- "smileys": {
- "smiley1": ":)",
- "smiley2": ":(",
- "smiley3": ":D",
- "smiley4": "(y)",
- "smiley5": " :P",
- "smiley6": "(wave)",
- "smiley7": "(blush)",
- "smiley8": "(chuckle)",
- "smiley9": "(shocked)",
- "smiley10": ":*",
- "smiley11": "(n)",
- "smiley12": "(search)",
- "smiley13": " <3",
- "smiley14": "(oops)",
- "smiley15": "(angry)",
- "smiley16": "(angel)",
- "smiley17": "(sick)",
- "smiley18": ";(",
- "smiley19": "(bomb)",
- "smiley20": "(clap)",
- "smiley21": " ;)"
- },
- "regexs": {
- "smiley2": /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi,
- "smiley3": /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi,
- "smiley1": /(:-\)|:\))/gi,
- "smiley4": /(\(y\)|\(Y\)|\(ok\))/gi,
- "smiley5": /(:-P|:P|:-p|:p)/gi,
- "smiley6": /(\(wave\))/gi,
- "smiley7": /(\(blush\))/gi,
- "smiley8": /(\(chuckle\))/gi,
- "smiley9": /(:-0|\(shocked\))/gi,
- "smiley10": /(:-\*|:\*|\(kiss\))/gi,
- "smiley11": /(\(n\))/gi,
- "smiley12": /(\(search\))/g,
- "smiley13": /(<3|<3|<3|\(L\)|\(l\)|\(H\)|\(h\))/gi,
- "smiley14": /(\(oops\))/gi,
- "smiley15": /(\(angry\))/gi,
- "smiley16": /(\(angel\))/gi,
- "smiley17": /(\(sick\))/gi,
- "smiley18": /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi,
- "smiley19": /(\(bomb\))/gi,
- "smiley20": /(\(clap\))/gi,
- "smiley21": /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi
- }
-}
+function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid)
+{
+ var constraints = {audio: false, video: false};
-},{}],27:[function(require,module,exports){
-
-var Avatar = require('../../avatar/Avatar');
-
-var numberOfContacts = 0;
-var notificationInterval;
-
-/**
- * Updates the number of participants in the contact list button and sets
- * the glow
- * @param delta indicates whether a new user has joined (1) or someone has
- * left(-1)
- */
-function updateNumberOfParticipants(delta) {
- numberOfContacts += delta;
- if (numberOfContacts === 1) {
- // when the user is alone we don't show the number of participants
- $("#numberOfParticipants").text('');
- ContactList.setVisualNotification(false);
- } else if (numberOfContacts > 1) {
- ContactList.setVisualNotification(!ContactList.isVisible());
- $("#numberOfParticipants").text(numberOfContacts);
- } else {
- console.error("Invalid number of participants: " + numberOfContacts);
- }
-}
-
-/**
- * Creates the avatar element.
- *
- * @return the newly created avatar element
- */
-function createAvatar(jid) {
- var avatar = document.createElement('img');
- avatar.className = "icon-avatar avatar";
- avatar.src = Avatar.getContactListUrl(jid);
-
- return avatar;
-}
-
-/**
- * Creates the display name paragraph.
- *
- * @param displayName the display name to set
- */
-function createDisplayNameParagraph(key, displayName) {
- var p = document.createElement('p');
- if(displayName)
- p.innerText = displayName;
- else if(key)
- {
- p.setAttribute("data-i18n",key);
- p.innerText = APP.translation.translateString(key);
- }
-
- return p;
-}
-
-
-function stopGlowing(glower) {
- window.clearInterval(notificationInterval);
- notificationInterval = false;
- glower.removeClass('glowing');
- if (!ContactList.isVisible()) {
- glower.removeClass('active');
- }
-}
-
-
-/**
- * Contact list.
- */
-var ContactList = {
- /**
- * Indicates if the chat is currently visible.
- *
- * @return true if the chat is currently visible, false -
- * otherwise
- */
- isVisible: function () {
- return $('#contactlist').is(":visible");
- },
-
- /**
- * Adds a contact for the given peerJid if such doesn't yet exist.
- *
- * @param peerJid the peerJid corresponding to the contact
- */
- ensureAddContact: function (peerJid) {
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contact = $('#contacts>li[id="' + resourceJid + '"]');
-
- if (!contact || contact.length <= 0)
- ContactList.addContact(peerJid);
- },
-
- /**
- * Adds a contact for the given peer jid.
- *
- * @param peerJid the jid of the contact to add
- */
- addContact: function (peerJid) {
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contactlist = $('#contacts');
-
- var newContact = document.createElement('li');
- newContact.id = resourceJid;
- newContact.className = "clickable";
- newContact.onclick = function (event) {
- if (event.currentTarget.className === "clickable") {
- $(ContactList).trigger('contactclicked', [peerJid]);
- }
- };
-
- newContact.appendChild(createAvatar(peerJid));
- newContact.appendChild(createDisplayNameParagraph("participant"));
-
- if (resourceJid === APP.xmpp.myResource()) {
- contactlist.prepend(newContact);
- }
- else {
- contactlist.append(newContact);
- }
- updateNumberOfParticipants(1);
- },
-
- /**
- * Removes a contact for the given peer jid.
- *
- * @param peerJid the peerJid corresponding to the contact to remove
- */
- removeContact: function (peerJid) {
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contact = $('#contacts>li[id="' + resourceJid + '"]');
-
- if (contact && contact.length > 0) {
- var contactlist = $('#contactlist>ul');
-
- contactlist.get(0).removeChild(contact.get(0));
-
- updateNumberOfParticipants(-1);
- }
- },
-
- setVisualNotification: function (show, stopGlowingIn) {
- var glower = $('#contactListButton');
-
- if (show && !notificationInterval) {
- notificationInterval = window.setInterval(function () {
- glower.toggleClass('active glowing');
- }, 800);
- }
- else if (!show && notificationInterval) {
- stopGlowing(glower);
- }
- if (stopGlowingIn) {
- setTimeout(function () {
- stopGlowing(glower);
- }, stopGlowingIn);
- }
- },
-
- setClickable: function (resourceJid, isClickable) {
- var contact = $('#contacts>li[id="' + resourceJid + '"]');
- if (isClickable) {
- contact.addClass('clickable');
- } else {
- contact.removeClass('clickable');
- }
- },
-
- onDisplayNameChange: function (peerJid, displayName) {
- if (peerJid === 'localVideoContainer')
- peerJid = APP.xmpp.myJid();
-
- var resourceJid = Strophe.getResourceFromJid(peerJid);
-
- var contactName = $('#contacts #' + resourceJid + '>p');
-
- if (contactName && displayName && displayName.length > 0)
- contactName.html(displayName);
- },
-
- userAvatarChanged: function (resourceJid, contactListUrl) {
- // set the avatar in the contact list
- var contact = $('#' + resourceJid + '>img');
- if (contact && contact.length > 0) {
- contact.get(0).src = contactListUrl;
- }
-
- }
-};
-
-module.exports = ContactList;
-},{"../../avatar/Avatar":18}],28:[function(require,module,exports){
-var Avatar = require("../../avatar/Avatar");
-var Settings = require("./../../../settings/Settings");
-var UIUtil = require("../../util/UIUtil");
-var languages = require("../../../../service/translation/languages");
-
-function generateLanguagesSelectBox()
-{
- var currentLang = APP.translation.getCurrentLanguage();
- var html = "";
-}
-
-
-var SettingsMenu = {
-
- init: function () {
- $("#startMutedOptions").before(generateLanguagesSelectBox());
- APP.translation.translateElement($("#languages_selectbox"));
- $('#settingsmenu>input').keyup(function(event){
- if(event.keyCode === 13) {//enter
- SettingsMenu.update();
- }
- });
-
- if(APP.xmpp.isModerator())
- {
- $("#startMutedOptions").css("display", "block");
- }
- else
- {
- $("#startMutedOptions").css("display", "none");
- }
-
- $("#updateSettings").click(function () {
- SettingsMenu.update();
- });
- },
-
- onRoleChanged: function () {
- if(APP.xmpp.isModerator())
- {
- $("#startMutedOptions").css("display", "block");
- }
- else
- {
- $("#startMutedOptions").css("display", "none");
- }
- },
-
- setStartMuted: function (audio, video) {
- $("#startAudioMuted").attr("checked", audio);
- $("#startVideoMuted").attr("checked", video);
- },
-
- update: function() {
- var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
- var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
-
- if(newDisplayName) {
- var displayName = Settings.setDisplayName(newDisplayName);
- APP.xmpp.addToPresence("displayName", displayName, true);
- }
-
- var language = $("#languages_selectbox").val();
- APP.translation.setLanguage(language);
- Settings.setLanguage(language);
-
- APP.xmpp.addToPresence("email", newEmail);
- var email = Settings.setEmail(newEmail);
-
- var startAudioMuted = ($("#startAudioMuted").is(":checked"));
- var startVideoMuted = ($("#startVideoMuted").is(":checked"));
- APP.xmpp.addToPresence("startMuted",
- [startAudioMuted, startVideoMuted]);
-
- Avatar.setUserAvatar(APP.xmpp.myJid(), email);
- },
-
- isVisible: function() {
- return $('#settingsmenu').is(':visible');
- },
-
- setDisplayName: function(newDisplayName) {
- var displayName = Settings.setDisplayName(newDisplayName);
- $('#setDisplayName').get(0).value = displayName;
- },
-
- onDisplayNameChange: function(peerJid, newDisplayName) {
- if(peerJid === 'localVideoContainer' ||
- peerJid === APP.xmpp.myJid()) {
- this.setDisplayName(newDisplayName);
- }
- },
- changeAvatar: function (thumbUrl) {
- $('#avatar').get(0).src = thumbUrl;
- }
-};
-
-
-module.exports = SettingsMenu;
-},{"../../../../service/translation/languages":109,"../../avatar/Avatar":18,"../../util/UIUtil":35,"./../../../settings/Settings":49}],29:[function(require,module,exports){
-var PanelToggler = require("../side_pannels/SidePanelToggler");
-
-var buttonHandlers = {
- "bottom_toolbar_contact_list": function () {
- BottomToolbar.toggleContactList();
- },
- "bottom_toolbar_film_strip": function () {
- BottomToolbar.toggleFilmStrip();
- },
- "bottom_toolbar_chat": function () {
- BottomToolbar.toggleChat();
- }
-};
-
-var BottomToolbar = (function (my) {
- my.init = function () {
- for(var k in buttonHandlers)
- $("#" + k).click(buttonHandlers[k]);
- };
-
- my.toggleChat = function() {
- PanelToggler.toggleChat();
- };
-
- my.toggleContactList = function() {
- PanelToggler.toggleContactList();
- };
-
- my.toggleFilmStrip = function() {
- var filmstrip = $("#remoteVideos");
- filmstrip.toggleClass("hidden");
- };
-
- $(document).bind("remotevideo.resized", function (event, width, height) {
- var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;
-
- $('#bottomToolbar').css({bottom: bottom + 'px'});
- });
-
- return my;
-}(BottomToolbar || {}));
-
-module.exports = BottomToolbar;
+ if (um.indexOf('video') >= 0) {
+ constraints.video = { mandatory: {}, optional: [] };// same behaviour as true
+ }
+ if (um.indexOf('audio') >= 0) {
+ constraints.audio = { mandatory: {}, optional: []};// same behaviour as true
+ }
+ if (um.indexOf('screen') >= 0) {
+ if (RTCBrowserType.isChrome()) {
+ constraints.video = {
+ mandatory: {
+ chromeMediaSource: "screen",
+ googLeakyBucket: true,
+ maxWidth: window.screen.width,
+ maxHeight: window.screen.height,
+ maxFrameRate: 3
+ },
+ optional: []
+ };
+ } else if (RTCBrowserType.isTemasysPluginUsed()) {
+ constraints.video = {
+ optional: [
+ {
+ sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey
+ }
+ ]
+ };
+ } else {
+ console.error(
+ "'screen' WebRTC media source is supported only in Chrome" +
+ " and with Temasys plugin");
+ }
+ }
+ if (um.indexOf('desktop') >= 0) {
+ constraints.video = {
+ mandatory: {
+ chromeMediaSource: "desktop",
+ chromeMediaSourceId: desktopStream,
+ googLeakyBucket: true,
+ maxWidth: window.screen.width,
+ maxHeight: window.screen.height,
+ maxFrameRate: 3
+ },
+ optional: []
+ };
+ }
-},{"../side_pannels/SidePanelToggler":22}],30:[function(require,module,exports){
-/* global APP,$, buttonClick, config, lockRoom,
- setSharedKey, Util */
-var messageHandler = require("../util/MessageHandler");
-var BottomToolbar = require("./BottomToolbar");
-var Prezi = require("../prezi/Prezi");
-var Etherpad = require("../etherpad/Etherpad");
-var PanelToggler = require("../side_pannels/SidePanelToggler");
-var Authentication = require("../authentication/Authentication");
-var UIUtil = require("../util/UIUtil");
-var AuthenticationEvents
- = require("../../../service/authentication/AuthenticationEvents");
-
-var roomUrl = null;
-var sharedKey = '';
-var UI = null;
-
-var buttonHandlers =
-{
- "toolbar_button_mute": function () {
- return APP.UI.toggleAudio();
- },
- "toolbar_button_camera": function () {
- return APP.UI.toggleVideo();
- },
- /*"toolbar_button_authentication": function () {
- return Toolbar.authenticateClicked();
- },*/
- "toolbar_button_record": function () {
- return toggleRecording();
- },
- "toolbar_button_security": function () {
- return Toolbar.openLockDialog();
- },
- "toolbar_button_link": function () {
- return Toolbar.openLinkDialog();
- },
- "toolbar_button_chat": function () {
- return BottomToolbar.toggleChat();
- },
- "toolbar_button_prezi": function () {
- return Prezi.openPreziDialog();
- },
- "toolbar_button_etherpad": function () {
- return Etherpad.toggleEtherpad(0);
- },
- "toolbar_button_desktopsharing": function () {
- return APP.desktopsharing.toggleScreenSharing();
- },
- "toolbar_button_fullScreen": function()
- {
- UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
- return Toolbar.toggleFullScreen();
- },
- "toolbar_button_sip": function () {
- return callSipButtonClicked();
- },
- "toolbar_button_dialpad": function () {
- return dialpadButtonClicked();
- },
- "toolbar_button_settings": function () {
- PanelToggler.toggleSettingsMenu();
- },
- "toolbar_button_hangup": function () {
- return hangup();
- },
- "toolbar_button_login": function () {
- Toolbar.authenticateClicked();
- },
- "toolbar_button_logout": function () {
- // Ask for confirmation
- messageHandler.openTwoButtonDialog(
- "dialog.logoutTitle",
- null,
- "dialog.logoutQuestion",
- null,
- false,
- "dialog.Yes",
- function (evt, yes) {
- if (yes) {
- APP.xmpp.logout(function (url) {
- if (url) {
- window.location.href = url;
- } else {
- hangup();
- }
- });
- }
- });
- }
-};
-
-function hangup() {
- APP.xmpp.disposeConference();
- if(config.enableWelcomePage)
- {
- setTimeout(function()
- {
- window.localStorage.welcomePageDisabled = false;
- window.location.pathname = "/";
- }, 10000);
-
- }
-
- var title = APP.translation.generateTranslationHTML(
- "dialog.sessTerminated");
- var msg = APP.translation.generateTranslationHTML(
- "dialog.hungUp");
- var button = APP.translation.generateTranslationHTML(
- "dialog.joinAgain");
- var buttons = [];
- buttons.push({title: button, value: true});
-
- UI.messageHandler.openDialog(
- title,
- msg,
- true,
- buttons,
- function(event, value, message, formVals)
- {
- window.location.reload();
- return false;
- }
- );
-}
-
-/**
- * Starts or stops the recording for the conference.
- */
-
-function toggleRecording() {
- APP.xmpp.toggleRecording(function (callback) {
- var msg = APP.translation.generateTranslationHTML(
- "dialog.recordingToken");
- var token = APP.translation.translateString("dialog.token");
- APP.UI.messageHandler.openTwoButtonDialog(null, null, null,
- '' + msg + '
' +
- '',
- false,
- "dialog.Save",
- function (e, v, m, f) {
- if (v) {
- var token = f.recordingToken;
-
- if (token) {
- callback(UIUtil.escapeHtml(token));
- }
- }
- },
- null,
- function () { },
- ':input:first'
- );
- }, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
-}
-
-/**
- * Locks / unlocks the room.
- */
-function lockRoom(lock) {
- var currentSharedKey = '';
- if (lock)
- currentSharedKey = sharedKey;
-
- APP.xmpp.lockRoom(currentSharedKey, function (res) {
- // password is required
- if (sharedKey)
- {
- console.log('set room password');
- Toolbar.lockLockButton();
- }
- else
- {
- console.log('removed room password');
- Toolbar.unlockLockButton();
- }
- }, function (err) {
- console.warn('setting password failed', err);
- messageHandler.showError("dialog.lockTitle",
- "dialog.lockMessage");
- Toolbar.setSharedKey('');
- }, function () {
- console.warn('room passwords not supported');
- messageHandler.showError("dialog.warning",
- "dialog.passwordNotSupported");
- Toolbar.setSharedKey('');
- });
-};
-
-/**
- * Invite participants to conference.
- */
-function inviteParticipants() {
- if (roomUrl === null)
- return;
-
- var sharedKeyText = "";
- if (sharedKey && sharedKey.length > 0) {
- sharedKeyText =
- APP.translation.translateString("email.sharedKey",
- {sharedKey: sharedKey});
- sharedKeyText = sharedKeyText.replace(/\n/g, "%0D%0A");
- }
-
- var supportedBrowsers = "Chromium, Google Chrome " +
- APP.translation.translateString("email.and") + " Opera";
- var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1);
- var subject = APP.translation.translateString("email.subject",
- {appName:interfaceConfig.APP_NAME, conferenceName: conferenceName});
- var body = APP.translation.translateString("email.body",
- {appName:interfaceConfig.APP_NAME, sharedKeyText: sharedKeyText,
- roomUrl: roomUrl, supportedBrowsers: supportedBrowsers});
- body = body.replace(/\n/g, "%0D%0A");
-
- if (window.localStorage.displayname) {
- body += "%0D%0A%0D%0A" + window.localStorage.displayname;
- }
-
- if (interfaceConfig.INVITATION_POWERED_BY) {
- body += "%0D%0A%0D%0A--%0D%0Apowered by jitsi.org";
- }
-
- window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
-}
-
-function dialpadButtonClicked()
-{
- //TODO show the dialpad window
-}
-
-function callSipButtonClicked()
-{
- var defaultNumber
- = config.defaultSipNumber ? config.defaultSipNumber : '';
-
- var sipMsg = APP.translation.generateTranslationHTML(
- "dialog.sipMsg");
- messageHandler.openTwoButtonDialog(null, null, null,
- '' + sipMsg + '
' +
- '',
- false,
- "dialog.Dial",
- function (e, v, m, f) {
- if (v) {
- var numberInput = f.sipNumber;
- if (numberInput) {
- APP.xmpp.dial(
- numberInput, 'fromnumber', UI.getRoomName(), sharedKey);
- }
- }
- },
- null, null, ':input:first'
- );
-}
-
-var Toolbar = (function (my) {
-
- my.init = function (ui) {
- for(var k in buttonHandlers)
- $("#" + k).click(buttonHandlers[k]);
- UI = ui;
- // Update login info
- APP.xmpp.addListener(
- AuthenticationEvents.IDENTITY_UPDATED,
- function (authenticationEnabled, userIdentity) {
-
- var loggedIn = false;
- if (userIdentity) {
- loggedIn = true;
- }
-
- Toolbar.showAuthenticateButton(authenticationEnabled);
-
- if (authenticationEnabled) {
- Toolbar.setAuthenticatedIdentity(userIdentity);
-
- Toolbar.showLoginButton(!loggedIn);
- Toolbar.showLogoutButton(loggedIn);
- }
- }
- );
- },
-
- /**
- * Sets shared key
- * @param sKey the shared key
- */
- my.setSharedKey = function (sKey) {
- sharedKey = sKey;
- };
-
- my.authenticateClicked = function () {
- Authentication.focusAuthenticationWindow();
- if (!APP.xmpp.isExternalAuthEnabled()) {
- Authentication.xmppAuthenticate();
- return;
- }
- // Get authentication URL
- if (!APP.xmpp.getMUCJoined()) {
- APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {
- // If conference has not been started yet - redirect to login page
- window.location.href = url;
- });
- } else {
- APP.xmpp.getPopupLoginUrl(UI.getRoomName(), function (url) {
- // Otherwise - open popup with authentication URL
- var authenticationWindow = Authentication.createAuthenticationWindow(
- function () {
- // On popup closed - retry room allocation
- APP.xmpp.allocateConferenceFocus(
- APP.UI.getRoomName(),
- function () { console.info("AUTH DONE"); }
- );
- }, url);
- if (!authenticationWindow) {
- messageHandler.openMessageDialog(
- null, "dialog.popupError");
- }
- });
- }
- };
-
- /**
- * Updates the room invite url.
- */
- my.updateRoomUrl = function (newRoomUrl) {
- roomUrl = newRoomUrl;
-
- // If the invite dialog has been already opened we update the information.
- var inviteLink = document.getElementById('inviteLinkRef');
- if (inviteLink) {
- inviteLink.value = roomUrl;
- inviteLink.select();
- $('#inviteLinkRef').parent()
- .find('button[value=true]').prop('disabled', false);
- }
- };
-
- /**
- * Disables and enables some of the buttons.
- */
- my.setupButtonsFromConfig = function () {
- if (config.disablePrezi)
- {
- $("#prezi_button").css({display: "none"});
- }
- };
-
- /**
- * Opens the lock room dialog.
- */
- my.openLockDialog = function () {
- // Only the focus is able to set a shared key.
- if (!APP.xmpp.isModerator()) {
- if (sharedKey) {
- messageHandler.openMessageDialog(null,
- "dialog.passwordError");
- } else {
- messageHandler.openMessageDialog(null, "dialog.passwordError2");
- }
- } else {
- if (sharedKey) {
- messageHandler.openTwoButtonDialog(null, null,
- "dialog.passwordCheck",
- null,
- false,
- "dialog.Remove",
- function (e, v) {
- if (v) {
- Toolbar.setSharedKey('');
- lockRoom(false);
- }
- });
- } else {
- var msg = APP.translation.generateTranslationHTML(
- "dialog.passwordMsg");
- var yourPassword = APP.translation.translateString(
- "dialog.yourPassword");
- messageHandler.openTwoButtonDialog(null, null, null,
- '' + msg + '
' +
- '',
- false,
- "dialog.Save",
- function (e, v, m, f) {
- if (v) {
- var lockKey = f.lockKey;
-
- if (lockKey) {
- Toolbar.setSharedKey(
- UIUtil.escapeHtml(lockKey));
- lockRoom(true);
- }
- }
- },
- null, null, 'input:first'
- );
- }
- }
- };
-
- /**
- * Opens the invite link dialog.
- */
- my.openLinkDialog = function () {
- var inviteAttreibutes;
-
- if (roomUrl === null) {
- inviteAttreibutes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
- APP.translation.translateString("roomUrlDefaultMsg") + '"';
- } else {
- inviteAttreibutes = "value=\"" + encodeURI(roomUrl) + "\"";
- }
- messageHandler.openTwoButtonDialog("dialog.shareLink",
- null, null,
- '',
- false,
- "dialog.Invite",
- function (e, v) {
- if (v) {
- if (roomUrl) {
- inviteParticipants();
- }
- }
- },
- function (event) {
- if (roomUrl) {
- document.getElementById('inviteLinkRef').select();
- } else {
- if (event && event.target)
- $(event.target)
- .find('button[value=true]').prop('disabled', true);
- }
- }
- );
- };
-
- /**
- * Opens the settings dialog.
- * FIXME: not used ?
- */
- my.openSettingsDialog = function () {
- var settings1 = APP.translation.generateTranslationHTML(
- "dialog.settings1");
- var settings2 = APP.translation.generateTranslationHTML(
- "dialog.settings2");
- var settings3 = APP.translation.generateTranslationHTML(
- "dialog.settings3");
-
- var yourPassword = APP.translation.translateString(
- "dialog.yourPassword");
-
- messageHandler.openTwoButtonDialog(null,
- '' + settings1 + '
' +
- '' +
- settings2 + '
' +
- '' +
- settings3 +
- '',
- null,
- null,
- false,
- "dialog.Save",
- function () {
- document.getElementById('lockKey').focus();
- },
- function (e, v) {
- if (v) {
- if ($('#initMuted').is(":checked")) {
- // it is checked
- }
-
- if ($('#requireNicknames').is(":checked")) {
- // it is checked
- }
- /*
- var lockKey = document.getElementById('lockKey');
-
- if (lockKey.value) {
- setSharedKey(lockKey.value);
- lockRoom(true);
- }
- */
- }
- }
- );
- };
-
- /**
- * Toggles the application in and out of full screen mode
- * (a.k.a. presentation mode in Chrome).
- */
- my.toggleFullScreen = function () {
- var fsElement = document.documentElement;
-
- if (!document.mozFullScreen && !document.webkitIsFullScreen) {
- //Enter Full Screen
- if (fsElement.mozRequestFullScreen) {
- fsElement.mozRequestFullScreen();
- }
- else {
- fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
- }
- } else {
- //Exit Full Screen
- if (document.mozCancelFullScreen) {
- document.mozCancelFullScreen();
- } else {
- document.webkitCancelFullScreen();
- }
- }
- };
- /**
- * Unlocks the lock button state.
- */
- my.unlockLockButton = function () {
- if ($("#lockIcon").hasClass("icon-security-locked"))
- UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
- };
- /**
- * Updates the lock button state to locked.
- */
- my.lockLockButton = function () {
- if ($("#lockIcon").hasClass("icon-security"))
- UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
- };
-
- /**
- * Shows or hides authentication button
- * @param show true to show or false to hide
- */
- my.showAuthenticateButton = function (show) {
- if (show) {
- $('#authentication').css({display: "inline"});
- }
- else {
- $('#authentication').css({display: "none"});
- }
- };
-
- // Shows or hides the 'recording' button.
- my.showRecordingButton = function (show) {
- if (!config.enableRecording) {
- return;
- }
-
- if (show) {
- $('#recording').css({display: "inline"});
- }
- else {
- $('#recording').css({display: "none"});
- }
- };
-
- // Sets the state of the recording button
- my.setRecordingButtonState = function (isRecording) {
- var selector = $('#recordButton');
- if (isRecording) {
- selector.removeClass("icon-recEnable");
- selector.addClass("icon-recEnable active");
- } else {
- selector.removeClass("icon-recEnable active");
- selector.addClass("icon-recEnable");
- }
- };
-
- // Shows or hides SIP calls button
- my.showSipCallButton = function (show) {
- if (APP.xmpp.isSipGatewayEnabled() && show) {
- $('#sipCallButton').css({display: "inline-block"});
- } else {
- $('#sipCallButton').css({display: "none"});
- }
- };
-
- // Shows or hides the dialpad button
- my.showDialPadButton = function (show) {
- if (show) {
- $('#dialPadButton').css({display: "inline-block"});
- } else {
- $('#dialPadButton').css({display: "none"});
- }
- };
-
- /**
- * Displays user authenticated identity name(login).
- * @param authIdentity identity name to be displayed.
- */
- my.setAuthenticatedIdentity = function (authIdentity) {
- if (authIdentity) {
- var selector = $('#toolbar_auth_identity');
- selector.css({display: "list-item"});
- selector.text(authIdentity);
- } else {
- $('#toolbar_auth_identity').css({display: "none"});
- }
- };
-
- /**
- * Shows/hides login button.
- * @param show true to show
- */
- my.showLoginButton = function (show) {
- if (show) {
- $('#toolbar_button_login').css({display: "list-item"});
- } else {
- $('#toolbar_button_login').css({display: "none"});
- }
- };
-
- /**
- * Shows/hides logout button.
- * @param show true to show
- */
- my.showLogoutButton = function (show) {
- if (show) {
- $('#toolbar_button_logout').css({display: "list-item"});
- } else {
- $('#toolbar_button_logout').css({display: "none"});
- }
- };
-
- /**
- * Sets the state of the button. The button has blue glow if desktop
- * streaming is active.
- * @param active the state of the desktop streaming.
- */
- my.changeDesktopSharingButtonState = function (active) {
- var button = $("#desktopsharing > a");
- if (active)
- {
- button.addClass("glow");
- }
- else
- {
- button.removeClass("glow");
- }
- };
-
- return my;
-}(Toolbar || {}));
-
-module.exports = Toolbar;
-},{"../../../service/authentication/AuthenticationEvents":105,"../authentication/Authentication":16,"../etherpad/Etherpad":19,"../prezi/Prezi":20,"../side_pannels/SidePanelToggler":22,"../util/MessageHandler":33,"../util/UIUtil":35,"./BottomToolbar":29}],31:[function(require,module,exports){
-/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
-
-var toolbarTimeoutObject,
- toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
-
-function showDesktopSharingButton() {
- if (APP.desktopsharing.isDesktopSharingEnabled()) {
- $('#desktopsharing').css({display: "inline"});
- } else {
- $('#desktopsharing').css({display: "none"});
- }
-}
-
-/**
- * Hides the toolbar.
- */
-function hideToolbar() {
- if(config.alwaysVisibleToolbar)
- return;
-
- var header = $("#header"),
- bottomToolbar = $("#bottomToolbar");
- var isToolbarHover = false;
- header.find('*').each(function () {
- var id = $(this).attr('id');
- if ($("#" + id + ":hover").length > 0) {
- isToolbarHover = true;
- }
- });
- if ($("#bottomToolbar:hover").length > 0) {
- isToolbarHover = true;
- }
-
- clearTimeout(toolbarTimeoutObject);
- toolbarTimeoutObject = null;
-
- if (!isToolbarHover) {
- header.hide("slide", { direction: "up", duration: 300});
- $('#subject').animate({top: "-=40"}, 300);
- if ($("#remoteVideos").hasClass("hidden")) {
- bottomToolbar.hide(
- "slide", {direction: "right", duration: 300});
- }
- }
- else {
- toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
- }
-}
-
-var ToolbarToggler = {
- /**
- * Shows the main toolbar.
- */
- showToolbar: function () {
- var header = $("#header"),
- bottomToolbar = $("#bottomToolbar");
- if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
- header.show("slide", { direction: "up", duration: 300});
- $('#subject').animate({top: "+=40"}, 300);
- if (!bottomToolbar.is(":visible")) {
- bottomToolbar.show(
- "slide", {direction: "right", duration: 300});
- }
-
- if (toolbarTimeoutObject) {
- clearTimeout(toolbarTimeoutObject);
- toolbarTimeoutObject = null;
- }
- toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
- toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
- }
-
- if (APP.xmpp.isModerator())
- {
-// TODO: Enable settings functionality.
-// Need to uncomment the settings button in index.html.
-// $('#settingsButton').css({visibility:"visible"});
- }
-
- // Show/hide desktop sharing button
- showDesktopSharingButton();
- },
-
-
- /**
- * Docks/undocks the toolbar.
- *
- * @param isDock indicates what operation to perform
- */
- dockToolbar: function (isDock) {
- if (isDock) {
- // First make sure the toolbar is shown.
- if (!$('#header').is(':visible')) {
- this.showToolbar();
- }
-
- // Then clear the time out, to dock the toolbar.
- if (toolbarTimeoutObject) {
- clearTimeout(toolbarTimeoutObject);
- toolbarTimeoutObject = null;
- }
- }
- else {
- if (!$('#header').is(':visible')) {
- this.showToolbar();
- }
- else {
- toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
- }
- }
- },
-
- showDesktopSharingButton: showDesktopSharingButton
-
-};
-
-module.exports = ToolbarToggler;
-},{}],32:[function(require,module,exports){
-var JitsiPopover = (function () {
- /**
- * Constructs new JitsiPopover and attaches it to the element
- * @param element jquery selector
- * @param options the options for the popover.
- * @constructor
- */
- function JitsiPopover(element, options)
- {
- this.options = {
- skin: "white",
- content: ""
- };
- if(options)
- {
- if(options.skin)
- this.options.skin = options.skin;
-
- if(options.content)
- this.options.content = options.content;
- }
-
- this.elementIsHovered = false;
- this.popoverIsHovered = false;
- this.popoverShown = false;
-
- element.data("jitsi_popover", this);
- this.element = element;
- this.template = ' ';
- var self = this;
- this.element.on("mouseenter", function () {
- self.elementIsHovered = true;
- self.show();
- }).on("mouseleave", function () {
- self.elementIsHovered = false;
- setTimeout(function () {
- self.hide();
- }, 10);
- });
- }
-
- /**
- * Shows the popover
- */
- JitsiPopover.prototype.show = function () {
- this.createPopover();
- this.popoverShown = true;
-
- };
-
- /**
- * Hides the popover
- */
- JitsiPopover.prototype.hide = function () {
- if(!this.elementIsHovered && !this.popoverIsHovered && this.popoverShown)
- {
- this.forceHide();
- }
- };
-
- /**
- * Hides the popover
- */
- JitsiPopover.prototype.forceHide = function () {
- $(".jitsipopover").remove();
- this.popoverShown = false;
- };
-
- /**
- * Creates the popover html
- */
- JitsiPopover.prototype.createPopover = function () {
- $("body").append(this.template);
- $(".jitsipopover > .jitsipopover-content").html(this.options.content);
- var self = this;
- $(".jitsipopover").on("mouseenter", function () {
- self.popoverIsHovered = true;
- }).on("mouseleave", function () {
- self.popoverIsHovered = false;
- self.hide();
- });
-
- this.refreshPosition();
- };
-
- /**
- * Refreshes the position of the popover
- */
- JitsiPopover.prototype.refreshPosition = function () {
- $(".jitsipopover").position({
- my: "bottom",
- at: "top",
- collision: "fit",
- of: this.element,
- using: function (position, elements) {
- var calcLeft = elements.target.left - elements.element.left + elements.target.width/2;
- $(".jitsipopover").css({top: position.top, left: position.left, display: "table"});
- $(".jitsipopover > .arrow").css({left: calcLeft});
- $(".jitsipopover > .jitsiPopupmenuPadding").css({left: calcLeft - 50});
- }
- });
- };
-
- /**
- * Updates the content of popover.
- * @param content new content
- */
- JitsiPopover.prototype.updateContent = function (content) {
- this.options.content = content;
- if(!this.popoverShown)
- return;
- $(".jitsipopover").remove();
- this.createPopover();
- };
-
- return JitsiPopover;
-
-
-})();
-
-module.exports = JitsiPopover;
-},{}],33:[function(require,module,exports){
-/* global $, APP, jQuery, toastr */
-var messageHandler = (function(my) {
-
- /**
- * Shows a message to the user.
- *
- * @param titleString the title of the message
- * @param messageString the text of the message
- */
- my.openMessageDialog = function(titleKey, messageKey) {
- var title = null;
- if(titleKey)
- {
- title = APP.translation.generateTranslationHTML(titleKey);
- }
- var message = APP.translation.generateTranslationHTML(messageKey);
- $.prompt(message,
- {
- title: title,
- persistent: false
- }
- );
- };
-
- /**
- * Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
- *
- * @param titleString the title of the message
- * @param msgString the text of the message
- * @param persistent boolean value which determines whether the message is persistent or not
- * @param leftButton the fist button's text
- * @param submitFunction function to be called on submit
- * @param loadedFunction function to be called after the prompt is fully loaded
- * @param closeFunction function to be called after the prompt is closed
- * @param focus optional focus selector or button index to be focused after
- * the dialog is opened
- * @param defaultButton index of default button which will be activated when
- * the user press 'enter'. Indexed from 0.
- */
- my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString,
- persistent, leftButtonKey, submitFunction, loadedFunction,
- closeFunction, focus, defaultButton)
- {
- var buttons = [];
-
- var leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
- buttons.push({ title: leftButton, value: true});
-
- var cancelButton
- = APP.translation.generateTranslationHTML("dialog.Cancel");
- buttons.push({title: cancelButton, value: false});
-
- var message = msgString, title = titleString;
- if (titleKey)
- {
- title = APP.translation.generateTranslationHTML(titleKey);
- }
- if (msgKey) {
- message = APP.translation.generateTranslationHTML(msgKey);
- }
- $.prompt(message, {
- title: title,
- persistent: false,
- buttons: buttons,
- defaultButton: defaultButton,
- focus: focus,
- loaded: loadedFunction,
- submit: submitFunction,
- close: closeFunction
- });
- };
-
- /**
- * Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
- *
- * @param titleString the title of the message
- * @param msgString the text of the message
- * @param persistent boolean value which determines whether the message is persistent or not
- * @param buttons object with the buttons. The keys must be the name of the button and value is the value
- * that will be passed to submitFunction
- * @param submitFunction function to be called on submit
- * @param loadedFunction function to be called after the prompt is fully loaded
- */
- my.openDialog = function (titleString, msgString, persistent, buttons,
- submitFunction, loadedFunction) {
- var args = {
- title: titleString,
- persistent: persistent,
- buttons: buttons,
- defaultButton: 1,
- loaded: loadedFunction,
- submit: submitFunction
- };
- if (persistent) {
- args.closeText = '';
- }
- return new Impromptu(msgString, args);
- };
-
- /**
- * Closes currently opened dialog.
- */
- my.closeDialog = function () {
- $.prompt.close();
- };
-
- /**
- * Shows a dialog with different states to the user.
- *
- * @param statesObject object containing all the states of the dialog
- */
- my.openDialogWithStates = function (statesObject, options) {
-
- return new Impromptu(statesObject, options);
- };
-
- /**
- * Opens new popup window for given url centered over current
- * window.
- *
- * @param url the URL to be displayed in the popup window
- * @param w the width of the popup window
- * @param h the height of the popup window
- * @param onPopupClosed optional callback function called when popup window
- * has been closed.
- *
- * @returns popup window object if opened successfully or undefined
- * in case we failed to open it(popup blocked)
- */
- my.openCenteredPopup = function (url, w, h, onPopupClosed) {
- var l = window.screenX + (window.innerWidth / 2) - (w / 2);
- var t = window.screenY + (window.innerHeight / 2) - (h / 2);
- var popup = window.open(
- url, '_blank',
- 'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + '');
- if (popup && onPopupClosed) {
- var pollTimer = window.setInterval(function () {
- if (popup.closed !== false) {
- window.clearInterval(pollTimer);
- onPopupClosed();
- }
- }, 200);
- }
- return popup;
- };
-
- /**
- * Shows a dialog prompting the user to send an error report.
- *
- * @param titleString the title of the message
- * @param msgString the text of the message
- * @param error the error that is being reported
- */
- my.openReportDialog = function(titleKey, msgKey, error) {
- my.openMessageDialog(titleKey, msgKey);
- console.log(error);
- //FIXME send the error to the server
- };
-
- /**
- * Shows an error dialog to the user.
- * @param title the title of the message
- * @param message the text of the messafe
- */
- my.showError = function(titleKey, msgKey) {
-
- if(!titleKey) {
- titleKey = "dialog.oops";
- }
- if(!msgKey)
- {
- msgKey = "dialog.defaultError";
- }
- messageHandler.openMessageDialog(titleKey, msgKey);
- };
-
- my.notify = function(displayName, displayNameKey,
- cls, messageKey, messageArguments, options) {
- var displayNameSpan = '" + displayName;
- }
- else
- {
- displayNameSpan += "data-i18n='" + displayNameKey +
- "'>" + APP.translation.translateString(displayNameKey);
- }
- displayNameSpan += "";
- toastr.info(
- displayNameSpan + '
' +
- '" +
- APP.translation.translateString(messageKey,
- messageArguments) +
- '', null, options);
- };
-
- return my;
-}(messageHandler || {}));
-
-module.exports = messageHandler;
-
-
+ if (constraints.audio) {
+ // if it is good enough for hangouts...
+ constraints.audio.optional.push(
+ {googEchoCancellation: true},
+ {googAutoGainControl: true},
+ {googNoiseSupression: true},
+ {googHighpassFilter: true},
+ {googNoisesuppression2: true},
+ {googEchoCancellation2: true},
+ {googAutoGainControl2: true}
+ );
+ }
+ if (constraints.video) {
+ constraints.video.optional.push(
+ {googNoiseReduction: false} // chrome 37 workaround for issue 3807, reenable in M38
+ );
+ if (um.indexOf('video') >= 0) {
+ constraints.video.optional.push(
+ {googLeakyBucket: true}
+ );
+ }
+ }
-},{}],34:[function(require,module,exports){
-var UIEvents = require("../../../service/UI/UIEvents");
-
-var nickname = null;
-var eventEmitter = null;
-
-var NickanameHandler = {
- init: function (emitter) {
- eventEmitter = emitter;
- var storedDisplayName = window.localStorage.displayname;
- if (storedDisplayName) {
- nickname = storedDisplayName;
- }
- },
- setNickname: function (newNickname) {
- if (!newNickname || nickname === newNickname)
- return;
-
- nickname = newNickname;
- window.localStorage.displayname = nickname;
- eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newNickname);
- },
- getNickname: function () {
- return nickname;
- },
- addListener: function (type, listener) {
- eventEmitter.on(type, listener);
- }
-};
-
-module.exports = NickanameHandler;
-},{"../../../service/UI/UIEvents":104}],35:[function(require,module,exports){
-/**
- * Created by hristo on 12/22/14.
- */
-module.exports = {
- /**
- * Returns the available video width.
- */
- getAvailableVideoWidth: function (isVisible) {
- var PanelToggler = require("../side_pannels/SidePanelToggler");
- if(typeof isVisible === "undefined" || isVisible === null)
- isVisible = PanelToggler.isVisible();
- var rightPanelWidth
- = isVisible ? PanelToggler.getPanelSize()[0] : 0;
-
- return window.innerWidth - rightPanelWidth;
- },
- /**
- * Changes the style class of the element given by id.
- */
- buttonClick: function(id, classname) {
- $(id).toggleClass(classname); // add the class to the clicked element
- },
- /**
- * Returns the text width for the given element.
- *
- * @param el the element
- */
- getTextWidth: function (el) {
- return (el.clientWidth + 1);
- },
-
- /**
- * Returns the text height for the given element.
- *
- * @param el the element
- */
- getTextHeight: function (el) {
- return (el.clientHeight + 1);
- },
-
- /**
- * Plays the sound given by id.
- *
- * @param id the identifier of the audio element.
- */
- playSoundNotification: function (id) {
- document.getElementById(id).play();
- },
-
- /**
- * Escapes the given text.
- */
- escapeHtml: function (unsafeText) {
- return $('').text(unsafeText).html();
- },
-
- imageToGrayScale: function (canvas) {
- var context = canvas.getContext('2d');
- var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
- var pixels = imgData.data;
-
- for (var i = 0, n = pixels.length; i < n; i += 4) {
- var grayscale
- = pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
- pixels[i ] = grayscale; // red
- pixels[i+1] = grayscale; // green
- pixels[i+2] = grayscale; // blue
- // pixels[i+3] is alpha
- }
- // redraw the image in black & white
- context.putImageData(imgData, 0, 0);
- },
-
- setTooltip: function (element, key, position) {
- element.setAttribute("data-i18n", "[data-content]" + key);
- element.setAttribute("data-toggle", "popover");
- element.setAttribute("data-placement", position);
- element.setAttribute("data-html", true);
- element.setAttribute("data-container", "body");
- }
-
-
+ if (um.indexOf('video') >= 0) {
+ setResolutionConstraints(constraints, resolution, isAndroid);
+ }
+
+ if (bandwidth) { // doesn't work currently, see webrtc issue 1846
+ if (!constraints.video) constraints.video = {mandatory: {}, optional: []};//same behaviour as true
+ constraints.video.optional.push({bandwidth: bandwidth});
+ }
+ if (fps) { // for some cameras it might be necessary to request 30fps
+ // so they choose 30fps mjpg over 10fps yuy2
+ if (!constraints.video) constraints.video = {mandatory: {}, optional: []};// same behaviour as true;
+ constraints.video.mandatory.minFrameRate = fps;
+ }
+
+ return constraints;
+}
+
+
+function RTCUtils(RTCService, onTemasysPluginReady)
+{
+ var self = this;
+ this.service = RTCService;
+ if (RTCBrowserType.isFirefox()) {
+ var FFversion = RTCBrowserType.getFirefoxVersion();
+ if (FFversion >= 40 && config.useBundle && config.useRtcpMux) {
+ this.peerconnection = mozRTCPeerConnection;
+ this.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
+ this.pc_constraints = {};
+ this.attachMediaStream = function (element, stream) {
+ // srcObject is being standardized and FF will eventually
+ // support that unprefixed. FF also supports the
+ // "element.src = URL.createObjectURL(...)" combo, but that
+ // will be deprecated in favour of srcObject.
+ //
+ // https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg
+ // https://github.com/webrtc/samples/issues/302
+ if(!element[0])
+ return;
+ element[0].mozSrcObject = stream;
+ element[0].play();
+ };
+ this.getStreamID = function (stream) {
+ var id = stream.id;
+ if (!id) {
+ var tracks = stream.getVideoTracks();
+ if (!tracks || tracks.length === 0)
+ {
+ tracks = stream.getAudioTracks();
+ }
+ id = tracks[0].id;
+ }
+ return SDPUtil.filter_special_chars(id);
+ };
+ this.getVideoSrc = function (element) {
+ if(!element)
+ return null;
+ return element.mozSrcObject;
+ };
+ this.setVideoSrc = function (element, src) {
+ if(element)
+ element.mozSrcObject = src;
+ };
+ RTCSessionDescription = mozRTCSessionDescription;
+ RTCIceCandidate = mozRTCIceCandidate;
+ } else {
+ console.error(
+ "Firefox requirements not met, ver: " + FFversion +
+ ", bundle: " + config.useBundle +
+ ", rtcp-mux: " + config.useRtcpMux);
+ window.location.href = 'unsupported_browser.html';
+ return;
+ }
+
+ } else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) {
+ this.peerconnection = webkitRTCPeerConnection;
+ this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+ this.attachMediaStream = function (element, stream) {
+ element.attr('src', webkitURL.createObjectURL(stream));
+ };
+ this.getStreamID = function (stream) {
+ // streams from FF endpoints have the characters '{' and '}'
+ // that make jQuery choke.
+ return SDPUtil.filter_special_chars(stream.id);
+ };
+ this.getVideoSrc = function (element) {
+ if(!element)
+ return null;
+ return element.getAttribute("src");
+ };
+ this.setVideoSrc = function (element, src) {
+ if(element)
+ element.setAttribute("src", src);
+ };
+ // DTLS should now be enabled by default but..
+ this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]};
+ if (navigator.userAgent.indexOf('Android') != -1) {
+ this.pc_constraints = {}; // disable DTLS on Android
+ }
+ if (!webkitMediaStream.prototype.getVideoTracks) {
+ webkitMediaStream.prototype.getVideoTracks = function () {
+ return this.videoTracks;
+ };
+ }
+ if (!webkitMediaStream.prototype.getAudioTracks) {
+ webkitMediaStream.prototype.getAudioTracks = function () {
+ return this.audioTracks;
+ };
+ }
+ }
+ // Detect IE/Safari
+ else if (RTCBrowserType.isTemasysPluginUsed()) {
+
+ //AdapterJS.WebRTCPlugin.setLogLevel(
+ // AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS.VERBOSE);
+
+ AdapterJS.webRTCReady(function (isPlugin) {
+
+ self.peerconnection = RTCPeerConnection;
+ self.getUserMedia = getUserMedia;
+ self.attachMediaStream = function (elSel, stream) {
+
+ if (stream.id === "dummyAudio" || stream.id === "dummyVideo") {
+ return;
+ }
+
+ attachMediaStream(elSel[0], stream);
+ };
+ self.getStreamID = function (stream) {
+ var id = SDPUtil.filter_special_chars(stream.label);
+ return id;
+ };
+ self.getVideoSrc = function (element) {
+ if (!element) {
+ console.warn("Attempt to get video SRC of null element");
+ return null;
+ }
+ var src = null;
+ var children = element.children;
+ for (var i = 0; i !== children.length; ++i) {
+ if (children[i].name === 'streamId') {
+ return children[i].value;
+ }
+ }
+ //console.info(element.id + " SRC: " + src);
+ return null;
+ };
+ self.setVideoSrc = function (element, src) {
+ //console.info("Set video src: ", element, src);
+ if (!src) {
+ console.warn("Not attaching video stream, 'src' is null");
+ return;
+ }
+ AdapterJS.WebRTCPlugin.WaitForPluginReady();
+ var stream = AdapterJS.WebRTCPlugin.plugin
+ .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, src);
+ attachMediaStream(element, stream);
+ };
+
+ onTemasysPluginReady(isPlugin);
+ });
+ } else {
+ try {
+ console.log('Browser does not appear to be WebRTC-capable');
+ } catch (e) { }
+ window.location.href = 'unsupported_browser.html';
+ }
+}
+
+
+RTCUtils.prototype.getUserMediaWithConstraints = function(
+ um, success_callback, failure_callback, resolution,bandwidth, fps,
+ desktopStream)
+{
+ currentResolution = resolution;
+ // Check if we are running on Android device
+ var isAndroid = navigator.userAgent.indexOf('Android') != -1;
+
+ var constraints = getConstraints(
+ um, resolution, bandwidth, fps, desktopStream, isAndroid);
+
+ console.info("Get media constraints", constraints);
+
+ var self = this;
+
+ try {
+ this.getUserMedia(constraints,
+ function (stream) {
+ console.log('onUserMediaSuccess');
+ self.setAvailableDevices(um, true);
+ success_callback(stream);
+ },
+ function (error) {
+ self.setAvailableDevices(um, false);
+ console.warn('Failed to get access to local media. Error ',
+ error, constraints);
+ if (failure_callback) {
+ failure_callback(error);
+ }
+ });
+ } catch (e) {
+ console.error('GUM failed: ', e);
+ if(failure_callback) {
+ failure_callback(e);
+ }
+ }
};
-},{"../side_pannels/SidePanelToggler":22}],36:[function(require,module,exports){
-var JitsiPopover = require("../util/JitsiPopover");
-
-/**
- * Constructs new connection indicator.
- * @param videoContainer the video container associated with the indicator.
- * @constructor
- */
-function ConnectionIndicator(videoContainer, jid)
-{
- this.videoContainer = videoContainer;
- this.bandwidth = null;
- this.packetLoss = null;
- this.bitrate = null;
- this.showMoreValue = false;
- this.resolution = null;
- this.transport = [];
- this.popover = null;
- this.jid = jid;
- this.create();
-}
-
-/**
- * Values for the connection quality
- * @type {{98: string,
- * 81: string,
- * 64: string,
- * 47: string,
- * 30: string,
- * 0: string}}
- */
-ConnectionIndicator.connectionQualityValues = {
- 98: "18px", //full
- 81: "15px",//4 bars
- 64: "11px",//3 bars
- 47: "7px",//2 bars
- 30: "3px",//1 bar
- 0: "0px"//empty
-};
-
-ConnectionIndicator.getIP = function(value)
-{
- return value.substring(0, value.lastIndexOf(":"));
-};
-
-ConnectionIndicator.getPort = function(value)
-{
- return value.substring(value.lastIndexOf(":") + 1, value.length);
-};
-
-ConnectionIndicator.getStringFromArray = function (array) {
- var res = "";
- for(var i = 0; i < array.length; i++)
- {
- res += (i === 0? "" : ", ") + array[i];
- }
- return res;
-};
-
-/**
- * Generates the html content.
- * @returns {string} the html content.
- */
-ConnectionIndicator.prototype.generateText = function () {
- var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
-
- var translate = APP.translation.translateString;
-
- if(this.bitrate === null)
- {
- downloadBitrate = "N/A";
- uploadBitrate = "N/A";
- }
- else
- {
- downloadBitrate =
- this.bitrate.download? this.bitrate.download + " Kbps" : "N/A";
- uploadBitrate =
- this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A";
- }
-
- if(this.packetLoss === null)
- {
- packetLoss = "N/A";
- }
- else
- {
-
- packetLoss = "↓" +
- (this.packetLoss.download !== null? this.packetLoss.download : "N/A") +
- "% ↑" +
- (this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") + "%";
- }
-
- var resolutionValue = null;
- if(this.resolution && this.jid != null)
- {
- var keys = Object.keys(this.resolution);
- for(var ssrc in this.resolution)
- {
- resolutionValue = this.resolution[ssrc];
- }
- }
-
- if(this.jid === null)
- {
- resolution = "";
- if(this.resolution === null || !Object.keys(this.resolution) ||
- Object.keys(this.resolution).length === 0)
- {
- resolution = "N/A";
- }
- else
- for(i in this.resolution)
- {
- resolutionValue = this.resolution[i];
- if(resolutionValue)
- {
- if(resolutionValue.height &&
- resolutionValue.width)
- {
- resolution += (resolution === ""? "" : ", ") +
- resolutionValue.width + "x" +
- resolutionValue.height;
- }
- }
- }
- }
- else if(!resolutionValue ||
- !resolutionValue.height ||
- !resolutionValue.width)
- {
- resolution = "N/A";
- }
- else
- {
- resolution = resolutionValue.width + "x" + resolutionValue.height;
- }
-
- var result = "" +
- "" +
- "" +
- translate("connectionindicator.bitrate") + " | " +
- "↓" +
- downloadBitrate + " ↑" +
- uploadBitrate + " | " +
- "
" +
- "" +
- translate("connectionindicator.packetloss") + " | " +
- "" + packetLoss + " | " +
- "
" +
- "" +
- translate("connectionindicator.resolution") + " | " +
- "" + resolution + " |
";
-
- if(this.videoContainer.videoSpanId == "localVideoContainer") {
- result += "" +
- translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
- "
";
- }
-
- if(this.showMoreValue)
- {
- var downloadBandwidth, uploadBandwidth, transport;
- if(this.bandwidth === null)
- {
- downloadBandwidth = "N/A";
- uploadBandwidth = "N/A";
- }
- else
- {
- downloadBandwidth = this.bandwidth.download?
- this.bandwidth.download + " Kbps" :
- "N/A";
- uploadBandwidth = this.bandwidth.upload?
- this.bandwidth.upload + " Kbps" :
- "N/A";
- }
-
- if(!this.transport || this.transport.length === 0)
- {
- transport = "" +
- "" +
- translate("connectionindicator.address") + " | " +
- " N/A |
";
- }
- else
- {
- var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
- for(i = 0; i < this.transport.length; i++)
- {
- var ip = ConnectionIndicator.getIP(this.transport[i].ip);
- var port = ConnectionIndicator.getPort(this.transport[i].ip);
- var localIP =
- ConnectionIndicator.getIP(this.transport[i].localip);
- var localPort =
- ConnectionIndicator.getPort(this.transport[i].localip);
- if(data.remoteIP.indexOf(ip) == -1)
- {
- data.remoteIP.push(ip);
- }
-
- if(data.remotePort.indexOf(port) == -1)
- {
- data.remotePort.push(port);
- }
-
- if(data.localIP.indexOf(localIP) == -1)
- {
- data.localIP.push(localIP);
- }
-
- if(data.localPort.indexOf(localPort) == -1)
- {
- data.localPort.push(localPort);
- }
-
- }
-
- var local_address_key = "connectionindicator.localaddress";
- var remote_address_key = "connectionindicator.remoteaddress";
- var localTransport =
- "" +
- translate(local_address_key, {count: data.localIP.length}) +
- " | " +
- ConnectionIndicator.getStringFromArray(data.localIP) +
- " |
";
- transport =
- "" +
- translate(remote_address_key,
- {count: data.remoteIP.length}) +
- " | " +
- ConnectionIndicator.getStringFromArray(data.remoteIP) +
- " |
";
-
- var key_remote = "connectionindicator.remoteport",
- key_local = "connectionindicator.localport";
-
- transport += "" +
- "" +
- "" +
- translate(key_remote, {count: this.transport.length}) +
- " | ";
- localTransport += " |
" +
- "" +
- "" +
- translate(key_local, {count: this.transport.length}) +
- " | ";
-
- transport +=
- ConnectionIndicator.getStringFromArray(data.remotePort);
- localTransport +=
- ConnectionIndicator.getStringFromArray(data.localPort);
- transport += " |
";
- transport += localTransport + "";
- transport +="" +
- "" +
- translate("connectionindicator.transport") + " | " +
- "" + this.transport[0].type + " |
";
-
- }
-
- result += "" +
- "" +
- "" +
- "" +
- translate("connectionindicator.bandwidth") + "" +
- " | " +
- "↓" +
- downloadBandwidth +
- " ↑" +
- uploadBandwidth + " |
";
-
- result += transport + "
";
-
- }
-
- return result;
-};
-
-/**
- * Shows or hide the additional information.
- */
-ConnectionIndicator.prototype.showMore = function () {
- this.showMoreValue = !this.showMoreValue;
- this.updatePopoverData();
-};
-
-
-function createIcon(classes)
-{
- var icon = document.createElement("span");
- for(var i in classes)
- {
- icon.classList.add(classes[i]);
- }
- icon.appendChild(
- document.createElement("i")).classList.add("icon-connection");
- return icon;
-}
-
-/**
- * Creates the indicator
- */
-ConnectionIndicator.prototype.create = function () {
- this.connectionIndicatorContainer = document.createElement("div");
- this.connectionIndicatorContainer.className = "connectionindicator";
- this.connectionIndicatorContainer.style.display = "none";
- this.videoContainer.container.appendChild(this.connectionIndicatorContainer);
- this.popover = new JitsiPopover(
- $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"),
- {content: "" +
- APP.translation.translateString("connectionindicator.na") + "
",
- skin: "black"});
-
- this.emptyIcon = this.connectionIndicatorContainer.appendChild(
- createIcon(["connection", "connection_empty"]));
- this.fullIcon = this.connectionIndicatorContainer.appendChild(
- createIcon(["connection", "connection_full"]));
-
-};
-
-/**
- * Removes the indicator
- */
-ConnectionIndicator.prototype.remove = function()
-{
- if (this.connectionIndicatorContainer.parentNode) {
- this.connectionIndicatorContainer.parentNode.removeChild(
- this.connectionIndicatorContainer);
- }
- this.popover.forceHide();
-
-};
-
-/**
- * Updates the data of the indicator
- * @param percent the percent of connection quality
- * @param object the statistics data.
- */
-ConnectionIndicator.prototype.updateConnectionQuality =
-function (percent, object) {
-
- if(percent === null)
- {
- this.connectionIndicatorContainer.style.display = "none";
- this.popover.forceHide();
- return;
- }
- else
- {
- if(this.connectionIndicatorContainer.style.display == "none") {
- this.connectionIndicatorContainer.style.display = "block";
- this.videoContainer.updateIconPositions();
- }
- }
- this.bandwidth = object.bandwidth;
- this.bitrate = object.bitrate;
- this.packetLoss = object.packetLoss;
- this.transport = object.transport;
- if(object.resolution)
- {
- this.resolution = object.resolution;
- }
- for(var quality in ConnectionIndicator.connectionQualityValues)
- {
- if(percent >= quality)
- {
- this.fullIcon.style.width =
- ConnectionIndicator.connectionQualityValues[quality];
- }
- }
- this.updatePopoverData();
-};
-
-/**
- * Updates the resolution
- * @param resolution the new resolution
- */
-ConnectionIndicator.prototype.updateResolution = function (resolution) {
- this.resolution = resolution;
- this.updatePopoverData();
-};
-
-/**
- * Updates the content of the popover
- */
-ConnectionIndicator.prototype.updatePopoverData = function () {
- this.popover.updateContent(
- "" + this.generateText() + "
");
- APP.translation.translateElement($(".connection_info"));
-};
-
-/**
- * Hides the popover
- */
-ConnectionIndicator.prototype.hide = function () {
- this.popover.forceHide();
-};
-
-/**
- * Hides the indicator
- */
-ConnectionIndicator.prototype.hideIndicator = function () {
- this.connectionIndicatorContainer.style.display = "none";
- if(this.popover)
- this.popover.forceHide();
-};
-
+
+RTCUtils.prototype.setAvailableDevices = function (um, available) {
+ var devices = {};
+ if(um.indexOf("video") != -1)
+ {
+ devices.video = available;
+ }
+ if(um.indexOf("audio") != -1)
+ {
+ devices.audio = available;
+ }
+ this.service.setDeviceAvailability(devices);
+}
+
+/**
+ * We ask for audio and video combined stream in order to get permissions and
+ * not to ask twice.
+ */
+RTCUtils.prototype.obtainAudioAndVideoPermissions =
+ function(devices, callback, usageOptions)
+{
+ var self = this;
+ // Get AV
+
+ var successCallback = function (stream) {
+ if(callback)
+ callback(stream, usageOptions);
+ else
+ self.successCallback(stream, usageOptions);
+ };
+
+ if(!devices)
+ devices = ['audio', 'video'];
+
+ var newDevices = [];
+
+
+ if(usageOptions)
+ for(var i = 0; i < devices.length; i++)
+ {
+ var device = devices[i];
+ if(usageOptions[device] === true)
+ newDevices.push(device);
+ }
+ else
+ newDevices = devices;
+
+ if(newDevices.length === 0)
+ {
+ successCallback();
+ return;
+ }
+
+ if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) {
+
+ // With FF/IE we can't split the stream into audio and video because FF
+ // doesn't support media stream constructors. So, we need to get the
+ // audio stream separately from the video stream using two distinct GUM
+ // calls. Not very user friendly :-( but we don't have many other
+ // options neither.
+ //
+ // Note that we pack those 2 streams in a single object and pass it to
+ // the successCallback method.
+ var obtainVideo = function (audioStream) {
+ self.getUserMediaWithConstraints(
+ ['video'],
+ function (videoStream) {
+ return successCallback({
+ audioStream: audioStream,
+ videoStream: videoStream
+ });
+ },
+ function (error) {
+ console.error(
+ 'failed to obtain video stream - stop', error);
+ self.errorCallback(error);
+ },
+ config.resolution || '360');
+ };
+ var obtainAudio = function () {
+ self.getUserMediaWithConstraints(
+ ['audio'],
+ function (audioStream) {
+ if (newDevices.indexOf('video') !== -1)
+ obtainVideo(audioStream);
+ },
+ function (error) {
+ console.error(
+ 'failed to obtain audio stream - stop', error);
+ self.errorCallback(error);
+ }
+ );
+ };
+ if (newDevices.indexOf('audio') !== -1) {
+ obtainAudio();
+ } else {
+ obtainVideo(null);
+ }
+ } else {
+ this.getUserMediaWithConstraints(
+ newDevices,
+ function (stream) {
+ successCallback(stream);
+ },
+ function (error) {
+ self.errorCallback(error);
+ },
+ config.resolution || '360');
+ }
+
+};
+
+RTCUtils.prototype.successCallback = function (stream, usageOptions) {
+ // If this is FF or IE, the stream parameter is *not* a MediaStream object,
+ // it's an object with two properties: audioStream, videoStream.
+ if (stream && stream.getAudioTracks && stream.getVideoTracks)
+ console.log('got', stream, stream.getAudioTracks().length,
+ stream.getVideoTracks().length);
+ this.handleLocalStream(stream, usageOptions);
+};
+
+RTCUtils.prototype.errorCallback = function (error) {
+ var self = this;
+ console.error('failed to obtain audio/video stream - trying audio only', error);
+ var resolution = getPreviousResolution(currentResolution);
+ if(typeof error == "object" && error.constraintName && error.name
+ && (error.name == "ConstraintNotSatisfiedError" ||
+ error.name == "OverconstrainedError") &&
+ (error.constraintName == "minWidth" || error.constraintName == "maxWidth" ||
+ error.constraintName == "minHeight" || error.constraintName == "maxHeight")
+ && resolution != null)
+ {
+ self.getUserMediaWithConstraints(['audio', 'video'],
+ function (stream) {
+ return self.successCallback(stream);
+ }, function (error) {
+ return self.errorCallback(error);
+ }, resolution);
+ }
+ else
+ {
+ self.getUserMediaWithConstraints(
+ ['audio'],
+ function (stream) {
+ return self.successCallback(stream);
+ },
+ function (error) {
+ console.error('failed to obtain audio/video stream - stop',
+ error);
+ return self.successCallback(null);
+ }
+ );
+ }
+
+}
+
+RTCUtils.prototype.handleLocalStream = function(stream, usageOptions)
+{
+ // If this is FF, the stream parameter is *not* a MediaStream object, it's
+ // an object with two properties: audioStream, videoStream.
+ var audioStream, videoStream;
+ if(window.webkitMediaStream)
+ {
+ audioStream = new webkitMediaStream();
+ videoStream = new webkitMediaStream();
+ if(stream) {
+ var audioTracks = stream.getAudioTracks();
+
+ for (var i = 0; i < audioTracks.length; i++) {
+ audioStream.addTrack(audioTracks[i]);
+ }
+
+ var videoTracks = stream.getVideoTracks();
+
+ for (i = 0; i < videoTracks.length; i++) {
+ videoStream.addTrack(videoTracks[i]);
+ }
+ }
+ }
+ else if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed())
+ { // Firefox and Temasys plugin
+ if (stream && stream.audioStream)
+ audioStream = stream.audioStream;
+ else
+ audioStream = new DummyMediaStream("dummyAudio");
+
+ if (stream && stream.videoStream)
+ videoStream = stream.videoStream;
+ else
+ videoStream = new DummyMediaStream("dummyVideo");
+ }
+
+ var audioMuted = (usageOptions && usageOptions.audio === false),
+ videoMuted = (usageOptions && usageOptions.video === false);
+
+ var audioGUM = (!usageOptions || usageOptions.audio !== false),
+ videoGUM = (!usageOptions || usageOptions.video !== false);
+
+
+ this.service.createLocalStream(audioStream, "audio", null, null,
+ audioMuted, audioGUM);
+
+ this.service.createLocalStream(videoStream, "video", null, null,
+ videoMuted, videoGUM);
+};
+
+function DummyMediaStream(id) {
+ this.id = id;
+ this.label = id;
+ this.stop = function() { };
+ this.getAudioTracks = function() { return []; }
+ this.getVideoTracks = function() { return []; }
+}
+
+RTCUtils.prototype.createStream = function(stream, isVideo) {
+ var newStream = null;
+ if (window.webkitMediaStream) {
+ newStream = new webkitMediaStream();
+ if (newStream) {
+ var tracks = (isVideo ? stream.getVideoTracks() : stream.getAudioTracks());
+
+ for (i = 0; i < tracks.length; i++) {
+ newStream.addTrack(tracks[i]);
+ }
+ }
+
+ }
+ else {
+ // FIXME: this is duplicated with 'handleLocalStream' !!!
+ if (stream) {
+ newStream = stream;
+ } else {
+ newStream = new DummyMediaStream(isVideo ? "dummyVideo" : "dummyAudio");
+ }
+ }
+
+ return newStream;
+};
+
+module.exports = RTCUtils;
+
+},{"../../service/RTC/Resolutions":100,"../xmpp/SDPUtil":56,"./RTCBrowserType":8,"./adapter.screenshare":10}],10:[function(require,module,exports){
+/*! adapterjs - v0.11.0 - 2015-06-08 */
+
+// Adapter's interface.
+var AdapterJS = AdapterJS || {};
+
+// Browserify compatibility
+if(typeof exports !== 'undefined') {
+ module.exports = AdapterJS;
+}
+
+AdapterJS.options = AdapterJS.options || {};
+
+// uncomment to get virtual webcams
+// AdapterJS.options.getAllCams = true;
+
+// uncomment to prevent the install prompt when the plugin in not yet installed
+// AdapterJS.options.hidePluginInstallPrompt = true;
+
+// AdapterJS version
+AdapterJS.VERSION = '0.11.0';
+
+// This function will be called when the WebRTC API is ready to be used
+// Whether it is the native implementation (Chrome, Firefox, Opera) or
+// the plugin
+// You may Override this function to synchronise the start of your application
+// with the WebRTC API being ready.
+// If you decide not to override use this synchronisation, it may result in
+// an extensive CPU usage on the plugin start (once per tab loaded)
+// Params:
+// - isUsingPlugin: true is the WebRTC plugin is being used, false otherwise
+//
+AdapterJS.onwebrtcready = AdapterJS.onwebrtcready || function(isUsingPlugin) {
+ // The WebRTC API is ready.
+ // Override me and do whatever you want here
+};
+
+// Sets a callback function to be called when the WebRTC interface is ready.
+// The first argument is the function to callback.\
+// Throws an error if the first argument is not a function
+AdapterJS.webRTCReady = function (callback) {
+ if (typeof callback !== 'function') {
+ throw new Error('Callback provided is not a function');
+ }
+
+ if (true === AdapterJS.onwebrtcreadyDone) {
+ // All WebRTC interfaces are ready, just call the callback
+ callback(null !== AdapterJS.WebRTCPlugin.plugin);
+ } else {
+ // will be triggered automatically when your browser/plugin is ready.
+ AdapterJS.onwebrtcready = callback;
+ }
+};
+
+// Plugin namespace
+AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {};
+
+// The object to store plugin information
+AdapterJS.WebRTCPlugin.pluginInfo = {
+ prefix : 'Tem',
+ plugName : 'TemWebRTCPlugin',
+ pluginId : 'plugin0',
+ type : 'application/x-temwebrtcplugin',
+ onload : '__TemWebRTCReady0',
+ portalLink : 'http://skylink.io/plugin/',
+ downloadLink : null, //set below
+ companyName: 'Temasys'
+};
+if(!!navigator.platform.match(/^Mac/i)) {
+ AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1n77hco';
+}
+else if(!!navigator.platform.match(/^Win/i)) {
+ AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN';
+}
+
+// Unique identifier of each opened page
+AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2);
+
+// Use this whenever you want to call the plugin.
+AdapterJS.WebRTCPlugin.plugin = null;
+
+// Set log level for the plugin once it is ready.
+// The different values are
+// This is an asynchronous function that will run when the plugin is ready
+AdapterJS.WebRTCPlugin.setLogLevel = null;
+
+// Defines webrtc's JS interface according to the plugin's implementation.
+// Define plugin Browsers as WebRTC Interface.
+AdapterJS.WebRTCPlugin.defineWebRTCInterface = null;
+
+// This function detects whether or not a plugin is installed.
+// Checks if Not IE (firefox, for example), else if it's IE,
+// we're running IE and do something. If not it is not supported.
+AdapterJS.WebRTCPlugin.isPluginInstalled = null;
+
+ // Lets adapter.js wait until the the document is ready before injecting the plugin
+AdapterJS.WebRTCPlugin.pluginInjectionInterval = null;
+
+// Inject the HTML DOM object element into the page.
+AdapterJS.WebRTCPlugin.injectPlugin = null;
+
+// States of readiness that the plugin goes through when
+// being injected and stated
+AdapterJS.WebRTCPlugin.PLUGIN_STATES = {
+ NONE : 0, // no plugin use
+ INITIALIZING : 1, // Detected need for plugin
+ INJECTING : 2, // Injecting plugin
+ INJECTED: 3, // Plugin element injected but not usable yet
+ READY: 4 // Plugin ready to be used
+};
+
+// Current state of the plugin. You cannot use the plugin before this is
+// equal to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY
+AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE;
+
+// True is AdapterJS.onwebrtcready was already called, false otherwise
+// Used to make sure AdapterJS.onwebrtcready is only called once
+AdapterJS.onwebrtcreadyDone = false;
+
+// Log levels for the plugin.
+// To be set by calling AdapterJS.WebRTCPlugin.setLogLevel
+/*
+Log outputs are prefixed in some cases.
+ INFO: Information reported by the plugin.
+ ERROR: Errors originating from within the plugin.
+ WEBRTC: Error originating from within the libWebRTC library
+*/
+// From the least verbose to the most verbose
+AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS = {
+ NONE : 'NONE',
+ ERROR : 'ERROR',
+ WARNING : 'WARNING',
+ INFO: 'INFO',
+ VERBOSE: 'VERBOSE',
+ SENSITIVE: 'SENSITIVE'
+};
+
+// Does a waiting check before proceeding to load the plugin.
+AdapterJS.WebRTCPlugin.WaitForPluginReady = null;
+
+// This methid will use an interval to wait for the plugin to be ready.
+AdapterJS.WebRTCPlugin.callWhenPluginReady = null;
+
+// !!!! WARNING: DO NOT OVERRIDE THIS FUNCTION. !!!
+// This function will be called when plugin is ready. It sends necessary
+// details to the plugin.
+// The function will wait for the document to be ready and the set the
+// plugin state to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY,
+// indicating that it can start being requested.
+// This function is not in the IE/Safari condition brackets so that
+// TemPluginLoaded function might be called on Chrome/Firefox.
+// This function is the only private function that is not encapsulated to
+// allow the plugin method to be called.
+__TemWebRTCReady0 = function () {
+ if (document.readyState === 'complete') {
+ AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
+
+ AdapterJS.maybeThroughWebRTCReady();
+ } else {
+ AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () {
+ if (document.readyState === 'complete') {
+ // TODO: update comments, we wait for the document to be ready
+ clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval);
+ AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY;
+
+ AdapterJS.maybeThroughWebRTCReady();
+ }
+ }, 100);
+ }
+};
+
+AdapterJS.maybeThroughWebRTCReady = function() {
+ if (!AdapterJS.onwebrtcreadyDone) {
+ AdapterJS.onwebrtcreadyDone = true;
+
+ if (typeof(AdapterJS.onwebrtcready) === 'function') {
+ AdapterJS.onwebrtcready(AdapterJS.WebRTCPlugin.plugin !== null);
+ }
+ }
+};
+
+// Text namespace
+AdapterJS.TEXT = {
+ PLUGIN: {
+ REQUIRE_INSTALLATION: 'This website requires you to install a WebRTC-enabling plugin ' +
+ 'to work on this browser.',
+ NOT_SUPPORTED: 'Your browser does not support WebRTC.',
+ BUTTON: 'Install Now'
+ },
+ REFRESH: {
+ REQUIRE_REFRESH: 'Please refresh page',
+ BUTTON: 'Refresh Page'
+ }
+};
+
+// The result of ice connection states.
+// - starting: Ice connection is starting.
+// - checking: Ice connection is checking.
+// - connected Ice connection is connected.
+// - completed Ice connection is connected.
+// - done Ice connection has been completed.
+// - disconnected Ice connection has been disconnected.
+// - failed Ice connection has failed.
+// - closed Ice connection is closed.
+AdapterJS._iceConnectionStates = {
+ starting : 'starting',
+ checking : 'checking',
+ connected : 'connected',
+ completed : 'connected',
+ done : 'completed',
+ disconnected : 'disconnected',
+ failed : 'failed',
+ closed : 'closed'
+};
+
+//The IceConnection states that has been fired for each peer.
+AdapterJS._iceConnectionFiredStates = [];
+
+
+// Check if WebRTC Interface is defined.
+AdapterJS.isDefined = null;
+
+// This function helps to retrieve the webrtc detected browser information.
+// This sets:
+// - webrtcDetectedBrowser: The browser agent name.
+// - webrtcDetectedVersion: The browser version.
+// - webrtcDetectedType: The types of webRTC support.
+// - 'moz': Mozilla implementation of webRTC.
+// - 'webkit': WebKit implementation of webRTC.
+// - 'plugin': Using the plugin implementation.
+AdapterJS.parseWebrtcDetectedBrowser = function () {
+ var hasMatch, checkMatch = navigator.userAgent.match(
+ /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
+ if (/trident/i.test(checkMatch[1])) {
+ hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || [];
+ webrtcDetectedBrowser = 'IE';
+ webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10);
+ } else if (checkMatch[1] === 'Chrome') {
+ hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/);
+ if (hasMatch !== null) {
+ webrtcDetectedBrowser = 'opera';
+ webrtcDetectedVersion = parseInt(hasMatch[1], 10);
+ }
+ }
+ if (navigator.userAgent.indexOf('Safari')) {
+ if (typeof InstallTrigger !== 'undefined') {
+ webrtcDetectedBrowser = 'firefox';
+ } else if (/*@cc_on!@*/ false || !!document.documentMode) {
+ webrtcDetectedBrowser = 'IE';
+ } else if (
+ Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) {
+ webrtcDetectedBrowser = 'safari';
+ } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) {
+ webrtcDetectedBrowser = 'opera';
+ } else if (!!window.chrome) {
+ webrtcDetectedBrowser = 'chrome';
+ }
+ }
+ if (!webrtcDetectedBrowser) {
+ webrtcDetectedVersion = checkMatch[1];
+ }
+ if (!webrtcDetectedVersion) {
+ try {
+ checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] :
+ [navigator.appName, navigator.appVersion, '-?'];
+ if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) {
+ checkMatch.splice(1, 1, hasMatch[1]);
+ }
+ webrtcDetectedVersion = parseInt(checkMatch[1], 10);
+ } catch (error) { }
+ }
+};
+
+// To fix configuration as some browsers does not support
+// the 'urls' attribute.
+AdapterJS.maybeFixConfiguration = function (pcConfig) {
+ if (pcConfig === null) {
+ return;
+ }
+ for (var i = 0; i < pcConfig.iceServers.length; i++) {
+ if (pcConfig.iceServers[i].hasOwnProperty('urls')) {
+ pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls;
+ delete pcConfig.iceServers[i].urls;
+ }
+ }
+};
+
+AdapterJS.addEvent = function(elem, evnt, func) {
+ if (elem.addEventListener) { // W3C DOM
+ elem.addEventListener(evnt, func, false);
+ } else if (elem.attachEvent) {// OLD IE DOM
+ elem.attachEvent('on'+evnt, func);
+ } else { // No much to do
+ elem[evnt] = func;
+ }
+};
+
+AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNewTab, displayRefreshBar) {
+ // only inject once the page is ready
+ if (document.readyState !== 'complete') {
+ return;
+ }
+
+ var w = window;
+ var i = document.createElement('iframe');
+ i.style.position = 'fixed';
+ i.style.top = '-41px';
+ i.style.left = 0;
+ i.style.right = 0;
+ i.style.width = '100%';
+ i.style.height = '40px';
+ i.style.backgroundColor = '#ffffe1';
+ i.style.border = 'none';
+ i.style.borderBottom = '1px solid #888888';
+ i.style.zIndex = '9999999';
+ if(typeof i.style.webkitTransition === 'string') {
+ i.style.webkitTransition = 'all .5s ease-out';
+ } else if(typeof i.style.transition === 'string') {
+ i.style.transition = 'all .5s ease-out';
+ }
+ document.body.appendChild(i);
+ c = (i.contentWindow) ? i.contentWindow :
+ (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument;
+ c.document.open();
+ c.document.write('' + text + '');
+ if(buttonText && buttonLink) {
+ c.document.write('');
+ c.document.close();
+
+ AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function(e) {
+ if (!!displayRefreshBar) {
+ AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION ?
+ AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH : AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH,
+ AdapterJS.TEXT.REFRESH.BUTTON, 'javascript:location.reload()');
+ }
+ window.open(buttonLink, !!openNewTab ? '_blank' : '_top');
+
+ e.preventDefault();
+ try {
+ event.cancelBubble = true;
+ } catch(error) { }
+ });
+ }
+ else {
+ c.document.close();
+ }
+ AdapterJS.addEvent(c.document, 'click', function() {
+ w.document.body.removeChild(i);
+ });
+ setTimeout(function() {
+ if(typeof i.style.webkitTransform === 'string') {
+ i.style.webkitTransform = 'translateY(40px)';
+ } else if(typeof i.style.transform === 'string') {
+ i.style.transform = 'translateY(40px)';
+ } else {
+ i.style.top = '0px';
+ }
+ }, 300);
+};
+
+// -----------------------------------------------------------
+// Detected webrtc implementation. Types are:
+// - 'moz': Mozilla implementation of webRTC.
+// - 'webkit': WebKit implementation of webRTC.
+// - 'plugin': Using the plugin implementation.
+webrtcDetectedType = null;
+
+// Detected webrtc datachannel support. Types are:
+// - 'SCTP': SCTP datachannel support.
+// - 'RTP': RTP datachannel support.
+webrtcDetectedDCSupport = null;
+
+// Set the settings for creating DataChannels, MediaStream for
+// Cross-browser compability.
+// - This is only for SCTP based support browsers.
+// the 'urls' attribute.
+checkMediaDataChannelSettings =
+ function (peerBrowserAgent, peerBrowserVersion, callback, constraints) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+ var beOfferer = true;
+ var isLocalFirefox = webrtcDetectedBrowser === 'firefox';
+ // Nightly version does not require MozDontOfferDataChannel for interop
+ var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30;
+ var isPeerFirefox = peerBrowserAgent === 'firefox';
+ var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' &&
+ ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false);
+
+ // Resends an updated version of constraints for MozDataChannel to work
+ // If other userAgent is firefox and user is firefox, remove MozDataChannel
+ if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) {
+ try {
+ delete constraints.mandatory.MozDontOfferDataChannel;
+ } catch (error) {
+ console.error('Failed deleting MozDontOfferDataChannel');
+ console.error(error);
+ }
+ } else if ((isLocalFirefox && !isPeerFirefox)) {
+ constraints.mandatory.MozDontOfferDataChannel = true;
+ }
+ if (!isLocalFirefox) {
+ // temporary measure to remove Moz* constraints in non Firefox browsers
+ for (var prop in constraints.mandatory) {
+ if (constraints.mandatory.hasOwnProperty(prop)) {
+ if (prop.indexOf('Moz') !== -1) {
+ delete constraints.mandatory[prop];
+ }
+ }
+ }
+ }
+ // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the
+ // interopability of the media stream
+ if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) {
+ beOfferer = false;
+ }
+ callback(beOfferer, constraints);
+};
+
+// Handles the differences for all browsers ice connection state output.
+// - Tested outcomes are:
+// - Chrome (offerer) : 'checking' > 'completed' > 'completed'
+// - Chrome (answerer) : 'checking' > 'connected'
+// - Firefox (offerer) : 'checking' > 'connected'
+// - Firefox (answerer): 'checking' > 'connected'
+checkIceConnectionState = function (peerId, iceConnectionState, callback) {
+ if (typeof callback !== 'function') {
+ console.warn('No callback specified in checkIceConnectionState. Aborted.');
+ return;
+ }
+ peerId = (peerId) ? peerId : 'peer';
+
+ if (!AdapterJS._iceConnectionFiredStates[peerId] ||
+ iceConnectionState === AdapterJS._iceConnectionStates.disconnected ||
+ iceConnectionState === AdapterJS._iceConnectionStates.failed ||
+ iceConnectionState === AdapterJS._iceConnectionStates.closed) {
+ AdapterJS._iceConnectionFiredStates[peerId] = [];
+ }
+ iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState];
+ if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) {
+ AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState);
+ if (iceConnectionState === AdapterJS._iceConnectionStates.connected) {
+ setTimeout(function () {
+ AdapterJS._iceConnectionFiredStates[peerId]
+ .push(AdapterJS._iceConnectionStates.done);
+ callback(AdapterJS._iceConnectionStates.done);
+ }, 1000);
+ }
+ callback(iceConnectionState);
+ }
+ return;
+};
+
+// Firefox:
+// - Creates iceServer from the url for Firefox.
+// - Create iceServer with stun url.
+// - Create iceServer with turn url.
+// - Ignore the transport parameter from TURN url for FF version <=27.
+// - Return null for createIceServer if transport=tcp.
+// - FF 27 and above supports transport parameters in TURN url,
+// - So passing in the full url to create iceServer.
+// Chrome:
+// - Creates iceServer from the url for Chrome M33 and earlier.
+// - Create iceServer with stun url.
+// - Chrome M28 & above uses below TURN format.
+// Plugin:
+// - Creates Ice Server for Plugin Browsers
+// - If Stun - Create iceServer with stun url.
+// - Else - Create iceServer with turn url
+// - This is a WebRTC Function
+createIceServer = null;
+
+// Firefox:
+// - Creates IceServers for Firefox
+// - Use .url for FireFox.
+// - Multiple Urls support
+// Chrome:
+// - Creates iceServers from the urls for Chrome M34 and above.
+// - .urls is supported since Chrome M34.
+// - Multiple Urls support
+// Plugin:
+// - Creates Ice Servers for Plugin Browsers
+// - Multiple Urls support
+// - This is a WebRTC Function
+createIceServers = null;
+//------------------------------------------------------------
+
+//The RTCPeerConnection object.
+RTCPeerConnection = null;
+
+// Creates RTCSessionDescription object for Plugin Browsers
+RTCSessionDescription = (typeof RTCSessionDescription === 'function') ?
+ RTCSessionDescription : null;
+
+// Creates RTCIceCandidate object for Plugin Browsers
+RTCIceCandidate = (typeof RTCIceCandidate === 'function') ?
+ RTCIceCandidate : null;
+
+// Get UserMedia (only difference is the prefix).
+// Code from Adam Barth.
+getUserMedia = null;
+
+// Attach a media stream to an element.
+attachMediaStream = null;
+
+// Re-attach a media stream to an element.
+reattachMediaStream = null;
+
+
+// Detected browser agent name. Types are:
+// - 'firefox': Firefox browser.
+// - 'chrome': Chrome browser.
+// - 'opera': Opera browser.
+// - 'safari': Safari browser.
+// - 'IE' - Internet Explorer browser.
+webrtcDetectedBrowser = null;
+
+// Detected browser version.
+webrtcDetectedVersion = null;
+
+// Check for browser types and react accordingly
+if (navigator.mozGetUserMedia) {
+ webrtcDetectedBrowser = 'firefox';
+ webrtcDetectedVersion = parseInt(navigator
+ .userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
+ webrtcDetectedType = 'moz';
+ webrtcDetectedDCSupport = 'SCTP';
+
+ RTCPeerConnection = function (pcConfig, pcConstraints) {
+ AdapterJS.maybeFixConfiguration(pcConfig);
+ return new mozRTCPeerConnection(pcConfig, pcConstraints);
+ };
+
+ // The RTCSessionDescription object.
+ RTCSessionDescription = mozRTCSessionDescription;
+ window.RTCSessionDescription = RTCSessionDescription;
+
+ // The RTCIceCandidate object.
+ RTCIceCandidate = mozRTCIceCandidate;
+ window.RTCIceCandidate = RTCIceCandidate;
+
+ window.getUserMedia = navigator.mozGetUserMedia.bind(navigator);
+ navigator.getUserMedia = window.getUserMedia;
+
+ // Shim for MediaStreamTrack.getSources.
+ MediaStreamTrack.getSources = function(successCb) {
+ setTimeout(function() {
+ var infos = [
+ { kind: 'audio', id: 'default', label:'', facing:'' },
+ { kind: 'video', id: 'default', label:'', facing:'' }
+ ];
+ successCb(infos);
+ }, 0);
+ };
+
+ createIceServer = function (url, username, password) {
+ var iceServer = null;
+ var url_parts = url.split(':');
+ if (url_parts[0].indexOf('stun') === 0) {
+ iceServer = { url : url };
+ } else if (url_parts[0].indexOf('turn') === 0) {
+ if (webrtcDetectedVersion < 27) {
+ var turn_url_parts = url.split('?');
+ if (turn_url_parts.length === 1 ||
+ turn_url_parts[1].indexOf('transport=udp') === 0) {
+ iceServer = {
+ url : turn_url_parts[0],
+ credential : password,
+ username : username
+ };
+ }
+ } else {
+ iceServer = {
+ url : url,
+ credential : password,
+ username : username
+ };
+ }
+ }
+ return iceServer;
+ };
+
+ createIceServers = function (urls, username, password) {
+ var iceServers = [];
+ for (i = 0; i < urls.length; i++) {
+ var iceServer = createIceServer(urls[i], username, password);
+ if (iceServer !== null) {
+ iceServers.push(iceServer);
+ }
+ }
+ return iceServers;
+ };
+
+ attachMediaStream = function (element, stream) {
+ element.mozSrcObject = stream;
+ if (stream !== null)
+ element.play();
+
+ return element;
+ };
+
+ reattachMediaStream = function (to, from) {
+ to.mozSrcObject = from.mozSrcObject;
+ to.play();
+ return to;
+ };
+
+ MediaStreamTrack.getSources = MediaStreamTrack.getSources || function (callback) {
+ if (!callback) {
+ throw new TypeError('Failed to execute \'getSources\' on \'MediaStreamTrack\'' +
+ ': 1 argument required, but only 0 present.');
+ }
+ return callback([]);
+ };
+
+ // Fake get{Video,Audio}Tracks
+ if (!MediaStream.prototype.getVideoTracks) {
+ MediaStream.prototype.getVideoTracks = function () {
+ return [];
+ };
+ }
+ if (!MediaStream.prototype.getAudioTracks) {
+ MediaStream.prototype.getAudioTracks = function () {
+ return [];
+ };
+ }
+
+ AdapterJS.maybeThroughWebRTCReady();
+} else if (navigator.webkitGetUserMedia) {
+ webrtcDetectedBrowser = 'chrome';
+ webrtcDetectedType = 'webkit';
+ webrtcDetectedVersion = parseInt(navigator
+ .userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10);
+ // check if browser is opera 20+
+ var checkIfOpera = navigator.userAgent.match(/\bOPR\/(\d+)/);
+ if (checkIfOpera !== null) {
+ webrtcDetectedBrowser = 'opera';
+ webrtcDetectedVersion = parseInt(checkIfOpera[1], 10);
+ }
+ // check browser datachannel support
+ if ((webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion >= 31) ||
+ (webrtcDetectedBrowser === 'opera' && webrtcDetectedVersion >= 20)) {
+ webrtcDetectedDCSupport = 'SCTP';
+ } else if (webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion < 30 &&
+ webrtcDetectedVersion > 24) {
+ webrtcDetectedDCSupport = 'RTP';
+ } else {
+ webrtcDetectedDCSupport = '';
+ }
+
+ createIceServer = function (url, username, password) {
+ var iceServer = null;
+ var url_parts = url.split(':');
+ if (url_parts[0].indexOf('stun') === 0) {
+ iceServer = { 'url' : url };
+ } else if (url_parts[0].indexOf('turn') === 0) {
+ iceServer = {
+ 'url' : url,
+ 'credential' : password,
+ 'username' : username
+ };
+ }
+ return iceServer;
+ };
+
+ createIceServers = function (urls, username, password) {
+ var iceServers = [];
+ if (webrtcDetectedVersion >= 34) {
+ iceServers = {
+ 'urls' : urls,
+ 'credential' : password,
+ 'username' : username
+ };
+ } else {
+ for (i = 0; i < urls.length; i++) {
+ var iceServer = createIceServer(urls[i], username, password);
+ if (iceServer !== null) {
+ iceServers.push(iceServer);
+ }
+ }
+ }
+ return iceServers;
+ };
+
+ RTCPeerConnection = function (pcConfig, pcConstraints) {
+ if (webrtcDetectedVersion < 34) {
+ AdapterJS.maybeFixConfiguration(pcConfig);
+ }
+ return new webkitRTCPeerConnection(pcConfig, pcConstraints);
+ };
+
+ window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator);
+ navigator.getUserMedia = window.getUserMedia;
+
+ attachMediaStream = function (element, stream) {
+ if (typeof element.srcObject !== 'undefined') {
+ element.srcObject = stream;
+ } else if (typeof element.mozSrcObject !== 'undefined') {
+ element.mozSrcObject = stream;
+ } else if (typeof element.src !== 'undefined') {
+ element.src = (stream === null ? '' : URL.createObjectURL(stream));
+ } else {
+ console.log('Error attaching stream to element.');
+ }
+ return element;
+ };
+
+ reattachMediaStream = function (to, from) {
+ to.src = from.src;
+ return to;
+ };
+
+ AdapterJS.maybeThroughWebRTCReady();
+} else { // TRY TO USE PLUGIN
+ // IE 9 is not offering an implementation of console.log until you open a console
+ if (typeof console !== 'object' || typeof console.log !== 'function') {
+ /* jshint -W020 */
+ console = {} || console;
+ // Implemented based on console specs from MDN
+ // You may override these functions
+ console.log = function (arg) {};
+ console.info = function (arg) {};
+ console.error = function (arg) {};
+ console.dir = function (arg) {};
+ console.exception = function (arg) {};
+ console.trace = function (arg) {};
+ console.warn = function (arg) {};
+ console.count = function (arg) {};
+ console.debug = function (arg) {};
+ console.count = function (arg) {};
+ console.time = function (arg) {};
+ console.timeEnd = function (arg) {};
+ console.group = function (arg) {};
+ console.groupCollapsed = function (arg) {};
+ console.groupEnd = function (arg) {};
+ /* jshint +W020 */
+ }
+ webrtcDetectedType = 'plugin';
+ webrtcDetectedDCSupport = 'plugin';
+ AdapterJS.parseWebrtcDetectedBrowser();
+ isIE = webrtcDetectedBrowser === 'IE';
+
+ /* jshint -W035 */
+ AdapterJS.WebRTCPlugin.WaitForPluginReady = function() {
+ while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
+ /* empty because it needs to prevent the function from running. */
+ }
+ };
+ /* jshint +W035 */
+
+ AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) {
+ if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
+ // Call immediately if possible
+ // Once the plugin is set, the code will always take this path
+ callback();
+ } else {
+ // otherwise start a 100ms interval
+ var checkPluginReadyState = setInterval(function () {
+ if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) {
+ clearInterval(checkPluginReadyState);
+ callback();
+ }
+ }, 100);
+ }
+ };
+
+ AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) {
+ AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
+ AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel);
+ });
+ };
+
+ AdapterJS.WebRTCPlugin.injectPlugin = function () {
+ // only inject once the page is ready
+ if (document.readyState !== 'complete') {
+ return;
+ }
+
+ // Prevent multiple injections
+ if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) {
+ return;
+ }
+
+ AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING;
+
+ if (webrtcDetectedBrowser === 'IE' && webrtcDetectedVersion <= 10) {
+ var frag = document.createDocumentFragment();
+ AdapterJS.WebRTCPlugin.plugin = document.createElement('div');
+ AdapterJS.WebRTCPlugin.plugin.innerHTML = '';
+ while (AdapterJS.WebRTCPlugin.plugin.firstChild) {
+ frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild);
+ }
+ document.body.appendChild(frag);
+
+ // Need to re-fetch the plugin
+ AdapterJS.WebRTCPlugin.plugin =
+ document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId);
+ } else {
+ // Load Plugin
+ AdapterJS.WebRTCPlugin.plugin = document.createElement('object');
+ AdapterJS.WebRTCPlugin.plugin.id =
+ AdapterJS.WebRTCPlugin.pluginInfo.pluginId;
+ // IE will only start the plugin if it's ACTUALLY visible
+ if (isIE) {
+ AdapterJS.WebRTCPlugin.plugin.width = '1px';
+ AdapterJS.WebRTCPlugin.plugin.height = '1px';
+ } else { // The size of the plugin on Safari should be 0x0px
+ // so that the autorisation prompt is at the top
+ AdapterJS.WebRTCPlugin.plugin.width = '0px';
+ AdapterJS.WebRTCPlugin.plugin.height = '0px';
+ }
+ AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type;
+ AdapterJS.WebRTCPlugin.plugin.innerHTML = '' +
+ '' +
+ ' ' +
+ (AdapterJS.options.getAllCams ? '':'') +
+ '';
+ document.body.appendChild(AdapterJS.WebRTCPlugin.plugin);
+ }
+
+
+ AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED;
+ };
+
+ AdapterJS.WebRTCPlugin.isPluginInstalled =
+ function (comName, plugName, installedCb, notInstalledCb) {
+ if (!isIE) {
+ var pluginArray = navigator.plugins;
+ for (var i = 0; i < pluginArray.length; i++) {
+ if (pluginArray[i].name.indexOf(plugName) >= 0) {
+ installedCb();
+ return;
+ }
+ }
+ notInstalledCb();
+ } else {
+ try {
+ var axo = new ActiveXObject(comName + '.' + plugName);
+ } catch (e) {
+ notInstalledCb();
+ return;
+ }
+ installedCb();
+ }
+ };
+
+ AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () {
+ AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING;
+
+ AdapterJS.isDefined = function (variable) {
+ return variable !== null && variable !== undefined;
+ };
+
+ createIceServer = function (url, username, password) {
+ var iceServer = null;
+ var url_parts = url.split(':');
+ if (url_parts[0].indexOf('stun') === 0) {
+ iceServer = {
+ 'url' : url,
+ 'hasCredentials' : false
+ };
+ } else if (url_parts[0].indexOf('turn') === 0) {
+ iceServer = {
+ 'url' : url,
+ 'hasCredentials' : true,
+ 'credential' : password,
+ 'username' : username
+ };
+ }
+ return iceServer;
+ };
+
+ createIceServers = function (urls, username, password) {
+ var iceServers = [];
+ for (var i = 0; i < urls.length; ++i) {
+ iceServers.push(createIceServer(urls[i], username, password));
+ }
+ return iceServers;
+ };
+
+ RTCSessionDescription = function (info) {
+ AdapterJS.WebRTCPlugin.WaitForPluginReady();
+ return AdapterJS.WebRTCPlugin.plugin.
+ ConstructSessionDescription(info.type, info.sdp);
+ };
+
+ RTCPeerConnection = function (servers, constraints) {
+ var iceServers = null;
+ if (servers) {
+ iceServers = servers.iceServers;
+ for (var i = 0; i < iceServers.length; i++) {
+ if (iceServers[i].urls && !iceServers[i].url) {
+ iceServers[i].url = iceServers[i].urls;
+ }
+ iceServers[i].hasCredentials = AdapterJS.
+ isDefined(iceServers[i].username) &&
+ AdapterJS.isDefined(iceServers[i].credential);
+ }
+ }
+ var mandatory = (constraints && constraints.mandatory) ?
+ constraints.mandatory : null;
+ var optional = (constraints && constraints.optional) ?
+ constraints.optional : null;
+
+ AdapterJS.WebRTCPlugin.WaitForPluginReady();
+ return AdapterJS.WebRTCPlugin.plugin.
+ PeerConnection(AdapterJS.WebRTCPlugin.pageId,
+ iceServers, mandatory, optional);
+ };
+
+ MediaStreamTrack = {};
+ MediaStreamTrack.getSources = function (callback) {
+ AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
+ AdapterJS.WebRTCPlugin.plugin.GetSources(callback);
+ });
+ };
+
+ window.getUserMedia = function (constraints, successCallback, failureCallback) {
+ constraints.audio = constraints.audio || false;
+ constraints.video = constraints.video || false;
+
+ AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
+ AdapterJS.WebRTCPlugin.plugin.
+ getUserMedia(constraints, successCallback, failureCallback);
+ });
+ };
+ window.navigator.getUserMedia = window.getUserMedia;
+
+ attachMediaStream = function (element, stream) {
+ if (!element || !element.parentNode) {
+ return;
+ }
+
+ var streamId
+ if (stream === null) {
+ streamId = '';
+ }
+ else {
+ stream.enableSoundTracks(true);
+ streamId = stream.id;
+ }
+
+ if (element.nodeName.toLowerCase() !== 'audio') {
+ var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id;
+ if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) {
+ var frag = document.createDocumentFragment();
+ var temp = document.createElement('div');
+ var classHTML = '';
+ if (element.className) {
+ classHTML = 'class="' + element.className + '" ';
+ } else if (element.attributes && element.attributes['class']) {
+ classHTML = 'class="' + element.attributes['class'].value + '" ';
+ }
+
+ temp.innerHTML = '';
+ while (temp.firstChild) {
+ frag.appendChild(temp.firstChild);
+ }
+
+ var height = '';
+ var width = '';
+ if (element.getBoundingClientRect) {
+ var rectObject = element.getBoundingClientRect();
+ width = rectObject.width + 'px';
+ height = rectObject.height + 'px';
+ }
+ else if (element.width) {
+ width = element.width;
+ height = element.height;
+ } else {
+ // TODO: What scenario could bring us here?
+ }
+
+ element.parentNode.insertBefore(frag, element);
+ frag = document.getElementById(elementId);
+ frag.width = width;
+ frag.height = height;
+ element.parentNode.removeChild(element);
+ } else {
+ var children = element.children;
+ for (var i = 0; i !== children.length; ++i) {
+ if (children[i].name === 'streamId') {
+ children[i].value = streamId;
+ break;
+ }
+ }
+ element.setStreamId(streamId);
+ }
+ var newElement = document.getElementById(elementId);
+ newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {};
+ if (isIE) { // on IE the event needs to be plugged manually
+ newElement.attachEvent('onplaying', newElement.onplaying);
+ newElement.onclick = (element.onclick) ? element.onclick : function (arg) {};
+ newElement._TemOnClick = function (id) {
+ var arg = {
+ srcElement : document.getElementById(id)
+ };
+ newElement.onclick(arg);
+ };
+ }
+ return newElement;
+ } else {
+ return element;
+ }
+ };
+
+ reattachMediaStream = function (to, from) {
+ var stream = null;
+ var children = from.children;
+ for (var i = 0; i !== children.length; ++i) {
+ if (children[i].name === 'streamId') {
+ AdapterJS.WebRTCPlugin.WaitForPluginReady();
+ stream = AdapterJS.WebRTCPlugin.plugin
+ .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value);
+ break;
+ }
+ }
+ if (stream !== null) {
+ return attachMediaStream(to, stream);
+ } else {
+ console.log('Could not find the stream associated with this element');
+ }
+ };
+
+ RTCIceCandidate = function (candidate) {
+ if (!candidate.sdpMid) {
+ candidate.sdpMid = '';
+ }
+
+ AdapterJS.WebRTCPlugin.WaitForPluginReady();
+ return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate(
+ candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate
+ );
+ };
+
+ // inject plugin
+ AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin);
+ AdapterJS.WebRTCPlugin.injectPlugin();
+ };
+
+ // This function will be called if the plugin is needed (browser different
+ // from Chrome or Firefox), but the plugin is not installed.
+ AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb ||
+ function() {
+ AdapterJS.addEvent(document,
+ 'readystatechange',
+ AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv);
+ AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv();
+ };
+
+ AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () {
+ if (AdapterJS.options.hidePluginInstallPrompt) {
+ return;
+ }
+
+ var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink;
+ if(downloadLink) { // if download link
+ var popupString;
+ if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link
+ popupString = 'This website requires you to install the ' +
+ ' ' + AdapterJS.WebRTCPlugin.pluginInfo.companyName +
+ ' WebRTC Plugin' +
+ ' to work on this browser.';
+ } else { // no portal link, just print a generic explanation
+ popupString = AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION;
+ }
+
+ AdapterJS.renderNotificationBar(popupString, AdapterJS.TEXT.PLUGIN.BUTTON, downloadLink);
+ } else { // no download link, just print a generic explanation
+ AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED);
+ }
+ };
+
+ // Try to detect the plugin and act accordingly
+ AdapterJS.WebRTCPlugin.isPluginInstalled(
+ AdapterJS.WebRTCPlugin.pluginInfo.prefix,
+ AdapterJS.WebRTCPlugin.pluginInfo.plugName,
+ AdapterJS.WebRTCPlugin.defineWebRTCInterface,
+ AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb);
+}
+
+
+
+(function () {
+
+ 'use strict';
+
+ var baseGetUserMedia = null;
+
+ AdapterJS.TEXT.EXTENSION = {
+ REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.',
+ REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.',
+ REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.',
+ BUTTON_FF: 'Install Now',
+ BUTTON_CHROME: 'Go to Chrome Web Store'
+ };
+
+ var clone = function(obj) {
+ if (null == obj || "object" != typeof obj) return obj;
+ var copy = obj.constructor();
+ for (var attr in obj) {
+ if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
+ }
+ return copy;
+ };
+
+ if (window.navigator.mozGetUserMedia) {
+ baseGetUserMedia = window.navigator.getUserMedia;
+
+ navigator.getUserMedia = function (constraints, successCb, failureCb) {
+
+ if (constraints && constraints.video && !!constraints.video.mediaSource) {
+ // intercepting screensharing requests
+
+ if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') {
+ throw new Error('Only "screen" and "window" option is available as mediaSource');
+ }
+
+ var updatedConstraints = clone(constraints);
+
+ //constraints.video.mediaSource = constraints.video.mediaSource;
+ updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource;
+
+ // so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked.
+ // strange but this works anyway
+ var checkIfReady = setInterval(function () {
+ if (document.readyState === 'complete') {
+ clearInterval(checkIfReady);
+
+ baseGetUserMedia(updatedConstraints, successCb, function (error) {
+ if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') {
+ AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,
+ AdapterJS.TEXT.EXTENSION.BUTTON_FF,
+ 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true);
+ //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname;
+ } else {
+ failureCb(error);
+ }
+ });
+ }
+ }, 1);
+
+ } else { // regular GetUserMediaRequest
+ baseGetUserMedia(constraints, successCb, failureCb);
+ }
+ };
+
+ getUserMedia = navigator.getUserMedia;
+
+ } else if (window.navigator.webkitGetUserMedia) {
+ baseGetUserMedia = window.navigator.getUserMedia;
+
+ navigator.getUserMedia = function (constraints, successCb, failureCb) {
+
+ if (constraints && constraints.video && !!constraints.video.mediaSource) {
+ if (window.webrtcDetectedBrowser !== 'chrome') {
+ throw new Error('Current browser does not support screensharing');
+ }
+
+ // would be fine since no methods
+ var updatedConstraints = clone(constraints);
+
+ var chromeCallback = function(error, sourceId) {
+ if(!error) {
+ updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {};
+ updatedConstraints.video.mandatory.chromeMediaSource = 'desktop';
+ updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920;
+ updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080;
+
+ if (sourceId) {
+ updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId;
+ }
+
+ delete updatedConstraints.video.mediaSource;
+
+ baseGetUserMedia(updatedConstraints, successCb, failureCb);
+
+ } else {
+ if (error === 'permission-denied') {
+ throw new Error('Permission denied for screen retrieval');
+ } else {
+ throw new Error('Failed retrieving selected screen');
+ }
+ }
+ };
+
+ var onIFrameCallback = function (event) {
+ if (!event.data) {
+ return;
+ }
+
+ if (event.data.chromeMediaSourceId) {
+ if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
+ chromeCallback('permission-denied');
+ } else {
+ chromeCallback(null, event.data.chromeMediaSourceId);
+ }
+ }
+
+ if (event.data.chromeExtensionStatus) {
+ if (event.data.chromeExtensionStatus === 'not-installed') {
+ AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,
+ AdapterJS.TEXT.EXTENSION.BUTTON_CHROME,
+ event.data.data, true, true);
+ } else {
+ chromeCallback(event.data.chromeExtensionStatus, null);
+ }
+ }
+
+ // this event listener is no more needed
+ window.removeEventListener('message', onIFrameCallback);
+ };
+
+ window.addEventListener('message', onIFrameCallback);
+
+ postFrameMessage({
+ captureSourceId: true
+ });
+
+ } else {
+ baseGetUserMedia(constraints, successCb, failureCb);
+ }
+ };
+
+ getUserMedia = navigator.getUserMedia;
+
+ } else {
+ baseGetUserMedia = window.navigator.getUserMedia;
+
+ navigator.getUserMedia = function (constraints, successCb, failureCb) {
+ if (constraints && constraints.video && !!constraints.video.mediaSource) {
+ // would be fine since no methods
+ var updatedConstraints = clone(constraints);
+
+ // wait for plugin to be ready
+ AdapterJS.WebRTCPlugin.callWhenPluginReady(function() {
+ // check if screensharing feature is available
+ if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature &&
+ !!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) {
+
+
+ // set the constraints
+ updatedConstraints.video.optional = updatedConstraints.video.optional || [];
+ updatedConstraints.video.optional.push({
+ sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing'
+ });
+
+ delete updatedConstraints.video.mediaSource;
+ } else {
+ throw new Error('Your WebRTC plugin does not support screensharing');
+ }
+ baseGetUserMedia(updatedConstraints, successCb, failureCb);
+ });
+ } else {
+ baseGetUserMedia(constraints, successCb, failureCb);
+ }
+ };
+
+ getUserMedia = window.navigator.getUserMedia;
+ }
+
+ if (window.webrtcDetectedBrowser === 'chrome') {
+ var iframe = document.createElement('iframe');
+
+ iframe.onload = function() {
+ iframe.isLoaded = true;
+ };
+
+ iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html';
+ //'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html';
+ iframe.style.display = 'none';
+
+ (document.body || document.documentElement).appendChild(iframe);
+
+ var postFrameMessage = function (object) {
+ object = object || {};
+
+ if (!iframe.isLoaded) {
+ setTimeout(function () {
+ iframe.contentWindow.postMessage(object, '*');
+ }, 100);
+ return;
+ }
+
+ iframe.contentWindow.postMessage(object, '*');
+ };
+ }
+})();
+},{}],11:[function(require,module,exports){
+var UI = {};
+
+var VideoLayout = require("./videolayout/VideoLayout.js");
+var AudioLevels = require("./audio_levels/AudioLevels.js");
+var Prezi = require("./prezi/Prezi.js");
+var Etherpad = require("./etherpad/Etherpad.js");
+var Chat = require("./side_pannels/chat/Chat.js");
+var Toolbar = require("./toolbars/Toolbar");
+var ToolbarToggler = require("./toolbars/ToolbarToggler");
+var BottomToolbar = require("./toolbars/BottomToolbar");
+var ContactList = require("./side_pannels/contactlist/ContactList");
+var Avatar = require("./avatar/Avatar");
+var EventEmitter = require("events");
+var SettingsMenu = require("./side_pannels/settings/SettingsMenu");
+var Settings = require("./../settings/Settings");
+var PanelToggler = require("./side_pannels/SidePanelToggler");
+var RoomNameGenerator = require("./welcome_page/RoomnameGenerator");
+UI.messageHandler = require("./util/MessageHandler");
+var messageHandler = UI.messageHandler;
+var Authentication = require("./authentication/Authentication");
+var UIUtil = require("./util/UIUtil");
+var NicknameHandler = require("./util/NicknameHandler");
+var CQEvents = require("../../service/connectionquality/CQEvents");
+var DesktopSharingEventTypes
+ = require("../../service/desktopsharing/DesktopSharingEventTypes");
+var RTCEvents = require("../../service/RTC/RTCEvents");
+var RTCBrowserType = require("../RTC/RTCBrowserType");
+var StreamEventTypes = require("../../service/RTC/StreamEventTypes");
+var XMPPEvents = require("../../service/xmpp/XMPPEvents");
+var UIEvents = require("../../service/UI/UIEvents");
+var MemberEvents = require("../../service/members/Events");
+
+var eventEmitter = new EventEmitter();
+var roomName = null;
+
+
+function promptDisplayName() {
+ var message = '';
+ message += APP.translation.translateString(
+ "dialog.displayNameRequired");
+ message += '
' +
+ '';
+
+ var buttonTxt
+ = APP.translation.generateTranslationHTML("dialog.Ok");
+ var buttons = [];
+ buttons.push({title: buttonTxt, value: "ok"});
+
+ messageHandler.openDialog(null, message,
+ true,
+ buttons,
+ function (e, v, m, f) {
+ if (v == "ok") {
+ var displayName = f.displayName;
+ if (displayName) {
+ VideoLayout.inputDisplayNameHandler(displayName);
+ return true;
+ }
+ }
+ e.preventDefault();
+ },
+ function () {
+ var form = $.prompt.getPrompt();
+ var input = form.find("input[name='displayName']");
+ input.focus();
+ var button = form.find("button");
+ button.attr("disabled", "disabled");
+ input.keyup(function () {
+ if(!input.val())
+ button.attr("disabled", "disabled");
+ else
+ button.removeAttr("disabled");
+ });
+ }
+ );
+}
+
+
+function notifyForInitialMute()
+{
+ messageHandler.notify(null, "notify.mutedTitle", "connected",
+ "notify.muted", null, {timeOut: 120000});
+}
+
+function setupPrezi()
+{
+ $("#reloadPresentationLink").click(function()
+ {
+ Prezi.reloadPresentation();
+ });
+}
+
+function setupChat()
+{
+ Chat.init();
+ $("#toggle_smileys").click(function() {
+ Chat.toggleSmileys();
+ });
+}
+
+function setupToolbars() {
+ Toolbar.init(UI);
+ Toolbar.setupButtonsFromConfig();
+ BottomToolbar.init();
+}
+
+function streamHandler(stream, isMuted) {
+ switch (stream.type)
+ {
+ case "audio":
+ VideoLayout.changeLocalAudio(stream, isMuted);
+ break;
+ case "video":
+ VideoLayout.changeLocalVideo(stream, isMuted);
+ break;
+ case "stream":
+ VideoLayout.changeLocalStream(stream, isMuted);
+ break;
+ }
+}
+
+function onXmppConnectionFailed(stropheErrorMsg) {
+
+ var title = APP.translation.generateTranslationHTML(
+ "dialog.error");
+
+ var message;
+ if (stropheErrorMsg) {
+ message = APP.translation.generateTranslationHTML(
+ "dialog.connectErrorWithMsg", {msg: stropheErrorMsg});
+ } else {
+ message = APP.translation.generateTranslationHTML(
+ "dialog.connectError");
+ }
+
+ messageHandler.openDialog(
+ title, message, true, {}, function (e, v, m, f) { return false; });
+}
+
+function onDisposeConference(unload) {
+ Toolbar.showAuthenticateButton(false);
+}
+
+function onDisplayNameChanged(jid, displayName) {
+ ContactList.onDisplayNameChange(jid, displayName);
+ SettingsMenu.onDisplayNameChange(jid, displayName);
+ VideoLayout.onDisplayNameChanged(jid, displayName);
+}
+
+function registerListeners() {
+ APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CREATED);
+
+ APP.RTC.addStreamListener(streamHandler, StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED);
+ APP.RTC.addStreamListener(function (stream) {
+ VideoLayout.onRemoteStreamAdded(stream);
+ }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED);
+ APP.RTC.addStreamListener(function (jid) {
+ VideoLayout.onVideoTypeChanged(jid);
+ }, StreamEventTypes.EVENT_TYPE_REMOTE_CHANGED);
+ APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged);
+ APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, function (resourceJid) {
+ VideoLayout.onDominantSpeakerChanged(resourceJid);
+ });
+ APP.RTC.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED,
+ function (lastNEndpoints, endpointsEnteringLastN, stream) {
+ VideoLayout.onLastNEndpointsChanged(lastNEndpoints,
+ endpointsEnteringLastN, stream);
+ });
+ APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED,
+ function (devices) {
+ VideoLayout.setDeviceAvailabilityIcons(null, devices);
+ });
+ APP.RTC.addListener(RTCEvents.VIDEO_MUTE, UI.setVideoMuteButtonsState);
+ APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function() {
+ // when the data channel becomes available, tell the bridge about video
+ // selections so that it can do adaptive simulcast,
+ // we want the notification to trigger even if userJid is undefined,
+ // or null.
+ var userJid = APP.UI.getLargeVideoJid();
+ eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, userJid);
+ });
+ APP.statistics.addAudioLevelListener(function(jid, audioLevel)
+ {
+ var resourceJid;
+ if(jid === APP.statistics.LOCAL_JID)
+ {
+ resourceJid = AudioLevels.LOCAL_LEVEL;
+ if(APP.RTC.localAudio.isMuted())
+ {
+ audioLevel = 0;
+ }
+ }
+ else
+ {
+ resourceJid = Strophe.getResourceFromJid(jid);
+ }
+
+ AudioLevels.updateAudioLevel(resourceJid, audioLevel,
+ UI.getLargeVideoJid());
+ });
+ APP.desktopsharing.addListener(function () {
+ ToolbarToggler.showDesktopSharingButton();
+ }, DesktopSharingEventTypes.INIT);
+ APP.desktopsharing.addListener(
+ Toolbar.changeDesktopSharingButtonState,
+ DesktopSharingEventTypes.SWITCHING_DONE);
+ APP.connectionquality.addListener(CQEvents.LOCALSTATS_UPDATED,
+ VideoLayout.updateLocalConnectionStats);
+ APP.connectionquality.addListener(CQEvents.REMOTESTATS_UPDATED,
+ VideoLayout.updateConnectionStats);
+ APP.connectionquality.addListener(CQEvents.STOP,
+ VideoLayout.onStatsStop);
+ APP.xmpp.addListener(XMPPEvents.CONNECTION_FAILED, onXmppConnectionFailed);
+ APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference);
+ APP.xmpp.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () {
+ messageHandler.openMessageDialog(
+ 'dialog.serviceUnavailable',
+ 'dialog.gracefulShutdown'
+ );
+ });
+ APP.xmpp.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) {
+ var title = APP.translation.generateTranslationHTML(
+ "dialog.reservationError");
+ var message = APP.translation.generateTranslationHTML(
+ "dialog.reservationErrorMsg", {code: code, msg: msg});
+ messageHandler.openDialog(
+ title,
+ message,
+ true, {},
+ function (event, value, message, formVals)
+ {
+ return false;
+ }
+ );
+ });
+ APP.xmpp.addListener(XMPPEvents.KICKED, function () {
+ messageHandler.openMessageDialog("dialog.sessTerminated",
+ "dialog.kickMessage");
+ });
+ APP.xmpp.addListener(XMPPEvents.MUC_DESTROYED, function (reason) {
+ //FIXME: use Session Terminated from translation, but
+ // 'reason' text comes from XMPP packet and is not translated
+ var title = APP.translation.generateTranslationHTML("dialog.sessTerminated");
+ messageHandler.openDialog(
+ title, reason, true, {},
+ function (event, value, message, formVals)
+ {
+ return false;
+ }
+ );
+ });
+ APP.xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () {
+ messageHandler.showError("dialog.error",
+ "dialog.bridgeUnavailable");
+ });
+ APP.xmpp.addListener(XMPPEvents.USER_ID_CHANGED, function (from, id) {
+ Avatar.setUserAvatar(from, id);
+ });
+ APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged);
+ APP.xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined);
+ APP.xmpp.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, onLocalRoleChanged);
+ APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, onMucMemberJoined);
+ APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged);
+ APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus);
+ APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject);
+ APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation);
+ APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft);
+ APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordRequired);
+ APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError);
+ APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad);
+ APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED,
+ onAuthenticationRequired);
+ APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE,
+ function (resource, devices) {
+ VideoLayout.setDeviceAvailabilityIcons(resource, devices);
+ });
+
+ APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED, VideoLayout.onAudioMute);
+ APP.xmpp.addListener(XMPPEvents.VIDEO_MUTED, VideoLayout.onVideoMute);
+ APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function(doMuteAudio) {
+ UI.setAudioMuted(doMuteAudio);
+ });
+ APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED,
+ onDtmfSupportChanged);
+ APP.xmpp.addListener(XMPPEvents.START_MUTED_SETTING_CHANGED, function (audio, video) {
+ SettingsMenu.setStartMuted(audio, video);
+ });
+ APP.xmpp.addListener(XMPPEvents.START_MUTED_FROM_FOCUS, function (audio, video) {
+ UI.setInitialMuteFromFocus(audio, video);
+ });
+
+ APP.xmpp.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function (session, error) {
+ UI.messageHandler.showError("dialog.sorry",
+ "dialog.internalError");
+ });
+
+ APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function() {
+ messageHandler.showError("dialog.error",
+ "dialog.SLDFailure");
+ });
+ APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function() {
+ messageHandler.showError("dialog.error",
+ "dialog.SRDFailure");
+ });
+ APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function() {
+ messageHandler.showError();
+ });
+ APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function() {
+ // FIXME: re-use LoginDialog which supports retries
+ UI.showLoginPopup(connect);
+ });
+
+ APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function(focusComponent, retrySec) {
+ UI.messageHandler.notify(
+ null, "notify.focus",
+ 'disconnected', "notify.focusFail",
+ {component: focusComponent, ms: retrySec});
+ });
+
+ APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function(pres) {
+ UI.messageHandler.openReportDialog(null,
+ "dialog.joinError", pres);
+ });
+ APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function(pres) {
+ UI.messageHandler.openReportDialog(null,
+ "dialog.connectError", pres);
+ });
+
+ APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function() {
+ var roomName = UI.generateRoomName();
+ APP.xmpp.allocateConferenceFocus(roomName, UI.checkForNicknameAndJoin);
+ });
+
+ //NicknameHandler emits this event
+ UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) {
+ APP.xmpp.addToPresence("displayName", nickname);
+ });
+}
+
+
+/**
+ * Mutes/unmutes the local video.
+ *
+ * @param mute true to mute the local video; otherwise, false
+ * @param options an object which specifies optional arguments such as the
+ * boolean key byUser with default value true which
+ * specifies whether the method was initiated in response to a user command (in
+ * contrast to an automatic decision taken by the application logic)
+ */
+function setVideoMute(mute, options) {
+ APP.RTC.setVideoMute(mute,
+ UI.setVideoMuteButtonsState,
+ options);
+}
+
+function onResize()
+{
+ Chat.resizeChat();
+ VideoLayout.resizeLargeVideoContainer();
+}
+
+function bindEvents()
+{
+ /**
+ * Resizes and repositions videos in full screen mode.
+ */
+ $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange',
+ onResize);
+
+ $(window).resize(onResize);
+}
+
+UI.start = function (init) {
+ document.title = interfaceConfig.APP_NAME;
+ if(config.enableWelcomePage && window.location.pathname == "/" &&
+ (!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
+ {
+ $("#videoconference_page").hide();
+ var setupWelcomePage = require("./welcome_page/WelcomePage");
+ setupWelcomePage();
+
+ return;
+ }
+
+ if (interfaceConfig.SHOW_JITSI_WATERMARK) {
+ var leftWatermarkDiv
+ = $("#largeVideoContainer div[class='watermark leftwatermark']");
+
+ leftWatermarkDiv.css({display: 'block'});
+ leftWatermarkDiv.parent().get(0).href
+ = interfaceConfig.JITSI_WATERMARK_LINK;
+ }
+
+ if (interfaceConfig.SHOW_BRAND_WATERMARK) {
+ var rightWatermarkDiv
+ = $("#largeVideoContainer div[class='watermark rightwatermark']");
+
+ rightWatermarkDiv.css({display: 'block'});
+ rightWatermarkDiv.parent().get(0).href
+ = interfaceConfig.BRAND_WATERMARK_LINK;
+ rightWatermarkDiv.get(0).style.backgroundImage
+ = "url(images/rightwatermark.png)";
+ }
+
+ if (interfaceConfig.SHOW_POWERED_BY) {
+ $("#largeVideoContainer>a[class='poweredby']").css({display: 'block'});
+ }
+
+ $("#welcome_page").hide();
+
+ $("#videospace").mousemove(function () {
+ return ToolbarToggler.showToolbar();
+ });
+ // Set the defaults for prompt dialogs.
+ jQuery.prompt.setDefaults({persistent: false});
+
+ VideoLayout.init(eventEmitter);
+ AudioLevels.init();
+ NicknameHandler.init(eventEmitter);
+ registerListeners();
+ bindEvents();
+ setupPrezi();
+ setupToolbars();
+ setupChat();
+
+
+ document.title = interfaceConfig.APP_NAME;
+
+ $("#downloadlog").click(function (event) {
+ dump(event.target);
+ });
+
+ if(config.enableWelcomePage && window.location.pathname == "/" &&
+ (!window.localStorage.welcomePageDisabled || window.localStorage.welcomePageDisabled == "false"))
+ {
+ $("#videoconference_page").hide();
+ var setupWelcomePage = require("./welcome_page/WelcomePage");
+ setupWelcomePage();
+
+ return;
+ }
+
+ $("#welcome_page").hide();
+
+ // Display notice message at the top of the toolbar
+ if (config.noticeMessage) {
+ $('#noticeText').text(config.noticeMessage);
+ $('#notice').css({display: 'block'});
+ }
+
+ if (!RTCBrowserType.isIExplorer()) {
+ document.getElementById('largeVideo').volume = 0;
+ }
+
+ if(config.requireDisplayName) {
+ var currentSettings = Settings.getSettings();
+ if (!currentSettings.displayName) {
+ promptDisplayName();
+ }
+ }
+
+ init();
+
+ toastr.options = {
+ "closeButton": true,
+ "debug": false,
+ "positionClass": "notification-bottom-right",
+ "onclick": null,
+ "showDuration": "300",
+ "hideDuration": "1000",
+ "timeOut": "2000",
+ "extendedTimeOut": "1000",
+ "showEasing": "swing",
+ "hideEasing": "linear",
+ "showMethod": "fadeIn",
+ "hideMethod": "fadeOut",
+ "reposition": function() {
+ if(PanelToggler.isVisible()) {
+ $("#toast-container").addClass("notification-bottom-right-center");
+ } else {
+ $("#toast-container").removeClass("notification-bottom-right-center");
+ }
+ },
+ "newestOnTop": false
+ };
+
+ SettingsMenu.init();
+
+};
+
+function chatAddError(errorMessage, originalText)
+{
+ return Chat.chatAddError(errorMessage, originalText);
+};
+
+function chatSetSubject(text)
+{
+ return Chat.chatSetSubject(text);
+};
+
+function updateChatConversation(from, displayName, message, myjid, stamp) {
+ return Chat.updateChatConversation(from, displayName, message, myjid, stamp);
+}
+
+function onMucJoined(jid, info) {
+ Toolbar.updateRoomUrl(window.location.href);
+ var meHTML = APP.translation.generateTranslationHTML("me");
+ $("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")");
+
+ var settings = Settings.getSettings();
+
+ // Make sure we configure our avatar id, before creating avatar for us
+ Avatar.setUserAvatar(jid, settings.email || settings.uid);
+
+ // Add myself to the contact list.
+ ContactList.addContact(jid);
+
+ // Once we've joined the muc show the toolbar
+ ToolbarToggler.showToolbar();
+
+ var displayName = !config.displayJids
+ ? info.displayName : Strophe.getResourceFromJid(jid);
+
+ if (displayName)
+ onDisplayNameChanged('localVideoContainer', displayName);
+
+
+ VideoLayout.mucJoined();
+}
+
+function initEtherpad(name) {
+ Etherpad.init(name);
+}
+
+function onMucMemberLeft(jid) {
+ console.log('left.muc', jid);
+ var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) +
+ '>.displayname').html();
+ messageHandler.notify(displayName,'notify.somebody',
+ 'disconnected',
+ 'notify.disconnected');
+ if(!config.startAudioMuted ||
+ config.startAudioMuted > APP.members.size())
+ UIUtil.playSoundNotification('userLeft');
+ // Need to call this with a slight delay, otherwise the element couldn't be
+ // found for some reason.
+ // XXX(gp) it works fine without the timeout for me (with Chrome 38).
+ window.setTimeout(function () {
+ var container = document.getElementById(
+ 'participant_' + Strophe.getResourceFromJid(jid));
+ if (container) {
+ ContactList.removeContact(jid);
+ VideoLayout.removeConnectionIndicator(jid);
+ // hide here, wait for video to close before removing
+ $(container).hide();
+ VideoLayout.resizeThumbnails();
+ }
+ }, 10);
+
+ VideoLayout.participantLeft(jid);
+
+};
+
+
+function onLocalRoleChanged(jid, info, pres, isModerator)
+{
+
+ console.info("My role changed, new role: " + info.role);
+ onModeratorStatusChanged(isModerator);
+ VideoLayout.showModeratorIndicator();
+ SettingsMenu.onRoleChanged();
+
+ if (isModerator) {
+ Authentication.closeAuthenticationWindow();
+ messageHandler.notify(null, "notify.me",
+ 'connected', "notify.moderator");
+ }
+}
+
+function onModeratorStatusChanged(isModerator) {
+
+ Toolbar.showSipCallButton(isModerator);
+ Toolbar.showRecordingButton(
+ isModerator); //&&
+ // FIXME:
+ // Recording visible if
+ // there are at least 2(+ 1 focus) participants
+ //Object.keys(connection.emuc.members).length >= 3);
+}
+
+function onPasswordRequired(callback) {
+ // password is required
+ Toolbar.lockLockButton();
+ var message = '';
+ message += APP.translation.translateString(
+ "dialog.passwordRequired");
+ message += '
' +
+ '';
+
+ messageHandler.openTwoButtonDialog(null, null, null, message,
+ true,
+ "dialog.Ok",
+ function (e, v, m, f) {},
+ null,
+ function (e, v, m, f) {
+ if (v) {
+ var lockKey = f.lockKey;
+ if (lockKey) {
+ Toolbar.setSharedKey(lockKey);
+ callback(lockKey);
+ }
+ }
+ },
+ ':input:first'
+ );
+}
+
+/**
+ * The dialpad button is shown iff there is at least one member that supports
+ * DTMF (e.g. jigasi).
+ */
+function onDtmfSupportChanged(dtmfSupport) {
+ //TODO: enable when the UI is ready
+ //Toolbar.showDialPadButton(dtmfSupport);
+}
+
+function onMucMemberJoined(jid, id, displayName) {
+ messageHandler.notify(displayName,'notify.somebody',
+ 'connected',
+ 'notify.connected');
+
+ if(!config.startAudioMuted ||
+ config.startAudioMuted > APP.members.size())
+ UIUtil.playSoundNotification('userJoined');
+
+ // Configure avatar
+ Avatar.setUserAvatar(jid, id);
+
+ // Add Peer's container
+ VideoLayout.ensurePeerContainerExists(jid);
+}
+
+function onMucPresenceStatus(jid, info) {
+ VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status);
+}
+
+function onMucRoleChanged(role, displayName) {
+ VideoLayout.showModeratorIndicator();
+
+ if (role === 'moderator') {
+ var messageKey, messageOptions = {};
+ if (!displayName) {
+ messageKey = "notify.grantedToUnknown";
+ }
+ else
+ {
+ messageKey = "notify.grantedTo";
+ messageOptions = {to: displayName};
+ }
+ messageHandler.notify(
+ displayName,'notify.somebody',
+ 'connected', messageKey,
+ messageOptions);
+ }
+}
+
+function onAuthenticationRequired(intervalCallback) {
+ Authentication.openAuthenticationDialog(
+ roomName, intervalCallback, function () {
+ Toolbar.authenticateClicked();
+ });
+};
+
+
+function onLastNChanged(oldValue, newValue) {
+ if (config.muteLocalVideoIfNotInLastN) {
+ setVideoMute(!newValue, { 'byUser': false });
+ }
+}
+
+
+UI.toggleSmileys = function () {
+ Chat.toggleSmileys();
+};
+
+UI.getSettings = function () {
+ return Settings.getSettings();
+};
+
+UI.toggleFilmStrip = function () {
+ return BottomToolbar.toggleFilmStrip();
+};
+
+UI.toggleChat = function () {
+ return BottomToolbar.toggleChat();
+};
+
+UI.toggleContactList = function () {
+ return BottomToolbar.toggleContactList();
+};
+
+UI.inputDisplayNameHandler = function (value) {
+ VideoLayout.inputDisplayNameHandler(value);
+};
+
+
+UI.getLargeVideoJid = function()
+{
+ return VideoLayout.getLargeVideoJid();
+};
+
+UI.generateRoomName = function() {
+ if(roomName)
+ return roomName;
+ var roomnode = null;
+ var path = window.location.pathname;
+
+ // determinde the room node from the url
+ // TODO: just the roomnode or the whole bare jid?
+ if (config.getroomnode && typeof config.getroomnode === 'function') {
+ // custom function might be responsible for doing the pushstate
+ roomnode = config.getroomnode(path);
+ } else {
+ /* fall back to default strategy
+ * this is making assumptions about how the URL->room mapping happens.
+ * It currently assumes deployment at root, with a rewrite like the
+ * following one (for nginx):
+ location ~ ^/([a-zA-Z0-9]+)$ {
+ rewrite ^/(.*)$ / break;
+ }
+ */
+ if (path.length > 1) {
+ roomnode = path.substr(1).toLowerCase();
+ } else {
+ var word = RoomNameGenerator.generateRoomWithoutSeparator();
+ roomnode = word.toLowerCase();
+
+ window.history.pushState('VideoChat',
+ 'Room: ' + word, window.location.pathname + word);
+ }
+ }
+
+ roomName = roomnode + '@' + config.hosts.muc;
+ return roomName;
+};
+
+
+UI.connectionIndicatorShowMore = function(jid)
+{
+ return VideoLayout.showMore(jid);
+};
+
+UI.showLoginPopup = function(callback)
+{
+ console.log('password is required');
+ var message = '';
+ message += APP.translation.translateString(
+ "dialog.passwordRequired");
+ message += '
' +
+ '' +
+ '';
+ UI.messageHandler.openTwoButtonDialog(null, null, null, message,
+ true,
+ "dialog.Ok",
+ function (e, v, m, f) {
+ if (v) {
+ if (f.username !== null && f.password != null) {
+ callback(f.username, f.password);
+ }
+ }
+ },
+ null, null, ':input:first'
+
+ );
+}
+
+UI.checkForNicknameAndJoin = function () {
+
+ Authentication.closeAuthenticationDialog();
+ Authentication.stopInterval();
+
+ var nick = null;
+ if (config.useNicks) {
+ nick = window.prompt('Your nickname (optional)');
+ }
+ APP.xmpp.joinRoom(roomName, config.useNicks, nick);
+};
+
+
+function dump(elem, filename) {
+ elem = elem.parentNode;
+ elem.download = filename || 'meetlog.json';
+ elem.href = 'data:application/json;charset=utf-8,\n';
+ var data = APP.xmpp.populateData();
+ var metadata = {};
+ metadata.time = new Date();
+ metadata.url = window.location.href;
+ metadata.ua = navigator.userAgent;
+ var log = APP.xmpp.getLogger();
+ if (log) {
+ metadata.xmpp = log;
+ }
+ data.metadata = metadata;
+ elem.href += encodeURIComponent(JSON.stringify(data, null, ' '));
+ return false;
+}
+
+UI.getRoomName = function () {
+ return roomName;
+};
+
+UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) {
+ if(muteAudio || muteVideo) notifyForInitialMute();
+ if(muteAudio) UI.setAudioMuted(true);
+ if(muteVideo) UI.setVideoMute(true);
+}
+
+/**
+ * Mutes/unmutes the local video.
+ */
+UI.toggleVideo = function () {
+ setVideoMute(!APP.RTC.localVideo.isMuted());
+};
+
+/**
+ * Mutes / unmutes audio for the local participant.
+ */
+UI.toggleAudio = function() {
+ UI.setAudioMuted(!APP.RTC.localAudio.isMuted());
+};
+
+/**
+ * Sets muted audio state for the local participant.
+ */
+UI.setAudioMuted = function (mute, earlyMute) {
+ var audioMute = null;
+ if(earlyMute)
+ audioMute = function (mute, cb) {
+ return APP.xmpp.sendAudioInfoPresence(mute, cb);
+ };
+ else
+ audioMute = function (mute, cb) {
+ return APP.xmpp.setAudioMute(mute, cb);
+ }
+ if(!audioMute(mute, function () {
+ VideoLayout.showLocalAudioIndicator(mute);
+
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ }))
+ {
+ // We still click the button.
+ UIUtil.buttonClick("#mute", "icon-microphone icon-mic-disabled");
+ return;
+ }
+
+}
+
+UI.addListener = function (type, listener) {
+ eventEmitter.on(type, listener);
+}
+
+UI.clickOnVideo = function (videoNumber) {
+ var remoteVideos = $(".videocontainer:not(#mixedstream)");
+ if (remoteVideos.length > videoNumber) {
+ remoteVideos[videoNumber].click();
+ }
+}
+
+//Used by torture
+UI.showToolbar = function () {
+ return ToolbarToggler.showToolbar();
+}
+
+//Used by torture
+UI.dockToolbar = function (isDock) {
+ return ToolbarToggler.dockToolbar(isDock);
+}
+
+UI.setVideoMuteButtonsState = function (mute) {
+ var video = $('#video');
+ var communicativeClass = "icon-camera";
+ var muteClass = "icon-camera icon-camera-disabled";
+
+ if (mute) {
+ video.removeClass(communicativeClass);
+ video.addClass(muteClass);
+ } else {
+ video.removeClass(muteClass);
+ video.addClass(communicativeClass);
+ }
+}
+
+UI.userAvatarChanged = function (resourceJid, thumbUrl, contactListUrl) {
+ VideoLayout.userAvatarChanged(resourceJid, thumbUrl);
+ ContactList.userAvatarChanged(resourceJid, contactListUrl);
+ if(resourceJid === APP.xmpp.myResource())
+ SettingsMenu.changeAvatar(thumbUrl);
+}
+
+UI.setVideoMute = setVideoMute;
+
+module.exports = UI;
+
+
+},{"../../service/RTC/RTCEvents":99,"../../service/RTC/StreamEventTypes":101,"../../service/UI/UIEvents":102,"../../service/connectionquality/CQEvents":104,"../../service/desktopsharing/DesktopSharingEventTypes":105,"../../service/members/Events":106,"../../service/xmpp/XMPPEvents":108,"../RTC/RTCBrowserType":8,"./../settings/Settings":47,"./audio_levels/AudioLevels.js":12,"./authentication/Authentication":14,"./avatar/Avatar":16,"./etherpad/Etherpad.js":17,"./prezi/Prezi.js":18,"./side_pannels/SidePanelToggler":20,"./side_pannels/chat/Chat.js":21,"./side_pannels/contactlist/ContactList":25,"./side_pannels/settings/SettingsMenu":26,"./toolbars/BottomToolbar":27,"./toolbars/Toolbar":28,"./toolbars/ToolbarToggler":29,"./util/MessageHandler":31,"./util/NicknameHandler":32,"./util/UIUtil":33,"./videolayout/VideoLayout.js":39,"./welcome_page/RoomnameGenerator":40,"./welcome_page/WelcomePage":41,"events":109}],12:[function(require,module,exports){
+var CanvasUtil = require("./CanvasUtils");
+
+var ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d');
+
+function initActiveSpeakerAudioLevels() {
+ var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2;
+ var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2;
+
+// Draw a circle.
+ ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI);
+
+// Add a shadow around the circle
+ ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR;
+ ASDrawContext.shadowOffsetX = 0;
+ ASDrawContext.shadowOffsetY = 0;
+}
+
+/**
+ * The audio Levels plugin.
+ */
+var AudioLevels = (function(my) {
+ var audioLevelCanvasCache = {};
+
+ my.LOCAL_LEVEL = 'local';
+
+ my.init = function () {
+ initActiveSpeakerAudioLevels();
+ }
+
+ /**
+ * Updates the audio level canvas for the given peerJid. If the canvas
+ * didn't exist we create it.
+ */
+ my.updateAudioLevelCanvas = function (peerJid, VideoLayout) {
+ var resourceJid = null;
+ var videoSpanId = null;
+ if (!peerJid)
+ videoSpanId = 'localVideoContainer';
+ else {
+ resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ videoSpanId = 'participant_' + resourceJid;
+ }
+
+ var videoSpan = document.getElementById(videoSpanId);
+
+ if (!videoSpan) {
+ if (resourceJid)
+ console.error("No video element for jid", resourceJid);
+ else
+ console.error("No video element for local video.");
+
+ return;
+ }
+
+ var audioLevelCanvas = $('#' + videoSpanId + '>canvas');
+
+ var videoSpaceWidth = $('#remoteVideos').width();
+ var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth);
+ var thumbnailWidth = thumbnailSize[0];
+ var thumbnailHeight = thumbnailSize[1];
+
+ if (!audioLevelCanvas || audioLevelCanvas.length === 0) {
+
+ audioLevelCanvas = document.createElement('canvas');
+ audioLevelCanvas.className = "audiolevel";
+ audioLevelCanvas.style.bottom = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
+ audioLevelCanvas.style.left = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px";
+ resizeAudioLevelCanvas( audioLevelCanvas,
+ thumbnailWidth,
+ thumbnailHeight);
+
+ videoSpan.appendChild(audioLevelCanvas);
+ } else {
+ audioLevelCanvas = audioLevelCanvas.get(0);
+
+ resizeAudioLevelCanvas( audioLevelCanvas,
+ thumbnailWidth,
+ thumbnailHeight);
+ }
+ };
+
+ /**
+ * Updates the audio level UI for the given resourceJid.
+ *
+ * @param resourceJid the resource jid indicating the video element for
+ * which we draw the audio level
+ * @param audioLevel the newAudio level to render
+ */
+ my.updateAudioLevel = function (resourceJid, audioLevel, largeVideoResourceJid) {
+ drawAudioLevelCanvas(resourceJid, audioLevel);
+
+ var videoSpanId = getVideoSpanId(resourceJid);
+
+ var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0);
+
+ if (!audioLevelCanvas)
+ return;
+
+ var drawContext = audioLevelCanvas.getContext('2d');
+
+ var canvasCache = audioLevelCanvasCache[resourceJid];
+
+ drawContext.clearRect (0, 0,
+ audioLevelCanvas.width, audioLevelCanvas.height);
+ drawContext.drawImage(canvasCache, 0, 0);
+
+ if(resourceJid === AudioLevels.LOCAL_LEVEL) {
+ if(!APP.xmpp.myJid()) {
+ return;
+ }
+ resourceJid = APP.xmpp.myResource();
+ }
+
+ if(resourceJid === largeVideoResourceJid) {
+ window.requestAnimationFrame(function () {
+ AudioLevels.updateActiveSpeakerAudioLevel(audioLevel);
+ });
+ }
+ };
+
+ my.updateActiveSpeakerAudioLevel = function(audioLevel) {
+ if($("#activeSpeaker").css("visibility") == "hidden")
+ return;
+
+
+ ASDrawContext.clearRect(0, 0, 300, 300);
+ if(audioLevel == 0)
+ return;
+
+ ASDrawContext.shadowBlur = getShadowLevel(audioLevel);
+
+
+ // Fill the shape.
+ ASDrawContext.fill();
+ };
+
+ /**
+ * Resizes the given audio level canvas to match the given thumbnail size.
+ */
+ function resizeAudioLevelCanvas(audioLevelCanvas,
+ thumbnailWidth,
+ thumbnailHeight) {
+ audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA;
+ audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA;
+ }
+
+ /**
+ * Draws the audio level canvas into the cached canvas object.
+ *
+ * @param resourceJid the resource jid indicating the video element for
+ * which we draw the audio level
+ * @param audioLevel the newAudio level to render
+ */
+ function drawAudioLevelCanvas(resourceJid, audioLevel) {
+ if (!audioLevelCanvasCache[resourceJid]) {
+
+ var videoSpanId = getVideoSpanId(resourceJid);
+
+ var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0);
+
+ /*
+ * FIXME Testing has shown that audioLevelCanvasOrig may not exist.
+ * In such a case, the method CanvasUtil.cloneCanvas may throw an
+ * error. Since audio levels are frequently updated, the errors have
+ * been observed to pile into the console, strain the CPU.
+ */
+ if (audioLevelCanvasOrig)
+ {
+ audioLevelCanvasCache[resourceJid]
+ = CanvasUtil.cloneCanvas(audioLevelCanvasOrig);
+ }
+ }
+
+ var canvas = audioLevelCanvasCache[resourceJid];
+
+ if (!canvas)
+ return;
+
+ var drawContext = canvas.getContext('2d');
+
+ drawContext.clearRect(0, 0, canvas.width, canvas.height);
+
+ var shadowLevel = getShadowLevel(audioLevel);
+
+ if (shadowLevel > 0)
+ // drawContext, x, y, w, h, r, shadowColor, shadowLevel
+ CanvasUtil.drawRoundRectGlow( drawContext,
+ interfaceConfig.CANVAS_EXTRA/2, interfaceConfig.CANVAS_EXTRA/2,
+ canvas.width - interfaceConfig.CANVAS_EXTRA,
+ canvas.height - interfaceConfig.CANVAS_EXTRA,
+ interfaceConfig.CANVAS_RADIUS,
+ interfaceConfig.SHADOW_COLOR,
+ shadowLevel);
+ }
+
+ /**
+ * Returns the shadow/glow level for the given audio level.
+ *
+ * @param audioLevel the audio level from which we determine the shadow
+ * level
+ */
+ function getShadowLevel (audioLevel) {
+ var shadowLevel = 0;
+
+ if (audioLevel <= 0.3) {
+ shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3));
+ }
+ else if (audioLevel <= 0.6) {
+ shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3));
+ }
+ else {
+ shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4));
+ }
+ return shadowLevel;
+ }
+
+ /**
+ * Returns the video span id corresponding to the given resourceJid or local
+ * user.
+ */
+ function getVideoSpanId(resourceJid) {
+ var videoSpanId = null;
+ if (resourceJid === AudioLevels.LOCAL_LEVEL
+ || (APP.xmpp.myResource() && resourceJid
+ === APP.xmpp.myResource()))
+ videoSpanId = 'localVideoContainer';
+ else
+ videoSpanId = 'participant_' + resourceJid;
+
+ return videoSpanId;
+ }
+
+ /**
+ * Indicates that the remote video has been resized.
+ */
+ $(document).bind('remotevideo.resized', function (event, width, height) {
+ var resized = false;
+ $('#remoteVideos>span>canvas').each(function() {
+ var canvas = $(this).get(0);
+ if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) {
+ canvas.width = width + interfaceConfig.CANVAS_EXTRA;
+ resized = true;
+ }
+
+ if (canvas.heigh !== height + interfaceConfig.CANVAS_EXTRA) {
+ canvas.height = height + interfaceConfig.CANVAS_EXTRA;
+ resized = true;
+ }
+ });
+
+ if (resized)
+ Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) {
+ audioLevelCanvasCache[resourceJid].width
+ = width + interfaceConfig.CANVAS_EXTRA;
+ audioLevelCanvasCache[resourceJid].height
+ = height + interfaceConfig.CANVAS_EXTRA;
+ });
+ });
+
+ return my;
+
+})(AudioLevels || {});
+
+module.exports = AudioLevels;
+},{"./CanvasUtils":13}],13:[function(require,module,exports){
+/**
+ * Utility class for drawing canvas shapes.
+ */
+var CanvasUtil = (function(my) {
+
+ /**
+ * Draws a round rectangle with a glow. The glowWidth indicates the depth
+ * of the glow.
+ *
+ * @param drawContext the context of the canvas to draw to
+ * @param x the x coordinate of the round rectangle
+ * @param y the y coordinate of the round rectangle
+ * @param w the width of the round rectangle
+ * @param h the height of the round rectangle
+ * @param glowColor the color of the glow
+ * @param glowWidth the width of the glow
+ */
+ my.drawRoundRectGlow
+ = function(drawContext, x, y, w, h, r, glowColor, glowWidth) {
+
+ // Save the previous state of the context.
+ drawContext.save();
+
+ if (w < 2 * r) r = w / 2;
+ if (h < 2 * r) r = h / 2;
+
+ // Draw a round rectangle.
+ drawContext.beginPath();
+ drawContext.moveTo(x+r, y);
+ drawContext.arcTo(x+w, y, x+w, y+h, r);
+ drawContext.arcTo(x+w, y+h, x, y+h, r);
+ drawContext.arcTo(x, y+h, x, y, r);
+ drawContext.arcTo(x, y, x+w, y, r);
+ drawContext.closePath();
+
+ // Add a shadow around the rectangle
+ drawContext.shadowColor = glowColor;
+ drawContext.shadowBlur = glowWidth;
+ drawContext.shadowOffsetX = 0;
+ drawContext.shadowOffsetY = 0;
+
+ // Fill the shape.
+ drawContext.fill();
+
+ drawContext.save();
+
+ drawContext.restore();
+
+// 1) Uncomment this line to use Composite Operation, which is doing the
+// same as the clip function below and is also antialiasing the round
+// border, but is said to be less fast performance wise.
+
+// drawContext.globalCompositeOperation='destination-out';
+
+ drawContext.beginPath();
+ drawContext.moveTo(x+r, y);
+ drawContext.arcTo(x+w, y, x+w, y+h, r);
+ drawContext.arcTo(x+w, y+h, x, y+h, r);
+ drawContext.arcTo(x, y+h, x, y, r);
+ drawContext.arcTo(x, y, x+w, y, r);
+ drawContext.closePath();
+
+// 2) Uncomment this line to use Composite Operation, which is doing the
+// same as the clip function below and is also antialiasing the round
+// border, but is said to be less fast performance wise.
+
+// drawContext.fill();
+
+ // Comment these two lines if choosing to do the same with composite
+ // operation above 1 and 2.
+ drawContext.clip();
+ drawContext.clearRect(0, 0, 277, 200);
+
+ // Restore the previous context state.
+ drawContext.restore();
+ };
+
+ /**
+ * Clones the given canvas.
+ *
+ * @return the new cloned canvas.
+ */
+ my.cloneCanvas = function (oldCanvas) {
+ /*
+ * FIXME Testing has shown that oldCanvas may not exist. In such a case,
+ * the method CanvasUtil.cloneCanvas may throw an error. Since audio
+ * levels are frequently updated, the errors have been observed to pile
+ * into the console, strain the CPU.
+ */
+ if (!oldCanvas)
+ return oldCanvas;
+
+ //create a new canvas
+ var newCanvas = document.createElement('canvas');
+ var context = newCanvas.getContext('2d');
+
+ //set dimensions
+ newCanvas.width = oldCanvas.width;
+ newCanvas.height = oldCanvas.height;
+
+ //apply the old canvas to the new one
+ context.drawImage(oldCanvas, 0, 0);
+
+ //return the new canvas
+ return newCanvas;
+ };
+
+ return my;
+})(CanvasUtil || {});
+
+module.exports = CanvasUtil;
+},{}],14:[function(require,module,exports){
+/* global $, APP*/
+
+var LoginDialog = require('./LoginDialog');
+var Moderator = require('../../xmpp/moderator');
+
+/* Initial "authentication required" dialog */
+var authDialog = null;
+/* Loop retry ID that wits for other user to create the room */
+var authRetryId = null;
+var authenticationWindow = null;
+
+var Authentication = {
+ openAuthenticationDialog: function (roomName, intervalCallback, callback) {
+ // This is the loop that will wait for the room to be created by
+ // someone else. 'auth_required.moderator' will bring us back here.
+ authRetryId = window.setTimeout(intervalCallback, 5000);
+ // Show prompt only if it's not open
+ if (authDialog !== null) {
+ return;
+ }
+ // extract room name from 'room@muc.server.net'
+ var room = roomName.substr(0, roomName.indexOf('@'));
+
+ var title
+ = APP.translation.generateTranslationHTML("dialog.WaitingForHost");
+ var msg
+ = APP.translation.generateTranslationHTML(
+ "dialog.WaitForHostMsg", {room: room});
+
+ var buttonTxt
+ = APP.translation.generateTranslationHTML("dialog.IamHost");
+ var buttons = [];
+ buttons.push({title: buttonTxt, value: "authNow"});
+
+ authDialog = APP.UI.messageHandler.openDialog(
+ title,
+ msg,
+ true,
+ buttons,
+ function (onSubmitEvent, submitValue) {
+
+ // Do not close the dialog yet
+ onSubmitEvent.preventDefault();
+
+ // Open login popup
+ if (submitValue === 'authNow') {
+ callback();
+ }
+ }
+ );
+ },
+ closeAuthenticationWindow: function () {
+ if (authenticationWindow) {
+ authenticationWindow.close();
+ authenticationWindow = null;
+ }
+ },
+ xmppAuthenticate: function () {
+
+ var loginDialog = LoginDialog.show(
+ function (connection, state) {
+ if (!state) {
+ // User cancelled
+ loginDialog.close();
+ return;
+ } else if (state == APP.xmpp.Status.CONNECTED) {
+
+ loginDialog.close();
+
+ Authentication.stopInterval();
+ Authentication.closeAuthenticationDialog();
+
+ // Close the connection as anonymous one will be used
+ // to create the conference. Session-id will authorize
+ // the request.
+ connection.disconnect();
+
+ var roomName = APP.UI.generateRoomName();
+ Moderator.allocateConferenceFocus(roomName, function () {
+ // If it's not "on the fly" authentication now join
+ // the conference room
+ if (!APP.xmpp.getMUCJoined()) {
+ APP.UI.checkForNicknameAndJoin();
+ }
+ });
+ }
+ }, true);
+ },
+ focusAuthenticationWindow: function () {
+ // If auth window exists just bring it to the front
+ if (authenticationWindow) {
+ authenticationWindow.focus();
+ return;
+ }
+ },
+ closeAuthenticationDialog: function () {
+ // Close authentication dialog if opened
+ if (authDialog) {
+ authDialog.close();
+ authDialog = null;
+ }
+ },
+ createAuthenticationWindow: function (callback, url) {
+ authenticationWindow = APP.UI.messageHandler.openCenteredPopup(
+ url, 910, 660,
+ // On closed
+ function () {
+ // Close authentication dialog if opened
+ Authentication.closeAuthenticationDialog();
+ callback();
+ authenticationWindow = null;
+ });
+ return authenticationWindow;
+ },
+ stopInterval: function () {
+ // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice
+ if (authRetryId) {
+ window.clearTimeout(authRetryId);
+ authRetryId = null;
+ }
+ }
+};
+
+module.exports = Authentication;
+},{"../../xmpp/moderator":58,"./LoginDialog":15}],15:[function(require,module,exports){
+/* global $, APP, config*/
+
+var XMPP = require('../../xmpp/xmpp');
+var Moderator = require('../../xmpp/moderator');
+
+//FIXME: use LoginDialog to add retries to XMPP.connect method used when
+// anonymous domain is not enabled
+
+/**
+ * Creates new Dialog instance.
+ * @param callback function(Strophe.Connection, Strophe.Status) called
+ * when we either fail to connect or succeed(check Strophe.Status).
+ * @param obtainSession true if we want to send ConferenceIQ to Jicofo
+ * in order to create session-id after the connection is established.
+ * @constructor
+ */
+function Dialog(callback, obtainSession) {
+
+ var self = this;
+
+ var stop = false;
+
+ var connection = APP.xmpp.createConnection();
+
+ var message = '';
+ message += APP.translation.translateString("dialog.passwordRequired");
+ message += '
' +
+ '' +
+ '';
+
+ var okButton = APP.translation.generateTranslationHTML("dialog.Ok");
+
+ var cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel");
+
+ var states = {
+ login: {
+ html: message,
+ buttons: [
+ { title: okButton, value: true},
+ { title: cancelButton, value: false}
+ ],
+ focus: ':input:first',
+ submit: function (e, v, m, f) {
+ e.preventDefault();
+ if (v) {
+ var jid = f.username;
+ var password = f.password;
+ if (jid && password) {
+ stop = false;
+ connection.reset();
+ connDialog.goToState('connecting');
+ connection.connect(jid, password, stateHandler);
+ }
+ } else {
+ // User cancelled
+ stop = true;
+ callback();
+ }
+ }
+ },
+ connecting: {
+ title: APP.translation.translateString('dialog.connecting'),
+ html: '',
+ buttons: [],
+ defaultButton: 0
+ },
+ finished: {
+ title: APP.translation.translateString('dialog.error'),
+ html: '',
+ buttons: [
+ {
+ title: APP.translation.translateString('dialog.retry'),
+ value: 'retry'
+ },
+ {
+ title: APP.translation.translateString('dialog.Cancel'),
+ value: 'cancel'
+ },
+ ],
+ defaultButton: 0,
+ submit: function (e, v, m, f) {
+ e.preventDefault();
+ if (v === 'retry')
+ connDialog.goToState('login');
+ else
+ callback();
+ }
+ }
+ };
+
+ var connDialog
+ = APP.UI.messageHandler.openDialogWithStates(states,
+ { persistent: true, closeText: '' }, null);
+
+ var stateHandler = function (status, message) {
+ if (stop) {
+ return;
+ }
+
+ var translateKey = "connection." + XMPP.getStatusString(status);
+ var statusStr = APP.translation.translateString(translateKey);
+
+ // Display current state
+ var connectionStatus =
+ connDialog.getState('connecting').find('#connectionStatus');
+
+ connectionStatus.text(statusStr);
+
+ switch (status) {
+ case XMPP.Status.CONNECTED:
+
+ stop = true;
+ if (!obtainSession) {
+ callback(connection, status);
+ return;
+ }
+ // Obtaining session-id status
+ connectionStatus.text(
+ APP.translation.translateString(
+ 'connection.FETCH_SESSION_ID'));
+
+ // Authenticate with Jicofo and obtain session-id
+ var roomName = APP.UI.generateRoomName();
+
+ // Jicofo will return new session-id when connected
+ // from authenticated domain
+ connection.sendIQ(
+ Moderator.createConferenceIq(roomName),
+ function (result) {
+
+ connectionStatus.text(
+ APP.translation.translateString(
+ 'connection.GOT_SESSION_ID'));
+
+ stop = true;
+
+ // Parse session-id
+ Moderator.parseSessionId(result);
+
+ callback(connection, status);
+ },
+ function (error) {
+ console.error("Auth on the fly failed", error);
+
+ stop = true;
+
+ var errorMsg =
+ APP.translation.translateString(
+ 'connection.GET_SESSION_ID_ERROR') +
+ $(error).find('>error').attr('code');
+
+ self.displayError(errorMsg);
+
+ connection.disconnect();
+ });
+
+ break;
+ case XMPP.Status.AUTHFAIL:
+ case XMPP.Status.CONNFAIL:
+ case XMPP.Status.DISCONNECTED:
+
+ stop = true;
+
+ callback(connection, status);
+
+ var errorMessage = statusStr;
+
+ if (message)
+ {
+ errorMessage += ': ' + message;
+ }
+ self.displayError(errorMessage);
+
+ break;
+ default:
+ break;
+ }
+ };
+
+ /**
+ * Displays error message in 'finished' state which allows either to cancel
+ * or retry.
+ * @param message the final message to be displayed.
+ */
+ this.displayError = function (message) {
+
+ var finishedState = connDialog.getState('finished');
+
+ var errorMessageElem = finishedState.find('#errorMessage');
+ errorMessageElem.text(message);
+
+ connDialog.goToState('finished');
+ };
+
+ /**
+ * Closes LoginDialog.
+ */
+ this.close = function () {
+ stop = true;
+ connDialog.close();
+ };
+}
+
+var LoginDialog = {
+
+ /**
+ * Displays login prompt used to establish new XMPP connection. Given
+ * callback(Strophe.Connection, Strophe.Status) function will be
+ * called when we connect successfully(status === CONNECTED) or when we fail
+ * to do so. On connection failure program can call Dialog.close() method in
+ * order to cancel or do nothing to let the user retry.
+ * @param callback function(Strophe.Connection, Strophe.Status)
+ * called when we either fail to connect or succeed(check
+ * Strophe.Status).
+ * @param obtainSession true if we want to send ConferenceIQ to
+ * Jicofo in order to create session-id after the connection is
+ * established.
+ * @returns {Dialog}
+ */
+ show: function (callback, obtainSession) {
+ return new Dialog(callback, obtainSession);
+ }
+};
+
+module.exports = LoginDialog;
+},{"../../xmpp/moderator":58,"../../xmpp/xmpp":66}],16:[function(require,module,exports){
+var Settings = require("../../settings/Settings");
+
+var users = {};
+
+var Avatar = {
+
+ /**
+ * Sets the user's avatar in the settings menu(if local user), contact list
+ * and thumbnail
+ * @param jid jid of the user
+ * @param id email or userID to be used as a hash
+ */
+ setUserAvatar: function (jid, id) {
+ if (id) {
+ if (users[jid] === id) {
+ return;
+ }
+ users[jid] = id;
+ }
+ var thumbUrl = this.getThumbUrl(jid);
+ var contactListUrl = this.getContactListUrl(jid);
+ var resourceJid = Strophe.getResourceFromJid(jid);
+
+ APP.UI.userAvatarChanged(resourceJid, thumbUrl, contactListUrl);
+ },
+ /**
+ * Returns image URL for the avatar to be displayed on large video area
+ * where current active speaker is presented.
+ * @param jid full MUC jid of the user for whom we want to obtain avatar URL
+ */
+ getActiveSpeakerUrl: function (jid) {
+ return this.getGravatarUrl(jid, 100);
+ },
+ /**
+ * Returns image URL for the avatar to be displayed on small video thumbnail
+ * @param jid full MUC jid of the user for whom we want to obtain avatar URL
+ */
+ getThumbUrl: function (jid) {
+ return this.getGravatarUrl(jid, 100);
+ },
+ /**
+ * Returns the URL for the avatar to be displayed as contactlist item
+ * @param jid full MUC jid of the user for whom we want to obtain avatar URL
+ */
+ getContactListUrl: function (jid) {
+ return this.getGravatarUrl(jid, 30);
+ },
+ getGravatarUrl: function (jid, size) {
+ if (!jid) {
+ console.error("Get gravatar - jid is undefined");
+ return null;
+ }
+ var id = users[jid];
+ if (!id) {
+ console.warn("No avatar stored yet for " + jid);
+ return null;
+ }
+ return 'https://www.gravatar.com/avatar/' +
+ MD5.hexdigest(id.trim().toLowerCase()) +
+ "?d=wavatar&size=" + (size || "30");
+ }
+
+};
+
+
+module.exports = Avatar;
+},{"../../settings/Settings":47}],17:[function(require,module,exports){
+/* global $, config,
+ setLargeVideoVisible, Util */
+
+var VideoLayout = require("../videolayout/VideoLayout");
+var Prezi = require("../prezi/Prezi");
+var UIUtil = require("../util/UIUtil");
+
+var etherpadName = null;
+var etherpadIFrame = null;
+var domain = null;
+var options = "?showControls=true&showChat=false&showLineNumbers=true&useMonospaceFont=false";
+
+
+/**
+ * Resizes the etherpad.
+ */
+function resize() {
+ if ($('#etherpad>iframe').length) {
+ var remoteVideos = $('#remoteVideos');
+ var availableHeight
+ = window.innerHeight - remoteVideos.outerHeight();
+ var availableWidth = UIUtil.getAvailableVideoWidth();
+
+ $('#etherpad>iframe').width(availableWidth);
+ $('#etherpad>iframe').height(availableHeight);
+ }
+}
+
+/**
+ * Creates the Etherpad button and adds it to the toolbar.
+ */
+function enableEtherpadButton() {
+ if (!$('#etherpadButton').is(":visible"))
+ $('#etherpadButton').css({display: 'inline-block'});
+}
+
+/**
+ * Creates the IFrame for the etherpad.
+ */
+function createIFrame() {
+ etherpadIFrame = document.createElement('iframe');
+ etherpadIFrame.src = domain + etherpadName + options;
+ etherpadIFrame.frameBorder = 0;
+ etherpadIFrame.scrolling = "no";
+ etherpadIFrame.width = $('#largeVideoContainer').width() || 640;
+ etherpadIFrame.height = $('#largeVideoContainer').height() || 480;
+ etherpadIFrame.setAttribute('style', 'visibility: hidden;');
+
+ document.getElementById('etherpad').appendChild(etherpadIFrame);
+
+ etherpadIFrame.onload = function() {
+
+ document.domain = document.domain;
+ bubbleIframeMouseMove(etherpadIFrame);
+ setTimeout(function() {
+ // the iframes inside of the etherpad are
+ // not yet loaded when the etherpad iframe is loaded
+ var outer = etherpadIFrame.
+ contentDocument.getElementsByName("ace_outer")[0];
+ bubbleIframeMouseMove(outer);
+ var inner = outer.
+ contentDocument.getElementsByName("ace_inner")[0];
+ bubbleIframeMouseMove(inner);
+ }, 2000);
+ };
+}
+
+function bubbleIframeMouseMove(iframe){
+ var existingOnMouseMove = iframe.contentWindow.onmousemove;
+ iframe.contentWindow.onmousemove = function(e){
+ if(existingOnMouseMove) existingOnMouseMove(e);
+ var evt = document.createEvent("MouseEvents");
+ var boundingClientRect = iframe.getBoundingClientRect();
+ evt.initMouseEvent(
+ "mousemove",
+ true, // bubbles
+ false, // not cancelable
+ window,
+ e.detail,
+ e.screenX,
+ e.screenY,
+ e.clientX + boundingClientRect.left,
+ e.clientY + boundingClientRect.top,
+ e.ctrlKey,
+ e.altKey,
+ e.shiftKey,
+ e.metaKey,
+ e.button,
+ null // no related element
+ );
+ iframe.dispatchEvent(evt);
+ };
+}
+
+
+/**
+ * On video selected event.
+ */
+$(document).bind('video.selected', function (event, isPresentation) {
+ if (config.etherpad_base && etherpadIFrame && etherpadIFrame.style.visibility !== 'hidden')
+ Etherpad.toggleEtherpad(isPresentation);
+});
+
+
+var Etherpad = {
+ /**
+ * Initializes the etherpad.
+ */
+ init: function (name) {
+
+ if (config.etherpad_base && !etherpadName && name) {
+
+ domain = config.etherpad_base;
+
+ etherpadName = name;
+
+ enableEtherpadButton();
+
+ /**
+ * Resizes the etherpad, when the window is resized.
+ */
+ $(window).resize(function () {
+ resize();
+ });
+ }
+ },
+
+ /**
+ * Opens/hides the Etherpad.
+ */
+ toggleEtherpad: function (isPresentation) {
+ if (!etherpadIFrame)
+ createIFrame();
+
+ var largeVideo = null;
+ if (Prezi.isPresentationVisible())
+ largeVideo = $('#presentation>iframe');
+ else
+ largeVideo = $('#largeVideo');
+
+ if ($('#etherpad>iframe').css('visibility') === 'hidden') {
+ $('#activeSpeaker').css('visibility', 'hidden');
+ largeVideo.fadeOut(300, function () {
+ if (Prezi.isPresentationVisible()) {
+ largeVideo.css({opacity: '0'});
+ } else {
+ VideoLayout.setLargeVideoVisible(false);
+ }
+ });
+
+ $('#etherpad>iframe').fadeIn(300, function () {
+ document.body.style.background = '#eeeeee';
+ $('#etherpad>iframe').css({visibility: 'visible'});
+ $('#etherpad').css({zIndex: 2});
+ });
+ }
+ else if ($('#etherpad>iframe')) {
+ $('#etherpad>iframe').fadeOut(300, function () {
+ $('#etherpad>iframe').css({visibility: 'hidden'});
+ $('#etherpad').css({zIndex: 0});
+ document.body.style.background = 'black';
+ });
+
+ if (!isPresentation) {
+ $('#largeVideo').fadeIn(300, function () {
+ VideoLayout.setLargeVideoVisible(true);
+ });
+ }
+ }
+ resize();
+ },
+
+ isVisible: function() {
+ var etherpadIframe = $('#etherpad>iframe');
+ return etherpadIframe && etherpadIframe.is(':visible');
+ }
+
+};
+
+module.exports = Etherpad;
+
+},{"../prezi/Prezi":18,"../util/UIUtil":33,"../videolayout/VideoLayout":39}],18:[function(require,module,exports){
+var ToolbarToggler = require("../toolbars/ToolbarToggler");
+var UIUtil = require("../util/UIUtil");
+var VideoLayout = require("../videolayout/VideoLayout");
+var messageHandler = require("../util/MessageHandler");
+var PreziPlayer = require("./PreziPlayer");
+
+var preziPlayer = null;
+
+var Prezi = {
+
+
+ /**
+ * Reloads the current presentation.
+ */
+ reloadPresentation: function() {
+ var iframe = document.getElementById(preziPlayer.options.preziId);
+ iframe.src = iframe.src;
+ },
+
+ /**
+ * Returns true if the presentation is visible, false -
+ * otherwise.
+ */
+ isPresentationVisible: function () {
+ return ($('#presentation>iframe') != null
+ && $('#presentation>iframe').css('opacity') == 1);
+ },
+
+ /**
+ * Opens the Prezi dialog, from which the user could choose a presentation
+ * to load.
+ */
+ openPreziDialog: function() {
+ var myprezi = APP.xmpp.getPrezi();
+ if (myprezi) {
+ messageHandler.openTwoButtonDialog("dialog.removePreziTitle",
+ null,
+ "dialog.removePreziMsg",
+ null,
+ false,
+ "dialog.Remove",
+ function(e,v,m,f) {
+ if(v) {
+ APP.xmpp.removePreziFromPresence();
+ }
+ }
+ );
+ }
+ else if (preziPlayer != null) {
+ messageHandler.openTwoButtonDialog("dialog.sharePreziTitle",
+ null, "dialog.sharePreziMsg",
+ null,
+ false,
+ "dialog.Ok",
+ function(e,v,m,f) {
+ $.prompt.close();
+ }
+ );
+ }
+ else {
+ var html = APP.translation.generateTranslationHTML(
+ "dialog.sharePreziTitle");
+ var cancelButton = APP.translation.generateTranslationHTML(
+ "dialog.Cancel");
+ var shareButton = APP.translation.generateTranslationHTML(
+ "dialog.Share");
+ var backButton = APP.translation.generateTranslationHTML(
+ "dialog.Back");
+ var buttons = [];
+ var buttons1 = [];
+ // Cancel button to both states
+ buttons.push({title: cancelButton, value: false});
+ buttons1.push({title: cancelButton, value: false});
+ // Share button
+ buttons.push({title: shareButton, value: true});
+ // Back button
+ buttons1.push({title: backButton, value: true});
+ var linkError = APP.translation.generateTranslationHTML(
+ "dialog.preziLinkError");
+ var defaultUrl = APP.translation.translateString("defaultPreziLink",
+ {url: "http://prezi.com/wz7vhjycl7e6/my-prezi"});
+ var openPreziState = {
+ state0: {
+ html: '' + html + '
' +
+ '',
+ persistent: false,
+ buttons: buttons,
+ focus: ':input:first',
+ defaultButton: 0,
+ submit: function (e, v, m, f) {
+ e.preventDefault();
+ if(v)
+ {
+ var preziUrl = f.preziUrl;
+
+ if (preziUrl)
+ {
+ var urlValue
+ = encodeURI(UIUtil.escapeHtml(preziUrl));
+
+ if (urlValue.indexOf('http://prezi.com/') != 0
+ && urlValue.indexOf('https://prezi.com/') != 0)
+ {
+ $.prompt.goToState('state1');
+ return false;
+ }
+ else {
+ var presIdTmp = urlValue.substring(
+ urlValue.indexOf("prezi.com/") + 10);
+ if (!isAlphanumeric(presIdTmp)
+ || presIdTmp.indexOf('/') < 2) {
+ $.prompt.goToState('state1');
+ return false;
+ }
+ else {
+ APP.xmpp.addToPresence("prezi", urlValue);
+ $.prompt.close();
+ }
+ }
+ }
+ }
+ else
+ $.prompt.close();
+ }
+ },
+ state1: {
+ html: '' + html + '
' +
+ linkError,
+ persistent: false,
+ buttons: buttons1,
+ focus: ':input:first',
+ defaultButton: 1,
+ submit: function (e, v, m, f) {
+ e.preventDefault();
+ if (v === 0)
+ $.prompt.close();
+ else
+ $.prompt.goToState('state0');
+ }
+ }
+ };
+ messageHandler.openDialogWithStates(openPreziState);
+ }
+ }
+
+};
+
+/**
+ * A new presentation has been added.
+ *
+ * @param event the event indicating the add of a presentation
+ * @param jid the jid from which the presentation was added
+ * @param presUrl url of the presentation
+ * @param currentSlide the current slide to which we should move
+ */
+function presentationAdded(event, jid, presUrl, currentSlide) {
+ console.log("presentation added", presUrl);
+
+ var presId = getPresentationId(presUrl);
+
+ var elementId = 'participant_'
+ + Strophe.getResourceFromJid(jid)
+ + '_' + presId;
+
+
+
+
+ VideoLayout.addPreziContainer(elementId);
+ VideoLayout.resizeThumbnails();
+
+ var controlsEnabled = false;
+ if (jid === APP.xmpp.myJid())
+ controlsEnabled = true;
+
+ setPresentationVisible(true);
+ $('#largeVideoContainer').hover(
+ function (event) {
+ if (Prezi.isPresentationVisible()) {
+ var reloadButtonRight = window.innerWidth
+ - $('#presentation>iframe').offset().left
+ - $('#presentation>iframe').width();
+
+ $('#reloadPresentation').css({ right: reloadButtonRight,
+ display:'inline-block'});
+ }
+ },
+ function (event) {
+ if (!Prezi.isPresentationVisible())
+ $('#reloadPresentation').css({display:'none'});
+ else {
+ var e = event.toElement || event.relatedTarget;
+
+ if (e && e.id != 'reloadPresentation' && e.id != 'header')
+ $('#reloadPresentation').css({display:'none'});
+ }
+ });
+
+ preziPlayer = new PreziPlayer(
+ 'presentation',
+ {preziId: presId,
+ width: getPresentationWidth(),
+ height: getPresentationHeihgt(),
+ controls: controlsEnabled,
+ debug: true
+ });
+
+ $('#presentation>iframe').attr('id', preziPlayer.options.preziId);
+
+ preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) {
+ console.log("prezi status", event.value);
+ if (event.value == PreziPlayer.STATUS_CONTENT_READY) {
+ if (jid != APP.xmpp.myJid())
+ preziPlayer.flyToStep(currentSlide);
+ }
+ });
+
+ preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) {
+ console.log("event value", event.value);
+ APP.xmpp.addToPresence("preziSlide", event.value);
+ });
+
+ $("#" + elementId).css( 'background-image',
+ 'url(../images/avatarprezi.png)');
+ $("#" + elementId).click(
+ function () {
+ setPresentationVisible(true);
+ }
+ );
+};
+
+/**
+ * A presentation has been removed.
+ *
+ * @param event the event indicating the remove of a presentation
+ * @param jid the jid for which the presentation was removed
+ * @param the url of the presentation
+ */
+function presentationRemoved(event, jid, presUrl) {
+ console.log('presentation removed', presUrl);
+ var presId = getPresentationId(presUrl);
+ setPresentationVisible(false);
+ $('#participant_'
+ + Strophe.getResourceFromJid(jid)
+ + '_' + presId).remove();
+ $('#presentation>iframe').remove();
+ if (preziPlayer != null) {
+ preziPlayer.destroy();
+ preziPlayer = null;
+ }
+};
+
+/**
+ * Indicates if the given string is an alphanumeric string.
+ * Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the
+ * purpose of checking URIs.
+ */
+function isAlphanumeric(unsafeText) {
+ var regex = /^[a-z0-9-_\/&\?=;]+$/i;
+ return regex.test(unsafeText);
+}
+
+/**
+ * Returns the presentation id from the given url.
+ */
+function getPresentationId (presUrl) {
+ var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10);
+ return presIdTmp.substring(0, presIdTmp.indexOf('/'));
+}
+
+/**
+ * Returns the presentation width.
+ */
+function getPresentationWidth() {
+ var availableWidth = UIUtil.getAvailableVideoWidth();
+ var availableHeight = getPresentationHeihgt();
+
+ var aspectRatio = 16.0 / 9.0;
+ if (availableHeight < availableWidth / aspectRatio) {
+ availableWidth = Math.floor(availableHeight * aspectRatio);
+ }
+ return availableWidth;
+}
+
+/**
+ * Returns the presentation height.
+ */
+function getPresentationHeihgt() {
+ var remoteVideos = $('#remoteVideos');
+ return window.innerHeight - remoteVideos.outerHeight();
+}
+
+/**
+ * Resizes the presentation iframe.
+ */
+function resize() {
+ if ($('#presentation>iframe')) {
+ $('#presentation>iframe').width(getPresentationWidth());
+ $('#presentation>iframe').height(getPresentationHeihgt());
+ }
+}
+
+/**
+ * Shows/hides a presentation.
+ */
+function setPresentationVisible(visible) {
+ var prezi = $('#presentation>iframe');
+ if (visible) {
+ // Trigger the video.selected event to indicate a change in the
+ // large video.
+ $(document).trigger("video.selected", [true]);
+
+ $('#largeVideo').fadeOut(300);
+ prezi.fadeIn(300, function() {
+ prezi.css({opacity:'1'});
+ ToolbarToggler.dockToolbar(true);
+ VideoLayout.setLargeVideoVisible(false);
+ });
+ $('#activeSpeaker').css('visibility', 'hidden');
+ }
+ else {
+ if (prezi.css('opacity') == '1') {
+ prezi.fadeOut(300, function () {
+ prezi.css({opacity:'0'});
+ $('#reloadPresentation').css({display:'none'});
+ $('#largeVideo').fadeIn(300, function() {
+ VideoLayout.setLargeVideoVisible(true);
+ ToolbarToggler.dockToolbar(false);
+ });
+ });
+ }
+ }
+}
+
+/**
+ * Presentation has been removed.
+ */
+$(document).bind('presentationremoved.muc', presentationRemoved);
+
+/**
+ * Presentation has been added.
+ */
+$(document).bind('presentationadded.muc', presentationAdded);
+
+/*
+ * Indicates presentation slide change.
+ */
+$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) {
+ if (preziPlayer && preziPlayer.getCurrentStep() != current) {
+ preziPlayer.flyToStep(current);
+
+ var animationStepsArray = preziPlayer.getAnimationCountOnSteps();
+ for (var i = 0; i < parseInt(animationStepsArray[current]); i++) {
+ preziPlayer.flyToStep(current, i);
+ }
+ }
+});
+
+/**
+ * On video selected event.
+ */
+$(document).bind('video.selected', function (event, isPresentation) {
+ if (!isPresentation && $('#presentation>iframe')) {
+ setPresentationVisible(false);
+ }
+});
+
+$(window).resize(function () {
+ resize();
+});
+
+module.exports = Prezi;
+
+},{"../toolbars/ToolbarToggler":29,"../util/MessageHandler":31,"../util/UIUtil":33,"../videolayout/VideoLayout":39,"./PreziPlayer":19}],19:[function(require,module,exports){
+(function() {
+ "use strict";
+ var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ window.PreziPlayer = (function() {
+
+ PreziPlayer.API_VERSION = 1;
+ PreziPlayer.CURRENT_STEP = 'currentStep';
+ PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep';
+ PreziPlayer.CURRENT_OBJECT = 'currentObject';
+ PreziPlayer.STATUS_LOADING = 'loading';
+ PreziPlayer.STATUS_READY = 'ready';
+ PreziPlayer.STATUS_CONTENT_READY = 'contentready';
+ PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange";
+ PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange";
+ PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange";
+ PreziPlayer.EVENT_STATUS = "statusChange";
+ PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange";
+ PreziPlayer.EVENT_IS_MOVING = "isMovingChange";
+ PreziPlayer.domain = "https://prezi.com";
+ PreziPlayer.path = "/player/";
+ PreziPlayer.players = {};
+ PreziPlayer.binded_methods = ['changesHandler'];
+
+ PreziPlayer.createMultiplePlayers = function(optionArray){
+ for(var i=0; i 0 &&
+ obj.values.animationCountOnSteps &&
+ obj.values.animationCountOnSteps[step] <= animation_step) {
+ animation_step = obj.values.animationCountOnSteps[step];
+ }
+ // jump to animation steps by calling flyToNextStep()
+ function doAnimationSteps() {
+ if (obj.values.isMoving == true) {
+ setTimeout(doAnimationSteps, 100); // wait until the flight ends
+ return;
+ }
+ while (animation_step-- > 0) {
+ obj.flyToNextStep(); // do the animation steps
+ }
+ }
+ setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time
+ // jump to the step
+ return this.sendMessage({
+ 'action': 'present',
+ 'data': ['moveToStep', step]
+ });
+ };
+
+ PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */
+ PreziPlayer.prototype.flyToObject = function(objectId) {
+ return this.sendMessage({
+ 'action': 'present',
+ 'data': ['moveToObject', objectId]
+ });
+ };
+
+ PreziPlayer.prototype.play = function(defaultDelay) {
+ return this.sendMessage({
+ 'action': 'present',
+ 'data': ['startAutoPlay', defaultDelay]
+ });
+ };
+
+ PreziPlayer.prototype.stop = function() {
+ return this.sendMessage({
+ 'action': 'present',
+ 'data': ['stopAutoPlay']
+ });
+ };
+
+ PreziPlayer.prototype.pause = function(defaultDelay) {
+ return this.sendMessage({
+ 'action': 'present',
+ 'data': ['pauseAutoPlay', defaultDelay]
+ });
+ };
+
+ PreziPlayer.prototype.getCurrentStep = function() {
+ return this.values.currentStep;
+ };
+
+ PreziPlayer.prototype.getCurrentAnimationStep = function() {
+ return this.values.currentAnimationStep;
+ };
+
+ PreziPlayer.prototype.getCurrentObject = function() {
+ return this.values.currentObject;
+ };
+
+ PreziPlayer.prototype.getStatus = function() {
+ return this.values.status;
+ };
+
+ PreziPlayer.prototype.isPlaying = function() {
+ return this.values.isAutoPlaying;
+ };
+
+ PreziPlayer.prototype.getStepCount = function() {
+ return this.values.stepCount;
+ };
+
+ PreziPlayer.prototype.getAnimationCountOnSteps = function() {
+ return this.values.animationCountOnSteps;
+ };
+
+ PreziPlayer.prototype.getTitle = function() {
+ return this.values.title;
+ };
+
+ PreziPlayer.prototype.setDimensions = function(dims) {
+ for (var parameter in dims) {
+ this.iframe[parameter] = dims[parameter];
+ }
+ }
+
+ PreziPlayer.prototype.getDimensions = function() {
+ return {
+ width: parseInt(this.iframe.width, 10),
+ height: parseInt(this.iframe.height, 10)
+ }
+ }
+
+ PreziPlayer.prototype.on = function(event, callback) {
+ this.callbacks.push({
+ event: event,
+ callback: callback
+ });
+ };
+
+ PreziPlayer.prototype.off = function(event, callback) {
+ var j, item;
+ if (event === undefined) {
+ this.callbacks = [];
+ }
+ j = this.callbacks.length;
+ while (j--) {
+ item = this.callbacks[j];
+ if (item && item.event === event && (callback === undefined || item.callback === callback)){
+ this.callbacks.splice(j, 1);
+ }
+ }
+ };
+
+ if (window.addEventListener) {
+ window.addEventListener('message', PreziPlayer.messageReceived, false);
+ } else {
+ window.attachEvent('onmessage', PreziPlayer.messageReceived);
+ }
+
+ return PreziPlayer;
+
+ })();
+
+})();
+
+module.exports = PreziPlayer;
+
+},{}],20:[function(require,module,exports){
+var Chat = require("./chat/Chat");
+var ContactList = require("./contactlist/ContactList");
+var Settings = require("./../../settings/Settings");
+var SettingsMenu = require("./settings/SettingsMenu");
+var VideoLayout = require("../videolayout/VideoLayout");
+var ToolbarToggler = require("../toolbars/ToolbarToggler");
+var UIUtil = require("../util/UIUtil");
+var LargeVideo = require("../videolayout/LargeVideo");
+
+/**
+ * Toggler for the chat, contact list, settings menu, etc..
+ */
+var PanelToggler = (function(my) {
+
+ var currentlyOpen = null;
+ var buttons = {
+ '#chatspace': '#chatBottomButton',
+ '#contactlist': '#contactListButton',
+ '#settingsmenu': '#settingsButton'
+ };
+
+ /**
+ * Toggles the windows in the side panel
+ * @param object the window that should be shown
+ * @param selector the selector for the element containing the panel
+ * @param onOpenComplete function to be called when the panel is opened
+ * @param onOpen function to be called if the window is going to be opened
+ * @param onClose function to be called if the window is going to be closed
+ */
+ var toggle = function(object, selector, onOpenComplete, onOpen, onClose) {
+ UIUtil.buttonClick(buttons[selector], "active");
+
+ if (object.isVisible()) {
+ $("#toast-container").animate({
+ right: '5px'
+ },
+ {
+ queue: false,
+ duration: 500
+ });
+ $(selector).hide("slide", {
+ direction: "right",
+ queue: false,
+ duration: 500
+ });
+ if(typeof onClose === "function") {
+ onClose();
+ }
+
+ currentlyOpen = null;
+ }
+ else {
+ // Undock the toolbar when the chat is shown and if we're in a
+ // video mode.
+ if (LargeVideo.isLargeVideoVisible()) {
+ ToolbarToggler.dockToolbar(false);
+ }
+
+ if(currentlyOpen) {
+ var current = $(currentlyOpen);
+ UIUtil.buttonClick(buttons[currentlyOpen], "active");
+ current.css('z-index', 4);
+ setTimeout(function () {
+ current.css('display', 'none');
+ current.css('z-index', 5);
+ }, 500);
+ }
+
+ $("#toast-container").animate({
+ right: (PanelToggler.getPanelSize()[0] + 5) + 'px'
+ },
+ {
+ queue: false,
+ duration: 500
+ });
+ $(selector).show("slide", {
+ direction: "right",
+ queue: false,
+ duration: 500,
+ complete: onOpenComplete
+ });
+ if(typeof onOpen === "function") {
+ onOpen();
+ }
+
+ currentlyOpen = selector;
+ }
+ };
+
+ /**
+ * Opens / closes the chat area.
+ */
+ my.toggleChat = function() {
+ var chatCompleteFunction = Chat.isVisible() ?
+ function() {} : function () {
+ Chat.scrollChatToBottom();
+ $('#chatspace').trigger('shown');
+ };
+
+ VideoLayout.resizeVideoArea(!Chat.isVisible(), chatCompleteFunction);
+
+ toggle(Chat,
+ '#chatspace',
+ function () {
+ // Request the focus in the nickname field or the chat input field.
+ if ($('#nickname').css('visibility') === 'visible') {
+ $('#nickinput').focus();
+ } else {
+ $('#usermsg').focus();
+ }
+ },
+ null,
+ Chat.resizeChat,
+ null);
+ };
+
+ /**
+ * Opens / closes the contact list area.
+ */
+ my.toggleContactList = function () {
+ var completeFunction = ContactList.isVisible() ?
+ function() {} : function () { $('#contactlist').trigger('shown');};
+ VideoLayout.resizeVideoArea(!ContactList.isVisible(), completeFunction);
+
+ toggle(ContactList,
+ '#contactlist',
+ null,
+ function() {
+ ContactList.setVisualNotification(false);
+ },
+ null);
+ };
+
+ /**
+ * Opens / closes the settings menu
+ */
+ my.toggleSettingsMenu = function() {
+ VideoLayout.resizeVideoArea(!SettingsMenu.isVisible(), function (){});
+ toggle(SettingsMenu,
+ '#settingsmenu',
+ null,
+ function() {
+ var settings = Settings.getSettings();
+ $('#setDisplayName').get(0).value = settings.displayName;
+ $('#setEmail').get(0).value = settings.email;
+ },
+ null);
+ };
+
+ /**
+ * Returns the size of the side panel.
+ */
+ my.getPanelSize = function () {
+ var availableHeight = window.innerHeight;
+ var availableWidth = window.innerWidth;
+
+ var panelWidth = 200;
+ if (availableWidth * 0.2 < 200) {
+ panelWidth = availableWidth * 0.2;
+ }
+
+ return [panelWidth, availableHeight];
+ };
+
+ my.isVisible = function() {
+ return (Chat.isVisible() || ContactList.isVisible() || SettingsMenu.isVisible());
+ };
+
+ return my;
+
+}(PanelToggler || {}));
+
+module.exports = PanelToggler;
+},{"../toolbars/ToolbarToggler":29,"../util/UIUtil":33,"../videolayout/LargeVideo":35,"../videolayout/VideoLayout":39,"./../../settings/Settings":47,"./chat/Chat":21,"./contactlist/ContactList":25,"./settings/SettingsMenu":26}],21:[function(require,module,exports){
+/* global $, Util, nickname:true */
+var Replacement = require("./Replacement");
+var CommandsProcessor = require("./Commands");
+var ToolbarToggler = require("../../toolbars/ToolbarToggler");
+var smileys = require("./smileys.json").smileys;
+var NicknameHandler = require("../../util/NicknameHandler");
+var UIUtil = require("../../util/UIUtil");
+var UIEvents = require("../../../../service/UI/UIEvents");
+
+var notificationInterval = false;
+var unreadMessages = 0;
+
+
+/**
+ * Shows/hides a visual notification, indicating that a message has arrived.
+ */
+function setVisualNotification(show) {
+ var unreadMsgElement = document.getElementById('unreadMessages');
+ var unreadMsgBottomElement
+ = document.getElementById('bottomUnreadMessages');
+
+ var glower = $('#chatButton');
+ var bottomGlower = $('#chatBottomButton');
+
+ if (unreadMessages) {
+ unreadMsgElement.innerHTML = unreadMessages.toString();
+ unreadMsgBottomElement.innerHTML = unreadMessages.toString();
+
+ ToolbarToggler.dockToolbar(true);
+
+ var chatButtonElement
+ = document.getElementById('chatButton').parentNode;
+ var leftIndent = (UIUtil.getTextWidth(chatButtonElement) -
+ UIUtil.getTextWidth(unreadMsgElement)) / 2;
+ var topIndent = (UIUtil.getTextHeight(chatButtonElement) -
+ UIUtil.getTextHeight(unreadMsgElement)) / 2 - 3;
+
+ unreadMsgElement.setAttribute(
+ 'style',
+ 'top:' + topIndent +
+ '; left:' + leftIndent + ';');
+
+ var chatBottomButtonElement
+ = document.getElementById('chatBottomButton').parentNode;
+ var bottomLeftIndent = (UIUtil.getTextWidth(chatBottomButtonElement) -
+ UIUtil.getTextWidth(unreadMsgBottomElement)) / 2;
+ var bottomTopIndent = (UIUtil.getTextHeight(chatBottomButtonElement) -
+ UIUtil.getTextHeight(unreadMsgBottomElement)) / 2 - 2;
+
+ unreadMsgBottomElement.setAttribute(
+ 'style',
+ 'top:' + bottomTopIndent +
+ '; left:' + bottomLeftIndent + ';');
+
+
+ if (!glower.hasClass('icon-chat-simple')) {
+ glower.removeClass('icon-chat');
+ glower.addClass('icon-chat-simple');
+ }
+ }
+ else {
+ unreadMsgElement.innerHTML = '';
+ unreadMsgBottomElement.innerHTML = '';
+ glower.removeClass('icon-chat-simple');
+ glower.addClass('icon-chat');
+ }
+
+ if (show && !notificationInterval) {
+ notificationInterval = window.setInterval(function () {
+ glower.toggleClass('active');
+ bottomGlower.toggleClass('active glowing');
+ }, 800);
+ }
+ else if (!show && notificationInterval) {
+ window.clearInterval(notificationInterval);
+ notificationInterval = false;
+ glower.removeClass('active');
+ bottomGlower.removeClass('glowing');
+ bottomGlower.addClass('active');
+ }
+}
+
+
+/**
+ * Returns the current time in the format it is shown to the user
+ * @returns {string}
+ */
+function getCurrentTime(stamp) {
+ var now = (stamp? new Date(stamp): new Date());
+ var hour = now.getHours();
+ var minute = now.getMinutes();
+ var second = now.getSeconds();
+ if(hour.toString().length === 1) {
+ hour = '0'+hour;
+ }
+ if(minute.toString().length === 1) {
+ minute = '0'+minute;
+ }
+ if(second.toString().length === 1) {
+ second = '0'+second;
+ }
+ return hour+':'+minute+':'+second;
+}
+
+function toggleSmileys()
+{
+ var smileys = $('#smileysContainer');
+ if(!smileys.is(':visible')) {
+ smileys.show("slide", { direction: "down", duration: 300});
+ } else {
+ smileys.hide("slide", { direction: "down", duration: 300});
+ }
+ $('#usermsg').focus();
+}
+
+function addClickFunction(smiley, number) {
+ smiley.onclick = function addSmileyToMessage() {
+ var usermsg = $('#usermsg');
+ var message = usermsg.val();
+ message += smileys['smiley' + number];
+ usermsg.val(message);
+ usermsg.get(0).setSelectionRange(message.length, message.length);
+ toggleSmileys();
+ usermsg.focus();
+ };
+}
+
+/**
+ * Adds the smileys container to the chat
+ */
+function addSmileys() {
+ var smileysContainer = document.createElement('div');
+ smileysContainer.id = 'smileysContainer';
+ for(var i = 1; i <= 21; i++) {
+ var smileyContainer = document.createElement('div');
+ smileyContainer.id = 'smiley' + i;
+ smileyContainer.className = 'smileyContainer';
+ var smiley = document.createElement('img');
+ smiley.src = 'images/smileys/smiley' + i + '.svg';
+ smiley.className = 'smiley';
+ addClickFunction(smiley, i);
+ smileyContainer.appendChild(smiley);
+ smileysContainer.appendChild(smileyContainer);
+ }
+
+ $("#chatspace").append(smileysContainer);
+}
+
+/**
+ * Resizes the chat conversation.
+ */
+function resizeChatConversation() {
+ var msgareaHeight = $('#usermsg').outerHeight();
+ var chatspace = $('#chatspace');
+ var width = chatspace.width();
+ var chat = $('#chatconversation');
+ var smileys = $('#smileysarea');
+
+ smileys.height(msgareaHeight);
+ $("#smileys").css('bottom', (msgareaHeight - 26) / 2);
+ $('#smileysContainer').css('bottom', msgareaHeight);
+ chat.width(width - 10);
+ chat.height(window.innerHeight - 15 - msgareaHeight);
+}
+
+/**
+ * Chat related user interface.
+ */
+var Chat = (function (my) {
+ /**
+ * Initializes chat related interface.
+ */
+ my.init = function () {
+ if(NicknameHandler.getNickname())
+ Chat.setChatConversationMode(true);
+ NicknameHandler.addListener(UIEvents.NICKNAME_CHANGED,
+ function (nickname) {
+ Chat.setChatConversationMode(true);
+ });
+
+ $('#nickinput').keydown(function (event) {
+ if (event.keyCode === 13) {
+ event.preventDefault();
+ var val = UIUtil.escapeHtml(this.value);
+ this.value = '';
+ if (!NicknameHandler.getNickname()) {
+ NicknameHandler.setNickname(val);
+
+ return;
+ }
+ }
+ });
+
+ $('#usermsg').keydown(function (event) {
+ if (event.keyCode === 13) {
+ event.preventDefault();
+ var value = this.value;
+ $('#usermsg').val('').trigger('autosize.resize');
+ this.focus();
+ var command = new CommandsProcessor(value);
+ if(command.isCommand())
+ {
+ command.processCommand();
+ }
+ else
+ {
+ var message = UIUtil.escapeHtml(value);
+ APP.xmpp.sendChatMessage(message, NicknameHandler.getNickname());
+ }
+ }
+ });
+
+ var onTextAreaResize = function () {
+ resizeChatConversation();
+ Chat.scrollChatToBottom();
+ };
+ $('#usermsg').autosize({callback: onTextAreaResize});
+
+ $("#chatspace").bind("shown",
+ function () {
+ unreadMessages = 0;
+ setVisualNotification(false);
+ });
+
+ addSmileys();
+ };
+
+ /**
+ * Appends the given message to the chat conversation.
+ */
+ my.updateChatConversation = function (from, displayName, message, myjid, stamp) {
+ var divClassName = '';
+
+ if (APP.xmpp.myJid() === from) {
+ divClassName = "localuser";
+ }
+ else {
+ divClassName = "remoteuser";
+
+ if (!Chat.isVisible()) {
+ unreadMessages++;
+ UIUtil.playSoundNotification('chatNotification');
+ setVisualNotification(true);
+ }
+ }
+
+ // replace links and smileys
+ // Strophe already escapes special symbols on sending,
+ // so we escape here only tags to avoid double &
+ var escMessage = message.replace(//g, '>').replace(/\n/g, '
');
+ var escDisplayName = UIUtil.escapeHtml(displayName);
+ message = Replacement.processReplacements(escMessage);
+
+ var messageContainer =
+ ''+
+ '
' +
+ '
' + escDisplayName +
+ '
' + '
' + getCurrentTime(stamp) +
+ '
' + '
' + message + '
' +
+ '
';
+
+ $('#chatconversation').append(messageContainer);
+ $('#chatconversation').animate(
+ { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
+ };
+
+ /**
+ * Appends error message to the conversation
+ * @param errorMessage the received error message.
+ * @param originalText the original message.
+ */
+ my.chatAddError = function(errorMessage, originalText)
+ {
+ errorMessage = UIUtil.escapeHtml(errorMessage);
+ originalText = UIUtil.escapeHtml(originalText);
+
+ $('#chatconversation').append(
+ 'Error: ' + 'Your message' +
+ (originalText? (' \"'+ originalText + '\"') : "") +
+ ' was not sent.' +
+ (errorMessage? (' Reason: ' + errorMessage) : '') + '
');
+ $('#chatconversation').animate(
+ { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000);
+ };
+
+ /**
+ * Sets the subject to the UI
+ * @param subject the subject
+ */
+ my.chatSetSubject = function(subject)
+ {
+ if(subject)
+ subject = subject.trim();
+ $('#subject').html(Replacement.linkify(UIUtil.escapeHtml(subject)));
+ if(subject === "")
+ {
+ $("#subject").css({display: "none"});
+ }
+ else
+ {
+ $("#subject").css({display: "block"});
+ }
+ };
+
+
+
+ /**
+ * Sets the chat conversation mode.
+ */
+ my.setChatConversationMode = function (isConversationMode) {
+ if (isConversationMode) {
+ $('#nickname').css({visibility: 'hidden'});
+ $('#chatconversation').css({visibility: 'visible'});
+ $('#usermsg').css({visibility: 'visible'});
+ $('#smileysarea').css({visibility: 'visible'});
+ $('#usermsg').focus();
+ }
+ };
+
+ /**
+ * Resizes the chat area.
+ */
+ my.resizeChat = function () {
+ var chatSize = require("../SidePanelToggler").getPanelSize();
+
+ $('#chatspace').width(chatSize[0]);
+ $('#chatspace').height(chatSize[1]);
+
+ resizeChatConversation();
+ };
+
+ /**
+ * Indicates if the chat is currently visible.
+ */
+ my.isVisible = function () {
+ return $('#chatspace').is(":visible");
+ };
+ /**
+ * Shows and hides the window with the smileys
+ */
+ my.toggleSmileys = toggleSmileys;
+
+ /**
+ * Scrolls chat to the bottom.
+ */
+ my.scrollChatToBottom = function() {
+ setTimeout(function () {
+ $('#chatconversation').scrollTop(
+ $('#chatconversation')[0].scrollHeight);
+ }, 5);
+ };
+
+
+ return my;
+}(Chat || {}));
+module.exports = Chat;
+},{"../../../../service/UI/UIEvents":102,"../../toolbars/ToolbarToggler":29,"../../util/NicknameHandler":32,"../../util/UIUtil":33,"../SidePanelToggler":20,"./Commands":22,"./Replacement":23,"./smileys.json":24}],22:[function(require,module,exports){
+var UIUtil = require("../../util/UIUtil");
+
+/**
+ * List with supported commands. The keys are the names of the commands and
+ * the value is the function that processes the message.
+ * @type {{String: function}}
+ */
+var commands = {
+ "topic" : processTopic
+};
+
+/**
+ * Extracts the command from the message.
+ * @param message the received message
+ * @returns {string} the command
+ */
+function getCommand(message)
+{
+ if(message)
+ {
+ for(var command in commands)
+ {
+ if(message.indexOf("/" + command) == 0)
+ return command;
+ }
+ }
+ return "";
+};
+
+/**
+ * Processes the data for topic command.
+ * @param commandArguments the arguments of the topic command.
+ */
+function processTopic(commandArguments)
+{
+ var topic = UIUtil.escapeHtml(commandArguments);
+ APP.xmpp.setSubject(topic);
+}
+
+/**
+ * Constructs new CommandProccessor instance from a message that
+ * handles commands received via chat messages.
+ * @param message the message
+ * @constructor
+ */
+function CommandsProcessor(message)
+{
+
+
+ var command = getCommand(message);
+
+ /**
+ * Returns the name of the command.
+ * @returns {String} the command
+ */
+ this.getCommand = function()
+ {
+ return command;
+ };
+
+
+ var messageArgument = message.substr(command.length + 2);
+
+ /**
+ * Returns the arguments of the command.
+ * @returns {string}
+ */
+ this.getArgument = function()
+ {
+ return messageArgument;
+ };
+}
+
+/**
+ * Checks whether this instance is valid command or not.
+ * @returns {boolean}
+ */
+CommandsProcessor.prototype.isCommand = function()
+{
+ if(this.getCommand())
+ return true;
+ return false;
+};
+
+/**
+ * Processes the command.
+ */
+CommandsProcessor.prototype.processCommand = function()
+{
+ if(!this.isCommand())
+ return;
+
+ commands[this.getCommand()](this.getArgument());
+
+};
+
+module.exports = CommandsProcessor;
+},{"../../util/UIUtil":33}],23:[function(require,module,exports){
+var Smileys = require("./smileys.json");
+/**
+ * Processes links and smileys in "body"
+ */
+function processReplacements(body)
+{
+ //make links clickable
+ body = linkify(body);
+
+ //add smileys
+ body = smilify(body);
+
+ return body;
+}
+
+/**
+ * Finds and replaces all links in the links in "body"
+ * with their
+ */
+function linkify(inputText)
+{
+ var replacedText, replacePattern1, replacePattern2, replacePattern3;
+
+ //URLs starting with http://, https://, or ftp://
+ replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
+ replacedText = inputText.replace(replacePattern1, '$1');
+
+ //URLs starting with "www." (without // before it, or it'd re-link the ones done above).
+ replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
+ replacedText = replacedText.replace(replacePattern2, '$1$2');
+
+ //Change email addresses to mailto:: links.
+ replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
+ replacedText = replacedText.replace(replacePattern3, '$1');
+
+ return replacedText;
+}
+
+/**
+ * Replaces common smiley strings with images
+ */
+function smilify(body)
+{
+ if(!body) {
+ return body;
+ }
+
+ var regexs = Smileys["regexs"];
+ for(var smiley in regexs) {
+ if(regexs.hasOwnProperty(smiley)) {
+ body = body.replace(regexs[smiley],
+ '');
+ }
+ }
+
+ return body;
+}
+
+module.exports = {
+ processReplacements: processReplacements,
+ linkify: linkify
+};
+
+},{"./smileys.json":24}],24:[function(require,module,exports){
+module.exports={
+ "smileys": {
+ "smiley1": ":)",
+ "smiley2": ":(",
+ "smiley3": ":D",
+ "smiley4": "(y)",
+ "smiley5": " :P",
+ "smiley6": "(wave)",
+ "smiley7": "(blush)",
+ "smiley8": "(chuckle)",
+ "smiley9": "(shocked)",
+ "smiley10": ":*",
+ "smiley11": "(n)",
+ "smiley12": "(search)",
+ "smiley13": " <3",
+ "smiley14": "(oops)",
+ "smiley15": "(angry)",
+ "smiley16": "(angel)",
+ "smiley17": "(sick)",
+ "smiley18": ";(",
+ "smiley19": "(bomb)",
+ "smiley20": "(clap)",
+ "smiley21": " ;)"
+ },
+ "regexs": {
+ "smiley2": /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi,
+ "smiley3": /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi,
+ "smiley1": /(:-\)|:\))/gi,
+ "smiley4": /(\(y\)|\(Y\)|\(ok\))/gi,
+ "smiley5": /(:-P|:P|:-p|:p)/gi,
+ "smiley6": /(\(wave\))/gi,
+ "smiley7": /(\(blush\))/gi,
+ "smiley8": /(\(chuckle\))/gi,
+ "smiley9": /(:-0|\(shocked\))/gi,
+ "smiley10": /(:-\*|:\*|\(kiss\))/gi,
+ "smiley11": /(\(n\))/gi,
+ "smiley12": /(\(search\))/g,
+ "smiley13": /(<3|<3|<3|\(L\)|\(l\)|\(H\)|\(h\))/gi,
+ "smiley14": /(\(oops\))/gi,
+ "smiley15": /(\(angry\))/gi,
+ "smiley16": /(\(angel\))/gi,
+ "smiley17": /(\(sick\))/gi,
+ "smiley18": /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi,
+ "smiley19": /(\(bomb\))/gi,
+ "smiley20": /(\(clap\))/gi,
+ "smiley21": /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi
+ }
+}
+
+},{}],25:[function(require,module,exports){
+
+var Avatar = require('../../avatar/Avatar');
+
+var numberOfContacts = 0;
+var notificationInterval;
+
+/**
+ * Updates the number of participants in the contact list button and sets
+ * the glow
+ * @param delta indicates whether a new user has joined (1) or someone has
+ * left(-1)
+ */
+function updateNumberOfParticipants(delta) {
+ numberOfContacts += delta;
+ if (numberOfContacts === 1) {
+ // when the user is alone we don't show the number of participants
+ $("#numberOfParticipants").text('');
+ ContactList.setVisualNotification(false);
+ } else if (numberOfContacts > 1) {
+ ContactList.setVisualNotification(!ContactList.isVisible());
+ $("#numberOfParticipants").text(numberOfContacts);
+ } else {
+ console.error("Invalid number of participants: " + numberOfContacts);
+ }
+}
+
+/**
+ * Creates the avatar element.
+ *
+ * @return the newly created avatar element
+ */
+function createAvatar(jid) {
+ var avatar = document.createElement('img');
+ avatar.className = "icon-avatar avatar";
+ avatar.src = Avatar.getContactListUrl(jid);
+
+ return avatar;
+}
+
+/**
+ * Creates the display name paragraph.
+ *
+ * @param displayName the display name to set
+ */
+function createDisplayNameParagraph(key, displayName) {
+ var p = document.createElement('p');
+ if(displayName)
+ p.innerText = displayName;
+ else if(key)
+ {
+ p.setAttribute("data-i18n",key);
+ p.innerText = APP.translation.translateString(key);
+ }
+
+ return p;
+}
+
+
+function stopGlowing(glower) {
+ window.clearInterval(notificationInterval);
+ notificationInterval = false;
+ glower.removeClass('glowing');
+ if (!ContactList.isVisible()) {
+ glower.removeClass('active');
+ }
+}
+
+
+/**
+ * Contact list.
+ */
+var ContactList = {
+ /**
+ * Indicates if the chat is currently visible.
+ *
+ * @return true if the chat is currently visible, false -
+ * otherwise
+ */
+ isVisible: function () {
+ return $('#contactlist').is(":visible");
+ },
+
+ /**
+ * Adds a contact for the given peerJid if such doesn't yet exist.
+ *
+ * @param peerJid the peerJid corresponding to the contact
+ */
+ ensureAddContact: function (peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contact = $('#contacts>li[id="' + resourceJid + '"]');
+
+ if (!contact || contact.length <= 0)
+ ContactList.addContact(peerJid);
+ },
+
+ /**
+ * Adds a contact for the given peer jid.
+ *
+ * @param peerJid the jid of the contact to add
+ */
+ addContact: function (peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactlist = $('#contacts');
+
+ var newContact = document.createElement('li');
+ newContact.id = resourceJid;
+ newContact.className = "clickable";
+ newContact.onclick = function (event) {
+ if (event.currentTarget.className === "clickable") {
+ $(ContactList).trigger('contactclicked', [peerJid]);
+ }
+ };
+
+ newContact.appendChild(createAvatar(peerJid));
+ newContact.appendChild(createDisplayNameParagraph("participant"));
+
+ if (resourceJid === APP.xmpp.myResource()) {
+ contactlist.prepend(newContact);
+ }
+ else {
+ contactlist.append(newContact);
+ }
+ updateNumberOfParticipants(1);
+ },
+
+ /**
+ * Removes a contact for the given peer jid.
+ *
+ * @param peerJid the peerJid corresponding to the contact to remove
+ */
+ removeContact: function (peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contact = $('#contacts>li[id="' + resourceJid + '"]');
+
+ if (contact && contact.length > 0) {
+ var contactlist = $('#contactlist>ul');
+
+ contactlist.get(0).removeChild(contact.get(0));
+
+ updateNumberOfParticipants(-1);
+ }
+ },
+
+ setVisualNotification: function (show, stopGlowingIn) {
+ var glower = $('#contactListButton');
+
+ if (show && !notificationInterval) {
+ notificationInterval = window.setInterval(function () {
+ glower.toggleClass('active glowing');
+ }, 800);
+ }
+ else if (!show && notificationInterval) {
+ stopGlowing(glower);
+ }
+ if (stopGlowingIn) {
+ setTimeout(function () {
+ stopGlowing(glower);
+ }, stopGlowingIn);
+ }
+ },
+
+ setClickable: function (resourceJid, isClickable) {
+ var contact = $('#contacts>li[id="' + resourceJid + '"]');
+ if (isClickable) {
+ contact.addClass('clickable');
+ } else {
+ contact.removeClass('clickable');
+ }
+ },
+
+ onDisplayNameChange: function (peerJid, displayName) {
+ if (peerJid === 'localVideoContainer')
+ peerJid = APP.xmpp.myJid();
+
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactName = $('#contacts #' + resourceJid + '>p');
+
+ if (contactName && displayName && displayName.length > 0)
+ contactName.html(displayName);
+ },
+
+ userAvatarChanged: function (resourceJid, contactListUrl) {
+ // set the avatar in the contact list
+ var contact = $('#' + resourceJid + '>img');
+ if (contact && contact.length > 0) {
+ contact.get(0).src = contactListUrl;
+ }
+
+ }
+};
+
+module.exports = ContactList;
+},{"../../avatar/Avatar":16}],26:[function(require,module,exports){
+var Avatar = require("../../avatar/Avatar");
+var Settings = require("./../../../settings/Settings");
+var UIUtil = require("../../util/UIUtil");
+var languages = require("../../../../service/translation/languages");
+
+function generateLanguagesSelectBox()
+{
+ var currentLang = APP.translation.getCurrentLanguage();
+ var html = "";
+}
+
+
+var SettingsMenu = {
+
+ init: function () {
+ $("#startMutedOptions").before(generateLanguagesSelectBox());
+ APP.translation.translateElement($("#languages_selectbox"));
+ $('#settingsmenu>input').keyup(function(event){
+ if(event.keyCode === 13) {//enter
+ SettingsMenu.update();
+ }
+ });
+
+ if(APP.xmpp.isModerator())
+ {
+ $("#startMutedOptions").css("display", "block");
+ }
+ else
+ {
+ $("#startMutedOptions").css("display", "none");
+ }
+
+ $("#updateSettings").click(function () {
+ SettingsMenu.update();
+ });
+ },
+
+ onRoleChanged: function () {
+ if(APP.xmpp.isModerator())
+ {
+ $("#startMutedOptions").css("display", "block");
+ }
+ else
+ {
+ $("#startMutedOptions").css("display", "none");
+ }
+ },
+
+ setStartMuted: function (audio, video) {
+ $("#startAudioMuted").attr("checked", audio);
+ $("#startVideoMuted").attr("checked", video);
+ },
+
+ update: function() {
+ var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value);
+ var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value);
+
+ if(newDisplayName) {
+ var displayName = Settings.setDisplayName(newDisplayName);
+ APP.xmpp.addToPresence("displayName", displayName, true);
+ }
+
+ var language = $("#languages_selectbox").val();
+ APP.translation.setLanguage(language);
+ Settings.setLanguage(language);
+
+ APP.xmpp.addToPresence("email", newEmail);
+ var email = Settings.setEmail(newEmail);
+
+ var startAudioMuted = ($("#startAudioMuted").is(":checked"));
+ var startVideoMuted = ($("#startVideoMuted").is(":checked"));
+ APP.xmpp.addToPresence("startMuted",
+ [startAudioMuted, startVideoMuted]);
+
+ Avatar.setUserAvatar(APP.xmpp.myJid(), email);
+ },
+
+ isVisible: function() {
+ return $('#settingsmenu').is(':visible');
+ },
+
+ setDisplayName: function(newDisplayName) {
+ var displayName = Settings.setDisplayName(newDisplayName);
+ $('#setDisplayName').get(0).value = displayName;
+ },
+
+ onDisplayNameChange: function(peerJid, newDisplayName) {
+ if(peerJid === 'localVideoContainer' ||
+ peerJid === APP.xmpp.myJid()) {
+ this.setDisplayName(newDisplayName);
+ }
+ },
+ changeAvatar: function (thumbUrl) {
+ $('#avatar').get(0).src = thumbUrl;
+ }
+};
+
+
+module.exports = SettingsMenu;
+},{"../../../../service/translation/languages":107,"../../avatar/Avatar":16,"../../util/UIUtil":33,"./../../../settings/Settings":47}],27:[function(require,module,exports){
+var PanelToggler = require("../side_pannels/SidePanelToggler");
+
+var buttonHandlers = {
+ "bottom_toolbar_contact_list": function () {
+ BottomToolbar.toggleContactList();
+ },
+ "bottom_toolbar_film_strip": function () {
+ BottomToolbar.toggleFilmStrip();
+ },
+ "bottom_toolbar_chat": function () {
+ BottomToolbar.toggleChat();
+ }
+};
+
+var BottomToolbar = (function (my) {
+ my.init = function () {
+ for(var k in buttonHandlers)
+ $("#" + k).click(buttonHandlers[k]);
+ };
+
+ my.toggleChat = function() {
+ PanelToggler.toggleChat();
+ };
+
+ my.toggleContactList = function() {
+ PanelToggler.toggleContactList();
+ };
+
+ my.toggleFilmStrip = function() {
+ var filmstrip = $("#remoteVideos");
+ filmstrip.toggleClass("hidden");
+ };
+
+ $(document).bind("remotevideo.resized", function (event, width, height) {
+ var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;
+
+ $('#bottomToolbar').css({bottom: bottom + 'px'});
+ });
+
+ return my;
+}(BottomToolbar || {}));
+
+module.exports = BottomToolbar;
+
+},{"../side_pannels/SidePanelToggler":20}],28:[function(require,module,exports){
+/* global APP,$, buttonClick, config, lockRoom,
+ setSharedKey, Util */
+var messageHandler = require("../util/MessageHandler");
+var BottomToolbar = require("./BottomToolbar");
+var Prezi = require("../prezi/Prezi");
+var Etherpad = require("../etherpad/Etherpad");
+var PanelToggler = require("../side_pannels/SidePanelToggler");
+var Authentication = require("../authentication/Authentication");
+var UIUtil = require("../util/UIUtil");
+var AuthenticationEvents
+ = require("../../../service/authentication/AuthenticationEvents");
+
+var roomUrl = null;
+var sharedKey = '';
+var UI = null;
+
+var buttonHandlers =
+{
+ "toolbar_button_mute": function () {
+ return APP.UI.toggleAudio();
+ },
+ "toolbar_button_camera": function () {
+ return APP.UI.toggleVideo();
+ },
+ /*"toolbar_button_authentication": function () {
+ return Toolbar.authenticateClicked();
+ },*/
+ "toolbar_button_record": function () {
+ return toggleRecording();
+ },
+ "toolbar_button_security": function () {
+ return Toolbar.openLockDialog();
+ },
+ "toolbar_button_link": function () {
+ return Toolbar.openLinkDialog();
+ },
+ "toolbar_button_chat": function () {
+ return BottomToolbar.toggleChat();
+ },
+ "toolbar_button_prezi": function () {
+ return Prezi.openPreziDialog();
+ },
+ "toolbar_button_etherpad": function () {
+ return Etherpad.toggleEtherpad(0);
+ },
+ "toolbar_button_desktopsharing": function () {
+ return APP.desktopsharing.toggleScreenSharing();
+ },
+ "toolbar_button_fullScreen": function()
+ {
+ UIUtil.buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");
+ return Toolbar.toggleFullScreen();
+ },
+ "toolbar_button_sip": function () {
+ return callSipButtonClicked();
+ },
+ "toolbar_button_dialpad": function () {
+ return dialpadButtonClicked();
+ },
+ "toolbar_button_settings": function () {
+ PanelToggler.toggleSettingsMenu();
+ },
+ "toolbar_button_hangup": function () {
+ return hangup();
+ },
+ "toolbar_button_login": function () {
+ Toolbar.authenticateClicked();
+ },
+ "toolbar_button_logout": function () {
+ // Ask for confirmation
+ messageHandler.openTwoButtonDialog(
+ "dialog.logoutTitle",
+ null,
+ "dialog.logoutQuestion",
+ null,
+ false,
+ "dialog.Yes",
+ function (evt, yes) {
+ if (yes) {
+ APP.xmpp.logout(function (url) {
+ if (url) {
+ window.location.href = url;
+ } else {
+ hangup();
+ }
+ });
+ }
+ });
+ }
+};
+
+function hangup() {
+ APP.xmpp.disposeConference();
+ if(config.enableWelcomePage)
+ {
+ setTimeout(function()
+ {
+ window.localStorage.welcomePageDisabled = false;
+ window.location.pathname = "/";
+ }, 10000);
+
+ }
+
+ var title = APP.translation.generateTranslationHTML(
+ "dialog.sessTerminated");
+ var msg = APP.translation.generateTranslationHTML(
+ "dialog.hungUp");
+ var button = APP.translation.generateTranslationHTML(
+ "dialog.joinAgain");
+ var buttons = [];
+ buttons.push({title: button, value: true});
+
+ UI.messageHandler.openDialog(
+ title,
+ msg,
+ true,
+ buttons,
+ function(event, value, message, formVals)
+ {
+ window.location.reload();
+ return false;
+ }
+ );
+}
+
+/**
+ * Starts or stops the recording for the conference.
+ */
+
+function toggleRecording() {
+ APP.xmpp.toggleRecording(function (callback) {
+ var msg = APP.translation.generateTranslationHTML(
+ "dialog.recordingToken");
+ var token = APP.translation.translateString("dialog.token");
+ APP.UI.messageHandler.openTwoButtonDialog(null, null, null,
+ '' + msg + '
' +
+ '',
+ false,
+ "dialog.Save",
+ function (e, v, m, f) {
+ if (v) {
+ var token = f.recordingToken;
+
+ if (token) {
+ callback(UIUtil.escapeHtml(token));
+ }
+ }
+ },
+ null,
+ function () { },
+ ':input:first'
+ );
+ }, Toolbar.setRecordingButtonState, Toolbar.setRecordingButtonState);
+}
+
+/**
+ * Locks / unlocks the room.
+ */
+function lockRoom(lock) {
+ var currentSharedKey = '';
+ if (lock)
+ currentSharedKey = sharedKey;
+
+ APP.xmpp.lockRoom(currentSharedKey, function (res) {
+ // password is required
+ if (sharedKey)
+ {
+ console.log('set room password');
+ Toolbar.lockLockButton();
+ }
+ else
+ {
+ console.log('removed room password');
+ Toolbar.unlockLockButton();
+ }
+ }, function (err) {
+ console.warn('setting password failed', err);
+ messageHandler.showError("dialog.lockTitle",
+ "dialog.lockMessage");
+ Toolbar.setSharedKey('');
+ }, function () {
+ console.warn('room passwords not supported');
+ messageHandler.showError("dialog.warning",
+ "dialog.passwordNotSupported");
+ Toolbar.setSharedKey('');
+ });
+};
+
+/**
+ * Invite participants to conference.
+ */
+function inviteParticipants() {
+ if (roomUrl === null)
+ return;
+
+ var sharedKeyText = "";
+ if (sharedKey && sharedKey.length > 0) {
+ sharedKeyText =
+ APP.translation.translateString("email.sharedKey",
+ {sharedKey: sharedKey});
+ sharedKeyText = sharedKeyText.replace(/\n/g, "%0D%0A");
+ }
+
+ var supportedBrowsers = "Chromium, Google Chrome " +
+ APP.translation.translateString("email.and") + " Opera";
+ var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1);
+ var subject = APP.translation.translateString("email.subject",
+ {appName:interfaceConfig.APP_NAME, conferenceName: conferenceName});
+ var body = APP.translation.translateString("email.body",
+ {appName:interfaceConfig.APP_NAME, sharedKeyText: sharedKeyText,
+ roomUrl: roomUrl, supportedBrowsers: supportedBrowsers});
+ body = body.replace(/\n/g, "%0D%0A");
+
+ if (window.localStorage.displayname) {
+ body += "%0D%0A%0D%0A" + window.localStorage.displayname;
+ }
+
+ if (interfaceConfig.INVITATION_POWERED_BY) {
+ body += "%0D%0A%0D%0A--%0D%0Apowered by jitsi.org";
+ }
+
+ window.open("mailto:?subject=" + subject + "&body=" + body, '_blank');
+}
+
+function dialpadButtonClicked()
+{
+ //TODO show the dialpad window
+}
+
+function callSipButtonClicked()
+{
+ var defaultNumber
+ = config.defaultSipNumber ? config.defaultSipNumber : '';
+
+ var sipMsg = APP.translation.generateTranslationHTML(
+ "dialog.sipMsg");
+ messageHandler.openTwoButtonDialog(null, null, null,
+ '' + sipMsg + '
' +
+ '',
+ false,
+ "dialog.Dial",
+ function (e, v, m, f) {
+ if (v) {
+ var numberInput = f.sipNumber;
+ if (numberInput) {
+ APP.xmpp.dial(
+ numberInput, 'fromnumber', UI.getRoomName(), sharedKey);
+ }
+ }
+ },
+ null, null, ':input:first'
+ );
+}
+
+var Toolbar = (function (my) {
+
+ my.init = function (ui) {
+ for(var k in buttonHandlers)
+ $("#" + k).click(buttonHandlers[k]);
+ UI = ui;
+ // Update login info
+ APP.xmpp.addListener(
+ AuthenticationEvents.IDENTITY_UPDATED,
+ function (authenticationEnabled, userIdentity) {
+
+ var loggedIn = false;
+ if (userIdentity) {
+ loggedIn = true;
+ }
+
+ Toolbar.showAuthenticateButton(authenticationEnabled);
+
+ if (authenticationEnabled) {
+ Toolbar.setAuthenticatedIdentity(userIdentity);
+
+ Toolbar.showLoginButton(!loggedIn);
+ Toolbar.showLogoutButton(loggedIn);
+ }
+ }
+ );
+ },
+
+ /**
+ * Sets shared key
+ * @param sKey the shared key
+ */
+ my.setSharedKey = function (sKey) {
+ sharedKey = sKey;
+ };
+
+ my.authenticateClicked = function () {
+ Authentication.focusAuthenticationWindow();
+ if (!APP.xmpp.isExternalAuthEnabled()) {
+ Authentication.xmppAuthenticate();
+ return;
+ }
+ // Get authentication URL
+ if (!APP.xmpp.getMUCJoined()) {
+ APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) {
+ // If conference has not been started yet - redirect to login page
+ window.location.href = url;
+ });
+ } else {
+ APP.xmpp.getPopupLoginUrl(UI.getRoomName(), function (url) {
+ // Otherwise - open popup with authentication URL
+ var authenticationWindow = Authentication.createAuthenticationWindow(
+ function () {
+ // On popup closed - retry room allocation
+ APP.xmpp.allocateConferenceFocus(
+ APP.UI.getRoomName(),
+ function () { console.info("AUTH DONE"); }
+ );
+ }, url);
+ if (!authenticationWindow) {
+ messageHandler.openMessageDialog(
+ null, "dialog.popupError");
+ }
+ });
+ }
+ };
+
+ /**
+ * Updates the room invite url.
+ */
+ my.updateRoomUrl = function (newRoomUrl) {
+ roomUrl = newRoomUrl;
+
+ // If the invite dialog has been already opened we update the information.
+ var inviteLink = document.getElementById('inviteLinkRef');
+ if (inviteLink) {
+ inviteLink.value = roomUrl;
+ inviteLink.select();
+ $('#inviteLinkRef').parent()
+ .find('button[value=true]').prop('disabled', false);
+ }
+ };
+
+ /**
+ * Disables and enables some of the buttons.
+ */
+ my.setupButtonsFromConfig = function () {
+ if (config.disablePrezi)
+ {
+ $("#prezi_button").css({display: "none"});
+ }
+ };
+
+ /**
+ * Opens the lock room dialog.
+ */
+ my.openLockDialog = function () {
+ // Only the focus is able to set a shared key.
+ if (!APP.xmpp.isModerator()) {
+ if (sharedKey) {
+ messageHandler.openMessageDialog(null,
+ "dialog.passwordError");
+ } else {
+ messageHandler.openMessageDialog(null, "dialog.passwordError2");
+ }
+ } else {
+ if (sharedKey) {
+ messageHandler.openTwoButtonDialog(null, null,
+ "dialog.passwordCheck",
+ null,
+ false,
+ "dialog.Remove",
+ function (e, v) {
+ if (v) {
+ Toolbar.setSharedKey('');
+ lockRoom(false);
+ }
+ });
+ } else {
+ var msg = APP.translation.generateTranslationHTML(
+ "dialog.passwordMsg");
+ var yourPassword = APP.translation.translateString(
+ "dialog.yourPassword");
+ messageHandler.openTwoButtonDialog(null, null, null,
+ '' + msg + '
' +
+ '',
+ false,
+ "dialog.Save",
+ function (e, v, m, f) {
+ if (v) {
+ var lockKey = f.lockKey;
+
+ if (lockKey) {
+ Toolbar.setSharedKey(
+ UIUtil.escapeHtml(lockKey));
+ lockRoom(true);
+ }
+ }
+ },
+ null, null, 'input:first'
+ );
+ }
+ }
+ };
+
+ /**
+ * Opens the invite link dialog.
+ */
+ my.openLinkDialog = function () {
+ var inviteAttreibutes;
+
+ if (roomUrl === null) {
+ inviteAttreibutes = 'data-i18n="[value]roomUrlDefaultMsg" value="' +
+ APP.translation.translateString("roomUrlDefaultMsg") + '"';
+ } else {
+ inviteAttreibutes = "value=\"" + encodeURI(roomUrl) + "\"";
+ }
+ messageHandler.openTwoButtonDialog("dialog.shareLink",
+ null, null,
+ '',
+ false,
+ "dialog.Invite",
+ function (e, v) {
+ if (v) {
+ if (roomUrl) {
+ inviteParticipants();
+ }
+ }
+ },
+ function (event) {
+ if (roomUrl) {
+ document.getElementById('inviteLinkRef').select();
+ } else {
+ if (event && event.target)
+ $(event.target)
+ .find('button[value=true]').prop('disabled', true);
+ }
+ }
+ );
+ };
+
+ /**
+ * Opens the settings dialog.
+ * FIXME: not used ?
+ */
+ my.openSettingsDialog = function () {
+ var settings1 = APP.translation.generateTranslationHTML(
+ "dialog.settings1");
+ var settings2 = APP.translation.generateTranslationHTML(
+ "dialog.settings2");
+ var settings3 = APP.translation.generateTranslationHTML(
+ "dialog.settings3");
+
+ var yourPassword = APP.translation.translateString(
+ "dialog.yourPassword");
+
+ messageHandler.openTwoButtonDialog(null,
+ '' + settings1 + '
' +
+ '' +
+ settings2 + '
' +
+ '' +
+ settings3 +
+ '',
+ null,
+ null,
+ false,
+ "dialog.Save",
+ function () {
+ document.getElementById('lockKey').focus();
+ },
+ function (e, v) {
+ if (v) {
+ if ($('#initMuted').is(":checked")) {
+ // it is checked
+ }
+
+ if ($('#requireNicknames').is(":checked")) {
+ // it is checked
+ }
+ /*
+ var lockKey = document.getElementById('lockKey');
+
+ if (lockKey.value) {
+ setSharedKey(lockKey.value);
+ lockRoom(true);
+ }
+ */
+ }
+ }
+ );
+ };
+
+ /**
+ * Toggles the application in and out of full screen mode
+ * (a.k.a. presentation mode in Chrome).
+ */
+ my.toggleFullScreen = function () {
+ var fsElement = document.documentElement;
+
+ if (!document.mozFullScreen && !document.webkitIsFullScreen) {
+ //Enter Full Screen
+ if (fsElement.mozRequestFullScreen) {
+ fsElement.mozRequestFullScreen();
+ }
+ else {
+ fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
+ }
+ } else {
+ //Exit Full Screen
+ if (document.mozCancelFullScreen) {
+ document.mozCancelFullScreen();
+ } else {
+ document.webkitCancelFullScreen();
+ }
+ }
+ };
+ /**
+ * Unlocks the lock button state.
+ */
+ my.unlockLockButton = function () {
+ if ($("#lockIcon").hasClass("icon-security-locked"))
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
+ };
+ /**
+ * Updates the lock button state to locked.
+ */
+ my.lockLockButton = function () {
+ if ($("#lockIcon").hasClass("icon-security"))
+ UIUtil.buttonClick("#lockIcon", "icon-security icon-security-locked");
+ };
+
+ /**
+ * Shows or hides authentication button
+ * @param show true to show or false to hide
+ */
+ my.showAuthenticateButton = function (show) {
+ if (show) {
+ $('#authentication').css({display: "inline"});
+ }
+ else {
+ $('#authentication').css({display: "none"});
+ }
+ };
+
+ // Shows or hides the 'recording' button.
+ my.showRecordingButton = function (show) {
+ if (!config.enableRecording) {
+ return;
+ }
+
+ if (show) {
+ $('#recording').css({display: "inline"});
+ }
+ else {
+ $('#recording').css({display: "none"});
+ }
+ };
+
+ // Sets the state of the recording button
+ my.setRecordingButtonState = function (isRecording) {
+ var selector = $('#recordButton');
+ if (isRecording) {
+ selector.removeClass("icon-recEnable");
+ selector.addClass("icon-recEnable active");
+ } else {
+ selector.removeClass("icon-recEnable active");
+ selector.addClass("icon-recEnable");
+ }
+ };
+
+ // Shows or hides SIP calls button
+ my.showSipCallButton = function (show) {
+ if (APP.xmpp.isSipGatewayEnabled() && show) {
+ $('#sipCallButton').css({display: "inline-block"});
+ } else {
+ $('#sipCallButton').css({display: "none"});
+ }
+ };
+
+ // Shows or hides the dialpad button
+ my.showDialPadButton = function (show) {
+ if (show) {
+ $('#dialPadButton').css({display: "inline-block"});
+ } else {
+ $('#dialPadButton').css({display: "none"});
+ }
+ };
+
+ /**
+ * Displays user authenticated identity name(login).
+ * @param authIdentity identity name to be displayed.
+ */
+ my.setAuthenticatedIdentity = function (authIdentity) {
+ if (authIdentity) {
+ var selector = $('#toolbar_auth_identity');
+ selector.css({display: "list-item"});
+ selector.text(authIdentity);
+ } else {
+ $('#toolbar_auth_identity').css({display: "none"});
+ }
+ };
+
+ /**
+ * Shows/hides login button.
+ * @param show true to show
+ */
+ my.showLoginButton = function (show) {
+ if (show) {
+ $('#toolbar_button_login').css({display: "list-item"});
+ } else {
+ $('#toolbar_button_login').css({display: "none"});
+ }
+ };
+
+ /**
+ * Shows/hides logout button.
+ * @param show true to show
+ */
+ my.showLogoutButton = function (show) {
+ if (show) {
+ $('#toolbar_button_logout').css({display: "list-item"});
+ } else {
+ $('#toolbar_button_logout').css({display: "none"});
+ }
+ };
+
+ /**
+ * Sets the state of the button. The button has blue glow if desktop
+ * streaming is active.
+ * @param active the state of the desktop streaming.
+ */
+ my.changeDesktopSharingButtonState = function (active) {
+ var button = $("#desktopsharing > a");
+ if (active)
+ {
+ button.addClass("glow");
+ }
+ else
+ {
+ button.removeClass("glow");
+ }
+ };
+
+ return my;
+}(Toolbar || {}));
+
+module.exports = Toolbar;
+},{"../../../service/authentication/AuthenticationEvents":103,"../authentication/Authentication":14,"../etherpad/Etherpad":17,"../prezi/Prezi":18,"../side_pannels/SidePanelToggler":20,"../util/MessageHandler":31,"../util/UIUtil":33,"./BottomToolbar":27}],29:[function(require,module,exports){
+/* global $, interfaceConfig, Moderator, DesktopStreaming.showDesktopSharingButton */
+
+var toolbarTimeoutObject,
+ toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT;
+
+function showDesktopSharingButton() {
+ if (APP.desktopsharing.isDesktopSharingEnabled()) {
+ $('#desktopsharing').css({display: "inline"});
+ } else {
+ $('#desktopsharing').css({display: "none"});
+ }
+}
+
+/**
+ * Hides the toolbar.
+ */
+function hideToolbar() {
+ if(config.alwaysVisibleToolbar)
+ return;
+
+ var header = $("#header"),
+ bottomToolbar = $("#bottomToolbar");
+ var isToolbarHover = false;
+ header.find('*').each(function () {
+ var id = $(this).attr('id');
+ if ($("#" + id + ":hover").length > 0) {
+ isToolbarHover = true;
+ }
+ });
+ if ($("#bottomToolbar:hover").length > 0) {
+ isToolbarHover = true;
+ }
+
+ clearTimeout(toolbarTimeoutObject);
+ toolbarTimeoutObject = null;
+
+ if (!isToolbarHover) {
+ header.hide("slide", { direction: "up", duration: 300});
+ $('#subject').animate({top: "-=40"}, 300);
+ if ($("#remoteVideos").hasClass("hidden")) {
+ bottomToolbar.hide(
+ "slide", {direction: "right", duration: 300});
+ }
+ }
+ else {
+ toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
+ }
+}
+
+var ToolbarToggler = {
+ /**
+ * Shows the main toolbar.
+ */
+ showToolbar: function () {
+ var header = $("#header"),
+ bottomToolbar = $("#bottomToolbar");
+ if (!header.is(':visible') || !bottomToolbar.is(":visible")) {
+ header.show("slide", { direction: "up", duration: 300});
+ $('#subject').animate({top: "+=40"}, 300);
+ if (!bottomToolbar.is(":visible")) {
+ bottomToolbar.show(
+ "slide", {direction: "right", duration: 300});
+ }
+
+ if (toolbarTimeoutObject) {
+ clearTimeout(toolbarTimeoutObject);
+ toolbarTimeoutObject = null;
+ }
+ toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
+ toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT;
+ }
+
+ if (APP.xmpp.isModerator())
+ {
+// TODO: Enable settings functionality.
+// Need to uncomment the settings button in index.html.
+// $('#settingsButton').css({visibility:"visible"});
+ }
+
+ // Show/hide desktop sharing button
+ showDesktopSharingButton();
+ },
+
+
+ /**
+ * Docks/undocks the toolbar.
+ *
+ * @param isDock indicates what operation to perform
+ */
+ dockToolbar: function (isDock) {
+ if (isDock) {
+ // First make sure the toolbar is shown.
+ if (!$('#header').is(':visible')) {
+ this.showToolbar();
+ }
+
+ // Then clear the time out, to dock the toolbar.
+ if (toolbarTimeoutObject) {
+ clearTimeout(toolbarTimeoutObject);
+ toolbarTimeoutObject = null;
+ }
+ }
+ else {
+ if (!$('#header').is(':visible')) {
+ this.showToolbar();
+ }
+ else {
+ toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
+ }
+ }
+ },
+
+ showDesktopSharingButton: showDesktopSharingButton
+
+};
+
+module.exports = ToolbarToggler;
+},{}],30:[function(require,module,exports){
+var JitsiPopover = (function () {
+ /**
+ * Constructs new JitsiPopover and attaches it to the element
+ * @param element jquery selector
+ * @param options the options for the popover.
+ * @constructor
+ */
+ function JitsiPopover(element, options)
+ {
+ this.options = {
+ skin: "white",
+ content: ""
+ };
+ if(options)
+ {
+ if(options.skin)
+ this.options.skin = options.skin;
+
+ if(options.content)
+ this.options.content = options.content;
+ }
+
+ this.elementIsHovered = false;
+ this.popoverIsHovered = false;
+ this.popoverShown = false;
+
+ element.data("jitsi_popover", this);
+ this.element = element;
+ this.template = ' ';
+ var self = this;
+ this.element.on("mouseenter", function () {
+ self.elementIsHovered = true;
+ self.show();
+ }).on("mouseleave", function () {
+ self.elementIsHovered = false;
+ setTimeout(function () {
+ self.hide();
+ }, 10);
+ });
+ }
+
+ /**
+ * Shows the popover
+ */
+ JitsiPopover.prototype.show = function () {
+ this.createPopover();
+ this.popoverShown = true;
+
+ };
+
+ /**
+ * Hides the popover
+ */
+ JitsiPopover.prototype.hide = function () {
+ if(!this.elementIsHovered && !this.popoverIsHovered && this.popoverShown)
+ {
+ this.forceHide();
+ }
+ };
+
+ /**
+ * Hides the popover
+ */
+ JitsiPopover.prototype.forceHide = function () {
+ $(".jitsipopover").remove();
+ this.popoverShown = false;
+ };
+
+ /**
+ * Creates the popover html
+ */
+ JitsiPopover.prototype.createPopover = function () {
+ $("body").append(this.template);
+ $(".jitsipopover > .jitsipopover-content").html(this.options.content);
+ var self = this;
+ $(".jitsipopover").on("mouseenter", function () {
+ self.popoverIsHovered = true;
+ }).on("mouseleave", function () {
+ self.popoverIsHovered = false;
+ self.hide();
+ });
+
+ this.refreshPosition();
+ };
+
+ /**
+ * Refreshes the position of the popover
+ */
+ JitsiPopover.prototype.refreshPosition = function () {
+ $(".jitsipopover").position({
+ my: "bottom",
+ at: "top",
+ collision: "fit",
+ of: this.element,
+ using: function (position, elements) {
+ var calcLeft = elements.target.left - elements.element.left + elements.target.width/2;
+ $(".jitsipopover").css({top: position.top, left: position.left, display: "table"});
+ $(".jitsipopover > .arrow").css({left: calcLeft});
+ $(".jitsipopover > .jitsiPopupmenuPadding").css({left: calcLeft - 50});
+ }
+ });
+ };
+
+ /**
+ * Updates the content of popover.
+ * @param content new content
+ */
+ JitsiPopover.prototype.updateContent = function (content) {
+ this.options.content = content;
+ if(!this.popoverShown)
+ return;
+ $(".jitsipopover").remove();
+ this.createPopover();
+ };
+
+ return JitsiPopover;
+
+
+})();
+
+module.exports = JitsiPopover;
+},{}],31:[function(require,module,exports){
+/* global $, APP, jQuery, toastr */
+var messageHandler = (function(my) {
+
+ /**
+ * Shows a message to the user.
+ *
+ * @param titleString the title of the message
+ * @param messageString the text of the message
+ */
+ my.openMessageDialog = function(titleKey, messageKey) {
+ var title = null;
+ if(titleKey)
+ {
+ title = APP.translation.generateTranslationHTML(titleKey);
+ }
+ var message = APP.translation.generateTranslationHTML(messageKey);
+ $.prompt(message,
+ {
+ title: title,
+ persistent: false
+ }
+ );
+ };
+
+ /**
+ * Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
+ *
+ * @param titleString the title of the message
+ * @param msgString the text of the message
+ * @param persistent boolean value which determines whether the message is persistent or not
+ * @param leftButton the fist button's text
+ * @param submitFunction function to be called on submit
+ * @param loadedFunction function to be called after the prompt is fully loaded
+ * @param closeFunction function to be called after the prompt is closed
+ * @param focus optional focus selector or button index to be focused after
+ * the dialog is opened
+ * @param defaultButton index of default button which will be activated when
+ * the user press 'enter'. Indexed from 0.
+ */
+ my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString,
+ persistent, leftButtonKey, submitFunction, loadedFunction,
+ closeFunction, focus, defaultButton)
+ {
+ var buttons = [];
+
+ var leftButton = APP.translation.generateTranslationHTML(leftButtonKey);
+ buttons.push({ title: leftButton, value: true});
+
+ var cancelButton
+ = APP.translation.generateTranslationHTML("dialog.Cancel");
+ buttons.push({title: cancelButton, value: false});
+
+ var message = msgString, title = titleString;
+ if (titleKey)
+ {
+ title = APP.translation.generateTranslationHTML(titleKey);
+ }
+ if (msgKey) {
+ message = APP.translation.generateTranslationHTML(msgKey);
+ }
+ $.prompt(message, {
+ title: title,
+ persistent: false,
+ buttons: buttons,
+ defaultButton: defaultButton,
+ focus: focus,
+ loaded: loadedFunction,
+ submit: submitFunction,
+ close: closeFunction
+ });
+ };
+
+ /**
+ * Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel.
+ *
+ * @param titleString the title of the message
+ * @param msgString the text of the message
+ * @param persistent boolean value which determines whether the message is persistent or not
+ * @param buttons object with the buttons. The keys must be the name of the button and value is the value
+ * that will be passed to submitFunction
+ * @param submitFunction function to be called on submit
+ * @param loadedFunction function to be called after the prompt is fully loaded
+ */
+ my.openDialog = function (titleString, msgString, persistent, buttons,
+ submitFunction, loadedFunction) {
+ var args = {
+ title: titleString,
+ persistent: persistent,
+ buttons: buttons,
+ defaultButton: 1,
+ loaded: loadedFunction,
+ submit: submitFunction
+ };
+ if (persistent) {
+ args.closeText = '';
+ }
+ return new Impromptu(msgString, args);
+ };
+
+ /**
+ * Closes currently opened dialog.
+ */
+ my.closeDialog = function () {
+ $.prompt.close();
+ };
+
+ /**
+ * Shows a dialog with different states to the user.
+ *
+ * @param statesObject object containing all the states of the dialog
+ */
+ my.openDialogWithStates = function (statesObject, options) {
+
+ return new Impromptu(statesObject, options);
+ };
+
+ /**
+ * Opens new popup window for given url centered over current
+ * window.
+ *
+ * @param url the URL to be displayed in the popup window
+ * @param w the width of the popup window
+ * @param h the height of the popup window
+ * @param onPopupClosed optional callback function called when popup window
+ * has been closed.
+ *
+ * @returns popup window object if opened successfully or undefined
+ * in case we failed to open it(popup blocked)
+ */
+ my.openCenteredPopup = function (url, w, h, onPopupClosed) {
+ var l = window.screenX + (window.innerWidth / 2) - (w / 2);
+ var t = window.screenY + (window.innerHeight / 2) - (h / 2);
+ var popup = window.open(
+ url, '_blank',
+ 'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + '');
+ if (popup && onPopupClosed) {
+ var pollTimer = window.setInterval(function () {
+ if (popup.closed !== false) {
+ window.clearInterval(pollTimer);
+ onPopupClosed();
+ }
+ }, 200);
+ }
+ return popup;
+ };
+
+ /**
+ * Shows a dialog prompting the user to send an error report.
+ *
+ * @param titleString the title of the message
+ * @param msgString the text of the message
+ * @param error the error that is being reported
+ */
+ my.openReportDialog = function(titleKey, msgKey, error) {
+ my.openMessageDialog(titleKey, msgKey);
+ console.log(error);
+ //FIXME send the error to the server
+ };
+
+ /**
+ * Shows an error dialog to the user.
+ * @param title the title of the message
+ * @param message the text of the messafe
+ */
+ my.showError = function(titleKey, msgKey) {
+
+ if(!titleKey) {
+ titleKey = "dialog.oops";
+ }
+ if(!msgKey)
+ {
+ msgKey = "dialog.defaultError";
+ }
+ messageHandler.openMessageDialog(titleKey, msgKey);
+ };
+
+ my.notify = function(displayName, displayNameKey,
+ cls, messageKey, messageArguments, options) {
+ var displayNameSpan = '" + displayName;
+ }
+ else
+ {
+ displayNameSpan += "data-i18n='" + displayNameKey +
+ "'>" + APP.translation.translateString(displayNameKey);
+ }
+ displayNameSpan += "";
+ toastr.info(
+ displayNameSpan + '
' +
+ '" +
+ APP.translation.translateString(messageKey,
+ messageArguments) +
+ '', null, options);
+ };
+
+ return my;
+}(messageHandler || {}));
+
+module.exports = messageHandler;
+
+
+
+},{}],32:[function(require,module,exports){
+var UIEvents = require("../../../service/UI/UIEvents");
+
+var nickname = null;
+var eventEmitter = null;
+
+var NickanameHandler = {
+ init: function (emitter) {
+ eventEmitter = emitter;
+ var storedDisplayName = window.localStorage.displayname;
+ if (storedDisplayName) {
+ nickname = storedDisplayName;
+ }
+ },
+ setNickname: function (newNickname) {
+ if (!newNickname || nickname === newNickname)
+ return;
+
+ nickname = newNickname;
+ window.localStorage.displayname = nickname;
+ eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newNickname);
+ },
+ getNickname: function () {
+ return nickname;
+ },
+ addListener: function (type, listener) {
+ eventEmitter.on(type, listener);
+ }
+};
+
+module.exports = NickanameHandler;
+},{"../../../service/UI/UIEvents":102}],33:[function(require,module,exports){
+/**
+ * Created by hristo on 12/22/14.
+ */
+module.exports = {
+ /**
+ * Returns the available video width.
+ */
+ getAvailableVideoWidth: function (isVisible) {
+ var PanelToggler = require("../side_pannels/SidePanelToggler");
+ if(typeof isVisible === "undefined" || isVisible === null)
+ isVisible = PanelToggler.isVisible();
+ var rightPanelWidth
+ = isVisible ? PanelToggler.getPanelSize()[0] : 0;
+
+ return window.innerWidth - rightPanelWidth;
+ },
+ /**
+ * Changes the style class of the element given by id.
+ */
+ buttonClick: function(id, classname) {
+ $(id).toggleClass(classname); // add the class to the clicked element
+ },
+ /**
+ * Returns the text width for the given element.
+ *
+ * @param el the element
+ */
+ getTextWidth: function (el) {
+ return (el.clientWidth + 1);
+ },
+
+ /**
+ * Returns the text height for the given element.
+ *
+ * @param el the element
+ */
+ getTextHeight: function (el) {
+ return (el.clientHeight + 1);
+ },
+
+ /**
+ * Plays the sound given by id.
+ *
+ * @param id the identifier of the audio element.
+ */
+ playSoundNotification: function (id) {
+ document.getElementById(id).play();
+ },
+
+ /**
+ * Escapes the given text.
+ */
+ escapeHtml: function (unsafeText) {
+ return $('').text(unsafeText).html();
+ },
+
+ imageToGrayScale: function (canvas) {
+ var context = canvas.getContext('2d');
+ var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
+ var pixels = imgData.data;
+
+ for (var i = 0, n = pixels.length; i < n; i += 4) {
+ var grayscale
+ = pixels[i] * .3 + pixels[i+1] * .59 + pixels[i+2] * .11;
+ pixels[i ] = grayscale; // red
+ pixels[i+1] = grayscale; // green
+ pixels[i+2] = grayscale; // blue
+ // pixels[i+3] is alpha
+ }
+ // redraw the image in black & white
+ context.putImageData(imgData, 0, 0);
+ },
+
+ setTooltip: function (element, key, position) {
+ element.setAttribute("data-i18n", "[data-content]" + key);
+ element.setAttribute("data-toggle", "popover");
+ element.setAttribute("data-placement", position);
+ element.setAttribute("data-html", true);
+ element.setAttribute("data-container", "body");
+ }
+
+
+};
+},{"../side_pannels/SidePanelToggler":20}],34:[function(require,module,exports){
+var JitsiPopover = require("../util/JitsiPopover");
+
+/**
+ * Constructs new connection indicator.
+ * @param videoContainer the video container associated with the indicator.
+ * @constructor
+ */
+function ConnectionIndicator(videoContainer, jid)
+{
+ this.videoContainer = videoContainer;
+ this.bandwidth = null;
+ this.packetLoss = null;
+ this.bitrate = null;
+ this.showMoreValue = false;
+ this.resolution = null;
+ this.transport = [];
+ this.popover = null;
+ this.jid = jid;
+ this.create();
+}
+
+/**
+ * Values for the connection quality
+ * @type {{98: string,
+ * 81: string,
+ * 64: string,
+ * 47: string,
+ * 30: string,
+ * 0: string}}
+ */
+ConnectionIndicator.connectionQualityValues = {
+ 98: "18px", //full
+ 81: "15px",//4 bars
+ 64: "11px",//3 bars
+ 47: "7px",//2 bars
+ 30: "3px",//1 bar
+ 0: "0px"//empty
+};
+
+ConnectionIndicator.getIP = function(value)
+{
+ return value.substring(0, value.lastIndexOf(":"));
+};
+
+ConnectionIndicator.getPort = function(value)
+{
+ return value.substring(value.lastIndexOf(":") + 1, value.length);
+};
+
+ConnectionIndicator.getStringFromArray = function (array) {
+ var res = "";
+ for(var i = 0; i < array.length; i++)
+ {
+ res += (i === 0? "" : ", ") + array[i];
+ }
+ return res;
+};
+
+/**
+ * Generates the html content.
+ * @returns {string} the html content.
+ */
+ConnectionIndicator.prototype.generateText = function () {
+ var downloadBitrate, uploadBitrate, packetLoss, resolution, i;
+
+ var translate = APP.translation.translateString;
+
+ if(this.bitrate === null)
+ {
+ downloadBitrate = "N/A";
+ uploadBitrate = "N/A";
+ }
+ else
+ {
+ downloadBitrate =
+ this.bitrate.download? this.bitrate.download + " Kbps" : "N/A";
+ uploadBitrate =
+ this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A";
+ }
+
+ if(this.packetLoss === null)
+ {
+ packetLoss = "N/A";
+ }
+ else
+ {
+
+ packetLoss = "↓" +
+ (this.packetLoss.download !== null? this.packetLoss.download : "N/A") +
+ "% ↑" +
+ (this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") + "%";
+ }
+
+ var resolutionValue = null;
+ if(this.resolution && this.jid != null)
+ {
+ var keys = Object.keys(this.resolution);
+ for(var ssrc in this.resolution)
+ {
+ resolutionValue = this.resolution[ssrc];
+ }
+ }
+
+ if(this.jid === null)
+ {
+ resolution = "";
+ if(this.resolution === null || !Object.keys(this.resolution) ||
+ Object.keys(this.resolution).length === 0)
+ {
+ resolution = "N/A";
+ }
+ else
+ for(i in this.resolution)
+ {
+ resolutionValue = this.resolution[i];
+ if(resolutionValue)
+ {
+ if(resolutionValue.height &&
+ resolutionValue.width)
+ {
+ resolution += (resolution === ""? "" : ", ") +
+ resolutionValue.width + "x" +
+ resolutionValue.height;
+ }
+ }
+ }
+ }
+ else if(!resolutionValue ||
+ !resolutionValue.height ||
+ !resolutionValue.width)
+ {
+ resolution = "N/A";
+ }
+ else
+ {
+ resolution = resolutionValue.width + "x" + resolutionValue.height;
+ }
+
+ var result = "" +
+ "" +
+ "" +
+ translate("connectionindicator.bitrate") + " | " +
+ "↓" +
+ downloadBitrate + " ↑" +
+ uploadBitrate + " | " +
+ "
" +
+ "" +
+ translate("connectionindicator.packetloss") + " | " +
+ "" + packetLoss + " | " +
+ "
" +
+ "" +
+ translate("connectionindicator.resolution") + " | " +
+ "" + resolution + " |
";
+
+ if(this.videoContainer.videoSpanId == "localVideoContainer") {
+ result += "" +
+ translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) +
+ "
";
+ }
+
+ if(this.showMoreValue)
+ {
+ var downloadBandwidth, uploadBandwidth, transport;
+ if(this.bandwidth === null)
+ {
+ downloadBandwidth = "N/A";
+ uploadBandwidth = "N/A";
+ }
+ else
+ {
+ downloadBandwidth = this.bandwidth.download?
+ this.bandwidth.download + " Kbps" :
+ "N/A";
+ uploadBandwidth = this.bandwidth.upload?
+ this.bandwidth.upload + " Kbps" :
+ "N/A";
+ }
+
+ if(!this.transport || this.transport.length === 0)
+ {
+ transport = "" +
+ "" +
+ translate("connectionindicator.address") + " | " +
+ " N/A |
";
+ }
+ else
+ {
+ var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]};
+ for(i = 0; i < this.transport.length; i++)
+ {
+ var ip = ConnectionIndicator.getIP(this.transport[i].ip);
+ var port = ConnectionIndicator.getPort(this.transport[i].ip);
+ var localIP =
+ ConnectionIndicator.getIP(this.transport[i].localip);
+ var localPort =
+ ConnectionIndicator.getPort(this.transport[i].localip);
+ if(data.remoteIP.indexOf(ip) == -1)
+ {
+ data.remoteIP.push(ip);
+ }
+
+ if(data.remotePort.indexOf(port) == -1)
+ {
+ data.remotePort.push(port);
+ }
+
+ if(data.localIP.indexOf(localIP) == -1)
+ {
+ data.localIP.push(localIP);
+ }
+
+ if(data.localPort.indexOf(localPort) == -1)
+ {
+ data.localPort.push(localPort);
+ }
+
+ }
+
+ var local_address_key = "connectionindicator.localaddress";
+ var remote_address_key = "connectionindicator.remoteaddress";
+ var localTransport =
+ "" +
+ translate(local_address_key, {count: data.localIP.length}) +
+ " | " +
+ ConnectionIndicator.getStringFromArray(data.localIP) +
+ " |
";
+ transport =
+ "" +
+ translate(remote_address_key,
+ {count: data.remoteIP.length}) +
+ " | " +
+ ConnectionIndicator.getStringFromArray(data.remoteIP) +
+ " |
";
+
+ var key_remote = "connectionindicator.remoteport",
+ key_local = "connectionindicator.localport";
+
+ transport += "" +
+ "" +
+ "" +
+ translate(key_remote, {count: this.transport.length}) +
+ " | ";
+ localTransport += " |
" +
+ "" +
+ "" +
+ translate(key_local, {count: this.transport.length}) +
+ " | ";
+
+ transport +=
+ ConnectionIndicator.getStringFromArray(data.remotePort);
+ localTransport +=
+ ConnectionIndicator.getStringFromArray(data.localPort);
+ transport += " |
";
+ transport += localTransport + "";
+ transport +="" +
+ "" +
+ translate("connectionindicator.transport") + " | " +
+ "" + this.transport[0].type + " |
";
+
+ }
+
+ result += "" +
+ "" +
+ "" +
+ "" +
+ translate("connectionindicator.bandwidth") + "" +
+ " | " +
+ "↓" +
+ downloadBandwidth +
+ " ↑" +
+ uploadBandwidth + " |
";
+
+ result += transport + "
";
+
+ }
+
+ return result;
+};
+
+/**
+ * Shows or hide the additional information.
+ */
+ConnectionIndicator.prototype.showMore = function () {
+ this.showMoreValue = !this.showMoreValue;
+ this.updatePopoverData();
+};
+
+
+function createIcon(classes)
+{
+ var icon = document.createElement("span");
+ for(var i in classes)
+ {
+ icon.classList.add(classes[i]);
+ }
+ icon.appendChild(
+ document.createElement("i")).classList.add("icon-connection");
+ return icon;
+}
+
+/**
+ * Creates the indicator
+ */
+ConnectionIndicator.prototype.create = function () {
+ this.connectionIndicatorContainer = document.createElement("div");
+ this.connectionIndicatorContainer.className = "connectionindicator";
+ this.connectionIndicatorContainer.style.display = "none";
+ this.videoContainer.container.appendChild(this.connectionIndicatorContainer);
+ this.popover = new JitsiPopover(
+ $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"),
+ {content: "" +
+ APP.translation.translateString("connectionindicator.na") + "
",
+ skin: "black"});
+
+ this.emptyIcon = this.connectionIndicatorContainer.appendChild(
+ createIcon(["connection", "connection_empty"]));
+ this.fullIcon = this.connectionIndicatorContainer.appendChild(
+ createIcon(["connection", "connection_full"]));
+
+};
+
+/**
+ * Removes the indicator
+ */
+ConnectionIndicator.prototype.remove = function()
+{
+ if (this.connectionIndicatorContainer.parentNode) {
+ this.connectionIndicatorContainer.parentNode.removeChild(
+ this.connectionIndicatorContainer);
+ }
+ this.popover.forceHide();
+
+};
+
+/**
+ * Updates the data of the indicator
+ * @param percent the percent of connection quality
+ * @param object the statistics data.
+ */
+ConnectionIndicator.prototype.updateConnectionQuality =
+function (percent, object) {
+
+ if(percent === null)
+ {
+ this.connectionIndicatorContainer.style.display = "none";
+ this.popover.forceHide();
+ return;
+ }
+ else
+ {
+ if(this.connectionIndicatorContainer.style.display == "none") {
+ this.connectionIndicatorContainer.style.display = "block";
+ this.videoContainer.updateIconPositions();
+ }
+ }
+ this.bandwidth = object.bandwidth;
+ this.bitrate = object.bitrate;
+ this.packetLoss = object.packetLoss;
+ this.transport = object.transport;
+ if(object.resolution)
+ {
+ this.resolution = object.resolution;
+ }
+ for(var quality in ConnectionIndicator.connectionQualityValues)
+ {
+ if(percent >= quality)
+ {
+ this.fullIcon.style.width =
+ ConnectionIndicator.connectionQualityValues[quality];
+ }
+ }
+ this.updatePopoverData();
+};
+
+/**
+ * Updates the resolution
+ * @param resolution the new resolution
+ */
+ConnectionIndicator.prototype.updateResolution = function (resolution) {
+ this.resolution = resolution;
+ this.updatePopoverData();
+};
+
+/**
+ * Updates the content of the popover
+ */
+ConnectionIndicator.prototype.updatePopoverData = function () {
+ this.popover.updateContent(
+ "" + this.generateText() + "
");
+ APP.translation.translateElement($(".connection_info"));
+};
+
+/**
+ * Hides the popover
+ */
+ConnectionIndicator.prototype.hide = function () {
+ this.popover.forceHide();
+};
+
+/**
+ * Hides the indicator
+ */
+ConnectionIndicator.prototype.hideIndicator = function () {
+ this.connectionIndicatorContainer.style.display = "none";
+ if(this.popover)
+ this.popover.forceHide();
+};
+
module.exports = ConnectionIndicator;
-},{"../util/JitsiPopover":32}],37:[function(require,module,exports){
-var Avatar = require("../avatar/Avatar");
-var RTCBrowserType = require("../../RTC/RTCBrowserType");
-var UIUtil = require("../util/UIUtil");
-var UIEvents = require("../../../service/UI/UIEvents");
-var xmpp = require("../../xmpp/xmpp");
-
-// FIXME: With Temasys we have to re-select everytime
-//var video = $('#largeVideo');
-
-var currentVideoWidth = null;
-var currentVideoHeight = null;
-// By default we use camera
-var getVideoSize = getCameraVideoSize;
-var getVideoPosition = getCameraVideoPosition;
-var currentSmallVideo = null;
-
-
-
-/**
- * Sets the size and position of the given video element.
- *
- * @param video the video element to position
- * @param width the desired video width
- * @param height the desired video height
- * @param horizontalIndent the left and right indent
- * @param verticalIndent the top and bottom indent
- */
-function positionVideo(video,
- width,
- height,
- horizontalIndent,
- verticalIndent,
- animate) {
- if(animate)
- {
- video.animate({
- width: width,
- height: height,
- top: verticalIndent,
- bottom: verticalIndent,
- left: horizontalIndent,
- right: horizontalIndent
- },
- {
- queue: false,
- duration: 500
- });
- }
- else
- {
- video.width(width);
- video.height(height);
- video.css({ top: verticalIndent + 'px',
- bottom: verticalIndent + 'px',
- left: horizontalIndent + 'px',
- right: horizontalIndent + 'px'});
- }
-
-}
-
-
-/**
- * Returns an array of the video dimensions, so that it keeps it's aspect
- * ratio and fits available area with it's larger dimension. This method
- * ensures that whole video will be visible and can leave empty areas.
- *
- * @return an array with 2 elements, the video width and the video height
- */
-function getDesktopVideoSize(videoWidth,
- videoHeight,
- videoSpaceWidth,
- videoSpaceHeight) {
- if (!videoWidth)
- videoWidth = currentVideoWidth;
- if (!videoHeight)
- videoHeight = currentVideoHeight;
-
- var aspectRatio = videoWidth / videoHeight;
-
- var availableWidth = Math.max(videoWidth, videoSpaceWidth);
- var availableHeight = Math.max(videoHeight, videoSpaceHeight);
-
- videoSpaceHeight -= $('#remoteVideos').outerHeight();
-
- if (availableWidth / aspectRatio >= videoSpaceHeight)
- {
- availableHeight = videoSpaceHeight;
- availableWidth = availableHeight * aspectRatio;
- }
-
- if (availableHeight * aspectRatio >= videoSpaceWidth)
- {
- availableWidth = videoSpaceWidth;
- availableHeight = availableWidth / aspectRatio;
- }
-
- return [availableWidth, availableHeight];
-}
-
-
-/**
- * Returns an array of the video horizontal and vertical indents,
- * so that if fits its parent.
- *
- * @return an array with 2 elements, the horizontal indent and the vertical
- * indent
- */
-function getCameraVideoPosition(videoWidth,
- videoHeight,
- videoSpaceWidth,
- videoSpaceHeight) {
- // Parent height isn't completely calculated when we position the video in
- // full screen mode and this is why we use the screen height in this case.
- // Need to think it further at some point and implement it properly.
- var isFullScreen = document.fullScreen ||
- document.mozFullScreen ||
- document.webkitIsFullScreen;
- if (isFullScreen)
- videoSpaceHeight = window.innerHeight;
-
- var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
- var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
-
- return [horizontalIndent, verticalIndent];
-}
-
-/**
- * Returns an array of the video horizontal and vertical indents.
- * Centers horizontally and top aligns vertically.
- *
- * @return an array with 2 elements, the horizontal indent and the vertical
- * indent
- */
-function getDesktopVideoPosition(videoWidth,
- videoHeight,
- videoSpaceWidth,
- videoSpaceHeight) {
-
- var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
-
- var verticalIndent = 0;// Top aligned
-
- return [horizontalIndent, verticalIndent];
-}
-
-
-/**
- * Returns an array of the video dimensions, so that it covers the screen.
- * It leaves no empty areas, but some parts of the video might not be visible.
- *
- * @return an array with 2 elements, the video width and the video height
- */
-function getCameraVideoSize(videoWidth,
- videoHeight,
- videoSpaceWidth,
- videoSpaceHeight) {
- if (!videoWidth)
- videoWidth = currentVideoWidth;
- if (!videoHeight)
- videoHeight = currentVideoHeight;
-
- var aspectRatio = videoWidth / videoHeight;
-
- var availableWidth = Math.max(videoWidth, videoSpaceWidth);
- var availableHeight = Math.max(videoHeight, videoSpaceHeight);
-
- if (availableWidth / aspectRatio < videoSpaceHeight) {
- availableHeight = videoSpaceHeight;
- availableWidth = availableHeight * aspectRatio;
- }
-
- if (availableHeight * aspectRatio < videoSpaceWidth) {
- availableWidth = videoSpaceWidth;
- availableHeight = availableWidth / aspectRatio;
- }
-
- return [availableWidth, availableHeight];
-}
-
-/**
- * Updates the src of the active speaker avatar
- * @param jid of the current active speaker
- */
-function updateActiveSpeakerAvatarSrc() {
- var avatar = $("#activeSpeakerAvatar")[0];
- var jid = currentSmallVideo.peerJid;
- var url = Avatar.getActiveSpeakerUrl(jid);
- if (avatar.src === url)
- return;
- var isMuted = null;
- if (!currentSmallVideo.isLocal &&
- !LargeVideo.VideoLayout.isInLastN(currentSmallVideo.getResourceJid())) {
- isMuted = true;
- }
- else
- {
- isMuted = APP.RTC.isVideoMuted(jid);
- }
-
- if (jid && isMuted !== null) {
- avatar.src = url;
- $("#largeVideo").css("visibility", isMuted ? "hidden" : "visible");
- currentSmallVideo.showAvatar(isMuted);
- }
-}
-
-function changeVideo(isVisible) {
- updateActiveSpeakerAvatarSrc();
-
- APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
-
- var videoTransform = document.getElementById('largeVideo')
- .style.webkitTransform;
-
- var flipX = currentSmallVideo.flipX;
-
- if (flipX && videoTransform !== 'scaleX(-1)') {
- document.getElementById('largeVideo').style.webkitTransform
- = "scaleX(-1)";
- }
- else if (!flipX && videoTransform === 'scaleX(-1)') {
- document.getElementById('largeVideo').style.webkitTransform
- = "none";
- }
-
- var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
- // Change the way we'll be measuring and positioning large video
-
- getVideoSize = isDesktop
- ? getDesktopVideoSize
- : getCameraVideoSize;
- getVideoPosition = isDesktop
- ? getDesktopVideoPosition
- : getCameraVideoPosition;
-
-
- // Only if the large video is currently visible.
- if (isVisible) {
- LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo);
-
- $('#largeVideo').fadeIn(300);
- }
-}
-
-var LargeVideo = {
-
- init: function (VideoLayout, emitter) {
- this.VideoLayout = VideoLayout;
- this.eventEmitter = emitter;
- var self = this;
- // Listen for large video size updates
- var largeVideo = $('#largeVideo')[0];
- var onplaying = function (arg1, arg2, arg3) {
- // re-select
- if (RTCBrowserType.isTemasysPluginUsed())
- largeVideo = $('#largeVideo')[0];
- currentVideoWidth = largeVideo.videoWidth;
- currentVideoHeight = largeVideo.videoHeight;
- self.position(currentVideoWidth, currentVideoHeight);
- };
- largeVideo.onplaying = onplaying;
- },
- /**
- * Indicates if the large video is currently visible.
- *
- * @return true if visible, false - otherwise
- */
- isLargeVideoVisible: function() {
- return $('#largeVideo').is(':visible');
- },
- /**
- * Returns true if the user is currently displayed on large video.
- */
- isCurrentlyOnLarge: function (resourceJid) {
- return currentSmallVideo && resourceJid &&
- currentSmallVideo.getResourceJid() === resourceJid;
- },
- /**
- * Updates the large video with the given new video source.
- */
- updateLargeVideo: function (resourceJid, forceUpdate) {
- var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
- console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
-
- if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) {
- $('#activeSpeaker').css('visibility', 'hidden');
-
- var oldSmallVideo = null;
- if (currentSmallVideo) {
- oldSmallVideo = currentSmallVideo;
- }
- currentSmallVideo = newSmallVideo;
-
- var oldJid = null;
- if (oldSmallVideo)
- oldJid = oldSmallVideo.peerJid;
- if (oldJid !== resourceJid) {
- // we want the notification to trigger even if userJid is undefined,
- // or null.
- this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, resourceJid);
- }
- if (RTCBrowserType.isSafari()) {
- // FIXME In Safari fadeOut works only for the first time
- changeVideo(this.isLargeVideoVisible());
- } else {
- $('#largeVideo').fadeOut(300,
- changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
- }
- } else {
- if (currentSmallVideo) {
- currentSmallVideo.showAvatar();
- }
- }
-
- },
-
- /**
- * Shows/hides the large video.
- */
- setLargeVideoVisible: function(isVisible) {
- if (isVisible) {
- $('#largeVideo').css({visibility: 'visible'});
- $('.watermark').css({visibility: 'visible'});
- if(currentSmallVideo)
- currentSmallVideo.enableDominantSpeaker(true);
- }
- else {
- $('#largeVideo').css({visibility: 'hidden'});
- $('#activeSpeaker').css('visibility', 'hidden');
- $('.watermark').css({visibility: 'hidden'});
- if(currentSmallVideo)
- currentSmallVideo.enableDominantSpeaker(false);
- }
- },
- onVideoTypeChanged: function (jid) {
- var resourceJid = Strophe.getResourceFromJid(jid);
- if (LargeVideo.isCurrentlyOnLarge(resourceJid))
- {
- var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
- getVideoSize = isDesktop
- ? getDesktopVideoSize
- : getCameraVideoSize;
- getVideoPosition = isDesktop
- ? getDesktopVideoPosition
- : getCameraVideoPosition;
- this.position(null, null);
- }
- },
- /**
- * Positions the large video.
- *
- * @param videoWidth the stream video width
- * @param videoHeight the stream video height
- */
- position: function (videoWidth, videoHeight,
- videoSpaceWidth, videoSpaceHeight, animate) {
- if(!videoSpaceWidth)
- videoSpaceWidth = $('#videospace').width();
- if(!videoSpaceHeight)
- videoSpaceHeight = window.innerHeight;
-
- var videoSize = getVideoSize(videoWidth,
- videoHeight,
- videoSpaceWidth,
- videoSpaceHeight);
-
- var largeVideoWidth = videoSize[0];
- var largeVideoHeight = videoSize[1];
-
- var videoPosition = getVideoPosition(largeVideoWidth,
- largeVideoHeight,
- videoSpaceWidth,
- videoSpaceHeight);
-
- var horizontalIndent = videoPosition[0];
- var verticalIndent = videoPosition[1];
-
- positionVideo($('#largeVideo'),
- largeVideoWidth,
- largeVideoHeight,
- horizontalIndent, verticalIndent, animate);
- },
-
- isLargeVideoOnTop: function () {
- var Etherpad = require("../etherpad/Etherpad");
- var Prezi = require("../prezi/Prezi");
- return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
- },
- resize: function (animate, isVisible, completeFunction) {
- var availableHeight = window.innerHeight;
- var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
-
- if (availableWidth < 0 || availableHeight < 0) return;
-
- var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
- var top = availableHeight / 2 - avatarSize / 4 * 3;
- $('#activeSpeaker').css('top', top);
-
- if(animate)
- {
- $('#videospace').animate({
- right: window.innerWidth - availableWidth,
- width: availableWidth,
- height: availableHeight
- },
- {
- queue: false,
- duration: 500,
- complete: completeFunction
- });
-
- $('#largeVideoContainer').animate({
- width: availableWidth,
- height: availableHeight
- },
- {
- queue: false,
- duration: 500
- });
-
-
- }
- else
- {
- $('#videospace').width(availableWidth);
- $('#videospace').height(availableHeight);
- $('#largeVideoContainer').width(availableWidth);
- $('#largeVideoContainer').height(availableHeight);
- }
- return [availableWidth, availableHeight];
-
- },
- resizeVideoAreaAnimated: function (isVisible, completeFunction) {
- var size = this.resize(true, isVisible, completeFunction);
- this.position(null, null, size[0], size[1], true);
- },
- getResourceJid: function () {
- return currentSmallVideo ? currentSmallVideo.getResourceJid() : null;
- },
- updateAvatar: function (resourceJid) {
- if (resourceJid === this.getResourceJid()) {
- updateActiveSpeakerAvatarSrc();
- }
- },
- showAvatar: function (resourceJid, show) {
- if(this.getResourceJid() === resourceJid
- && LargeVideo.isLargeVideoOnTop())
- {
- $("#largeVideo").css("visibility", show ? "hidden" : "visible");
- $('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
- return true;
- }
- return false;
- }
-
-}
-
-
+},{"../util/JitsiPopover":30}],35:[function(require,module,exports){
+var Avatar = require("../avatar/Avatar");
+var RTCBrowserType = require("../../RTC/RTCBrowserType");
+var UIUtil = require("../util/UIUtil");
+var UIEvents = require("../../../service/UI/UIEvents");
+var xmpp = require("../../xmpp/xmpp");
+
+// FIXME: With Temasys we have to re-select everytime
+//var video = $('#largeVideo');
+
+var currentVideoWidth = null;
+var currentVideoHeight = null;
+// By default we use camera
+var getVideoSize = getCameraVideoSize;
+var getVideoPosition = getCameraVideoPosition;
+var currentSmallVideo = null;
+
+
+
+/**
+ * Sets the size and position of the given video element.
+ *
+ * @param video the video element to position
+ * @param width the desired video width
+ * @param height the desired video height
+ * @param horizontalIndent the left and right indent
+ * @param verticalIndent the top and bottom indent
+ */
+function positionVideo(video,
+ width,
+ height,
+ horizontalIndent,
+ verticalIndent,
+ animate) {
+ if(animate)
+ {
+ video.animate({
+ width: width,
+ height: height,
+ top: verticalIndent,
+ bottom: verticalIndent,
+ left: horizontalIndent,
+ right: horizontalIndent
+ },
+ {
+ queue: false,
+ duration: 500
+ });
+ }
+ else
+ {
+ video.width(width);
+ video.height(height);
+ video.css({ top: verticalIndent + 'px',
+ bottom: verticalIndent + 'px',
+ left: horizontalIndent + 'px',
+ right: horizontalIndent + 'px'});
+ }
+
+}
+
+
+/**
+ * Returns an array of the video dimensions, so that it keeps it's aspect
+ * ratio and fits available area with it's larger dimension. This method
+ * ensures that whole video will be visible and can leave empty areas.
+ *
+ * @return an array with 2 elements, the video width and the video height
+ */
+function getDesktopVideoSize(videoWidth,
+ videoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight) {
+ if (!videoWidth)
+ videoWidth = currentVideoWidth;
+ if (!videoHeight)
+ videoHeight = currentVideoHeight;
+
+ var aspectRatio = videoWidth / videoHeight;
+
+ var availableWidth = Math.max(videoWidth, videoSpaceWidth);
+ var availableHeight = Math.max(videoHeight, videoSpaceHeight);
+
+ videoSpaceHeight -= $('#remoteVideos').outerHeight();
+
+ if (availableWidth / aspectRatio >= videoSpaceHeight)
+ {
+ availableHeight = videoSpaceHeight;
+ availableWidth = availableHeight * aspectRatio;
+ }
+
+ if (availableHeight * aspectRatio >= videoSpaceWidth)
+ {
+ availableWidth = videoSpaceWidth;
+ availableHeight = availableWidth / aspectRatio;
+ }
+
+ return [availableWidth, availableHeight];
+}
+
+
+/**
+ * Returns an array of the video horizontal and vertical indents,
+ * so that if fits its parent.
+ *
+ * @return an array with 2 elements, the horizontal indent and the vertical
+ * indent
+ */
+function getCameraVideoPosition(videoWidth,
+ videoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight) {
+ // Parent height isn't completely calculated when we position the video in
+ // full screen mode and this is why we use the screen height in this case.
+ // Need to think it further at some point and implement it properly.
+ var isFullScreen = document.fullScreen ||
+ document.mozFullScreen ||
+ document.webkitIsFullScreen;
+ if (isFullScreen)
+ videoSpaceHeight = window.innerHeight;
+
+ var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
+ var verticalIndent = (videoSpaceHeight - videoHeight) / 2;
+
+ return [horizontalIndent, verticalIndent];
+}
+
+/**
+ * Returns an array of the video horizontal and vertical indents.
+ * Centers horizontally and top aligns vertically.
+ *
+ * @return an array with 2 elements, the horizontal indent and the vertical
+ * indent
+ */
+function getDesktopVideoPosition(videoWidth,
+ videoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight) {
+
+ var horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
+
+ var verticalIndent = 0;// Top aligned
+
+ return [horizontalIndent, verticalIndent];
+}
+
+
+/**
+ * Returns an array of the video dimensions, so that it covers the screen.
+ * It leaves no empty areas, but some parts of the video might not be visible.
+ *
+ * @return an array with 2 elements, the video width and the video height
+ */
+function getCameraVideoSize(videoWidth,
+ videoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight) {
+ if (!videoWidth)
+ videoWidth = currentVideoWidth;
+ if (!videoHeight)
+ videoHeight = currentVideoHeight;
+
+ var aspectRatio = videoWidth / videoHeight;
+
+ var availableWidth = Math.max(videoWidth, videoSpaceWidth);
+ var availableHeight = Math.max(videoHeight, videoSpaceHeight);
+
+ if (availableWidth / aspectRatio < videoSpaceHeight) {
+ availableHeight = videoSpaceHeight;
+ availableWidth = availableHeight * aspectRatio;
+ }
+
+ if (availableHeight * aspectRatio < videoSpaceWidth) {
+ availableWidth = videoSpaceWidth;
+ availableHeight = availableWidth / aspectRatio;
+ }
+
+ return [availableWidth, availableHeight];
+}
+
+/**
+ * Updates the src of the active speaker avatar
+ * @param jid of the current active speaker
+ */
+function updateActiveSpeakerAvatarSrc() {
+ var avatar = $("#activeSpeakerAvatar")[0];
+ var jid = currentSmallVideo.peerJid;
+ var url = Avatar.getActiveSpeakerUrl(jid);
+ if (avatar.src === url)
+ return;
+ var isMuted = null;
+ if (!currentSmallVideo.isLocal &&
+ !LargeVideo.VideoLayout.isInLastN(currentSmallVideo.getResourceJid())) {
+ isMuted = true;
+ }
+ else
+ {
+ isMuted = APP.RTC.isVideoMuted(jid);
+ }
+
+ if (jid && isMuted !== null) {
+ avatar.src = url;
+ $("#largeVideo").css("visibility", isMuted ? "hidden" : "visible");
+ currentSmallVideo.showAvatar(isMuted);
+ }
+}
+
+function changeVideo(isVisible) {
+ updateActiveSpeakerAvatarSrc();
+
+ APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc());
+
+ var videoTransform = document.getElementById('largeVideo')
+ .style.webkitTransform;
+
+ var flipX = currentSmallVideo.flipX;
+
+ if (flipX && videoTransform !== 'scaleX(-1)') {
+ document.getElementById('largeVideo').style.webkitTransform
+ = "scaleX(-1)";
+ }
+ else if (!flipX && videoTransform === 'scaleX(-1)') {
+ document.getElementById('largeVideo').style.webkitTransform
+ = "none";
+ }
+
+ var isDesktop = APP.RTC.isVideoSrcDesktop(currentSmallVideo.peerJid);
+ // Change the way we'll be measuring and positioning large video
+
+ getVideoSize = isDesktop
+ ? getDesktopVideoSize
+ : getCameraVideoSize;
+ getVideoPosition = isDesktop
+ ? getDesktopVideoPosition
+ : getCameraVideoPosition;
+
+
+ // Only if the large video is currently visible.
+ if (isVisible) {
+ LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo);
+
+ $('#largeVideo').fadeIn(300);
+ }
+}
+
+var LargeVideo = {
+
+ init: function (VideoLayout, emitter) {
+ this.VideoLayout = VideoLayout;
+ this.eventEmitter = emitter;
+ var self = this;
+ // Listen for large video size updates
+ var largeVideo = $('#largeVideo')[0];
+ var onplaying = function (arg1, arg2, arg3) {
+ // re-select
+ if (RTCBrowserType.isTemasysPluginUsed())
+ largeVideo = $('#largeVideo')[0];
+ currentVideoWidth = largeVideo.videoWidth;
+ currentVideoHeight = largeVideo.videoHeight;
+ self.position(currentVideoWidth, currentVideoHeight);
+ };
+ largeVideo.onplaying = onplaying;
+ },
+ /**
+ * Indicates if the large video is currently visible.
+ *
+ * @return true if visible, false - otherwise
+ */
+ isLargeVideoVisible: function() {
+ return $('#largeVideo').is(':visible');
+ },
+ /**
+ * Returns true if the user is currently displayed on large video.
+ */
+ isCurrentlyOnLarge: function (resourceJid) {
+ return currentSmallVideo && resourceJid &&
+ currentSmallVideo.getResourceJid() === resourceJid;
+ },
+ /**
+ * Updates the large video with the given new video source.
+ */
+ updateLargeVideo: function (resourceJid, forceUpdate) {
+ var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid);
+ console.log('hover in ' + resourceJid + ', video: ', newSmallVideo);
+
+ if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) {
+ $('#activeSpeaker').css('visibility', 'hidden');
+
+ var oldSmallVideo = null;
+ if (currentSmallVideo) {
+ oldSmallVideo = currentSmallVideo;
+ }
+ currentSmallVideo = newSmallVideo;
+
+ var oldJid = null;
+ if (oldSmallVideo)
+ oldJid = oldSmallVideo.peerJid;
+ if (oldJid !== resourceJid) {
+ // we want the notification to trigger even if userJid is undefined,
+ // or null.
+ this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, resourceJid);
+ }
+ if (RTCBrowserType.isSafari()) {
+ // FIXME In Safari fadeOut works only for the first time
+ changeVideo(this.isLargeVideoVisible());
+ } else {
+ $('#largeVideo').fadeOut(300,
+ changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible()));
+ }
+ } else {
+ if (currentSmallVideo) {
+ currentSmallVideo.showAvatar();
+ }
+ }
+
+ },
+
+ /**
+ * Shows/hides the large video.
+ */
+ setLargeVideoVisible: function(isVisible) {
+ if (isVisible) {
+ $('#largeVideo').css({visibility: 'visible'});
+ $('.watermark').css({visibility: 'visible'});
+ if(currentSmallVideo)
+ currentSmallVideo.enableDominantSpeaker(true);
+ }
+ else {
+ $('#largeVideo').css({visibility: 'hidden'});
+ $('#activeSpeaker').css('visibility', 'hidden');
+ $('.watermark').css({visibility: 'hidden'});
+ if(currentSmallVideo)
+ currentSmallVideo.enableDominantSpeaker(false);
+ }
+ },
+ onVideoTypeChanged: function (jid) {
+ var resourceJid = Strophe.getResourceFromJid(jid);
+ if (LargeVideo.isCurrentlyOnLarge(resourceJid))
+ {
+ var isDesktop = APP.RTC.isVideoSrcDesktop(jid);
+ getVideoSize = isDesktop
+ ? getDesktopVideoSize
+ : getCameraVideoSize;
+ getVideoPosition = isDesktop
+ ? getDesktopVideoPosition
+ : getCameraVideoPosition;
+ this.position(null, null);
+ }
+ },
+ /**
+ * Positions the large video.
+ *
+ * @param videoWidth the stream video width
+ * @param videoHeight the stream video height
+ */
+ position: function (videoWidth, videoHeight,
+ videoSpaceWidth, videoSpaceHeight, animate) {
+ if(!videoSpaceWidth)
+ videoSpaceWidth = $('#videospace').width();
+ if(!videoSpaceHeight)
+ videoSpaceHeight = window.innerHeight;
+
+ var videoSize = getVideoSize(videoWidth,
+ videoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight);
+
+ var largeVideoWidth = videoSize[0];
+ var largeVideoHeight = videoSize[1];
+
+ var videoPosition = getVideoPosition(largeVideoWidth,
+ largeVideoHeight,
+ videoSpaceWidth,
+ videoSpaceHeight);
+
+ var horizontalIndent = videoPosition[0];
+ var verticalIndent = videoPosition[1];
+
+ positionVideo($('#largeVideo'),
+ largeVideoWidth,
+ largeVideoHeight,
+ horizontalIndent, verticalIndent, animate);
+ },
+
+ isLargeVideoOnTop: function () {
+ var Etherpad = require("../etherpad/Etherpad");
+ var Prezi = require("../prezi/Prezi");
+ return !Prezi.isPresentationVisible() && !Etherpad.isVisible();
+ },
+ resize: function (animate, isVisible, completeFunction) {
+ var availableHeight = window.innerHeight;
+ var availableWidth = UIUtil.getAvailableVideoWidth(isVisible);
+
+ if (availableWidth < 0 || availableHeight < 0) return;
+
+ var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE;
+ var top = availableHeight / 2 - avatarSize / 4 * 3;
+ $('#activeSpeaker').css('top', top);
+
+ if(animate)
+ {
+ $('#videospace').animate({
+ right: window.innerWidth - availableWidth,
+ width: availableWidth,
+ height: availableHeight
+ },
+ {
+ queue: false,
+ duration: 500,
+ complete: completeFunction
+ });
+
+ $('#largeVideoContainer').animate({
+ width: availableWidth,
+ height: availableHeight
+ },
+ {
+ queue: false,
+ duration: 500
+ });
+
+
+ }
+ else
+ {
+ $('#videospace').width(availableWidth);
+ $('#videospace').height(availableHeight);
+ $('#largeVideoContainer').width(availableWidth);
+ $('#largeVideoContainer').height(availableHeight);
+ }
+ return [availableWidth, availableHeight];
+
+ },
+ resizeVideoAreaAnimated: function (isVisible, completeFunction) {
+ var size = this.resize(true, isVisible, completeFunction);
+ this.position(null, null, size[0], size[1], true);
+ },
+ getResourceJid: function () {
+ return currentSmallVideo ? currentSmallVideo.getResourceJid() : null;
+ },
+ updateAvatar: function (resourceJid) {
+ if (resourceJid === this.getResourceJid()) {
+ updateActiveSpeakerAvatarSrc();
+ }
+ },
+ showAvatar: function (resourceJid, show) {
+ if(this.getResourceJid() === resourceJid
+ && LargeVideo.isLargeVideoOnTop())
+ {
+ $("#largeVideo").css("visibility", show ? "hidden" : "visible");
+ $('#activeSpeaker').css("visibility", show ? "visible" : "hidden");
+ return true;
+ }
+ return false;
+ }
+
+}
+
+
module.exports = LargeVideo;
-},{"../../../service/UI/UIEvents":104,"../../RTC/RTCBrowserType":10,"../../xmpp/xmpp":68,"../avatar/Avatar":18,"../etherpad/Etherpad":19,"../prezi/Prezi":20,"../util/UIUtil":35}],38:[function(require,module,exports){
-var SmallVideo = require("./SmallVideo");
-var ConnectionIndicator = require("./ConnectionIndicator");
-var NicknameHandler = require("../util/NicknameHandler");
-var UIUtil = require("../util/UIUtil");
-var LargeVideo = require("./LargeVideo");
-var RTCBrowserType = require("../../RTC/RTCBrowserType");
-
-function LocalVideo(VideoLayout)
-{
- this.videoSpanId = "localVideoContainer";
- this.container = $("#localVideoContainer").get(0);
- this.VideoLayout = VideoLayout;
- this.flipX = true;
- this.isLocal = true;
- this.peerJid = null;
-}
-
-LocalVideo.prototype = Object.create(SmallVideo.prototype);
-LocalVideo.prototype.constructor = LocalVideo;
-
-/**
- * Creates the edit display name button.
- *
- * @returns the edit button
- */
-function createEditDisplayNameButton() {
- var editButton = document.createElement('a');
- editButton.className = 'displayname';
- UIUtil.setTooltip(editButton,
- "videothumbnail.editnickname",
- "top");
- editButton.innerHTML = '';
-
- return editButton;
-}
-
-
-/**
- * Sets the display name for the given video span id.
- */
-LocalVideo.prototype.setDisplayName = function(displayName, key) {
-
- if (!this.container) {
- console.warn(
- "Unable to set displayName - " + this.videoSpanId + " does not exist");
- return;
- }
-
- var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
- var defaultLocalDisplayName = APP.translation.generateTranslationHTML(
- interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
-
- // If we already have a display name for this video.
- if (nameSpan.length > 0) {
-
- if (nameSpan.text() !== displayName) {
- if (displayName && displayName.length > 0)
- {
- var meHTML = APP.translation.generateTranslationHTML("me");
- $('#localDisplayName').html(displayName + ' (' + meHTML + ')');
- }
- else
- $('#localDisplayName').html(defaultLocalDisplayName);
- }
- } else {
- var editButton = createEditDisplayNameButton();
-
- nameSpan = document.createElement('span');
- nameSpan.className = 'displayname';
- $('#' + this.videoSpanId)[0].appendChild(nameSpan);
-
-
- if (displayName && displayName.length > 0) {
- var meHTML = APP.translation.generateTranslationHTML("me");
- nameSpan.innerHTML = displayName + meHTML;
- }
- else {
- nameSpan.innerHTML = defaultLocalDisplayName;
- }
-
-
- nameSpan.id = 'localDisplayName';
- this.container.appendChild(editButton);
- //translates popover of edit button
- APP.translation.translateElement($("a.displayname"));
-
- var editableText = document.createElement('input');
- editableText.className = 'displayname';
- editableText.type = 'text';
- editableText.id = 'editDisplayName';
-
- if (displayName && displayName.length) {
- editableText.value = displayName;
- }
-
- var defaultNickname = APP.translation.translateString(
- "defaultNickname", {name: "Jane Pink"});
- editableText.setAttribute('style', 'display:none;');
- editableText.setAttribute('data-18n',
- '[placeholder]defaultNickname');
- editableText.setAttribute("data-i18n-options",
- JSON.stringify({name: "Jane Pink"}));
- editableText.setAttribute("placeholder", defaultNickname);
-
- this.container.appendChild(editableText);
-
- var self = this;
- $('#localVideoContainer .displayname')
- .bind("click", function (e) {
-
- e.preventDefault();
- e.stopPropagation();
- $('#localDisplayName').hide();
- $('#editDisplayName').show();
- $('#editDisplayName').focus();
- $('#editDisplayName').select();
-
- $('#editDisplayName').one("focusout", function (e) {
- self.VideoLayout.inputDisplayNameHandler(this.value);
- });
-
- $('#editDisplayName').on('keydown', function (e) {
- if (e.keyCode === 13) {
- e.preventDefault();
- self.VideoLayout.inputDisplayNameHandler(this.value);
- }
- });
- });
- }
-}
-
-LocalVideo.prototype.inputDisplayNameHandler = function (name) {
- NicknameHandler.setNickname(name);
-
- if (!$('#localDisplayName').is(":visible")) {
- if (NicknameHandler.getNickname())
- {
- var meHTML = APP.translation.generateTranslationHTML("me");
- $('#localDisplayName').html(NicknameHandler.getNickname() + " (" + meHTML + ")");
- }
- else
- {
- var defaultHTML = APP.translation.generateTranslationHTML(
- interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
- $('#localDisplayName')
- .html(defaultHTML);
- }
- $('#localDisplayName').show();
- }
-
- $('#editDisplayName').hide();
-}
-
-LocalVideo.prototype.createConnectionIndicator = function()
-{
- if(this.connectionIndicator)
- return;
-
- this.connectionIndicator
- = new ConnectionIndicator(this, null);
-}
-
-LocalVideo.prototype.changeVideo = function (stream, isMuted) {
- var self = this;
-
- function localVideoClick(event) {
- // FIXME: with Temasys plugin event arg is not an event, but
- // the clicked object itself, so we have to skip this call
- if (event.stopPropagation) {
- event.stopPropagation();
- }
- self.VideoLayout.handleVideoThumbClicked(
- false,
- APP.xmpp.myResource());
- }
-
- $('#localVideoContainer').off('click');
- $('#localVideoContainer').on('click', localVideoClick);
-
- // Add hover handler
- $('#localVideoContainer').hover(
- function() {
- self.showDisplayName(true);
- },
- function() {
- if (!LargeVideo.isLargeVideoVisible() ||
- !LargeVideo.isCurrentlyOnLarge(self.getResourceJid()))
- self.showDisplayName(false);
- }
- );
-
- if(isMuted)
- {
- APP.UI.setVideoMute(true);
- return;
- }
- this.flipX = (stream.videoType == "screen")? false : true;
- var localVideo = document.createElement('video');
- localVideo.id = 'localVideo_' +
- APP.RTC.getStreamID(stream.getOriginalStream());
- if (!RTCBrowserType.isIExplorer()) {
- localVideo.autoplay = true;
- localVideo.volume = 0; // is it required if audio is separated ?
- }
- localVideo.oncontextmenu = function () { return false; };
-
- var localVideoContainer = document.getElementById('localVideoWrapper');
- localVideoContainer.appendChild(localVideo);
-
- var localVideoSelector = $('#' + localVideo.id);
-
- // Add click handler to both video and video wrapper elements in case
- // there's no video.
-
- // onclick has to be used with Temasys plugin
- localVideo.onclick = localVideoClick;
-
- if (this.flipX) {
- localVideoSelector.addClass("flipVideoX");
- }
-
- // Attach WebRTC stream
- APP.RTC.attachMediaStream(localVideoSelector, stream.getOriginalStream());
-
- // Add stream ended handler
- stream.getOriginalStream().onended = function () {
- // We have to re-select after attach when Temasys plugin is used,
- // because