2016-05-01 18:35:18 +00:00
|
|
|
/* global $, config, interfaceConfig, APP, JitsiMeetJS */
|
2015-12-14 12:26:50 +00:00
|
|
|
import ConnectionIndicator from "./ConnectionIndicator";
|
|
|
|
import UIUtil from "../util/UIUtil";
|
|
|
|
import UIEvents from "../../../service/UI/UIEvents";
|
|
|
|
import SmallVideo from "./SmallVideo";
|
|
|
|
|
2016-02-02 21:50:02 +00:00
|
|
|
const RTCUIUtils = JitsiMeetJS.util.RTCUIHelper;
|
2016-01-06 22:39:13 +00:00
|
|
|
const TrackEvents = JitsiMeetJS.events.track;
|
|
|
|
|
2015-12-01 12:53:01 +00:00
|
|
|
function LocalVideo(VideoLayout, emitter) {
|
2015-06-23 08:00:46 +00:00
|
|
|
this.videoSpanId = "localVideoContainer";
|
|
|
|
this.container = $("#localVideoContainer").get(0);
|
2016-05-07 01:50:37 +00:00
|
|
|
this.localVideoId = null;
|
2015-08-21 14:37:57 +00:00
|
|
|
this.bindHoverHandler();
|
2016-05-09 17:39:42 +00:00
|
|
|
if(config.enableLocalVideoFlip)
|
|
|
|
this._buildContextMenu();
|
2015-07-15 10:14:34 +00:00
|
|
|
this.isLocal = true;
|
2015-12-01 12:53:01 +00:00
|
|
|
this.emitter = emitter;
|
2016-02-09 10:19:43 +00:00
|
|
|
Object.defineProperty(this, 'id', {
|
|
|
|
get: function () {
|
|
|
|
return APP.conference.localId;
|
|
|
|
}
|
|
|
|
});
|
2016-03-15 20:42:53 +00:00
|
|
|
SmallVideo.call(this, VideoLayout);
|
2015-06-23 08:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
|
|
|
LocalVideo.prototype.constructor = LocalVideo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates the edit display name button.
|
|
|
|
*
|
2015-07-28 21:52:32 +00:00
|
|
|
* @returns {object} the edit button
|
2015-06-23 08:00:46 +00:00
|
|
|
*/
|
|
|
|
function createEditDisplayNameButton() {
|
|
|
|
var editButton = document.createElement('a');
|
|
|
|
editButton.className = 'displayname';
|
|
|
|
UIUtil.setTooltip(editButton,
|
|
|
|
"videothumbnail.editnickname",
|
|
|
|
"top");
|
|
|
|
editButton.innerHTML = '<i class="fa fa-pencil"></i>';
|
|
|
|
|
|
|
|
return editButton;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the display name for the given video span id.
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype.setDisplayName = function(displayName, key) {
|
|
|
|
if (!this.container) {
|
|
|
|
console.warn(
|
2015-07-28 21:52:32 +00:00
|
|
|
"Unable to set displayName - " + this.videoSpanId +
|
|
|
|
" does not exist");
|
2015-06-23 08:00:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var nameSpan = $('#' + this.videoSpanId + '>span.displayname');
|
2015-07-03 09:34:05 +00:00
|
|
|
var defaultLocalDisplayName = APP.translation.generateTranslationHTML(
|
2015-06-23 08:00:46 +00:00
|
|
|
interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME);
|
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
var meHTML;
|
2015-06-23 08:00:46 +00:00
|
|
|
// If we already have a display name for this video.
|
|
|
|
if (nameSpan.length > 0) {
|
|
|
|
if (nameSpan.text() !== displayName) {
|
2015-07-28 21:52:32 +00:00
|
|
|
if (displayName && displayName.length > 0) {
|
|
|
|
meHTML = APP.translation.generateTranslationHTML("me");
|
2016-02-12 12:48:57 +00:00
|
|
|
$('#localDisplayName').html(
|
|
|
|
UIUtil.escapeHtml(displayName) + ' (' + meHTML + ')'
|
|
|
|
);
|
2015-07-28 21:52:32 +00:00
|
|
|
} else {
|
2015-06-23 08:00:46 +00:00
|
|
|
$('#localDisplayName').html(defaultLocalDisplayName);
|
2015-07-28 21:52:32 +00:00
|
|
|
}
|
2015-06-23 08:00:46 +00:00
|
|
|
}
|
2016-01-14 22:21:03 +00:00
|
|
|
this.updateView();
|
2015-06-23 08:00:46 +00:00
|
|
|
} else {
|
|
|
|
var editButton = createEditDisplayNameButton();
|
|
|
|
|
|
|
|
nameSpan = document.createElement('span');
|
|
|
|
nameSpan.className = 'displayname';
|
|
|
|
$('#' + this.videoSpanId)[0].appendChild(nameSpan);
|
|
|
|
|
|
|
|
|
|
|
|
if (displayName && displayName.length > 0) {
|
2015-07-28 21:52:32 +00:00
|
|
|
meHTML = APP.translation.generateTranslationHTML("me");
|
2016-02-12 12:48:57 +00:00
|
|
|
nameSpan.innerHTML = UIUtil.escapeHtml(displayName) + meHTML;
|
2015-06-23 08:00:46 +00:00
|
|
|
}
|
|
|
|
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) {
|
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
var editDisplayName = $('#editDisplayName');
|
2015-06-23 08:00:46 +00:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
$('#localDisplayName').hide();
|
2015-07-28 21:52:32 +00:00
|
|
|
editDisplayName.show();
|
|
|
|
editDisplayName.focus();
|
|
|
|
editDisplayName.select();
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
editDisplayName.one("focusout", function (e) {
|
2016-02-12 12:48:57 +00:00
|
|
|
self.emitter.emit(UIEvents.NICKNAME_CHANGED, this.value);
|
2015-12-01 12:53:01 +00:00
|
|
|
$('#editDisplayName').hide();
|
2015-06-23 08:00:46 +00:00
|
|
|
});
|
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
editDisplayName.on('keydown', function (e) {
|
2015-06-23 08:00:46 +00:00
|
|
|
if (e.keyCode === 13) {
|
|
|
|
e.preventDefault();
|
2015-12-01 12:53:01 +00:00
|
|
|
$('#editDisplayName').hide();
|
2015-12-18 13:59:38 +00:00
|
|
|
// focusout handler will save display name
|
2015-06-23 08:00:46 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2015-07-28 21:52:32 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
LocalVideo.prototype.createConnectionIndicator = function() {
|
2015-06-23 08:00:46 +00:00
|
|
|
if(this.connectionIndicator)
|
|
|
|
return;
|
|
|
|
|
2015-07-28 21:52:32 +00:00
|
|
|
this.connectionIndicator = new ConnectionIndicator(this, null);
|
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
LocalVideo.prototype.changeVideo = function (stream) {
|
2016-01-29 00:33:27 +00:00
|
|
|
this.videoStream = stream;
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
let localVideoClick = (event) => {
|
2015-07-10 09:57:20 +00:00
|
|
|
// 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();
|
|
|
|
}
|
2016-03-24 01:43:29 +00:00
|
|
|
this.VideoLayout.handleVideoThumbClicked(this.id);
|
2015-12-14 12:26:50 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
let localVideoContainerSelector = $('#localVideoContainer');
|
2015-07-28 21:52:32 +00:00
|
|
|
localVideoContainerSelector.off('click');
|
|
|
|
localVideoContainerSelector.on('click', localVideoClick);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
let localVideo = document.createElement('video');
|
2016-05-07 01:50:37 +00:00
|
|
|
localVideo.id = this.localVideoId = 'localVideo_' + stream.getId();
|
2016-02-02 21:50:02 +00:00
|
|
|
|
|
|
|
RTCUIUtils.setAutoPlay(localVideo, true);
|
|
|
|
RTCUIUtils.setVolume(localVideo, 0);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
|
|
|
var localVideoContainer = document.getElementById('localVideoWrapper');
|
2015-08-12 10:13:30 +00:00
|
|
|
// Put the new video always in front
|
|
|
|
UIUtil.prependChild(localVideoContainer, localVideo);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
|
|
|
// Add click handler to both video and video wrapper elements in case
|
|
|
|
// there's no video.
|
2015-07-10 09:57:20 +00:00
|
|
|
|
|
|
|
// onclick has to be used with Temasys plugin
|
|
|
|
localVideo.onclick = localVideoClick;
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2016-05-07 01:50:37 +00:00
|
|
|
let isVideo = stream.videoType != "desktop";
|
|
|
|
this._enableDisableContextMenu(isVideo);
|
|
|
|
this.setFlipX(isVideo? APP.settings.getLocalFlipX() : false);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
|
|
|
// Attach WebRTC stream
|
2016-02-01 21:00:51 +00:00
|
|
|
localVideo = stream.attach(localVideo);
|
2015-12-14 12:26:50 +00:00
|
|
|
|
2016-01-06 22:39:13 +00:00
|
|
|
let endedHandler = () => {
|
2015-06-23 08:00:46 +00:00
|
|
|
localVideoContainer.removeChild(localVideo);
|
2016-03-23 22:45:27 +00:00
|
|
|
// when removing only the video element and we are on stage
|
|
|
|
// update the stage
|
|
|
|
if(this.VideoLayout.isCurrentlyOnLarge(this.id))
|
|
|
|
this.VideoLayout.updateLargeVideo(this.id);
|
2016-03-03 12:53:36 +00:00
|
|
|
stream.off(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
|
2016-01-06 22:39:13 +00:00
|
|
|
};
|
2016-03-03 12:53:36 +00:00
|
|
|
stream.on(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
|
2015-07-20 17:32:04 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2016-05-01 18:35:18 +00:00
|
|
|
/**
|
|
|
|
* Shows or hides the local video container.
|
|
|
|
* @param {boolean} true to make the local video container visible, false
|
|
|
|
* otherwise
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype.setVisible = function(visible) {
|
|
|
|
|
|
|
|
// We toggle the hidden class as an indication to other interested parties
|
|
|
|
// that this container has been hidden on purpose.
|
|
|
|
$("#localVideoContainer").toggleClass("hidden");
|
|
|
|
|
|
|
|
// We still show/hide it as we need to overwrite the style property if we
|
|
|
|
// want our action to take effect. Toggling the display property through
|
|
|
|
// the above css class didn't succeed in overwriting the style.
|
|
|
|
if (visible) {
|
|
|
|
$("#localVideoContainer").show();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
$("#localVideoContainer").hide();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-07 01:50:37 +00:00
|
|
|
/**
|
|
|
|
* Sets the flipX state of the video.
|
|
|
|
* @param val {boolean} true for flipped otherwise false;
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype.setFlipX = function (val) {
|
|
|
|
this.emitter.emit(UIEvents.LOCAL_FLIPX_CHANGED, val);
|
|
|
|
if(!this.localVideoId)
|
|
|
|
return;
|
|
|
|
if(val) {
|
|
|
|
this.selectVideoElement().addClass("flipVideoX");
|
|
|
|
} else {
|
|
|
|
this.selectVideoElement().removeClass("flipVideoX");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu for the local video.
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype._buildContextMenu = function () {
|
|
|
|
$.contextMenu({
|
|
|
|
selector: '#' + this.videoSpanId,
|
|
|
|
zIndex: 10000,
|
|
|
|
items: {
|
|
|
|
flip: {
|
|
|
|
name: "Flip",
|
|
|
|
callback: () => {
|
|
|
|
let val = !APP.settings.getLocalFlipX();
|
|
|
|
this.setFlipX(val);
|
|
|
|
APP.settings.setLocalFlipX(val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
events: {
|
|
|
|
show : function(options){
|
|
|
|
options.items.flip.name =
|
|
|
|
APP.translation.translateString("videothumbnail.flip");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enables or disables the context menu for the local video.
|
|
|
|
* @param enable {boolean} true for enable, false for disable
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype._enableDisableContextMenu = function (enable) {
|
|
|
|
if($('#' + this.videoSpanId).contextMenu)
|
|
|
|
$('#' + this.videoSpanId).contextMenu(enable);
|
|
|
|
};
|
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
export default LocalVideo;
|