diff --git a/app.js b/app.js
index 964aecde1..afd786b38 100644
--- a/app.js
+++ b/app.js
@@ -11,6 +11,8 @@ var recordingToken ='';
var roomUrl = null;
var roomName = null;
var ssrc2jid = {};
+var mediaStreams = [];
+
/**
* The stats collector that process stats data and triggers updates to app.js.
* @type {StatsCollector}
@@ -231,40 +233,41 @@ function doJoin() {
connection.emuc.doJoin(roomjid);
}
-$(document).bind('remotestreamadded.jingle', function (event, data, sid) {
- function waitForRemoteVideo(selector, sid, ssrc) {
- if (selector.removed) {
- console.warn("media removed before had started", selector);
- return;
- }
- var sess = connection.jingle.sessions[sid];
- if (data.stream.id === 'mixedmslabel') return;
- var videoTracks = data.stream.getVideoTracks();
-// console.log("waiting..", videoTracks, selector[0]);
-
- if (videoTracks.length === 0 || selector[0].currentTime > 0) {
- RTC.attachMediaStream(selector, data.stream); // FIXME: why do i have to do this for FF?
-
- // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
- // in order to get rid of too many maps
- if (ssrc) {
- videoSrcToSsrc[sel.attr('src')] = ssrc;
- } else {
- console.warn("No ssrc given for video", sel);
- }
-
- $(document).trigger('callactive.jingle', [selector, sid]);
- console.log('waitForremotevideo', sess.peerconnection.iceConnectionState, sess.peerconnection.signalingState);
- } else {
- setTimeout(function () { waitForRemoteVideo(selector, sid, ssrc); }, 250);
- }
+function waitForRemoteVideo(selector, ssrc, stream) {
+ if (selector.removed || !selector.parent().is(":visible")) {
+ console.warn("Media removed before had started", selector);
+ return;
}
+
+ if (stream.id === 'mixedmslabel') return;
+
+ if (selector[0].currentTime > 0) {
+ RTC.attachMediaStream(selector, stream); // FIXME: why do i have to do this for FF?
+
+ // FIXME: add a class that will associate peer Jid, video.src, it's ssrc and video type
+ // in order to get rid of too many maps
+ if (ssrc && selector.attr('src')) {
+ videoSrcToSsrc[selector.attr('src')] = ssrc;
+ } else {
+ console.warn("No ssrc given for video", selector);
+ }
+
+ $(document).trigger('videoactive.jingle', [selector]);
+ } else {
+ setTimeout(function () {
+ waitForRemoteVideo(selector, ssrc, stream);
+ }, 250);
+ }
+}
+
+$(document).bind('remotestreamadded.jingle', function (event, data, sid) {
var sess = connection.jingle.sessions[sid];
var thessrc;
// look up an associated JID for a stream id
if (data.stream.id.indexOf('mixedmslabel') === -1) {
- var ssrclines = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc');
+ var ssrclines
+ = SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc');
ssrclines = ssrclines.filter(function (line) {
return line.indexOf('mslabel:' + data.stream.label) !== -1;
});
@@ -278,11 +281,14 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
}
}
+ mediaStreams.push(new MediaStream(data, sid, thessrc));
+
var container;
var remotes = document.getElementById('remoteVideos');
if (data.peerjid) {
VideoLayout.ensurePeerContainerExists(data.peerjid);
+
container = document.getElementById(
'participant_' + Strophe.getResourceFromJid(data.peerjid));
} else {
@@ -295,91 +301,22 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
}
// FIXME: for the mixed ms we dont need a video -- currently
container = document.createElement('span');
+ container.id = 'mixedstream';
container.className = 'videocontainer';
remotes.appendChild(container);
Util.playSoundNotification('userJoined');
}
var isVideo = data.stream.getVideoTracks().length > 0;
- var vid = isVideo ? document.createElement('video') : document.createElement('audio');
- var id = (isVideo ? 'remoteVideo_' : 'remoteAudio_') + sid + '_' + data.stream.id;
- vid.id = id;
- vid.autoplay = true;
- vid.oncontextmenu = function () { return false; };
-
- container.appendChild(vid);
-
- // TODO: make mixedstream display:none via css?
- if (id.indexOf('mixedmslabel') !== -1) {
- container.id = 'mixedstream';
- $(container).hide();
+ if (container) {
+ VideoLayout.addRemoteStreamElement( container,
+ sid,
+ data.stream,
+ data.peerjid,
+ thessrc);
}
- var sel = $('#' + id);
- sel.hide();
- RTC.attachMediaStream(sel, data.stream);
-
- if (isVideo) {
- waitForRemoteVideo(sel, sid, thessrc);
- }
-
- data.stream.onended = function () {
- console.log('stream ended', this.id);
-
- // Mark video as removed to cancel waiting loop(if video is removed
- // before has started)
- sel.removed = true;
- sel.remove();
-
- var audioCount = $('#' + container.id + '>audio').length;
- var videoCount = $('#' + container.id + '>video').length;
- if (!audioCount && !videoCount) {
- console.log("Remove whole user", container.id);
- // Remove whole container
- container.remove();
- Util.playSoundNotification('userLeft');
- VideoLayout.resizeThumbnails();
- }
-
- VideoLayout.checkChangeLargeVideo(vid.src);
- };
-
- // Add click handler.
- container.onclick = function (event) {
- /*
- * FIXME It turns out that videoThumb may not exist (if there is no
- * actual video).
- */
- var videoThumb = $('#' + container.id + '>video').get(0);
-
- if (videoThumb)
- VideoLayout.handleVideoThumbClicked(videoThumb.src);
-
- event.preventDefault();
- return false;
- };
-
- // Add hover handler
- $(container).hover(
- function() {
- VideoLayout.showDisplayName(container.id, true);
- },
- function() {
- var videoSrc = null;
- if ($('#' + container.id + '>video')
- && $('#' + container.id + '>video').length > 0) {
- videoSrc = $('#' + container.id + '>video').get(0).src;
- }
-
- // If the video has been "pinned" by the user we want to keep the
- // display name on place.
- if (!VideoLayout.isLargeVideoVisible()
- || videoSrc !== $('#largeVideo').attr('src'))
- VideoLayout.showDisplayName(container.id, false);
- }
- );
-
// an attempt to work around https://github.com/jitsi/jitmeet/issues/32
if (isVideo &&
data.peerjid && sess.peerjid === data.peerjid &&
@@ -587,21 +524,6 @@ $(document).bind('conferenceCreated.jingle', function (event, focus)
}
});
-$(document).bind('callactive.jingle', function (event, videoelem, sid) {
- if (videoelem.attr('id').indexOf('mixedmslabel') === -1) {
- // ignore mixedmslabela0 and v0
- videoelem.show();
- VideoLayout.resizeThumbnails();
-
- // Update the large video to the last added video only if there's no
- // current active or focused speaker.
- if (!focusedVideoSrc && !VideoLayout.getDominantSpeakerResourceJid())
- VideoLayout.updateLargeVideo(videoelem.attr('src'), 1);
-
- VideoLayout.showFocusIndicator();
- }
-});
-
$(document).bind('callterminated.jingle', function (event, sid, jid, reason) {
// Leave the room if my call has been remotely terminated.
if (connection.emuc.joined && focus == null && reason === 'kick') {
@@ -680,14 +602,20 @@ $(document).bind('joined.muc', function (event, jid, info) {
VideoLayout.showFocusIndicator();
+ // Add myself to the contact list.
+ ContactList.addContact(jid);
+
// Once we've joined the muc show the toolbar
Toolbar.showToolbar();
var displayName = '';
if (info.displayName)
displayName = info.displayName + ' (me)';
+ else
+ displayName = "Me";
- VideoLayout.setDisplayName('localVideoContainer', displayName);
+ $(document).trigger('displaynamechanged',
+ ['localVideoContainer', displayName]);
});
$(document).bind('entered.muc', function (event, jid, info, pres) {
@@ -811,21 +739,15 @@ $(document).bind('presence.muc', function (event, jid, info, pres) {
case 'recvonly':
el.hide();
// FIXME: Check if we have to change large video
- //VideoLayout.checkChangeLargeVideo(el);
+ //VideoLayout.updateLargeVideo(el);
break;
}
}
});
- if (jid === connection.emuc.myroomjid) {
- VideoLayout.setDisplayName('localVideoContainer',
- info.displayName);
- } else {
- VideoLayout.ensurePeerContainerExists(jid);
- VideoLayout.setDisplayName(
- 'participant_' + Strophe.getResourceFromJid(jid),
- info.displayName);
- }
+ if (info.displayName && info.displayName.length > 0)
+ $(document).trigger('displaynamechanged',
+ [jid, info.displayName]);
if (focus !== null && info.displayName !== null) {
focus.setEndpointDisplayName(jid, info.displayName);
@@ -1370,7 +1292,27 @@ function setView(viewName) {
// }
}
-
+function hangUp() {
+ if (connection && connection.connected) {
+ // ensure signout
+ $.ajax({
+ type: 'POST',
+ url: config.bosh,
+ async: false,
+ cache: false,
+ contentType: 'application/xml',
+ data: "
",
+ success: function (data) {
+ console.log('signed out');
+ console.log(data);
+ },
+ error: function (XMLHttpRequest, textStatus, errorThrown) {
+ console.log('signout error', textStatus + ' (' + errorThrown + ')');
+ }
+ });
+ }
+ disposeConference(true);
+}
$(document).bind('fatalError.jingle',
function (event, session, error)
diff --git a/bottom_toolbar.js b/bottom_toolbar.js
new file mode 100644
index 000000000..f8bfd4642
--- /dev/null
+++ b/bottom_toolbar.js
@@ -0,0 +1,32 @@
+var BottomToolbar = (function (my) {
+ my.toggleChat = function() {
+ if (ContactList.isVisible()) {
+ buttonClick("#contactListButton", "active");
+ ContactList.toggleContactList();
+ }
+
+ buttonClick("#chatBottomButton", "active");
+
+ Chat.toggleChat();
+ };
+
+ my.toggleContactList = function() {
+ if (Chat.isVisible()) {
+ buttonClick("#chatBottomButton", "active");
+ Chat.toggleChat();
+ }
+
+ buttonClick("#contactListButton", "active");
+
+ ContactList.toggleContactList();
+ };
+
+
+ $(document).bind("remotevideo.resized", function (event, width, height) {
+ var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18;
+
+ $('#bottomToolbar').css({bottom: bottom + 'px'});
+ });
+
+ return my;
+}(BottomToolbar || {}));
diff --git a/chat.js b/chat.js
index 8db249b7e..505c8a47f 100644
--- a/chat.js
+++ b/chat.js
@@ -80,7 +80,7 @@ var Chat = (function (my) {
else {
divClassName = "remoteuser";
- if (!$('#chatspace').is(":visible")) {
+ if (!Chat.isVisible()) {
unreadMessages++;
Util.playSoundNotification('chatNotification');
setVisualNotification(true);
@@ -301,6 +301,13 @@ var Chat = (function (my) {
return [chatWidth, availableHeight];
};
+ /**
+ * Indicates if the chat is currently visible.
+ */
+ my.isVisible = function () {
+ return $('#chatspace').is(":visible");
+ };
+
/**
* Resizes the chat conversation.
*/
diff --git a/config.js b/config.js
index 9d967488f..920e056aa 100644
--- a/config.js
+++ b/config.js
@@ -16,7 +16,7 @@ var config = {
minChromeExtVersion: '0.1', // Required version of Chrome extension
enableRtpStats: true, // Enables RTP stats processing
openSctp: true, // Toggle to enable/disable SCTP channels
-// channelLastN: -1, // The default value of the channel attribute last-n.
+ channelLastN: -1, // The default value of the channel attribute last-n.
// useRtcpMux: true,
// useBundle: true,
enableRecording: false,
diff --git a/contact_list.js b/contact_list.js
new file mode 100644
index 000000000..cdbdc3e47
--- /dev/null
+++ b/contact_list.js
@@ -0,0 +1,235 @@
+/**
+ * Contact list.
+ */
+var ContactList = (function (my) {
+ /**
+ * Indicates if the chat is currently visible.
+ *
+ * @return true if the chat is currently visible, false -
+ * otherwise
+ */
+ my.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
+ */
+ my.ensureAddContact = function(peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contact = $('#contactlist>ul>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
+ */
+ my.addContact = function(peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactlist = $('#contactlist>ul');
+
+ var newContact = document.createElement('li');
+ newContact.id = resourceJid;
+
+ newContact.appendChild(createAvatar());
+ newContact.appendChild(createDisplayNameParagraph("Participant"));
+
+ var clElement = contactlist.get(0);
+
+ if (resourceJid === Strophe.getResourceFromJid(connection.emuc.myroomjid)
+ && $('#contactlist>ul .title')[0].nextSibling.nextSibling)
+ {
+ clElement.insertBefore(newContact,
+ $('#contactlist>ul .title')[0].nextSibling.nextSibling);
+ }
+ else {
+ clElement.appendChild(newContact);
+ }
+ };
+
+ /**
+ * Removes a contact for the given peer jid.
+ *
+ * @param peerJid the peerJid corresponding to the contact to remove
+ */
+ my.removeContact = function(peerJid) {
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contact = $('#contactlist>ul>li[id="' + resourceJid + '"]');
+
+ if (contact && contact.length > 0) {
+ var contactlist = $('#contactlist>ul');
+
+ contactlist.get(0).removeChild(contact.get(0));
+ }
+ };
+
+ /**
+ * Opens / closes the contact list area.
+ */
+ my.toggleContactList = function () {
+ var contactlist = $('#contactlist');
+ var videospace = $('#videospace');
+
+ var chatSize = (ContactList.isVisible()) ? [0, 0] : Chat.getChatSize();
+ var videospaceWidth = window.innerWidth - chatSize[0];
+ var videospaceHeight = window.innerHeight;
+ var videoSize
+ = getVideoSize(null, null, videospaceWidth, videospaceHeight);
+ var videoWidth = videoSize[0];
+ var videoHeight = videoSize[1];
+ var videoPosition = getVideoPosition(videoWidth,
+ videoHeight,
+ videospaceWidth,
+ videospaceHeight);
+ var horizontalIndent = videoPosition[0];
+ var verticalIndent = videoPosition[1];
+
+ var thumbnailSize = VideoLayout.calculateThumbnailSize(videospaceWidth);
+ var thumbnailsWidth = thumbnailSize[0];
+ var thumbnailsHeight = thumbnailSize[1];
+
+ if (ContactList.isVisible()) {
+ videospace.animate({right: chatSize[0],
+ width: videospaceWidth,
+ height: videospaceHeight},
+ {queue: false,
+ duration: 500});
+
+ $('#remoteVideos').animate({height: thumbnailsHeight},
+ {queue: false,
+ duration: 500});
+
+ $('#remoteVideos>span').animate({height: thumbnailsHeight,
+ width: thumbnailsWidth},
+ {queue: false,
+ duration: 500,
+ complete: function() {
+ $(document).trigger(
+ "remotevideo.resized",
+ [thumbnailsWidth,
+ thumbnailsHeight]);
+ }});
+
+ $('#largeVideoContainer').animate({ width: videospaceWidth,
+ height: videospaceHeight},
+ {queue: false,
+ duration: 500
+ });
+
+ $('#largeVideo').animate({ width: videoWidth,
+ height: videoHeight,
+ top: verticalIndent,
+ bottom: verticalIndent,
+ left: horizontalIndent,
+ right: horizontalIndent},
+ { queue: false,
+ duration: 500
+ });
+
+ $('#contactlist').hide("slide", { direction: "right",
+ queue: false,
+ duration: 500});
+ }
+ else {
+ // Undock the toolbar when the chat is shown and if we're in a
+ // video mode.
+ if (VideoLayout.isLargeVideoVisible())
+ Toolbar.dockToolbar(false);
+
+ videospace.animate({right: chatSize[0],
+ width: videospaceWidth,
+ height: videospaceHeight},
+ {queue: false,
+ duration: 500,
+ complete: function () {
+ contactlist.trigger('shown');
+ }
+ });
+
+ $('#remoteVideos').animate({height: thumbnailsHeight},
+ {queue: false,
+ duration: 500});
+
+ $('#remoteVideos>span').animate({height: thumbnailsHeight,
+ width: thumbnailsWidth},
+ {queue: false,
+ duration: 500,
+ complete: function() {
+ $(document).trigger(
+ "remotevideo.resized",
+ [thumbnailsWidth, thumbnailsHeight]);
+ }});
+
+ $('#largeVideoContainer').animate({ width: videospaceWidth,
+ height: videospaceHeight},
+ {queue: false,
+ duration: 500
+ });
+
+ $('#largeVideo').animate({ width: videoWidth,
+ height: videoHeight,
+ top: verticalIndent,
+ bottom: verticalIndent,
+ left: horizontalIndent,
+ right: horizontalIndent},
+ {queue: false,
+ duration: 500
+ });
+
+ $('#contactlist').show("slide", { direction: "right",
+ queue: false,
+ duration: 500});
+ }
+ };
+
+ /**
+ * Creates the avatar element.
+ *
+ * @return the newly created avatar element
+ */
+ function createAvatar() {
+ var avatar = document.createElement('i');
+ avatar.className = "icon-avatar avatar";
+
+ return avatar;
+ };
+
+ /**
+ * Creates the display name paragraph.
+ *
+ * @param displayName the display name to set
+ */
+ function createDisplayNameParagraph(displayName) {
+ var p = document.createElement('p');
+ p.innerHTML = displayName;
+
+ return p;
+ };
+
+ /**
+ * Indicates that the display name has changed.
+ */
+ $(document).bind( 'displaynamechanged',
+ function (event, peerJid, displayName) {
+ if (peerJid === 'localVideoContainer')
+ peerJid = connection.emuc.myroomjid;
+
+ var resourceJid = Strophe.getResourceFromJid(peerJid);
+
+ var contactName = $('#contactlist #' + resourceJid + '>p');
+
+ if (contactName && displayName && displayName.length > 0)
+ contactName.html(displayName);
+ });
+
+ return my;
+}(ContactList || {}));
diff --git a/css/contact_list.css b/css/contact_list.css
new file mode 100644
index 000000000..bb7823ee1
--- /dev/null
+++ b/css/contact_list.css
@@ -0,0 +1,35 @@
+#contactlist {
+ background-color:rgba(0,0,0,.65);
+}
+
+#contactlist>ul {
+ margin: 0px;
+ padding: 0px;
+}
+
+#contactlist>ul>li {
+ list-style-type: none;
+ text-align: left;
+ color: #FFF;
+ font-size: 10pt;
+ padding: 8px 10px;
+}
+
+#contactlist>ul>li>p {
+ display: inline-block;
+ vertical-align: middle;
+ margin: 0px;
+}
+
+#contactlist>ul>li.title {
+ color: #00ccff;
+ font-size: 11pt;
+ border-bottom: 1px solid #676767;
+}
+
+.avatar {
+ padding: 0px;
+ margin-right: 10px;
+ vertical-align: middle;
+ font-size: 22pt;
+}
\ No newline at end of file
diff --git a/css/font.css b/css/font.css
index b9ed14b00..89587c18f 100755
--- a/css/font.css
+++ b/css/font.css
@@ -23,6 +23,12 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
+.icon-contactList:before {
+ content: "\e615";
+}
+.icon-avatar:before {
+ content: "\e616";
+}
.icon-callRetro:before {
content: "\e611";
}
diff --git a/css/main.css b/css/main.css
index 3c9400ecc..d70f989e4 100644
--- a/css/main.css
+++ b/css/main.css
@@ -8,7 +8,8 @@ html, body{
overflow-x: hidden;
}
-#chatspace {
+#chatspace,
+#contactlist {
display:none;
position:absolute;
float: right;
@@ -18,12 +19,14 @@ html, body{
width: 20%;
max-width: 200px;
overflow: hidden;
- /* background-color:#dfebf1;*/
- background-color:#FFFFFF;
- border-left:1px solid #424242;
z-index: 5;
}
+#chatspace {
+ background-color:#FFF;
+ border-left:1px solid #424242;
+}
+
#chatconversation {
visibility: hidden;
position: relative;
@@ -172,7 +175,8 @@ html, body{
0 -1px 10px #00ccff;
}
-a.button:hover {
+a.button:hover,
+a.bottomToolbarButton:hover {
top: 0;
cursor: pointer;
background: rgba(0, 0, 0, 0.3);
@@ -408,4 +412,46 @@ form {
font-weight: 200;
}
+#bottomToolbar {
+ display:block;
+ position: absolute;
+ right: -1;
+ bottom: 40px;
+ width: 29px;
+ border-top-left-radius: 10px;
+ border-bottom-left-radius: 10px;
+ color: #FFF;
+ border: 1px solid #000;
+ background: rgba(50,50,50,.65);
+ padding-top: 5px;
+ padding-bottom: 5px;
+ z-index: 6; /*+1 from #remoteVideos*/
+}
+.bottomToolbarButton {
+ display: inline-block;
+ position: relative;
+ color: #FFFFFF;
+ top: 0;
+ padding-top: 3px;
+ width: 29px;
+ height: 20px;
+ cursor: pointer;
+ font-size: 10pt;
+ text-align: center;
+ text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
+ z-index: 1;
+}
+
+.active {
+ color: #00ccff;
+}
+
+.bottomToolbar_span>span {
+ display: inline-block;
+ position: absolute;
+ font-size: 7pt;
+ color: #ffffff;
+ text-align:center;
+ cursor: pointer;
+}
diff --git a/css/videolayout_default.css b/css/videolayout_default.css
index 229ef6910..9067c0ef2 100644
--- a/css/videolayout_default.css
+++ b/css/videolayout_default.css
@@ -15,7 +15,7 @@
padding: 18px;
bottom: 0;
left: 0;
- right: 0;
+ right: 20px;
width:auto;
border:1px solid transparent;
z-index: 5;
@@ -334,3 +334,7 @@
z-index: 0;
border-radius:10px;
}
+
+#mixedstream {
+ display:none !important;
+}
diff --git a/data_channels.js b/data_channels.js
index 9470f6153..17cc334d4 100644
--- a/data_channels.js
+++ b/data_channels.js
@@ -74,9 +74,15 @@ function onDataChannel(event)
*/
var endpointsEnteringLastN = obj.endpointsEnteringLastN;
- console.debug(
+ var stream = obj.stream;
+
+ console.log(
"Data channel new last-n event: ",
- lastNEndpoints);
+ lastNEndpoints, endpointsEnteringLastN, obj);
+
+ $(document).trigger(
+ 'lastnchanged',
+ [lastNEndpoints, endpointsEnteringLastN, stream]);
}
else
{
diff --git a/fonts/jitsi.eot b/fonts/jitsi.eot
index 41fe9eeea..de08ba656 100755
Binary files a/fonts/jitsi.eot and b/fonts/jitsi.eot differ
diff --git a/fonts/jitsi.svg b/fonts/jitsi.svg
index 789f6cd31..0276e562c 100755
--- a/fonts/jitsi.svg
+++ b/fonts/jitsi.svg
@@ -28,4 +28,6 @@
+
+
\ No newline at end of file
diff --git a/fonts/jitsi.ttf b/fonts/jitsi.ttf
index 654dddab5..1959f3756 100755
Binary files a/fonts/jitsi.ttf and b/fonts/jitsi.ttf differ
diff --git a/fonts/jitsi.woff b/fonts/jitsi.woff
index 3aa49fdc4..9c81e8ec1 100755
Binary files a/fonts/jitsi.woff and b/fonts/jitsi.woff differ
diff --git a/images/avatar2.png b/images/avatar2.png
new file mode 100644
index 000000000..f59231c87
Binary files /dev/null and b/images/avatar2.png differ
diff --git a/index.html b/index.html
index 3cab3be24..14334e6ba 100644
--- a/index.html
+++ b/index.html
@@ -23,15 +23,16 @@
-
-
+
+
-
-
+
+
-
-
+
+
+
@@ -40,18 +41,21 @@
-
-
+
+
+
+
-
-
-
+
+
+
+
@@ -158,7 +162,7 @@
-
+
@@ -222,6 +226,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+