Merge branch 'master' into tracking

Conflicts:
	index.html
This commit is contained in:
Philipp Hancke 2014-09-12 13:18:54 +02:00
commit 75b8c5b962
78 changed files with 1851 additions and 455 deletions

View File

@ -154,8 +154,8 @@ var config = {
bridge: 'jitsi-videobridge.jitsi.example.com'
},
useNicks: false,
bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that
desktopSharing: 'false', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
bosh: '//jitsi.example.com/http-bind', // FIXME: use xep-0156 for that
desktopSharing: 'false' // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
//chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
//minChromeExtVersion: '0.1' // Required version of Chrome extension
};

108
app.js
View File

@ -70,18 +70,18 @@ function init() {
}
obtainAudioAndVideoPermissions(function (stream) {
var audioStream = new webkitMediaStream(stream);
var videoStream = new webkitMediaStream(stream);
var videoTracks = stream.getVideoTracks();
var audioStream = new webkitMediaStream();
var videoStream = new webkitMediaStream();
var audioTracks = stream.getAudioTracks();
for (var i = 0; i < videoTracks.length; i++) {
audioStream.removeTrack(videoTracks[i]);
var videoTracks = stream.getVideoTracks();
for (var i = 0; i < audioTracks.length; i++) {
audioStream.addTrack(audioTracks[i]);
}
VideoLayout.changeLocalAudio(audioStream);
startLocalRtpStatsCollector(audioStream);
for (i = 0; i < audioTracks.length; i++) {
videoStream.removeTrack(audioTracks[i]);
for (i = 0; i < videoTracks.length; i++) {
videoStream.addTrack(videoTracks[i]);
}
VideoLayout.changeLocalVideo(videoStream, true);
maybeDoJoin();
@ -245,7 +245,9 @@ function waitForRemoteVideo(selector, ssrc, stream) {
if (stream.id === 'mixedmslabel') return;
if (selector[0].currentTime > 0) {
RTC.attachMediaStream(selector, stream); // FIXME: why do i have to do this for FF?
var simulcast = new Simulcast();
var videoStream = simulcast.getReceivingVideoStream(stream);
RTC.attachMediaStream(selector, videoStream); // 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
@ -264,18 +266,40 @@ function waitForRemoteVideo(selector, ssrc, stream) {
}
$(document).bind('remotestreamadded.jingle', function (event, data, sid) {
waitForPresence(data, sid);
});
function waitForPresence(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) {
// look only at a=ssrc: and _not_ at a=ssrc-group: lines
var ssrclines
= SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc');
= SDPUtil.find_lines(sess.peerconnection.remoteDescription.sdp, 'a=ssrc:');
ssrclines = ssrclines.filter(function (line) {
return line.indexOf('mslabel:' + data.stream.label) !== -1;
});
if (ssrclines.length) {
thessrc = ssrclines[0].substring(7).split(' ')[0];
// We signal our streams (through Jingle to the focus) before we set
// our presence (through which peers associate remote streams to
// jids). So, it might arrive that a remote stream is added but
// ssrc2jid is not yet updated and thus data.peerjid cannot be
// successfully set. Here we wait for up to a second for the
// presence to arrive.
if (!ssrc2jid[thessrc]) {
setTimeout(function(d, s) {
return function() {
waitForPresence(d, s);
}
}(data, sid), 250);
return;
}
// ok to overwrite the one from focus? might save work in colibri.js
console.log('associated jid', ssrc2jid[thessrc], data.peerjid);
if (ssrc2jid[thessrc]) {
@ -284,6 +308,9 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
}
}
// NOTE(gp) now that we have simulcast, a media stream can have more than 1
// ssrc. We should probably take that into account in our MediaStream
// wrapper.
mediaStreams.push(new MediaStream(data, sid, thessrc));
var container;
@ -330,7 +357,7 @@ $(document).bind('remotestreamadded.jingle', function (event, data, sid) {
sendKeyframe(sess.peerconnection);
}, 3000);
}
});
}
/**
* Returns the JID of the user to whom given <tt>videoSrc</tt> belongs.
@ -540,40 +567,35 @@ $(document).bind('callterminated.jingle', function (event, sid, jid, reason) {
$(document).bind('setLocalDescription.jingle', function (event, sid) {
// put our ssrcs into presence so other clients can identify our stream
var sess = connection.jingle.sessions[sid];
var newssrcs = {};
var directions = {};
var localSDP = new SDP(sess.peerconnection.localDescription.sdp);
localSDP.media.forEach(function (media) {
var type = SDPUtil.parse_mid(SDPUtil.find_line(media, 'a=mid:'));
var newssrcs = [];
var simulcast = new Simulcast();
var media = simulcast.parseMedia(sess.peerconnection.localDescription);
media.forEach(function (media) {
if (SDPUtil.find_line(media, 'a=ssrc:')) {
// assumes a single local ssrc
var ssrc = SDPUtil.find_line(media, 'a=ssrc:').substring(7).split(' ')[0];
newssrcs[type] = ssrc;
directions[type] = (
SDPUtil.find_line(media, 'a=sendrecv') ||
SDPUtil.find_line(media, 'a=recvonly') ||
SDPUtil.find_line(media, 'a=sendonly') ||
SDPUtil.find_line(media, 'a=inactive') ||
'a=sendrecv').substr(2);
}
// TODO(gp) maybe exclude FID streams?
Object.keys(media.sources).forEach(function(ssrc) {
newssrcs.push({
'ssrc': ssrc,
'type': media.type,
'direction': media.direction
});
});
});
console.log('new ssrcs', newssrcs);
// Have to clear presence map to get rid of removed streams
connection.emuc.clearPresenceMedia();
var i = 0;
Object.keys(newssrcs).forEach(function (mtype) {
i++;
var type = mtype;
// Change video type to screen
if (mtype === 'video' && isUsingScreenStream) {
type = 'screen';
if (newssrcs.length > 0) {
for (var i = 1; i <= newssrcs.length; i ++) {
// Change video type to screen
if (newssrcs[i-1].type === 'video' && isUsingScreenStream) {
newssrcs[i-1].type = 'screen';
}
connection.emuc.addMediaToPresence(i,
newssrcs[i-1].type, newssrcs[i-1].ssrc, newssrcs[i-1].direction);
}
connection.emuc.addMediaToPresence(i, type, newssrcs[mtype], directions[mtype]);
});
if (i > 0) {
connection.emuc.sendPresence();
}
});
@ -642,7 +664,7 @@ $(document).bind('joined.muc', function (event, jid, info) {
ContactList.addContact(jid);
// Once we've joined the muc show the toolbar
Toolbar.showToolbar();
ToolbarToggler.showToolbar();
if (info.displayName)
$(document).trigger('displaynamechanged',
@ -652,7 +674,7 @@ $(document).bind('joined.muc', function (event, jid, info) {
$(document).bind('entered.muc', function (event, jid, info, pres) {
console.log('entered', jid, info);
console.log('is focus?' + focus ? 'true' : 'false');
console.log('is focus? ' + (focus ? 'true' : 'false'));
// Add Peer's container
VideoLayout.ensurePeerContainerExists(jid);
@ -877,6 +899,7 @@ function getConferenceHandler() {
}
function toggleVideo() {
buttonClick("#video", "icon-camera icon-camera-disabled");
if (!(connection && connection.jingle.localVideo))
return;
@ -1140,7 +1163,12 @@ $(document).ready(function () {
Chat.init();
$('body').popover({ selector: '[data-toggle=popover]',
trigger: 'click hover'});
trigger: 'click hover',
content: function() {
return this.getAttribute("content") +
KeyboardShortcut.getShortcut(this.getAttribute("shortcut"));
}
});
// Set the defaults for prompt dialogs.
jQuery.prompt.setDefaults({persistent: false});

View File

@ -21,6 +21,11 @@ var BottomToolbar = (function (my) {
ContactList.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;

21
chat.js
View File

@ -207,7 +207,7 @@ var Chat = (function (my) {
// Undock the toolbar when the chat is shown and if we're in a
// video mode.
if (VideoLayout.isLargeVideoVisible())
Toolbar.dockToolbar(false);
ToolbarToggler.dockToolbar(false);
videospace.animate({right: chatSize[0],
width: videospaceWidth,
@ -252,15 +252,18 @@ var Chat = (function (my) {
$('#chatspace').show("slide", { direction: "right",
queue: false,
duration: 500});
duration: 500,
complete: function () {
// Request the focus in the nickname field or the chat input field.
if ($('#nickname').css('visibility') === 'visible') {
$('#nickinput').focus();
} else {
$('#usermsg').focus();
}
}
});
}
// Request the focus in the nickname field or the chat input field.
if ($('#nickname').css('visibility') === 'visible')
$('#nickinput').focus();
else {
$('#usermsg').focus();
}
};
/**
@ -333,7 +336,7 @@ var Chat = (function (my) {
if (unreadMessages) {
unreadMsgElement.innerHTML = unreadMessages.toString();
Toolbar.dockToolbar(true);
ToolbarToggler.dockToolbar(true);
var chatButtonElement
= document.getElementById('chatButton').parentNode;

View File

@ -13,13 +13,16 @@ var config = {
bosh: '//jitsi-meet.example.com/http-bind', // FIXME: use xep-0156 for that
desktopSharing: 'ext', // Desktop sharing method. Can be set to 'ext', 'webrtc' or false to disable.
chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension
desktopSharingSources: ['screen', 'window'],
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.
// useRtcpMux: true,
// useBundle: true,
useRtcpMux: true,
useBundle: true,
enableRecording: false,
enableWelcomePage: false,
enableSimulcast: false,
useNativeSimulcast: false,
isBrand: false
};

View File

@ -143,7 +143,7 @@ var ContactList = (function (my) {
// Undock the toolbar when the chat is shown and if we're in a
// video mode.
if (VideoLayout.isLargeVideoVisible())
Toolbar.dockToolbar(false);
ToolbarToggler.dockToolbar(false);
videospace.animate({right: chatSize[0],
width: videospaceWidth,

3
css/font.css Executable file → Normal file
View File

@ -103,3 +103,6 @@
.icon-reload:before {
content: "\e618";
}
.icon-filmstrip:before {
content: "\e619";
}

View File

@ -19,6 +19,11 @@
width:auto;
border:1px solid transparent;
z-index: 5;
transition: bottom 2s;
}
#remotevideos.hidden {
bottom: -196px;
}
.videocontainer {

View File

@ -84,6 +84,11 @@ function onDataChannel(event)
'lastnchanged',
[lastNEndpoints, endpointsEnteringLastN, stream]);
}
else if ("SimulcastLayersChangedEvent" === colibriClass)
{
var endpointSimulcastLayers = obj.endpointSimulcastLayers;
$(document).trigger('simulcastlayerschanged', [endpointSimulcastLayers]);
}
else
{
console.debug("Data channel JSON-formatted message: ", obj);

5
debian/changelog vendored
View File

@ -1,6 +1,5 @@
jitsi-meet (1.0.1-1) unstable; urgency=low
* Initial release
* Jitsi Meet github snapshot from 2014-07-01
* Initial release. (Closes: #760485)
-- Yasen Pramatarov <yasen@bluejimp.com> Tue, 01 Jul 2014 16:31:41 +0300
-- Damian Minkov <damencho@jitsi.org> Fri, 29 Aug 2014 16:38:14 +0200

6
debian/control vendored
View File

@ -4,7 +4,7 @@ Priority: extra
Maintainer: Jitsi Team <dev@jitsi.org>
Uploaders: Emil Ivov <emcho@jitsi.org>, Damian Minkov <damencho@jitsi.org>
Build-Depends: debhelper (>= 8.0.0)
Standards-Version: 3.9.3
Standards-Version: 3.9.5
Homepage: https://jitsi.org/meet
Package: jitsi-meet
@ -20,8 +20,8 @@ Description: WebRTC JavaScript video conferences
Package: jitsi-meet-prosody
Architecture: all
Pre-Depends: adduser, openssl, prosody-trunk, jitsi-videobridge
Depends: ${misc:Depends}, nginx, prosody-modules-otalk, lua-sec
Pre-Depends: adduser, openssl, prosody | prosody-trunk, jitsi-videobridge
Depends: ${misc:Depends}, nginx, prosody-module-turncredentials | prosody-modules-otalk, lua-sec
Description: Prosody configuration for Jitsi Meet
Jitsi Meet is a WebRTC JavaScript application that uses Jitsi
Videobridge to provide high quality, scalable video conferences.

View File

@ -1 +1 @@
debian/usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example.gz
debian/usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example

View File

@ -1 +1 @@
debian/usr/share/* usr/share/
debian/usr/share/doc/jitsi-meet-prosody /usr/share/doc/

View File

@ -1,4 +1,4 @@
#!/bin/sh
#!/bin/bash
# postinst script for jitsi-meet-prosody
#
# see: dh_installdeb(1)
@ -24,14 +24,24 @@ case "$1" in
. /etc/default/jitsi-videobridge
PROSODY_CONFIG_PRESENT="true"
# if there is no prosody config extract our template
if [ ! -f /etc/prosody/prosody.cfg.lua ]; then
PROSODY_CONFIG_PRESENT="false"
gunzip -c /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example.gz > /etc/prosody/prosody.cfg.lua
fi
if [ ! grep "VirtualHost \"$JVB_HOSTNAME\"" /etc/prosody/prosody.cfg.lua > /dev/null ]; then
# if there is no config for our domain, lets create it
if ! grep -q "VirtualHost \"$JVB_HOSTNAME\"" /etc/prosody/prosody.cfg.lua; then
# if its not our template, save the original and extract our template
if ! grep -q "VirtualHost \"jitmeet.example.com\"" /etc/prosody/prosody.cfg.lua; then
PROSODY_CONFIG_PRESENT="false"
cp /etc/prosody/prosody.cfg.lua /etc/prosody/prosody.cfg.lua.orig
gunzip -c /usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example.gz > /etc/prosody/prosody.cfg.lua
fi
if [ "PROSODY_CONFIG_PRESENT" = "true" ]; then
mv /etc/prosody/prosody.cfg.lua /etc/prosody/prosody.cfg.lua.orig
cp /etc/prosody/prosody.cfg.lua /etc/prosody/prosody.cfg.lua.orig
fi
sed -i "s/jitmeet.example.com/$JVB_HOSTNAME/g" /etc/prosody/prosody.cfg.lua
sed -i "s/jitmeetSecret/$JVB_SECRET/g" /etc/prosody/prosody.cfg.lua

View File

@ -1,35 +0,0 @@
#!/bin/sh
# preinst script for jitsi-meet-prosody
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@ -1,36 +0,0 @@
#!/bin/sh
# prerm script for jitsi-meet-prosody
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|purge)
;;
upgrade|deconfigure)
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

View File

@ -1 +0,0 @@
misc:Depends=

8
debian/jitsi-meet.config vendored Normal file
View File

@ -0,0 +1,8 @@
#!/bin/sh -e
# Source debconf library.
. /usr/share/debconf/confmodule
# certificate type choice
db_input critical jitsi-meet/cert-choice || true
db_go

View File

@ -1,31 +0,0 @@
Format: http://dep.debian.net/deps/dep5
Upstream-Name: Jitsi Meet
Upstream-Contact: Emil Ivov <emcho@jitsi.org>
Source: https://github.com/jitsi/jitsi-meet
Files: *
Copyright: 2013-2014 Jitsi
License: MIT
License: MIT
The MIT License (MIT)
.
Copyright (c) 2013 ESTOS GmbH
Copyright (c) 2013 BlueJimp SARL
.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,2 +1,10 @@
* usr/share/jitsi-meet/
debian/usr/share/* usr/share/
*.js /usr/share/jitsi-meet/
*.json /usr/share/jitsi-meet/
*.html /usr/share/jitsi-meet/
*.ico /usr/share/jitsi-meet/
libs /usr/share/jitsi-meet/
css /usr/share/jitsi-meet/
sounds /usr/share/jitsi-meet/
fonts /usr/share/jitsi-meet/
images /usr/share/jitsi-meet/
debian/usr/share/doc/jitsi-meet /usr/share/doc/

View File

@ -17,12 +17,12 @@ set -e
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
configure)
# nginx conf
. /etc/default/jitsi-videobridge
# nginx conf
if [ ! -f /etc/nginx/sites-available/$JVB_HOSTNAME.conf ]; then
cp /usr/share/doc/jitsi-meet/jitsi-meet.example /etc/nginx/sites-available/$JVB_HOSTNAME.conf
if [ ! -f /etc/nginx/sites-enabled/$JVB_HOSTNAME.conf ]; then
@ -35,6 +35,39 @@ case "$1" in
sed -i "s/#\ server_names_hash_bucket_size\ 64/\ server_names_hash_bucket_size\ 64/" /etc/nginx/nginx.conf
fi
if [ ! -f /etc/ssl/$JVB_HOSTNAME.key ] || [ ! -f /etc/ssl/$JVB_HOSTNAME.crt ]; then
# loading debconf
. /usr/share/debconf/confmodule
# SSL for nginx
db_get jitsi-meet/cert-choice
CERT_CHOICE="$RET"
if [ "$CERT_CHOICE" = 'I have a certificate and will upload the files on the server' ]; then
db_set jitsi-meet/cert-path-key "/etc/ssl/$JVB_HOSTNAME.key"
db_input critical jitsi-meet/cert-path-key || true
db_go
db_get jitsi-meet/cert-path-key
CERT_KEY="$RET"
db_set jitsi-meet/cert-path-crt "/etc/ssl/$JVB_HOSTNAME.crt"
db_input critical jitsi-meet/cert-path-crt || true
db_go
db_get jitsi-meet/cert-path-crt
CERT_CRT="$RET"
# replace self-signed certificate paths with user provided ones
CERT_KEY_ESC=$(echo $CERT_KEY | sed 's/\./\\\./g')
CERT_KEY_ESC=$(echo $CERT_KEY_ESC | sed 's/\//\\\//g')
sed -i "s/ssl_certificate_key\ \/var\/lib\/prosody\/.*key/ssl_certificate_key\ $CERT_KEY_ESC/g" \
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
CERT_CRT_ESC=$(echo $CERT_CRT | sed 's/\./\\\./g')
CERT_CRT_ESC=$(echo $CERT_CRT_ESC | sed 's/\//\\\//g')
sed -i "s/ssl_certificate\ \/var\/lib\/prosody\/.*crt/ssl_certificate\ $CERT_CRT_ESC/g" \
/etc/nginx/sites-available/$JVB_HOSTNAME.conf
fi
# and we're done with debconf
db_stop
fi
# jitsi meet
chown -R www-data:www-data /usr/share/jitsi-meet/
sed -i "s/jitsi-meet.example.com/$JVB_HOSTNAME/g" /usr/share/jitsi-meet/config.js

View File

@ -1,35 +0,0 @@
#!/bin/sh
# preinst script for jitsi-meet
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

View File

@ -1,36 +0,0 @@
#!/bin/sh
# prerm script for jitsi-meet
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
remove|purge)
;;
upgrade|deconfigure)
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
exit 0

23
debian/jitsi-meet.templates vendored Normal file
View File

@ -0,0 +1,23 @@
Template: jitsi-meet/cert-choice
Type: select
__Choices: Self-signed certificate will be generated, A certificate is available and the files are uploaded on the server
_Description: SSL certificate for the Jitsi Meet instance
Jitsi Meet is best to be set up with an SSL certificate.
Having no certificate, a self-signed one will be generated.
Having a certificate signed by a recognised CA, it can be uploaded on the server
and point its location. The default filenames will be /etc/ssl/--domain.name--.key
for the key and /etc/ssl/--domain.name--.crt for the certificate.
Template: jitsi-meet/cert-path-key
Type: string
Default: ${default-key}
_Description: Full local server path to the SSL key file:
The full path to the SSL key file on the server.
If it has not been uploaded, now is a good time to do so.
Template: jitsi-meet/cert-path-crt
Type: string
Default: ${default-crt}
_Description: Full local server path to the SSL certificate file:
The full path to the SSL certificate file on the server.
If you haven't uploaded it, now is a good time to upload it in another console.

1
debian/po/POTFILES.in vendored Normal file
View File

@ -0,0 +1 @@
[type: gettext/rfc822deb] jitsi-meet.templates

75
debian/po/templates.pot vendored Normal file
View File

@ -0,0 +1,75 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: jitsi-meet\n"
"Report-Msgid-Bugs-To: jitsi-meet@packages.debian.org\n"
"POT-Creation-Date: 2014-09-03 17:26+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#. Type: select
#. Choices
#: ../jitsi-meet.templates:1001
msgid "Self-signed certificate will be generated"
msgstr ""
#. Type: select
#. Choices
#: ../jitsi-meet.templates:1001
msgid "A certificate is available and the files are uploaded on the server"
msgstr ""
#. Type: select
#. Description
#: ../jitsi-meet.templates:1002
msgid "SSL certificate for the Jitsi Meet instance"
msgstr ""
#. Type: select
#. Description
#: ../jitsi-meet.templates:1002
msgid ""
"Jitsi Meet is best to be set up with an SSL certificate. Having no "
"certificate, a self-signed one will be generated. Having a certificate "
"signed by a recognised CA, it can be uploaded on the server and point its "
"location. The default filenames will be /etc/ssl/--domain.name--.key for the "
"key and /etc/ssl/--domain.name--.crt for the certificate."
msgstr ""
#. Type: string
#. Description
#: ../jitsi-meet.templates:2001
msgid "Full local server path to the SSL key file:"
msgstr ""
#. Type: string
#. Description
#: ../jitsi-meet.templates:2001
msgid ""
"The full path to the SSL key file on the server. If it has not been "
"uploaded, now is a good time to do so."
msgstr ""
#. Type: string
#. Description
#: ../jitsi-meet.templates:3001
msgid "Full local server path to the SSL certificate file:"
msgstr ""
#. Type: string
#. Description
#: ../jitsi-meet.templates:3001
msgid ""
"The full path to the SSL certificate file on the server. If you haven't "
"uploaded it, now is a good time to upload it in another console."
msgstr ""

7
debian/rules vendored
View File

@ -10,7 +10,6 @@
#export DH_VERBOSE=1
%:
dh $@
override_dh_install-indep:
dh_install -Xdebian -Xdoc -XINSTALL.md -XLICENSE -XREADME.md usr/share/jitsi-meet/
dh_install
dh_installdirs
dh $@

View File

@ -15,5 +15,4 @@ debian/usr/share/jitsi-meet/images/avatar1.png
debian/usr/share/jitsi-meet/images/popupPointer.png
debian/usr/share/jitsi-meet/images/favicon.ico
debian/usr/share/doc/jitsi-meet/changelog.Debian.gz
debian/usr/share/doc/jitsi-meet-prosody/changelog.Debian.gz
debian/usr/share/doc/jitsi-meet-prosody/prosody.cfg.lua-jvb.example.gz
debian/usr/share/doc/jitsi-meet-prosody/changelog.Debian.gz

3
debian/source/lintian-overrides vendored Normal file
View File

@ -0,0 +1,3 @@
# The strophe.min.js file, from http://strophe.im/strophejs/ (https://github.com/strophe/strophejs)
# contains the source with minimum space characters
source-is-missing libs/strophe/strophe.jingle.bundle.js

View File

@ -0,0 +1,210 @@
-- Prosody XMPP Server Configuration
--
-- Information on configuring Prosody can be found on our
-- website at http://prosody.im/doc/configure
--
-- Tip: You can check that the syntax of this file is correct
-- when you have finished by running: prosodyctl check config
-- If there are any errors, it will let you know what and where
-- they are, otherwise it will keep quiet.
--
-- Good luck, and happy Jabbering!
---------- Server-wide settings ----------
-- Settings in this section apply to the whole server and are the default settings
-- for any virtual hosts
-- This is a (by default, empty) list of accounts that are admins
-- for the server. Note that you must create the accounts separately
-- (see http://prosody.im/doc/creating_accounts for info)
-- Example: admins = { "user1@example.com", "user2@example.net" }
admins = { }
daemonize = true
cross_domain_bosh = true;
component_ports = { 5347 }
-- Enable use of libevent for better performance under high load
-- For more information see: http://prosody.im/doc/libevent
--use_libevent = true
-- This is the list of modules Prosody will load on startup.
-- It looks for mod_modulename.lua in the plugins folder, so make sure that exists too.
-- Documentation on modules can be found at: http://prosody.im/doc/modules
modules_enabled = {
-- Generally required
"roster"; -- Allow users to have a roster. Recommended ;)
"saslauth"; -- Authentication for clients and servers. Recommended if you want to log in.
"tls"; -- Add support for secure TLS on c2s/s2s connections
"dialback"; -- s2s dialback support
"disco"; -- Service discovery
"posix"; -- POSIX functionality, sends server to background, enables syslog, etc.
-- Not essential, but recommended
"private"; -- Private XML storage (for room bookmarks, etc.)
"vcard"; -- Allow users to set vCards
-- These are commented by default as they have a performance impact
--"privacy"; -- Support privacy lists
"compression"; -- Stream compression (requires the lua-zlib package installed)
-- Nice to have
"version"; -- Replies to server version requests
"uptime"; -- Report how long server has been running
"time"; -- Let others know the time here on this server
"ping"; -- Replies to XMPP pings with pongs
"pep"; -- Enables users to publish their mood, activity, playing music and more
"register"; -- Allow users to register on this server using a client and change passwords
-- Admin interfaces
"admin_adhoc"; -- Allows administration via an XMPP client that supports ad-hoc commands
--"admin_telnet"; -- Opens telnet console interface on localhost port 5582
-- HTTP modules
"bosh"; -- Enable BOSH clients, aka "Jabber over HTTP"
--"http_files"; -- Serve static files from a directory over HTTP
-- Other specific functionality
--"groups"; -- Shared roster support
--"announce"; -- Send announcement to all online users
--"welcome"; -- Welcome users who register accounts
--"watchregistrations"; -- Alert admins of registrations
--"motd"; -- Send a message to users when they log in
--"legacyauth"; -- Legacy authentication. Only used by some old clients and bots.
-- jitmeet
--"smacks";
--"carbons";
--"mam";
--"lastactivity";
--"offline";
"pubsub";
--"adhoc";
--"websocket";
--"http_altconnect";
"turncredentials";
}
-- These modules are auto-loaded, but should you want
-- to disable them then uncomment them here:
modules_disabled = {
-- "offline"; -- Store offline messages
-- "c2s"; -- Handle client connections
-- "s2s"; -- Handle server-to-server connections
}
-- Disable account creation by default, for security
-- For more information see http://prosody.im/doc/creating_accounts
allow_registration = false
-- These are the SSL/TLS-related settings. If you don't want
-- to use SSL/TLS, you may comment or remove this
ssl = {
key = "/etc/prosody/certs/localhost.key";
certificate = "/etc/prosody/certs/localhost.crt";
}
-- Force clients to use encrypted connections? This option will
-- prevent clients from authenticating unless they are using encryption.
-- c2s_require_encryption = true
-- Force certificate authentication for server-to-server connections?
-- This provides ideal security, but requires servers you communicate
-- with to support encryption AND present valid, trusted certificates.
-- NOTE: Your version of LuaSec must support certificate verification!
-- For more information see http://prosody.im/doc/s2s#security
-- s2s_secure_auth = false
-- Many servers don't support encryption or have invalid or self-signed
-- certificates. You can list domains here that will not be required to
-- authenticate using certificates. They will be authenticated using DNS.
--s2s_insecure_domains = { "gmail.com" }
-- Even if you leave s2s_secure_auth disabled, you can still require valid
-- certificates for some domains by specifying a list here.
--s2s_secure_domains = { "jabber.org" }
-- Required for init scripts and prosodyctl
pidfile = "/var/run/prosody/prosody.pid"
-- Select the authentication backend to use. The 'internal' providers
-- use Prosody's configured data storage to store the authentication data.
-- To allow Prosody to offer secure authentication mechanisms to clients, the
-- default provider stores passwords in plaintext. If you do not trust your
-- server please see http://prosody.im/doc/modules/mod_auth_internal_hashed
-- for information about using the hashed backend.
-- authentication = "internal_plain"
authentication = "internal_hashed"
-- Select the storage backend to use. By default Prosody uses flat files
-- in its configured data directory, but it also supports more backends
-- through modules. An "sql" backend is included by default, but requires
-- additional dependencies. See http://prosody.im/doc/storage for more info.
--storage = "sql" -- Default is "internal"
-- For the "sql" backend, you can uncomment *one* of the below to configure:
--sql = { driver = "SQLite3", database = "prosody.sqlite" } -- Default. 'database' is the filename.
--sql = { driver = "MySQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
--sql = { driver = "PostgreSQL", database = "prosody", username = "prosody", password = "secret", host = "localhost" }
storage = {archive2 = "sql2"}
sql = { driver = "SQLite3", database = "prosody.sqlite" }
default_archive_policy = "roster"
-- Logging configuration
-- For advanced logging see http://prosody.im/doc/logging
log = {
info = "/var/log/prosody/prosody.log"; -- Change 'info' to 'debug' for verbose logging
error = "/var/log/prosody/prosody.err";
"*syslog";
}
----------- Virtual hosts -----------
-- You need to add a VirtualHost entry for each domain you wish Prosody to serve.
-- Settings under each VirtualHost entry apply *only* to that host.
-- VirtualHost "localhost"
VirtualHost "jitmeet.example.com"
-- enabled = false -- Remove this line to enable this host
authentication = "anonymous"
-- Assign this host a certificate for TLS, otherwise it would use the one
-- set in the global section (if any).
-- Note that old-style SSL on port 5223 only supports one certificate, and will always
-- use the global one.
ssl = {
key = "/etc/prosody/certs/jitmeet.example.com.key";
certificate = "/etc/prosody/certs/jitmeet.example.com.crt";
}
------ Components ------
-- You can specify components to add hosts that provide special services,
-- like multi-user conferences, and transports.
-- For more information on components, see http://prosody.im/doc/components
---Set up a MUC (multi-user chat) room server on conference.example.com:
--Component "conference.example.com" "muc"
-- Set up a SOCKS5 bytestream proxy for server-proxied file transfers:
--Component "proxy.example.com" "proxy65"
---Set up an external component (default component port is 5347)
--
-- External components allow adding various services, such as gateways/
-- transports to other networks like ICQ, MSN and Yahoo. For more info
-- see: http://prosody.im/doc/components#adding_an_external_component
--
--Component "gateway.example.com"
-- component_secret = "password"
Component "conference.jitmeet.example.com" "muc"
Component "jitsi-videobridge.jitmeet.example.com"
component_secret = "jitmeetSecret"

View File

@ -23,14 +23,4 @@ server {
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $http_host;
}
# xmpp websockets
location /xmpp-websocket {
proxy_pass http://localhost:5280;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
tcp_nodelay on;
}
}

View File

@ -125,7 +125,7 @@ function doGetStreamFromExtension(streamCallback, failCallback) {
// Sends 'getStream' msg to the extension. Extension id must be defined in the config.
chrome.runtime.sendMessage(
config.chromeExtensionId,
{ getStream: true},
{ getStream: true, sources: config.desktopSharingSources },
function (response) {
if (!response) {
failCallback(chrome.runtime.lastError);

View File

@ -46,7 +46,7 @@ var Etherpad = (function (my) {
largeVideo.css({opacity: '0'});
} else {
VideoLayout.setLargeVideoVisible(false);
Toolbar.dockToolbar(true);
ToolbarToggler.dockToolbar(true);
}
$('#etherpad>iframe').fadeIn(300, function () {
@ -64,7 +64,7 @@ var Etherpad = (function (my) {
if (!isPresentation) {
$('#largeVideo').fadeIn(300, function () {
VideoLayout.setLargeVideoVisible(true);
Toolbar.dockToolbar(false);
ToolbarToggler.dockToolbar(false);
});
}
});

BIN
fonts/jitsi.eot Executable file → Normal file

Binary file not shown.

1
fonts/jitsi.svg Executable file → Normal file
View File

@ -32,4 +32,5 @@
<glyph unicode="&#xe616;" d="M513.087 224.534c0-141.673-114.855-256.526-256.554-256.526-141.674 0-256.534 114.851-256.534 256.526 0 141.692 114.861 256.553 256.534 256.553 141.7 0 256.554-114.861 256.554-256.553zM256.534-31.993c67.863 0 129.556 26.356 175.437 69.37-4.858 5.825-11.276 10.557-19.557 14.171-29.467 12.873-58.313 27.128-87.267 41.128-20.935 10.161-19.702 33.293-12.82 43.029 4.471 6.346 8.402 12.999 8.864 21.373 0.166 3.084 3.12 7.142 5.945 8.761 9.802 5.652 14.349 14.873 18.802 24.43 1.17 2.515 2.777 4.945 4.615 7.038 3.185 3.622 4.612 7.218 2.286 11.971-0.543 1.104 0.502 3.12 1.053 4.637 1.619 4.534 3.866 8.901 4.93 13.558 1.335 5.774 2.029 11.74 2.351 17.661 0.14 2.451-2.272 5.092-2.017 7.493 1.31 12.011-5.471 21.136-9.299 31.579-4.688 12.857-13.885 20.91-22.002 30.485-1.529 1.812-1.964 4.919-2.094 7.476-0.269 5.49-2.53 8.207-8.272 7.462-2.299-0.3-4.805-0.943-6.921-0.378-1.864 0.494-4.663 2.362-4.767 3.802-0.577 7.269-3.106 8.465-10.533 7.238-4.648-0.772-10.697 3.429-15.257 6.601-3.816 2.646-7.104 3.777-11.534 2.69-1.849-0.45-4.341-0.373-5.908 0.547-1.671 0.988-3.303 1.592-4.933 1.919v0.276c-0.36-0.007-0.745-0.065-1.104-0.108-0.361 0.044-0.72 0.103-1.078 0.108v-0.276c-1.645-0.327-3.287-0.931-4.945-1.919-1.556-0.918-4.046-0.996-5.899-0.547-4.443 1.087-7.724-0.044-11.532-2.69-4.578-3.173-10.611-7.373-15.259-6.601-7.431 1.226-9.97 0.031-10.547-7.238-0.109-1.439-2.897-3.308-4.758-3.802-2.139-0.565-4.624 0.077-6.944 0.378-5.728 0.745-7.994-1.971-8.258-7.462-0.131-2.555-0.565-5.665-2.095-7.476-8.111-9.575-17.308-17.629-22.009-30.485-3.814-10.443-10.602-19.568-9.285-31.579 0.256-2.401-2.152-5.042-2.023-7.493 0.327-5.923 1.020-11.888 2.351-17.661 1.065-4.656 3.313-9.024 4.945-13.558 0.547-1.516 1.587-3.531 1.041-4.637-2.325-4.754-0.894-8.351 2.291-11.971 1.837-2.094 3.437-4.523 4.612-7.038 4.45-9.555 8.996-18.779 18.798-24.43 2.827-1.619 5.78-5.676 5.952-8.761 0.457-8.374 4.387-15.027 8.869-21.373 6.873-9.735 7.951-32.623-12.837-43.029-28.76-14.386-57.8-28.255-87.251-41.128-8.285-3.615-14.704-8.347-19.561-14.169 45.88-43.015 107.569-69.372 175.422-69.372z" horiz-adv-x="513" />
<glyph unicode="&#xe617;" d="M32.887 258.374c5.026 4.679 12.994 10.886 21.642 16.349 25.668 16.31 54.057 25.449 83.415 32.066 24.381 5.475 49.123 8.444 74.033 10.101 27.877 1.877 55.779 1.89 83.696 0.399 19.972-1.092 39.843-3.251 59.56-6.606 21.978-3.753 43.519-8.997 64.392-16.875 12.209-4.587 24.086-10.053 35.267-16.786 14.858-8.946 28.276-19.612 38.61-33.674 10.409-14.151 15.861-30.204 16.914-47.696 0.873-13.701 0.358-27.349-2.828-40.794-1.438-6.041-4.113-11.567-8.277-16.193-5.709-6.324-13.212-8.51-21.386-8.818-10.231-0.334-20.205 2.057-30.18 4.113-19.456 3.985-38.918 8.123-58.349 12.364-7.069 1.517-14.344 2.546-20.825 6.298-11.154 6.478-17.223 15.887-17.017 28.892 0.129 8.435 1.108 16.891 1.235 25.348 0.156 12.505-4.962 22.581-15.449 29.521-7.197 4.769-15.347 7.456-23.726 9.333-20.206 4.523-40.693 5.089-61.281 5.025-14.411-0.063-28.791-0.834-43.047-3.071-9.974-1.581-19.781-3.906-28.866-8.507-12.159-6.182-19.677-15.732-20.036-29.676-0.22-8.175 0.487-16.401 0.964-24.575 0.321-5.911-0.040-11.723-2.648-17.144-4.63-9.692-12.468-15.836-22.685-18.482-11.323-2.933-22.802-5.27-34.252-7.611-19.051-3.882-38.108-7.684-57.208-11.259-7.263-1.387-14.627-0.976-21.567 1.801-9.371 3.728-14.462 11.387-17.069 20.668-3.548 12.699-3.921 25.757-3.483 38.865 0.45 13.52 2.942 26.618 9.202 38.803 4.897 9.532 11.246 17.977 21.246 27.821z" horiz-adv-x="513" />
<glyph unicode="&#xe618;" d="M398.543 56.151c-0.029 0.082-0.060 0.164-0.080 0.243-35.7-22.819-75.891-34.966-117.012-34.966-0.007 0-0.010 0-0.014 0-61.26 0-118.75 26.386-157.734 72.37-49.889 58.849-67.126 164.977-36.511 213.894 2.002-0.831 3.938-1.616 5.84-2.387 6.793-2.756 13.207-5.358 21.153-9.548 3.031-1.601 6.169-2.406 9.337-2.406 5.857 0 11.3 2.824 14.924 7.743 3.907 5.309 5.156 12.389 3.269 18.476l-1.762 5.705c-5.344 17.295-10.862 35.177-17.106 52.539-4.992 13.882-11.2 31.163-29.613 31.163-6.028 0-13.019-1.828-23.365-6.102-22.147-9.159-35.529-14.981-57.267-24.905-7.551-3.444-12.617-11.349-12.601-19.672 0.014-7.921 4.496-14.668 11.988-18.058 9.104-4.128 15.268-6.858 21.734-9.723l5.343-2.377c-50.969-129.551 12.401-263.229 105.657-319.606 41.749-25.237 89.25-38.57 137.385-38.57h0.021c51.36 0 102.781 15.55 142.25 42.599-15.865 14.401-22.783 34.584-25.836 43.586zM549.101 105.045c-9.057 4.288-15.178 7.129-21.611 10.122l-5.248 2.446c53.224 128.634-7.784 263.401-100.034 321.394-42.68 26.832-91.562 41.016-141.358 41.016-52.424 0-103.205-15.297-142.983-43.083l-2.692-1.882c15.798-13.782 22.93-33.394 26.459-43.205 36.463 23.97 77.838 36.704 119.947 36.704 62.704 0 121.071-27.392 160.147-75.158 48.841-59.724 64.219-166.128 32.749-214.508-1.995 0.868-3.908 1.692-5.812 2.499-6.736 2.88-13.102 5.59-20.977 9.911-3.101 1.712-6.322 2.577-9.606 2.577-5.793 0-11.2-2.779-14.845-7.634-3.906-5.217-5.239-12.216-3.483-18.257l1.639-5.651c5.048-17.423 10.265-35.428 16.206-52.921 4.794-14.119 10.757-31.691 29.589-31.691 5.921 0 12.788 1.712 22.94 5.7 22.175 8.719 35.66 14.3 57.704 23.889 7.595 3.312 12.801 11.126 12.929 19.447 0.14 7.911-4.222 14.75-11.663 18.284z" horiz-adv-x="561" />
<glyph unicode="&#xe619;" d="M23.497 480.85c230.617 0 276.897 0 507.512 0 17.96 0 26.678-12.98 26.678-28.98-0.29-151.63-0.163-303.244-0.225-454.904 0-21.992-6.601-28.529-28.851-28.529-221.536-0.063-278.226-0.063-499.776 0-22.267 0-28.899 6.505-28.899 28.529-0.049 151.629 0.242 304.036-0.046 455.664-0.017 13.105 5.651 26.88 23.608 28.219zM155.702 225.149c0-59.522-0.036-86.084 0.029-145.625 0.018-25.022 5.604-30.525 31.060-30.525 116.676 0 68.537 0 185.261 0 23.538 0 29.625 5.991 29.625 29.048 0.063 119.555 0.063 173.169 0 292.695 0 24.069-5.344 29.495-28.884 29.495-117.661 0.050-70.422 0.050-188.078 0-23.522 0-28.965-5.522-28.983-29.445-0.065-59.554-0.029-86.105-0.029-145.643zM76.972 419.283c-37.465-0.031-33.343 2.979-33.422-33.343-0.1-31.975-3.527-31.767 31.264-31.686 36.499 0.097 33.6-1.882 33.651 33.777 0 33.861 2.043 31.298-31.493 31.251zM481.822 419.283c-35.579-0.017-32.78 3.092-32.875-32.682-0.065-33.651-2.254-32.346 32.264-32.346 36.544 0 32.649-1.015 32.649 33.119-0.001 34.323 3.478 31.955-32.038 31.909zM108.414 61.204c0.18 36.547 2.32 33.457-33.679 33.585-34.052 0.096-31.285 1.382-31.203-31.655 0.065-36.738-3.477-33.26 33.537-33.325 33.021-0.033 31.571-3.028 31.346 31.394zM513.859 62.2c0.067 34.167 3.221 32.652-31.649 32.589-35.066-0.066-33.328 2.897-33.264-32.652 0.065-35.322-2.192-32.361 31.878-32.329 35.998 0.066 33.101-3.349 33.034 32.392zM513.859 171.038c0 35.275 3.61 33.421-33.743 33.261-0.449 0-0.937 0-1.419 0-29.688 0-29.688 0-29.688-29.012 0-38.961-3.221-34.968 34.647-35.098 33.038-0.193 30.269-1.546 30.202 30.849zM75.653 244.936c34.147-0.082 32.907-2.784 32.812 31.491-0.097 35.564 2.448 32.459-33.007 32.505-34.953 0.050-31.907 2.352-31.942-31.989-0.031-33.715-2.85-32.231 32.138-32.007zM480.632 244.936c36.256-0.129 33.295-2.302 33.228 32.247 0 34.279 3.092 31.769-32.134 31.749-35.098-0.014-32.843 3.026-32.749-32.747 0.066-31.25 0.034-31.25 31.655-31.25zM75.2 140.19c35.502 0 33.329-3.284 33.233 32.264-0.082 31.847-0.018 31.75-32.507 31.878-35.403 0.129-32.411 1.337-32.411-31.878 0.018-34.584-2.959-32.394 31.684-32.264z" horiz-adv-x="558" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 29 KiB

BIN
fonts/jitsi.ttf Executable file → Normal file

Binary file not shown.

BIN
fonts/jitsi.woff Executable file → Normal file

Binary file not shown.

82
fonts/selection.json Executable file → Normal file
View File

@ -1,6 +1,34 @@
{
"IcoMoonType": "selection",
"icons": [
{
"icon": {
"paths": [
"M46.993-1.7c461.234 0 553.793 0 1015.024 0 35.919 0 53.356 25.959 53.356 57.959-0.581 303.259-0.325 606.488-0.449 909.809 0 43.984-13.203 57.058-57.703 57.058-443.072 0.126-556.453 0.126-999.553 0-44.534 0-57.799-13.009-57.799-57.058-0.098-303.257 0.485-608.072-0.093-911.329-0.034-26.21 11.301-53.761 47.217-56.439zM311.405 509.702c0 119.045-0.072 172.168 0.057 291.249 0.036 50.043 11.208 61.050 62.12 61.050 233.352 0 137.075 0 370.522 0 47.075 0 59.249-11.982 59.249-58.095 0.126-239.111 0.126-346.338 0-585.389 0-48.138-10.687-58.991-57.768-58.991-235.323-0.101-140.844-0.101-376.157 0-47.044 0-57.93 11.043-57.966 58.89-0.129 119.109-0.057 172.209-0.057 291.287zM153.944 121.434c-74.929 0.062-66.687-5.958-66.845 66.685-0.201 63.95-7.054 63.534 62.528 63.372 72.999-0.194 67.201 3.764 67.302-67.554 0-67.722 4.087-62.595-62.985-62.502zM963.644 121.434c-71.159 0.034-65.56-6.185-65.751 65.364-0.129 67.302-4.508 64.693 64.528 64.693 73.089 0 65.299 2.031 65.299-66.238-0.003-68.646 6.956-63.911-64.076-63.818zM216.828 837.592c0.359-73.094 4.639-66.914-67.358-67.17-68.104-0.191-62.569-2.763-62.407 63.31 0.129 73.476-6.954 66.52 67.074 66.649 66.042 0.065 63.142 6.056 62.691-62.789zM1027.718 835.6c0.134-68.334 6.443-65.304-63.297-65.178-70.132 0.132-66.656-5.793-66.527 65.304 0.129 70.645-4.384 64.721 63.756 64.657 71.995-0.132 66.202 6.698 66.068-64.783zM1027.718 617.923c0-70.55 7.219-66.842-67.485-66.522-0.898 0-1.873 0-2.838 0-59.375 0-59.375 0-59.375 58.023 0 77.922-6.443 69.936 69.293 70.196 66.076 0.387 60.539 3.091 60.405-61.697zM151.307 470.127c68.295 0.163 65.815 5.568 65.624-62.982-0.194-71.128 4.895-64.917-66.014-65.010-69.905-0.101-63.813-4.704-63.885 63.978-0.062 67.431-5.7 64.463 64.275 64.014zM961.263 470.127c72.511 0.258 66.589 4.603 66.455-64.494 0-68.558 6.185-63.537-64.267-63.498-70.196 0.028-65.686-6.053-65.498 65.493 0.132 62.5 0.067 62.5 63.31 62.5zM150.399 679.62c71.004 0 66.659 6.567 66.466-64.528-0.163-63.694-0.036-63.501-65.013-63.756-70.805-0.258-64.822-2.673-64.822 63.756 0.036 69.167-5.919 64.788 63.369 64.528z"
],
"attrs": [
{}
],
"width": 1115,
"grid": 0,
"tags": [
"filmstrip"
]
},
"attrs": [
{}
],
"properties": {
"order": 26,
"id": 29,
"prevSize": 32,
"code": 58905,
"name": "filmstrip",
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 0
},
{
"icon": {
"paths": [
@ -33,7 +61,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 0
"iconIdx": 1
},
{
"icon": {
@ -61,7 +89,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 1
"iconIdx": 2
},
{
"icon": {
@ -89,7 +117,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 2
"iconIdx": 3
},
{
"icon": {
@ -128,7 +156,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 3
"iconIdx": 4
},
{
"icon": {
@ -151,7 +179,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 4
"iconIdx": 5
},
{
"icon": {
@ -174,7 +202,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 5
"iconIdx": 6
},
{
"icon": {
@ -198,7 +226,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 6
"iconIdx": 7
},
{
"icon": {
@ -223,7 +251,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 7
"iconIdx": 8
},
{
"icon": {
@ -246,7 +274,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 8
"iconIdx": 9
},
{
"icon": {
@ -270,7 +298,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 9
"iconIdx": 10
},
{
"icon": {
@ -294,7 +322,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 10
"iconIdx": 11
},
{
"icon": {
@ -316,7 +344,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 11
"iconIdx": 12
},
{
"icon": {
@ -340,7 +368,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 12
"iconIdx": 13
},
{
"icon": {
@ -364,7 +392,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 13
"iconIdx": 14
},
{
"icon": {
@ -391,7 +419,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 14
"iconIdx": 15
},
{
"icon": {
@ -415,7 +443,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 15
"iconIdx": 16
},
{
"icon": {
@ -439,7 +467,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 16
"iconIdx": 17
},
{
"icon": {
@ -464,7 +492,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 17
"iconIdx": 18
},
{
"icon": {
@ -486,7 +514,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 18
"iconIdx": 19
},
{
"icon": {
@ -509,7 +537,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 19
"iconIdx": 20
},
{
"icon": {
@ -531,7 +559,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 20
"iconIdx": 21
},
{
"icon": {
@ -554,7 +582,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 21
"iconIdx": 22
},
{
"icon": {
@ -577,7 +605,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 22
"iconIdx": 23
},
{
"icon": {
@ -601,7 +629,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 23
"iconIdx": 24
},
{
"icon": {
@ -624,7 +652,7 @@
"ligatures": ""
},
"setIdx": 0,
"iconIdx": 24
"iconIdx": 25
}
],
"height": 1024,
@ -637,7 +665,9 @@
"fontPref": {
"prefix": "icon-",
"metadata": {
"fontFamily": "jitsi"
"fontFamily": "jitsi",
"majorVersion": 1,
"minorVersion": 0
},
"metrics": {
"emSize": 512,

BIN
images/smileys/smiley1.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
images/smileys/smiley10.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/smileys/smiley11.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/smileys/smiley12.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/smileys/smiley13.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/smileys/smiley14.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/smileys/smiley15.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/smileys/smiley16.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
images/smileys/smiley17.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
images/smileys/smiley18.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/smileys/smiley19.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/smileys/smiley2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/smileys/smiley20.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/smileys/smiley3.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
images/smileys/smiley4.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
images/smileys/smiley5.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/smileys/smiley6.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
images/smileys/smiley7.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
images/smileys/smiley8.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
images/smileys/smiley9.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -10,6 +10,7 @@
<meta itemprop="description" content="Join a WebRTC video conference powered by the Jitsi Videobridge"/>
<meta itemprop="image" content="/images/jitsilogo.png"/>
<script src="libs/jquery-2.1.1.min.js"></script>
<script src="simulcast.js?v=1"></script><!-- simulcast handling -->
<script src="libs/strophe/strophe.jingle.adapter.js?v=1"></script><!-- strophe.jingle bundles -->
<script src="libs/strophe/strophe.jingle.bundle.js?v=8"></script>
<script src="libs/strophe/strophe.jingle.js?v=1"></script>
@ -17,6 +18,7 @@
<script src="libs/strophe/strophe.jingle.sdp.util.js?v=1"></script>
<script src="libs/strophe/strophe.jingle.sessionbase.js?v=1"></script>
<script src="libs/strophe/strophe.jingle.session.js?v=1"></script>
<script src="libs/strophe/strophe.util.js"></script>
<script src="libs/colibri/colibri.focus.js?v=8"></script><!-- colibri focus implementation -->
<script src="libs/colibri/colibri.session.js?v=1"></script>
<script src="libs/jquery-ui.js"></script>
@ -29,13 +31,13 @@
<script src="estos_log.js?v=2"></script><!-- simple stanza logger -->
<script src="desktopsharing.js?v=2"></script><!-- desktop sharing -->
<script src="data_channels.js?v=3"></script><!-- data channels -->
<script src="app.js?v=7"></script><!-- application logic -->
<script src="app.js?v=9"></script><!-- application logic -->
<script src="commands.js?v=1"></script><!-- application logic -->
<script src="chat.js?v=9"></script><!-- chat logic -->
<script src="contact_list.js?v=1"></script><!-- contact list logic -->
<script src="chat.js?v=10"></script><!-- chat logic -->
<script src="contact_list.js?v=2"></script><!-- contact list logic -->
<script src="util.js?v=6"></script><!-- utility functions -->
<script src="etherpad.js?v=8"></script><!-- etherpad plugin -->
<script src="prezi.js?v=4"></script><!-- prezi plugin -->
<script src="etherpad.js?v=9"></script><!-- etherpad plugin -->
<script src="prezi.js?v=5"></script><!-- prezi plugin -->
<script src="smileys.js?v=2"></script><!-- smiley images -->
<script src="replacement.js?v=6"></script><!-- link and smiley replacement -->
<script src="moderatemuc.js?v=3"></script><!-- moderator plugin -->
@ -43,17 +45,19 @@
<script src="rtp_sts.js?v=1"></script><!-- RTP stats processing -->
<script src="local_sts.js?v=1"></script><!-- Local stats processing -->
<script src="videolayout.js?v=8"></script><!-- video ui -->
<script src="toolbar.js?v=4"></script><!-- toolbar ui -->
<script src="toolbar.js?v=5"></script><!-- toolbar ui -->
<script src="toolbar_toggler.js?v=1"></script>
<script src="canvas_util.js?v=1"></script><!-- canvas drawing utils -->
<script src="audio_levels.js?v=1"></script><!-- audio levels plugin -->
<script src="media_stream.js?v=1"></script><!-- media stream -->
<script src="bottom_toolbar.js?v=1"></script><!-- media stream -->
<script src="bottom_toolbar.js?v=2"></script><!-- media stream -->
<script src="roomname_generator.js?v=1"></script><!-- generator for random room names -->
<script src="keyboard_shortcut.js?v=1"></script>
<script src="tracking.js?v=1"></script><!-- tracking -->
<link href="//netdna.bootstrapcdn.com/font-awesome/4.0.3/css/font-awesome.css" rel="stylesheet">
<link rel="stylesheet" href="css/font.css?v=3"/>
<link rel="stylesheet" href="css/font.css?v=4"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/main.css?v=23"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=9" id="videolayout_default"/>
<link rel="stylesheet" type="text/css" media="screen" href="css/videolayout_default.css?v=10" id="videolayout_default"/>
<link rel="stylesheet" href="css/jquery-impromptu.css?v=4">
<link rel="stylesheet" href="css/modaldialog.css?v=3">
<link rel="stylesheet" href="css/popup_menu.css?v=2">
@ -156,62 +160,62 @@
<div style="position: relative;" id="header_container">
<div id="header">
<span id="toolbar">
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Mute / Unmute" onclick='toggleAudio();'>
<a class="button" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" content="Mute / Unmute" onclick='toggleAudio();'>
<i id="mute" class="icon-microphone"></i>
</a>
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Start / stop camera" onclick='buttonClick("#video", "icon-camera icon-camera-disabled");toggleVideo();'>
<a class="button" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" content="Start / stop camera" onclick='toggleVideo();'>
<i id="video" class="icon-camera"></i>
</a>
<span id="recording" style="display: none">
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Record" onclick='toggleRecording();'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Record" onclick='toggleRecording();'>
<i id="recordButton" class="icon-recEnable"></i>
</a>
</span>
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Lock / unlock room" onclick="Toolbar.openLockDialog();">
<a class="button" data-toggle="popover" data-placement="bottom" content="Lock / unlock room" onclick="Toolbar.openLockDialog();">
<i id="lockIcon" class="icon-security"></i>
</a>
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Invite others" onclick="Toolbar.openLinkDialog();">
<a class="button" data-toggle="popover" data-placement="bottom" content="Invite others" onclick="Toolbar.openLinkDialog();">
<i class="icon-link"></i>
</a>
<div class="header_button_separator"></div>
<span class="toolbar_span">
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Open / close chat" onclick='BottomToolbar.toggleChat();'>
<a class="button" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" content="Open / close chat" onclick='BottomToolbar.toggleChat();'>
<i id="chatButton" class="icon-chat"></i>
</a>
<span id="unreadMessages"></span>
</span>
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Share Prezi" onclick='Prezi.openPreziDialog();'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Share Prezi" onclick='Prezi.openPreziDialog();'>
<i class="icon-prezi"></i>
</a>
<span id="etherpadButton">
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Shared document" onclick='Etherpad.toggleEtherpad(0);'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Shared document" onclick='Etherpad.toggleEtherpad(0);'>
<i class="icon-share-doc"></i>
</a>
</span>
<div class="header_button_separator"></div>
<span id="desktopsharing" style="display: none">
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Share screen" onclick="toggleScreenSharing();">
<a class="button" data-toggle="popover" data-placement="bottom" content="Share screen" onclick="toggleScreenSharing();">
<i class="icon-share-desktop"></i>
</a>
</span>
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Enter / Exit Full Screen" onclick='buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");Toolbar.toggleFullScreen();'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" onclick='buttonClick("#fullScreen", "icon-full-screen icon-exit-full-screen");Toolbar.toggleFullScreen();'>
<i id="fullScreen" class="icon-full-screen"></i>
</a>
<span id="sipCallButton">
<div class="header_button_separator"></div>
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Call SIP number" onclick='callSipButtonClicked();'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Call SIP number" onclick='callSipButtonClicked();'>
<i class="icon-telephone"></i></a>
</span>
<div class="header_button_separator"></div>
<span id="hangup">
<a class="button" data-toggle="popover" data-placement="bottom" data-content="Hang Up" onclick='hangup();'>
<a class="button" data-toggle="popover" data-placement="bottom" content="Hang Up" onclick='hangup();'>
<i class="icon-hangup" style="color:#ff0000;font-size: 1.4em;"></i>
</a>
</span>
@ -229,7 +233,7 @@
</form>
</div>
<div id="reloadPresentation"><a onclick='Prezi.reloadPresentation();'><i title="Reload Prezi" class="fa fa-repeat fa-lg"></i></a></div>
<div id="videospace" onmousemove="Toolbar.showToolbar();">
<div id="videospace" onmousemove="ToolbarToggler.showToolbar();">
<div id="largeVideoContainer" class="videocontainer">
<div id="presentation"></div>
<div id="etherpad"></div>
@ -251,16 +255,19 @@
</div>
<span id="bottomToolbar">
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" data-toggle="popover" data-placement="top" data-content="Open / close chat" onclick='BottomToolbar.toggleChat();'>
<a class="bottomToolbarButton" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" content="Open / close chat" onclick='BottomToolbar.toggleChat();'>
<i id="chatBottomButton" class="icon-chat-simple"></i>
</a>
<span id="unreadMessages"></span>
</span>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" data-toggle="popover" data-placement="top" data-content="Open / close contact list" onclick='BottomToolbar.toggleContactList();'>
<a class="bottomToolbarButton" data-container="body" data-toggle="popover" data-placement="top" data-container="body" id="contactlistpopover" content="Open / close contact list" onclick='BottomToolbar.toggleContactList();'>
<i id="contactListButton" class="icon-contactList"></i>
</a>
<span id="unreadMessages"></span>
</span>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" content="Show / hide film strip" onclick='BottomToolbar.toggleFilmStrip()'>
<i id="filmStripButton" class="icon-filmstrip"></i>
</a>
</span>
</span>
</div>

76
keyboard_shortcut.js Normal file
View File

@ -0,0 +1,76 @@
var KeyboardShortcut = (function(my) {
//maps keycode to character, id of popover for given function and function
var shortcuts = {
67: {
character: "C",
id: "toggleChatPopover",
function: BottomToolbar.toggleChat
},
70: {
character: "F",
id: "filmstripPopover",
function: BottomToolbar.toggleFilmStrip
},
77: {
character: "M",
id: "mutePopover",
function: toggleAudio
},
84: {
character: "T",
function: function() {
if(!isAudioMuted()) {
toggleAudio();
}
}
},
86: {
character: "V",
id: "toggleVideoPopover",
function: toggleVideo
}
};
window.onkeyup = function(e) {
if($("#chatspace").css("display") === "none" ||
!($("#usermsg").is(":focus") || $("#nickinput").is(":focus"))) {
var keycode = e.which;
if (typeof shortcuts[keycode] === "object") {
shortcuts[keycode].function();
} else if (keycode >= "1".charCodeAt(0) && keycode <= "9".charCodeAt(0)) {
var remoteVideos = $(".videocontainer:not(#mixedstream)"),
videoWanted = keycode - "0".charCodeAt(0);
if (remoteVideos.length > videoWanted) {
remoteVideos[videoWanted].click();
}
}
}
};
window.onkeydown = function(e) {
if($("#chatspace").css("display") === "none") {
if(e.which === "T".charCodeAt(0)) {
if(isAudioMuted()) {
toggleAudio();
}
}
}
};
/**
*
* @param id indicates the popover associated with the shortcut
* @returns {string} the keyboard shortcut used for the id given
*/
my.getShortcut = function(id) {
for(var keycode in shortcuts) {
if(shortcuts.hasOwnProperty(keycode)) {
if (shortcuts[keycode].id === id) {
return " (" + shortcuts[keycode].character + ")";
}
}
}
return "";
};
return my;
}(KeyboardShortcut || {}));

View File

@ -42,6 +42,7 @@ function ColibriFocus(connection, bridgejid) {
this.bridgejid = bridgejid;
this.peers = [];
this.remoteStreams = [];
this.confid = null;
/**
@ -143,6 +144,7 @@ ColibriFocus.prototype.makeConference = function (peers) {
event.peerjid = jid;
}
});
self.remoteStreams.push(event.stream);
$(document).trigger('remotestreamadded.jingle', [event, self.sid]);
};
this.peerconnection.onicecandidate = function (event) {
@ -526,9 +528,11 @@ ColibriFocus.prototype.createdConference = function (result) {
}
}
bridgeSDP.raw = bridgeSDP.session + bridgeSDP.media.join('');
var bridgeDesc = new RTCSessionDescription({type: 'offer', sdp: bridgeSDP.raw});
var simulcast = new Simulcast();
var bridgeDesc = simulcast.transformBridgeDescription(bridgeDesc);
this.peerconnection.setRemoteDescription(
new RTCSessionDescription({type: 'offer', sdp: bridgeSDP.raw}),
this.peerconnection.setRemoteDescription(bridgeDesc,
function () {
console.log('setRemoteDescription success');
self.peerconnection.createAnswer(
@ -554,6 +558,24 @@ ColibriFocus.prototype.createdConference = function (result) {
endpoint: self.myMucResource
});
// signal (through COLIBRI) to the bridge
// the SSRC groups of the participant
// that plays the role of the focus
var ssrc_group_lines = SDPUtil.find_lines(media, 'a=ssrc-group:');
var idx = 0;
ssrc_group_lines.forEach(function(line) {
idx = line.indexOf(' ');
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
ssrcs.forEach(function(ssrc) {
elem.c('source', { ssrc: ssrc })
.up();
});
elem.up();
}
});
// FIXME: should reuse code from .toJingle
for (var j = 0; j < mline.fmt.length; j++)
{
@ -647,6 +669,7 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) {
sdp.removeMediaLines(i, 'a=rtcp-mux');
}
sdp.removeMediaLines(i, 'a=ssrc:');
sdp.removeMediaLines(i, 'a=ssrc-group:');
sdp.removeMediaLines(i, 'a=crypto:');
sdp.removeMediaLines(i, 'a=candidate:');
sdp.removeMediaLines(i, 'a=ice-options:google-ice');
@ -656,14 +679,20 @@ ColibriFocus.prototype.initiate = function (peer, isInitiator) {
sdp.removeMediaLines(i, 'a=setup:');
if (1) { //i > 0) { // not for audio FIXME: does not work as intended
// re-add all remote a=ssrcs
// re-add all remote a=ssrcs _and_ a=ssrc-group
for (var jid in this.remotessrc) {
if (jid == peer || !this.remotessrc[jid][i])
continue;
sdp.media[i] += this.remotessrc[jid][i];
}
// and local a=ssrc lines
sdp.media[i] += SDPUtil.find_lines(localSDP.media[i], 'a=ssrc').join('\r\n') + '\r\n';
// add local a=ssrc-group: lines
lines = SDPUtil.find_lines(localSDP.media[i], 'a=ssrc-group:');
if (lines.length != 0)
sdp.media[i] += lines.join('\r\n') + '\r\n';
// and local a=ssrc: lines
sdp.media[i] += SDPUtil.find_lines(localSDP.media[i], 'a=ssrc:').join('\r\n') + '\r\n';
}
}
sdp.raw = sdp.session + sdp.media.join('');
@ -865,6 +894,24 @@ ColibriFocus.prototype.updateChannel = function (remoteSDP, participant) {
expire: self.channelExpire
});
// signal (throught COLIBRI) to the bridge the SSRC groups of this
// participant
var ssrc_group_lines = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:');
var idx = 0;
ssrc_group_lines.forEach(function(line) {
idx = line.indexOf(' ');
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
change.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
ssrcs.forEach(function(ssrc) {
change.c('source', { ssrc: ssrc })
.up();
});
change.up();
}
});
var rtpmap = SDPUtil.find_lines(remoteSDP.media[channel], 'a=rtpmap:');
rtpmap.forEach(function (val) {
// TODO: too much copy-paste
@ -1029,20 +1076,33 @@ ColibriFocus.prototype.setRemoteDescription = function (session, elem, desctype)
if (!remoteSDP.media[channel])
continue;
var lines = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:');
if (lines.length != 0)
// prepend ssrc-groups
this.remotessrc[session.peerjid][channel] = lines.join('\r\n') + '\r\n';
if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length)
{
this.remotessrc[session.peerjid][channel] =
if (!this.remotessrc[session.peerjid][channel])
this.remotessrc[session.peerjid][channel] = '';
this.remotessrc[session.peerjid][channel] +=
SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:')
.join('\r\n') + '\r\n';
}
}
// ACT 4: add new a=ssrc lines to local remotedescription
// ACT 4: add new a=ssrc and s=ssrc-group lines to local remotedescription
for (channel = 0; channel < this.channels[participant].length; channel++) {
//if (channel == 0) continue; FIXME: does not work as intended
if (!remoteSDP.media[channel])
continue;
var lines = SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:');
if (lines.length != 0)
this.peerconnection.enqueueAddSsrc(
channel, SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc-group:').join('\r\n') + '\r\n');
if (SDPUtil.find_lines(remoteSDP.media[channel], 'a=ssrc:').length) {
this.peerconnection.enqueueAddSsrc(
channel,
@ -1312,6 +1372,9 @@ ColibriFocus.prototype.sendTerminate = function (session, reason, text) {
ColibriFocus.prototype.setRTCPTerminationStrategy = function (strategyFQN) {
var self = this;
// TODO(gp) maybe move the RTCP termination strategy element under the
// content or channel element.
var strategyIQ = $iq({to: this.bridgejid, type: 'set'});
strategyIQ.c('conference', {
xmlns: 'http://jitsi.org/protocol/colibri',
@ -1379,3 +1442,50 @@ ColibriFocus.prototype.setChannelLastN = function (channelLastN) {
}
};
/**
* Sets the default value of the channel simulcast layer attribute in this
* conference and updates/patches the existing channels.
*/
ColibriFocus.prototype.setReceiveSimulcastLayer = function (receiveSimulcastLayer) {
if (('number' === typeof(receiveSimulcastLayer))
&& (this.receiveSimulcastLayer !== receiveSimulcastLayer))
{
// TODO(gp) be able to set the receiving simulcast layer on a per
// sender basis.
this.receiveSimulcastLayer = receiveSimulcastLayer;
// Update/patch the existing channels.
var patch = $iq({ to: this.bridgejid, type: 'set' });
patch.c(
'conference',
{ xmlns: 'http://jitsi.org/protocol/colibri', id: this.confid });
patch.c('content', { name: 'video' });
patch.c(
'channel',
{
id: $(this.mychannel[1 /* video */]).attr('id'),
'receive-simulcast-layer': this.receiveSimulcastLayer
});
patch.up(); // end of channel
for (var p = 0; p < this.channels.length; p++)
{
patch.c(
'channel',
{
id: $(this.channels[p][1 /* video */]).attr('id'),
'receive-simulcast-layer': this.receiveSimulcastLayer
});
patch.up(); // end of channel
}
this.connection.sendIQ(
patch,
function (res) {
console.info('Set channel simulcast receive layer succeeded:', res);
},
function (err) {
console.error('Set channel simulcast receive layer failed:', err);
});
}
};

0
libs/jquery.autosize.js Executable file → Normal file
View File

0
libs/popover.js Executable file → Normal file
View File

View File

@ -128,7 +128,11 @@ dumpSDP = function(description) {
if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() { return this.peerconnection.localDescription; });
TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() {
var simulcast = new Simulcast();
var publicLocalDescription = simulcast.makeLocalDescriptionPublic(this.peerconnection.localDescription);
return publicLocalDescription;
});
TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { return this.peerconnection.remoteDescription; });
}
@ -149,6 +153,8 @@ TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
var self = this;
var simulcast = new Simulcast();
description = simulcast.transformLocalDescription(description);
this.trace('setLocalDescription', dumpSDP(description));
this.peerconnection.setLocalDescription(description,
function () {
@ -169,6 +175,8 @@ TraceablePeerConnection.prototype.setLocalDescription = function (description, s
TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
var self = this;
var simulcast = new Simulcast();
description = simulcast.transformRemoteDescription(description);
this.trace('setRemoteDescription', dumpSDP(description));
this.peerconnection.setRemoteDescription(description,
function () {
@ -208,6 +216,16 @@ TraceablePeerConnection.prototype.addSource = function (elem) {
$(elem).each(function (idx, content) {
var name = $(content).attr('name');
var lines = '';
tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
var semantics = this.getAttribute('semantics');
var ssrcs = $(this).find('>source').map(function () {
return this.getAttribute('ssrc');
}).get();
if (ssrcs.length != 0) {
lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
}
});
tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
tmp.each(function () {
var ssrc = $(this).attr('ssrc');
@ -254,6 +272,16 @@ TraceablePeerConnection.prototype.removeSource = function (elem) {
$(elem).each(function (idx, content) {
var name = $(content).attr('name');
var lines = '';
tmp = $(content).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
var semantics = this.getAttribute('semantics');
var ssrcs = $(this).find('>source').map(function () {
return this.getAttribute('ssrc');
}).get();
if (ssrcs.length != 0) {
lines += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
}
});
tmp = $(content).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]'); // can handle both >source and >description>source
tmp.each(function () {
var ssrc = $(this).attr('ssrc');
@ -413,6 +441,8 @@ TraceablePeerConnection.prototype.createAnswer = function (successCallback, fail
this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
this.peerconnection.createAnswer(
function (answer) {
var simulcast = new Simulcast();
answer = simulcast.transformAnswer(answer);
self.trace('createAnswerOnSuccess', dumpSDP(answer));
successCallback(answer);
},
@ -628,18 +658,43 @@ function getUserMediaWithConstraints(um, success_callback, failure_callback, res
constraints.video.mandatory.minFrameRate = fps;
}
var isFF = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
try {
RTC.getUserMedia(constraints,
function (stream) {
console.log('onUserMediaSuccess');
success_callback(stream);
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
if(failure_callback) {
failure_callback(error);
}
});
if (config.enableSimulcast
&& constraints.video
&& constraints.video.chromeMediaSource !== 'screen'
&& constraints.video.chromeMediaSource !== 'desktop'
&& !isAndroid
// We currently do not support FF, as it doesn't have multistream support.
&& !isFF) {
var simulcast = new Simulcast();
simulcast.getUserMedia(constraints, function (stream) {
console.log('onUserMediaSuccess');
success_callback(stream);
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
if (failure_callback) {
failure_callback(error);
}
});
} else {
RTC.getUserMedia(constraints,
function (stream) {
console.log('onUserMediaSuccess');
success_callback(stream);
},
function (error) {
console.warn('Failed to get access to local media. Error ', error);
if (failure_callback) {
failure_callback(error);
}
});
}
} catch (e) {
console.error('GUM failed: ', e);
if(failure_callback) {

File diff suppressed because one or more lines are too long

View File

@ -31,6 +31,15 @@ SDP.prototype.getMediaSsrcMap = function() {
}
channel.ssrcs[linessrc].lines.push(line);
});
tmp = SDPUtil.find_lines(self.media[channelNum], 'a=ssrc-group:');
tmp.forEach(function(line){
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
var ssrcGroup = new ChannelSsrcGroup(semantics, ssrcs);
channel.ssrcGroups.push(ssrcGroup);
}
});
}
return media_ssrcs;
}
@ -56,6 +65,32 @@ SDP.prototype.containsSSRC = function(ssrc) {
* @param otherSdp the other SDP to check ssrc with.
*/
SDP.prototype.getNewMedia = function(otherSdp) {
// this could be useful in Array.prototype.
function arrayEquals(array) {
// if the other array is a falsy value, return
if (!array)
return false;
// compare lengths - can save a lot of time
if (this.length != array.length)
return false;
for (var i = 0, l=this.length; i < l; i++) {
// Check if we have nested arrays
if (this[i] instanceof Array && array[i] instanceof Array) {
// recurse into the nested arrays
if (!this[i].equals(array[i]))
return false;
}
else if (this[i] != array[i]) {
// Warning - two different object instances will never be equal: {x:20} != {x:20}
return false;
}
}
return true;
};
var myMedia = this.getMediaSsrcMap();
var othersMedia = otherSdp.getMediaSsrcMap();
var newMedia = {};
@ -77,6 +112,32 @@ SDP.prototype.getNewMedia = function(otherSdp) {
newMedia[channelNum].ssrcs[ssrc] = othersChannel.ssrcs[ssrc];
}
})
// Look for new ssrc groups across the channels
othersChannel.ssrcGroups.forEach(function(otherSsrcGroup){
// try to match the other ssrc-group with an ssrc-group of ours
var matched = false;
for (var i = 0; i < myChannel.ssrcGroups.length; i++) {
var mySsrcGroup = myChannel.ssrcGroups[i];
if (otherSsrcGroup.semantics == mySsrcGroup
&& arrayEquals.apply(otherSsrcGroup.ssrcs, [mySsrcGroup.ssrcs])) {
matched = true;
break;
}
}
if (!matched) {
// Allocate channel if we've found an ssrc-group that doesn't
// exist in our channel
if(!newMedia[channelNum]){
newMedia[channelNum] = new MediaChannel(othersChannel.chNumber, othersChannel.mediaType);
}
newMedia[channelNum].ssrcGroups.push(otherSsrcGroup);
}
});
});
return newMedia;
}
@ -241,6 +302,22 @@ SDP.prototype.toJingle = function (elem, thecreator) {
tmp.xmlns = 'http://estos.de/ns/ssrc';
tmp.ssrc = ssrc;
elem.c('ssrc', tmp).up(); // ssrc is part of description
// XEP-0339 handle ssrc-group attributes
var ssrc_group_lines = SDPUtil.find_lines(this.media[i], 'a=ssrc-group:');
ssrc_group_lines.forEach(function(line) {
idx = line.indexOf(' ');
var semantics = line.substr(0, idx).substr(13);
var ssrcs = line.substr(14 + semantics.length).split(' ');
if (ssrcs.length != 0) {
elem.c('ssrc-group', { semantics: semantics, xmlns: 'urn:xmpp:jingle:apps:rtp:ssma:0' });
ssrcs.forEach(function(ssrc) {
elem.c('source', { ssrc: ssrc })
.up();
});
elem.up();
}
});
}
if (SDPUtil.find_line(this.media[i], 'a=rtcp-mux')) {
@ -578,6 +655,18 @@ SDP.prototype.jingle2media = function (content) {
media += SDPUtil.candidateFromJingle(this);
});
// XEP-0339 handle ssrc-group attributes
tmp = content.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function() {
var semantics = this.getAttribute('semantics');
var ssrcs = $(this).find('>source').map(function() {
return this.getAttribute('ssrc');
}).get();
if (ssrcs.length != 0) {
media += 'a=ssrc-group:' + semantics + ' ' + ssrcs.join(' ') + '\r\n';
}
});
tmp = content.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]');
tmp.each(function () {
var ssrc = this.getAttribute('ssrc');

View File

@ -15,6 +15,17 @@ function ChannelSsrc(ssrc, type) {
this.lines = [];
}
/**
* Class holds a=ssrc-group: lines
* @param semantics
* @param ssrcs
* @constructor
*/
function ChannelSsrcGroup(semantics, ssrcs, line) {
this.semantics = semantics;
this.ssrcs = ssrcs;
}
/**
* Helper class represents media channel. Is a container for ChannelSsrc, holds channel idx and media type.
* @param channelNumber channel idx in SDP media array.
@ -36,6 +47,12 @@ function MediaChannel(channelNumber, mediaType) {
* The maps of ssrc numbers to ChannelSsrc objects.
*/
this.ssrcs = {};
/**
* The array of ChannelSsrcGroup objects.
* @type {Array}
*/
this.ssrcGroups = [];
}
SDPUtil = {

View File

@ -119,6 +119,8 @@ JingleSession.prototype.accept = function () {
// FIXME: change any inactive to sendrecv or whatever they were originally
pranswer.sdp = pranswer.sdp.replace('a=inactive', 'a=sendrecv');
}
var simulcast = new Simulcast();
pranswer = simulcast.makeLocalDescriptionPublic(pranswer);
var prsdp = new SDP(pranswer.sdp);
var accept = $iq({to: this.peerjid,
type: 'set'})
@ -565,7 +567,10 @@ JingleSession.prototype.createdAnswer = function (sdp, provisional) {
initiator: this.initiator,
responder: this.responder,
sid: this.sid });
this.localSDP.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
var simulcast = new Simulcast();
var publicLocalDesc = simulcast.makeLocalDescriptionPublic(sdp);
var publicLocalSDP = new SDP(publicLocalDesc.sdp);
publicLocalSDP.toJingle(accept, this.initiator == this.me ? 'initiator' : 'responder');
this.connection.sendIQ(accept,
function () {
var ack = {};

View File

@ -0,0 +1,14 @@
/**
* Strophe logger implementation. Logs from level WARN and above.
*/
Strophe.log = function (level, msg) {
switch(level) {
case Strophe.LogLevel.WARN:
console.warn("Strophe: "+msg);
break;
case Strophe.LogLevel.ERROR:
case Strophe.LogLevel.FATAL:
console.error("Strophe: "+msg);
break;
}
};

View File

@ -22,7 +22,7 @@ var Prezi = (function (my) {
VideoLayout.setLargeVideoVisible(false);
$('#presentation>iframe').fadeIn(300, function() {
$('#presentation>iframe').css({opacity:'1'});
Toolbar.dockToolbar(true);
ToolbarToggler.dockToolbar(true);
});
});
}
@ -33,7 +33,7 @@ var Prezi = (function (my) {
$('#reloadPresentation').css({display:'none'});
$('#largeVideo').fadeIn(300, function() {
VideoLayout.setLargeVideoVisible(true);
Toolbar.dockToolbar(false);
ToolbarToggler.dockToolbar(false);
});
});
}

View File

@ -118,7 +118,7 @@ var RoomNameGenerator = function(my) {
*/
function generateWord()
{
return words[( Math.round(((new Date().getTime() / 1000) +Math.random()*1000) % 1008))];
return words[Math.floor(Math.random() * words.length)];
}
/**

669
simulcast.js Normal file
View File

@ -0,0 +1,669 @@
/*jslint plusplus: true */
/*jslint nomen: true*/
/**
* Created by gp on 11/08/14.
*/
function Simulcast() {
"use strict";
// TODO(gp) split the Simulcast class in two classes : NativeSimulcast and ClassicSimulcast.
this.debugLvl = 1;
}
(function () {
"use strict";
// global state for all transformers.
var localExplosionMap = {}, localVideoSourceCache, emptyCompoundIndex,
remoteMaps = {
msid2Quality: {},
ssrc2Msid: {},
receivingVideoStreams: {}
}, localMaps = {
msids: [],
msid2ssrc: {}
};
Simulcast.prototype._generateGuid = (function () {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return function () {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
};
}());
Simulcast.prototype._cacheVideoSources = function (lines) {
localVideoSourceCache = this._getVideoSources(lines);
};
Simulcast.prototype._restoreVideoSources = function (lines) {
this._replaceVideoSources(lines, localVideoSourceCache);
};
Simulcast.prototype._replaceVideoSources = function (lines, videoSources) {
var i, inVideo = false, index = -1, howMany = 0;
if (this.debugLvl) {
console.info('Replacing video sources...');
}
for (i = 0; i < lines.length; i++) {
if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
// Out of video.
break;
}
if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
// In video.
inVideo = true;
}
if (inVideo && (lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:'
|| lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:')) {
if (index === -1) {
index = i;
}
howMany++;
}
}
// efficiency baby ;)
lines.splice.apply(lines,
[index, howMany].concat(videoSources));
};
Simulcast.prototype._getVideoSources = function (lines) {
var i, inVideo = false, sb = [];
if (this.debugLvl) {
console.info('Getting video sources...');
}
for (i = 0; i < lines.length; i++) {
if (inVideo && lines[i].substring(0, 'm='.length) === 'm=') {
// Out of video.
break;
}
if (!inVideo && lines[i].substring(0, 'm=video '.length) === 'm=video ') {
// In video.
inVideo = true;
}
if (inVideo && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
// In SSRC.
sb.push(lines[i]);
}
if (inVideo && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
sb.push(lines[i]);
}
}
return sb;
};
Simulcast.prototype._parseMedia = function (lines, mediatypes) {
var i, res = [], type, cur_media, idx, ssrcs, cur_ssrc, ssrc,
ssrc_attribute, group, semantics, skip;
if (this.debugLvl) {
console.info('Parsing media sources...');
}
for (i = 0; i < lines.length; i++) {
if (lines[i].substring(0, 'm='.length) === 'm=') {
type = lines[i]
.substr('m='.length, lines[i].indexOf(' ') - 'm='.length);
skip = mediatypes !== undefined && mediatypes.indexOf(type) === -1;
if (!skip) {
cur_media = {
'type': type,
'sources': {},
'groups': []
};
res.push(cur_media);
}
} else if (!skip && lines[i].substring(0, 'a=ssrc:'.length) === 'a=ssrc:') {
idx = lines[i].indexOf(' ');
ssrc = lines[i].substring('a=ssrc:'.length, idx);
if (cur_media.sources[ssrc] === undefined) {
cur_ssrc = {'ssrc': ssrc};
cur_media.sources[ssrc] = cur_ssrc;
}
ssrc_attribute = lines[i].substr(idx + 1).split(':', 2)[0];
cur_ssrc[ssrc_attribute] = lines[i].substr(idx + 1).split(':', 2)[1];
if (cur_media.base === undefined) {
cur_media.base = cur_ssrc;
}
} else if (!skip && lines[i].substring(0, 'a=ssrc-group:'.length) === 'a=ssrc-group:') {
idx = lines[i].indexOf(' ');
semantics = lines[i].substr(0, idx).substr('a=ssrc-group:'.length);
ssrcs = lines[i].substr(idx).trim().split(' ');
group = {
'semantics': semantics,
'ssrcs': ssrcs
};
cur_media.groups.push(group);
} else if (!skip && (lines[i].substring(0, 'a=sendrecv'.length) === 'a=sendrecv' ||
lines[i].substring(0, 'a=recvonly'.length) === 'a=recvonly' ||
lines[i].substring(0, 'a=sendonly'.length) === 'a=sendonly' ||
lines[i].substring(0, 'a=inactive'.length) === 'a=inactive')) {
cur_media.direction = lines[i].substring('a='.length, 8);
}
}
return res;
};
// Returns a random integer between min (included) and max (excluded)
// Using Math.round() will give you a non-uniform distribution!
Simulcast.prototype._generateRandomSSRC = function () {
var min = 0, max = 0xffffffff;
return Math.floor(Math.random() * (max - min)) + min;
};
function CompoundIndex(obj) {
if (obj !== undefined) {
this.row = obj.row;
this.column = obj.column;
}
}
emptyCompoundIndex = new CompoundIndex();
Simulcast.prototype._indexOfArray = function (needle, haystack, start) {
var length = haystack.length, idx, i;
if (!start) {
start = 0;
}
for (i = start; i < length; i++) {
idx = haystack[i].indexOf(needle);
if (idx !== -1) {
return new CompoundIndex({row: i, column: idx});
}
}
return emptyCompoundIndex;
};
Simulcast.prototype._removeSimulcastGroup = function (lines) {
var i;
for (i = lines.length - 1; i >= 0; i--) {
if (lines[i].indexOf('a=ssrc-group:SIM') !== -1) {
lines.splice(i, 1);
}
}
};
Simulcast.prototype._explodeLocalSimulcastSources = function (lines) {
var sb, msid, sid, tid, videoSources, self;
if (this.debugLvl) {
console.info('Exploding local video sources...');
}
videoSources = this._parseMedia(lines, ['video'])[0];
self = this;
if (videoSources.groups && videoSources.groups.length !== 0) {
videoSources.groups.forEach(function (group) {
if (group.semantics === 'SIM') {
group.ssrcs.forEach(function (ssrc) {
// Get the msid for this ssrc..
if (localExplosionMap[ssrc]) {
// .. either from the explosion map..
msid = localExplosionMap[ssrc];
} else {
// .. or generate a new one (msid).
sid = videoSources.sources[ssrc].msid
.substring(0, videoSources.sources[ssrc].msid.indexOf(' '));
tid = self._generateGuid();
msid = [sid, tid].join(' ');
localExplosionMap[ssrc] = msid;
}
// Assign it to the source object.
videoSources.sources[ssrc].msid = msid;
// TODO(gp) Change the msid of associated sources.
});
}
});
}
sb = this._compileVideoSources(videoSources);
this._replaceVideoSources(lines, sb);
};
Simulcast.prototype._groupLocalVideoSources = function (lines) {
var sb, videoSources, ssrcs = [], ssrc;
if (this.debugLvl) {
console.info('Grouping local video sources...');
}
videoSources = this._parseMedia(lines, ['video'])[0];
for (ssrc in videoSources.sources) {
// jitsi-meet destroys/creates streams at various places causing
// the original local stream ids to change. The only thing that
// remains unchanged is the trackid.
localMaps.msid2ssrc[videoSources.sources[ssrc].msid.split(' ')[1]] = ssrc;
}
// TODO(gp) add only "free" sources.
localMaps.msids.forEach(function (msid) {
ssrcs.push(localMaps.msid2ssrc[msid]);
});
if (!videoSources.groups) {
videoSources.groups = [];
}
videoSources.groups.push({
'semantics': 'SIM',
'ssrcs': ssrcs
});
sb = this._compileVideoSources(videoSources);
this._replaceVideoSources(lines, sb);
};
Simulcast.prototype._appendSimulcastGroup = function (lines) {
var videoSources, ssrcGroup, simSSRC, numOfSubs = 3, i, sb, msid;
if (this.debugLvl) {
console.info('Appending simulcast group...');
}
// Get the primary SSRC information.
videoSources = this._parseMedia(lines, ['video'])[0];
// Start building the SIM SSRC group.
ssrcGroup = ['a=ssrc-group:SIM'];
// The video source buffer.
sb = [];
// Create the simulcast sub-streams.
for (i = 0; i < numOfSubs; i++) {
// TODO(gp) prevent SSRC collision.
simSSRC = this._generateRandomSSRC();
ssrcGroup.push(simSSRC);
sb.splice.apply(sb, [sb.length, 0].concat(
[["a=ssrc:", simSSRC, " cname:", videoSources.base.cname].join(''),
["a=ssrc:", simSSRC, " msid:", videoSources.base.msid].join('')]
));
if (this.debugLvl) {
console.info(['Generated substream ', i, ' with SSRC ', simSSRC, '.'].join(''));
}
}
// Add the group sim layers.
sb.splice(0, 0, ssrcGroup.join(' '))
this._replaceVideoSources(lines, sb);
};
// Does the actual patching.
Simulcast.prototype._ensureSimulcastGroup = function (lines) {
if (this.debugLvl) {
console.info('Ensuring simulcast group...');
}
if (this._indexOfArray('a=ssrc-group:SIM', lines) === emptyCompoundIndex) {
this._appendSimulcastGroup(lines);
this._cacheVideoSources(lines);
} else {
// verify that the ssrcs participating in the SIM group are present
// in the SDP (needed for presence).
this._restoreVideoSources(lines);
}
};
Simulcast.prototype._ensureGoogConference = function (lines) {
var sb;
if (this.debugLvl) {
console.info('Ensuring x-google-conference flag...')
}
if (this._indexOfArray('a=x-google-flag:conference', lines) === emptyCompoundIndex) {
// Add the google conference flag
sb = this._getVideoSources(lines);
sb = ['a=x-google-flag:conference'].concat(sb);
this._replaceVideoSources(lines, sb);
}
};
Simulcast.prototype._compileVideoSources = function (videoSources) {
var sb = [], ssrc, addedSSRCs = [];
if (this.debugLvl) {
console.info('Compiling video sources...');
}
// Add the groups
if (videoSources.groups && videoSources.groups.length !== 0) {
videoSources.groups.forEach(function (group) {
if (group.ssrcs && group.ssrcs.length !== 0) {
sb.push([['a=ssrc-group:', group.semantics].join(''), group.ssrcs.join(' ')].join(' '));
// if (group.semantics !== 'SIM') {
group.ssrcs.forEach(function (ssrc) {
addedSSRCs.push(ssrc);
sb.splice.apply(sb, [sb.length, 0].concat([
["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
});
//}
}
});
}
// Then add any free sources.
if (videoSources.sources) {
for (ssrc in videoSources.sources) {
if (addedSSRCs.indexOf(ssrc) === -1) {
sb.splice.apply(sb, [sb.length, 0].concat([
["a=ssrc:", ssrc, " cname:", videoSources.sources[ssrc].cname].join(''),
["a=ssrc:", ssrc, " msid:", videoSources.sources[ssrc].msid].join('')]));
}
}
}
return sb;
};
Simulcast.prototype.transformAnswer = function (desc) {
if (config.enableSimulcast && config.useNativeSimulcast) {
var sb = desc.sdp.split('\r\n');
// Even if we have enabled native simulcasting previously
// (with a call to SLD with an appropriate SDP, for example),
// createAnswer seems to consistently generate incomplete SDP
// with missing SSRCS.
//
// So, subsequent calls to SLD will have missing SSRCS and presence
// won't have the complete list of SRCs.
this._ensureSimulcastGroup(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Transformed answer');
console.info(desc.sdp);
}
}
return desc;
};
Simulcast.prototype.makeLocalDescriptionPublic = function (desc) {
var sb;
if (!desc || desc == null)
return desc;
if (config.enableSimulcast) {
if (config.useNativeSimulcast) {
sb = desc.sdp.split('\r\n');
this._explodeLocalSimulcastSources(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Exploded local video sources');
console.info(desc.sdp);
}
} else {
sb = desc.sdp.split('\r\n');
this._groupLocalVideoSources(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Grouped local video sources');
console.info(desc.sdp);
}
}
}
return desc;
};
Simulcast.prototype._ensureOrder = function (lines) {
var videoSources, sb;
videoSources = this._parseMedia(lines, ['video'])[0];
sb = this._compileVideoSources(videoSources);
this._replaceVideoSources(lines, sb);
};
Simulcast.prototype.transformBridgeDescription = function (desc) {
if (config.enableSimulcast && config.useNativeSimulcast) {
var sb = desc.sdp.split('\r\n');
this._ensureGoogConference(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Transformed bridge description');
console.info(desc.sdp);
}
}
return desc;
};
Simulcast.prototype._updateRemoteMaps = function (lines) {
var remoteVideoSources = this._parseMedia(lines, ['video'])[0], videoSource, quality;
if (remoteVideoSources.groups && remoteVideoSources.groups.length !== 0) {
remoteVideoSources.groups.forEach(function (group) {
if (group.semantics === 'SIM' && group.ssrcs && group.ssrcs.length !== 0) {
quality = 0;
group.ssrcs.forEach(function (ssrc) {
videoSource = remoteVideoSources.sources[ssrc];
remoteMaps.msid2Quality[videoSource.msid] = quality++;
remoteMaps.ssrc2Msid[videoSource.ssrc] = videoSource.msid;
});
}
});
}
};
Simulcast.prototype.transformLocalDescription = function (desc) {
if (config.enableSimulcast && !config.useNativeSimulcast) {
var sb = desc.sdp.split('\r\n');
this._removeSimulcastGroup(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Transformed local description');
console.info(desc.sdp);
}
}
return desc;
};
Simulcast.prototype.transformRemoteDescription = function (desc) {
if (config.enableSimulcast) {
var sb = desc.sdp.split('\r\n');
this._updateRemoteMaps(sb);
this._removeSimulcastGroup(sb); // NOTE(gp) this needs to be called after updateRemoteMaps!
this._ensureGoogConference(sb);
desc = new RTCSessionDescription({
type: desc.type,
sdp: sb.join('\r\n')
});
if (this.debugLvl && this.debugLvl > 1) {
console.info('Transformed remote description');
console.info(desc.sdp);
}
}
return desc;
};
Simulcast.prototype.setReceivingVideoStream = function (ssrc) {
var receivingTrack = remoteMaps.ssrc2Msid[ssrc],
msidParts = receivingTrack.split(' ');
remoteMaps.receivingVideoStreams[msidParts[0]] = msidParts[1];
};
Simulcast.prototype.getReceivingVideoStream = function (stream) {
var tracks, track, i, electedTrack, msid, quality = 1, receivingTrackId;
if (config.enableSimulcast) {
if (remoteMaps.receivingVideoStreams[stream.id])
{
receivingTrackId = remoteMaps.receivingVideoStreams[stream.id];
tracks = stream.getVideoTracks();
for (i = 0; i < tracks.length; i++) {
if (receivingTrackId === tracks[i].id) {
electedTrack = tracks[i];
break;
}
}
}
if (!electedTrack) {
tracks = stream.getVideoTracks();
for (i = 0; i < tracks.length; i++) {
track = tracks[i];
msid = [stream.id, track.id].join(' ');
if (remoteMaps.msid2Quality[msid] === quality) {
electedTrack = track;
break;
}
}
}
}
return (electedTrack)
? new webkitMediaStream([electedTrack])
: stream;
};
Simulcast.prototype.getUserMedia = function (constraints, success, err) {
// TODO(gp) what if we request a resolution not supported by the hardware?
// TODO(gp) make the lq stream configurable; although this wouldn't work with native simulcast
var lqConstraints = {
audio: false,
video: {
mandatory: {
maxWidth: 320,
maxHeight: 180
}
}
};
if (config.enableSimulcast && !config.useNativeSimulcast) {
// NOTE(gp) if we request the lq stream first webkitGetUserMedia fails randomly. Tested with Chrome 37.
navigator.webkitGetUserMedia(constraints, function (hqStream) {
// reset local maps.
localMaps.msids = [];
localMaps.msid2ssrc = {};
// add hq trackid to local map
localMaps.msids.push(hqStream.getVideoTracks()[0].id);
navigator.webkitGetUserMedia(lqConstraints, function (lqStream) {
// add lq trackid to local map
localMaps.msids.push(lqStream.getVideoTracks()[0].id);
hqStream.addTrack(lqStream.getVideoTracks()[0]);
success(hqStream);
}, err);
}, err);
} else {
// There's nothing special to do for native simulcast, so just do a normal GUM.
navigator.webkitGetUserMedia(constraints, function (hqStream) {
// reset local maps.
localMaps.msids = [];
localMaps.msid2ssrc = {};
// add hq stream to local map
localMaps.msids.push(hqStream.getVideoTracks()[0].id);
success(hqStream);
}, err);
}
};
Simulcast.prototype.getRemoteVideoStreamIdBySSRC = function (primarySSRC) {
return remoteMaps.ssrc2Msid[primarySSRC];
};
Simulcast.prototype.parseMedia = function (desc, mediatypes) {
var lines = desc.sdp.split('\r\n');
return this._parseMedia(lines, mediatypes);
};
}());

View File

@ -1,45 +1,45 @@
var smiley1 = "data:image/gif;base64,R0lGODlhGAAaANU/ALW3uYWHicLExkRERurr7PaQcv3VxHR1d2VmafiojFZXWdna2/vKtzw7PZaYm+Pk5f39/SonKO0fJfJrTvX29vLy89PU1qKkpqmrrfn6+qapq5yeoe9FMo+RlGtsb/7v6MzOz6yusf/593+Ag5+hpHt9f/q8pq6ws4uMj29xc/f3+O/w8PP09Ly+wNXX2O3t7s/Q0i4sLf/7+P/28l1dX01NT+jo6eDh4jMyM6Kfn/3f0P/8+t7e3yMfIP///////yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRERGQjFEQzA1NjMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRERGQjFEQjA1NjMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgA/ACwAAAAAGAAaAAAG/0CfcEisEFjEpFJYwXgavSgOQSIsiZALNEorjVKDXgS1urI80QHphRUgejWXMvOObVRJyjAUw8GSHXA8QgJEGhh6PiA4DVZCDxE0ZSoOAUQUBxEKHiMXPSlDHQM2FqIKbEk8AAJsJT0gQjUAACMBJ4NKBACOkKAuNRBCKzU9LUkQxDV4PikRTSh7UQp5NTgeiSc9ArZDEMFXEBXfCz0bBydX6cI9KB4b6ukEPQEHoMcMBQUMM99EFj0OUMSwkWSGCQkSJsxY4glAi3lDdvjwRvHKmwcVcESANRGeEBc9aAhB0WPADY9D0AAQBmXAApQhQvYTEKFHDA39lrSI0OAkkToTNXsoaJFTCIEAPRr8USJgC5wAG0JgcJAihlCfS2y4isKV6wANGTwuGIGjaw+wKIdQENDBg4IR6YIAACH5BAUKAD8ALAMACAASAA4AAAZnwJ8QsxAahTzH7fjj/RQCJosmZB0dkRiASUD1DkXjI5ezQY6Q24XnY1IozLh8Tv+JjAmGrM7gSIQSEhwJO3I+Bnk6OiYFEwx3RjBCbXEze0ZFD3VMB0ibQgIRQhEXmxYDn0IvHThCQQAh+QQFZAA/ACwDAAgAEgAOAAAGZsCf8MQTGoUEAOH4W/16LSakJlQdQ72egvnoPQVHQLb3OK7GliOrFutAmI6ahvmDvOl4pimP3+NFeAkcBUwSEhMfRiBCBYlMHxOHQi4/RXwyBYQ/HkJpfEYYXkIknz8AEaVGBAGiQQA7";
var smiley1 = "images/smileys/smiley1.gif";
var smiley2 = "data:image/gif;base64,R0lGODlhGQAaALMMAP//////zP/MzP/Mmf+ZZv9mZszMzJnMzJmZmWaZmWZmZjMzM////wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoyNkQ0RkQxMTA1QkMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoyNkQ0RkQxMDA1QkMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAMACwAAAAAGQAaAAAErhDICQxSKy+FDP2UgWlKqS0ISCGkF7Kb+42KrI42AKfqB4cZg6XTs1g2E4wHcUF9RJsO5iBxhkoUq8SQ0S1CCY+sc+F5RWaARqVxcS8ybiZd0Zg33+wc1E52VxwqBmEUOD2HhXuIiBlYHwIEBZIEA4JRCzYBA5OSBZU+KHKLAWyYABgKiyAsqXUxqltBEzBah3J0tDWHOyq0cwdCFiOmRcMnJ62LUMeysBMHTYogEQAh+QQFCgAMACwAAAAAAQABAAAEApBFACH5BAVkAAwALAcAEAAMAAQAAAQRcJRJ5hQA1JCzuF3YSWI5CBEAOw==";
var smiley2 = "images/smileys/smiley2.gif";
var smiley3 = "data:image/gif;base64,R0lGODlhFAAaALMJAP///8zMzJnMzJmZzJmZmWZmmWZmZjMzZjMzM////wAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1NTM4Q0EwNjA1QjMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGQzBBMTE5MjA1QjIxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAJACwAAAAAFAAaAAAEmBDISau9MxiECAYBYWzIYYidpXFjGpIBFSBGDIgrvRnUSBEsj2QmmCBsQw4yMBPOkKCdyHDgOI8VoGlEQMUGqV6YkgIKJ9IuYQCMFbA/jlwu3EBvViaTYGtnS3c8EicWQAgFfBqCUVkkciZQAl0ZeXp3UTVDPh9DOBJ+nIOLhKEgYSKlgwWfi6GoAAWtnAIdTamDcpecXboRACH5BAUKAAkALAEAAAATABoAAASYEMhJg0GI0ErMRYfRZZuFeWQwIsEUIEYLdCZ8GZNHEacmvQIJq4KRAQIvzct4vHUMB4xyuAN5RKPAgES5+CgZ3lfiJJgHvECBOuFh3lLAhTmTIpGEVnrDO9BxcmMSbgV5FoAWfB9vIUYCZi52d3SHMocbmCoxg2yYOYCBniVcHaIbBgWDoKYzgAWrpgIZSaxdb3SsZri1phEAIfkEBRQACQAsAQAAABMAGQAABJMQyBkCJabYOQk2BoKA5CYFIfgRQIAh7AmaBoGK+IaZQDjagQFI4uEACDDjrhfr/DwfQyVjvJFAiEMlyUFqKuAaskkcgTdiBK+FEx1KyPVABF2xyccsz0U0GHs/GygyPC8iIAU1FAU2ZTAeiXgoUmV4f5MWk3+XigAClJtdlChrm0ukoRyoQqWcNjOpXTiWqS6tABEAOw==";
var smiley3 = "images/smileys/smiley3.gif";
var smiley4 = "data:image/gif;base64,R0lGODlhGAAaALMNAP/////MzP/Mmf+ZZv9mM/8zM8zMzJnMzJmZzJmZmWaZmWZmZjMzM////wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGNDE3QzIzNDA1NjYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGNDE3QzIzMzA1NjYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgANACwAAAAAGAAaAAAEqRDISY01NGuZFvvgkmxZAjJJcqTgSCogshnfssEoOXl29R06iskl+WB4mIkpB/AkaUkOQwYYRmk9Ra+yOHo+LtMxWmWILJUEhjZiCLvBidmwbcYpzvodz6DvN05uFAWEhYYFfABMEgGHhgIUZk2CEwOOBQNCOSZ6AoYEkHxJYBoBARucSqR3NH18RkGtZBJfcxoGOLOqJwu9tXA6dCfDRHEGHcO6e8cqJBEAIfkEBQoADQAsAAAAAAEAAQAABAKwRQAh+QQFCgANACwGAAUADgAKAAAENHCxZYAFibGEbUbeVl2WQZmSxmFTMl6Gm1BkbS12ru95QASWAmEgEAQEA0KhcFEun0+CJQIAOw==";
var smiley4 = "images/smileys/smiley4.gif";
var smiley5 = "data:image/gif;base64,R0lGODlhGQAaAMQRAP/////MzP/Mmf+Zmf+ZZv9mZv9mM/8zM8zM/8zMzJnMzJmZzJmZmWaZmWZmmWZmZjMzM////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1MjQzQUI1MzA1REIxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1MjQzQUI1MjA1REIxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjJCMkQwNzI5MDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgARACwAAAAAGQAaAAAFyyAgjmLCMM/DKGTrAigkz/KTvGTyzMxdMjUcIMET7h6IVy3ha5lMkIfraNoxRjoIYwGL5rxYlSh68u2aQCfKdGI4xAAwAD4k43xAH4SkFQ5LfTpYTS8JDVdxUgqIfyMBAwQEASIMDQ0iKTCXQw0+BQegoAZDDoiZKCILDjcGoa6jfFJEWAADrrcDsXWMALe3sLuYeyIBBwUCLQIEwGeUYAGTfnVSI0HSI81YS9dA1CRAMrxTci3g4U0IUOQuWTTufdw77+LXVTTX+EIhACH5BAUKABEALAYACgAMAAgAAAUoYCRGwUAQwTgWR9saqnGo0QFHw0zXQ72PhlRBACgWBQRRoEgzMn+iEAAh+QQFFAARACwGAAoADAAIAAAFKGACjGQpksFAEMHInMUhywaQiMas18Cg/wPA72cIHAqCEkBAKLaUpRAAOw==";
var smiley5 = "images/smileys/smiley5.gif";
var smiley6 = "data:image/gif;base64,R0lGODlhGgAVALMJAP//////zP/MzP/Mmf+Zmf+ZZv9mZv9mM/8zM////wAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowQjUwOTkwMDA1QjgxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowQjUwOThGRjA1QjgxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAJACwAAAAAGgAVAAAEQxDISau9OOvNu/9gyAmFMQCEYQigcBjEe6QI6xWHNNSS+vkSG8BQ+OyEQd4nFZi4CCIBgUU6iYICQfPK7Xq/4LB4EwEAIfkEBQoACQAsBAADABMADwAABGgQyEAKERKIerMUBiIe2HCIyEFkwmlUKSEaxomsQGgEUoHiAR9JIBp4QgUP4DSQkTyE5/HmxGQoyhxiQNxmlcSnL/nNICchY9nJ+9Ss4BR8UjDMNQd7eVCAtwptZQBcAQIFaoJXAncZEQAh+QQFZAAJACwAAAAAGgAVAAAEoRDIOcgIcwpSiMjgcCDkQWQFqRYYSKhwAQQGrB7f9JIGt6Y8w4inWVGGvBwAiDgtSy3JDmFQSoAHiTETADpBQ4GgZJUMqNFMDSE4I3AggKAXl6zFpUFdXA8MJUNfdXE7MgA7BoODdxM1JoouRBojBmmDAiNwGZhUkHYkZRI0CIaLoJAvpSApmpBzqleklpADBUoFB3qedQMDG4K7cQECsyARADs=";
var smiley6 = "images/smileys/smiley6.gif";
var smiley7 = "data:image/gif;base64,R0lGODlhIwAhAOZ/AP/69f/9+e4yKf/Zqf/jwfy2U/aSdPy0TP/r0v/u2bm7vYiKjP/16v/pzP29Z//x4vn5+v7Ge/R3WZmbnUlJS+nq6/Hx8lJSVbGztYSGiSUiI93e32prbfu8pmJjZnJzdjU0NSwpKv/48Hl7fv/esjw7PVtcXvz9/YGDhc3P0JSWmPy6XZaYm/7JhdjZ2/3i1cLDxezt7be5u8XGyH1/gf/Um/7t5UFBQvvDrf/mx9DS03R2eGVmaP/z5f3XxsvNz6Kkpnd5fP3Bb/yyR0dHSf/v3FVWWNbX2Y6QkiglJv///fuwQO7v8E1OUOPk5eDi4//47//8909QUufo6e0eJNvc3i8sLqSmqfb29//37e/w8GZoap6gotna27y9wPyyQ//05+rr7OXm5/P09IeJi9TV14yOkF1eYFdYWs/Q0j4+P/vItLS2uO89LpGTljEvMK6ws/q3n19gYqqsr+bn6Ovs7PFeQ/ijh+Lj5P/28aaoq/3Cc9HT1CMfIP///////yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZD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";
var smiley7 = "images/smileys/smiley7.gif";
var smiley8 = "data:image/gif;base64,R0lGODlhGQAhAOZ/AOrq6//u2/jq5EtLTfr6+/7jyearlNqHc/39/f/27jQzNLGztSUiI3JzdcLExv7as+e6riwqK1laXJCSlYSGiMW9tv/69ctaTjo5O/zRqbi6vICBhP7To2lqbNna21JSVfX19fLy85+ho7O1uKyuseTl5v7LlGFjZcrLzYqMjnZ3eu/Wznp7ft7g4aurrNvc3qSmqdJzY//x4urEt/3FiaKkpu7cypeanNTV15OVmEFBQvLc08bIyiglJo6Qkv7Omba4umxtcHd4e9HT1KeprJqcnmZoaoyOkWVmaHx+gK6wskRERkdHSU1OUP/fvVxdX0JCRG9wcy8tLvf3+GRlZ19gYjg2OO/w8N7f4H6AgtfY2mBhY09QUtmThP/n0FdYWv7Yrj49P7y9wP/gvb/Bw/Xg1rKoofPo3czO0PuwXv/q04aIi/y/febn6Ojo6fv29PXUwfbOr+/KuVVVV+zn4tXX2NfAuDIwMfXFntDS09PU1ufVy9rb2yMfIP///////yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZD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";
var smiley8 = "images/smileys/smiley8.gif";
var smiley9 = "data:image/gif;base64,R0lGODlhGQAcALMJAP///8zMzJnMzJmZzJmZmWaZmWZmZjMzZjMzM////wAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo5OEFGQ0QwRTA1QkExMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo5OEFGQ0QwRDA1QkExMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAJACwAAAAAGQAcAAAEuRDIKYUxKCNDqPeEtl1Y1n1UiZxUUAYooLFoYH6YMQSwFxSXEKInCZFyxMGKx8tQMkRAgBUtriS2asyDKRooBK3UkLySJ0bUa7KRPTHaA4fYRaAEH5qkvu3v7UN+W10cHy5rKAgHAF1cBGRpPm0CbU8AHWRnKYEyVxOQLoxRNnZWGwRhqC5TF5t4n04XPKJfpYyVmx22IVMdLrhcnC0HjzeDBR8imluhm8aCwqTAfjY6IXqCaM/Z3CgRACH5BAUKAAkALAUADAAMAA8AAARCEEgQjEEozGmICQRibAACEMD3bR9RpRXhyhVojVagqWVXTiFQKEOijDCFIvEyYrVOREkAg5GKBkPqZnigNovgMCACACH5BAUKAAkALAUADAAMAA4AAAQ/EMhJKwjGIBSsIUZAIEaFAAQQhlRIYCpGvDMmZmcWdCuwpSfJSDTiWGCaggUxQ5Vam6BREogGq4ZBcfMU/igRADs=";
var smiley9 = "images/smileys/smiley9.gif";
var smiley10 = "data:image/gif;base64,R0lGODlhFwAbAKIAAP///8zMzJnMzJmZzJmZmWZmmWZmZjMzMyH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpFNUM2RjcwRDA1NjcxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpFNUM2RjcwQzA1NjcxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQECgAAACwAAAAAFwAbAAADhQi63P4wyklVCPWRc0hmQScc2AcEhtKZZwoYnoliMwsrhFFmW5kPkwBhuCFZhpGQwcA5FHYvKAOJY0JzEOHlYgUZHcomBzr4NmBb4WrRexyAJ3WsCtksl5w5APsIEdIHLgoDgg0hO0U7NxAFgRwwOlyFD0wEA2p3enWBS0MCFxlaLKOkDAkAIfkEBAoAAAAsBAABABEAGAAAA3gIuhDBEJpzSIyk5neXKUrVeRZgGKNSlMPBdQSqnGkgNvSYlUD8BqfP5KYgWGzCIU8BDBgXTsprZnD+pBLXYUjBMiYno3iyNFHKGdA3OFZKqhQ2WFKwLW0HieXjMJ4OAgs+DV1bBECHMS6Chk5gjQxAW2EODh2VKQkAIfkEBGQAAAAsAwAAABMAGwAAA4wIuhr8EIRDSYzmkOwu0xLoNUdnGONSKsSRKuJkpefSdl47Aze9sgcUg0AkZkQNIYDCZBJwLFRAGWgFIbLqY/L7uAyCgHhDgcJKzfQAQjmJ31ylKrNbZOShk6a6OdZhBVxMJ3dsFkFQXGxrBwUMU2VDLjxBehVIOjZMgSFGFY9BAgJkg39AbU9mF2IvCQA7";
var smiley10 = "images/smileys/smiley10.gif";
var smiley11 = "data:image/gif;base64,R0lGODlhGQAaAMQAAM/R0u4oKP///yQgIYeJjHFyda2vsTs6POjo6fP09PikiPJmSvr6+/7q4P3czpOWmP/49Z+hpMPFx//y7GFiZfvAqf39/fb29+Hi47i6vNna3P/8+VhZW+/v8AAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0MjAyMUUzMDA1RkMxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozREVBMjcwRTA1RTYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MjlCMkQwNzI5MDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQECgAAACwAAAAAGQAaAAAF/6AgjgJAUMcwHBwhWWQ8SpS6cgWqHgYsCwyCrSC5CHyaR4qCkV0KKg7g+ENAD5qYcPBg/GKRFWIEUEm+v8eAMoKe0TIL1CBAcEeYtwwxFWEGHAwGBwl+KREyZQMEI0ISBYgiYSodMWoqYyWLUiIWBjZ0JJcDTUAsB5UiHSkFqSMaKYwjHClGIwyZe1kkNQO7cGg1PMBoFgcHFBw+xDEaa0IZJBbLxWoPZQdjyxAbJBswPgkpWTUcrhAKC+oLAQrdI2oFIormIg3qAfkLFRBkKxo+JnGQ4EUAhAYN+pEAkCJUIxscMjCgJiLBpQgUBUzaQcCAhI8RCqQ4EO2Lhl42UhWqKJALzSOVO0oxE9DBAAFaKjD+CAEAIfkEBAoAAAAsAgAQABEABwAABUVgJozWZo1oKhzMuC1LBaVONW3Dc0JL4MO9wKIhGAwKCIGlEvwpNqOI8RA5jSaNmephHHAyLWUqobMYuoMDwSBpRwqHQQgAIfkEBGQAAAAsAgAQABEABwAABUlgJgjNomxjOm6VciBkECwVpDrL0gxcJ+CyWU62cAgGPIwAUlkEZwqIRRBBHiKM1KRhUwkISF4mK5imEo+yITw4EAySeKRwGIQAADs=";
var smiley11 = "images/smileys/smiley11.gif";
var smiley12 = "data:image/gif;base64,R0lGODlhGQAaAMQRAP//////zP/MzP/Mmf+Zmf+ZZv9mZv8zM8z//8zMzMyZmZnMzJmZzJmZmWZmmWZmZjMzM////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3MDUyNTUzNTA1ODgxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3MDUyNTUzNDA1ODgxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgARACwAAAAAGQAaAAAFwCAgjmLSPOjjNGTrJg8kQ+j8JG4byw0+wrycaMcSAh25na9lQgGLP9mStID0ErjYVCtcuBC0qM/UE44aEN8JgIiVzaMwYAV4QHOJYB0yxE3xVmdpAHx4LoUlg4Mtej8keTiLPzd3LgqDdnA5BABoOGiaLgeEhXkPoSMGBqZxgZoBBgcCXCIMjSIDASICBQcHBGinJDvCAL2+yAcDeYjDM0UEqgYFA51SQjs8WFgMaDR/Ld4z47dw4uSoTCdK6e0kIQAh+QQFCgARACwIAAoADwAJAAAFHGAkjuQYlGiqRqegrGIgCA2MBqctCjrL27McLAQAIfkEBQoAEQAsBwAKABAACQAABR1gJI6kGAhCqYqooKwlOsB0rZ6pHcn6Psy6QE8UAgAh+QQFZAARACwIAAoADgAIAAAFE2AkjmQklOhIEGnJtnAsk+v8yiEAOw==";
var smiley12 = "images/smileys/smiley12.gif";
var smiley13 = "data:image/gif;base64,R0lGODlhGAAYAMQRAP//////zP/MzP/Mmf+Zmf+ZZv9mZv9mM/8zM8zMzJmZzJmZmWaZmWZmmWZmZjMzZjMzM////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo2QjQ5NEEwODA1QjExMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo2QjQ5NEEwNzA1QjExMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgARACwAAAAAGAAYAAAFoyAgjokDnSjkLGPrmueazAusuCTKJDiQnCzc6cHrjUzBo8qIQ+YczN5p1IBEfYtF8ReEFBUoRzFbgwRNPiu2yAwuvAnG1cjNzo0q+124SroCAgQEA1IrD0YCBQcIB4QtPw0/bC0DBQYHBY4jbyxLOAIGlgUCL14ATp8Cqi4/UGlme6emIm+wc2+uSiqTOLg9MLu9MW0pKg7AtkwlxWG8UWUpRiEAIfkEBQoAEQAsCQALAAcACwAABSRgNBDEIETCgSDHQQgrEiFFGt0GERhtO6CiUuBGLBqPSOKjGAIAIfkEBWQAEQAsCAAHAAkADAAABS1gJI6OMyZjqqrBILyjgMzzIR6ISA9GniKFnmoWoOkQhAiBZhQVmIKUYDAYhQAAOw==";
var smiley13 = "images/smileys/smiley13.gif";
var smiley14 = "data:image/gif;base64,R0lGODlhGQAaAMQTAP/////MzP/Mmf+Zmf+ZZv9mZv8zM8zM/8zMzJnMzJmZmWbM/2aZmWZmmWZmZjOZzDNmZjMzZjMzM////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo3QjY3RTVCNTA1QjYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3QjY3RTVCNDA1QjYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFFAATACwAAAAAGQAaAAAF6iAgjkDiOFIqOQrpuoq6nmjavmQt3SRSIziAiodD2F4oBzA4MkqWopiDCfCpaqSUj/VyNhiNyLH6FJnKIqPjwWZjAYppWsGQA1DtPGR3B6ppSysLAAt5DCtCUUESDQmEhimJVIxsBwAHbIcSkkx4DyNse5toIwE9Kw+WmA0rm1wkBQWnjGBiCRIRd5skAgYDPTpKaiWIJAQGBQIBAQOyAIcnaGMjAgUG1wRVES1OIzF8LssjSlUoRDV2OK07cS46RCQxN+3uMgwI+PhxLEm7MDIAhwRi8i1gHAVxijFBkDDFFIQIX1EpwlBhCAAh+QQFFAATACwMAAMACwAXAAAFdeA0IY4kiehoNkwTTYrqPDTtjGWtQ6WzAAvUgzGRNBLAFHHSoB0Ah1TuAaiKILIUU+IAIFatiIRUJZm4CICoymZw1esAIqJQx6RpRENbLEkUdygKf1UKNygIMD0nKAA3iQCMKCeGXHwwhoEiCIlvWmxsn6ATIQAh+QQFFAATACwKAAMADQAWAAAFieAkTkCJjCjiiJKDSNMZww3TRJICA+/0oI5cLvgoFkUQx6q1ACyOE0ZuImkknL6iFEBtFA+Ag1YCUEqKJUAxyX05HmAxFcGdVW2RWF2kkvhdJShALSQyKDk8V4ITgAgNLjCHQSI6EgkoOgolCiJ7MQ6cEjgkgqB0AGSelApBDoGLCqyaizE6KyIhADs=";
var smiley14 = "images/smileys/smiley14.gif";
var smiley15 = "data:image/gif;base64,R0lGODlhHQAaAMQSAP//////zP/MzP/Mmf+Zmf+ZZv9mZv9mM/8zM8zM/8zMzJnMzJmZzJmZmWZmmWZmZjMzZjMzM////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo1REJDRURCMDA1ODYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo1REJDRURBRjA1ODYxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgASACwAAAAAHQAaAAAF6SAgjiSgNE+kRk+jlDCspOtjr1ETx039kifV47cD0IZFo4oI6+WOTJJzp1i6FKcHY9d7xFJRma01iyxguSQQFalCSEfdKFw69V4ob7ttgqTVVToQVQAuDTpsciUpiiwzAA6IjyKHMisJIikOaRFGAJ0Mh2FVWCM0RicnRiieMjApZDlyd4RqIksRCQtEqny2VV6sTQ8FtoV/La8HCAcCMAIGI0sipCURAsvMBQQEBcsDlCxwXqbkA9kI6QbOJtNzLGx/IgED9QHv8vg4SFwqiv04GuzCEs8XIBo4EpIzRg1hQjoMQyH8RyIEACH5BAUKABIALAAABwAOAA8AAAVToCRKymiaivM80dOcEiA1EeBGJfrYwKK4sEepoWgYTy8Fg9hYNADQGfExIzUYxReg9jKpJEKAMTqiKiKiUm6kGKJO6/LOBETh2JJEcOaA6cBUJyEAIfkEBQoAEgAsAAAHAA4ADwAABVKgJCbiI54oYEZN9CioqLyP2phoIzWKAywKF4rVUAEkM5isRlPNUqabiFdTHHc8CePU6OpKsVbxFTspti1Z5HpaSE+s8hb1wCF1bLXJXj49vjEhACH5BAUKABIALAAABwAOAA8AAAVToCQCYiOeqOJIT/Q0JMo2EfBGipzbwKK8ptPjoWg0csaYxGFUMI6NRbBkeomKjGKwZpo6iI/T9PoAKGqyk3k2IuoeB9QwPZac1bmEzBZZptMufyEAOw==";
var smiley15 = "images/smileys/smiley15.gif";
var smiley16 = "data:image/gif;base64,R0lGODlhHgAaAOZ/AJyeoTQzNG1ucMjKzJaYm83P0Kqsr3l7fcDCxJSWmLe5u3x+gFJSVWFjZfLy84mLjZ6ho3BxdLq8vtzd3qOlqGlqbNXX2D08PoSGiIaIi5qcnpeanOHi411eYCUhIsXHydrc3by9wCkmJ3V2eaKkpmpsbnp8f66wskRERqyusaCipeTl5oCBhH6Agm5wctLT1VVWWKWoqpGTlYyOkTk4Ob7AwmZoak1OUMvNz4KEhrS2uFNTVUZGSEtLTXJ0do2PkkxMTsPFx1pbXXd5fEJCRC0rLO3t7ickJf39/ZOVmDs6PFhZW+rr7Onp6mBhY/z8/Dg2ODAtL+jo6ezt7SsoKenq6y4sLU9QUu/w8N/g4fr7+4uNkPb29/P09O7v8FBRU97f4EBAQSwpKlxcX6+xtMnLzPDx8fn5+uvs7NPU1ubn6NjZ2+Pk5fHx8mZnaaiqrUhISpudoFdYWj4+P7CytPf3+O7u7zw7PTIwMfj4+fT19dTW16eprCMfIP///////yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZD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";
var smiley16 = "images/smileys/smiley16.gif";
var smiley17 = "data:image/gif;base64,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";
var smiley17 = "images/smileys/smiley17.gif";
var smiley18 = "data:image/gif;base64,R0lGODlhGQAZALMPAP//////zP/MzP/Mmf+Zmf+ZZv9mZv9mM/8zM8zMzJmZzJmZmWZmmWZmZjMzM////yH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDo0M0ExQkM1NjA1NkQxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo0M0ExQkM1NTA1NkQxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAPACwAAAAAGQAZAAAEtxDICdJqGK9Ee0+NIzrY2HAexYwbZYlLKoUOI1dhky4iegM8m6vHc8Rci1ZIQQn5QLoZKSkRUYyeVtInUWBxrhwHJDpNSBJqmuRgSsgXFG/mO4WOOMe4qweYkVwJXBVYfz8pCYV4EgEBh4QxGB0DBwUCEwIFAxQ8HJ0UAgiio6KXEyFvaB0EBAYGlh+qfl+PUk8wtYRRE0WGMol9HTS0WT0yRWWCggrIg7Ej0Cy5F9FWuRMWJcQdEQAh+QQFCgAPACwFAAgADQAOAAAEMfCpR6utrqVbF01ABSSeCIQmp1rCKgRPcCAq0j7EOlwz4hcqgcFnuKkCsJVyqSp9KhEAIfkEBWQADwAsBQAIAA0ADgAABDHwpUerrSuBCjpt3WaFV2mewjMU53M8BNIObW0b8oPQZYH8iIApAGSdAgSeqaGoNCkRADs=";
var smiley18 = "images/smileys/smiley18.gif";
var smiley19 = "data:image/gif;base64,R0lGODlhGQAZALMOAP/////MzP/Mmf+Zmf+ZZv9mM/8zM8zMzJnMzJmZmWZmmWZmZjMzZjMzM////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDowNDJFODNBMDA1ODkxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDowNDJFODM5RjA1ODkxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUNFODNDRUIyNDIwNjgxMTgwODNDQUJGRjcyMERFNzgiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCgAOACwAAAAAGQAZAAAEtRDIKZNaba10qP9JI45k8nlH2U0I1qxf2jBwLNaTyB2cl/wyXCiByGhMld+CEWp4GibkicKDThKLqRaAmSxqm9MBFlo5rzeULmc6ZL2iN1rkzbopTWklDgckGD44EhowXSFbW0sAKXoUAQQEAgEfGRJ0HwEFBgYFAmpIGI0AA5ubBB4YK0EeApqcnhMpclxpHgG3qC9UI6IfoTZxghJulWIkGzBVVokkzRnCMS4kCtBaPC69ExEAIfkEBQoADgAsAAAAAAEAAQAABALQRQAh+QQFZAAOACwIAA0ACQAGAAAEGBBIQQqRkphtihwcNwAhRwTlNg6DQBFXBAA7";
var smiley19 = "images/smileys/smiley19.gif";
var smiley20 = "data:image/gif;base64,R0lGODlhFwAZALMNAP/////MzP/Mmf+Zmf+ZZv9mZv9mM/8zM8zM/8zMzJmZmWZmZjMzM////wAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNTgwMTE3NDA3MjA2ODExODA4M0JGM0E0OEZDMzA1MyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpENTFGRUVFMzA1ODAxMUUyOURBQUJGODExREIyNkYyMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpENTFGRUVFMjA1ODAxMUUyOURBQUJGODExREIyNkYyMSIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NkQzOTBDOUU4ODIwNjgxMTgwODM4QkU1NzkyNUFFNzUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDU4MDExNzQwNzIwNjgxMTgwODNCRjNBNDhGQzMwNTMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFCAANACwAAAAAFwAZAAAEvhDIKVNSytK9L/vgl3GUAi6ZpSwiCbDMkpAJu3Cw4gKzeU8mhmWBKsV0vslHNdS9UDMJa5YQdmQ1IssZA6yMMgRnZKpFn+cdgmGi/HZuIhxum+9gm0CAFCgUJjBnAQcGAwN7AAEDAgQHgC0SgwICAIsCAwCTBkpEVhIEAZR7epWKEiYYXRKLlZOLmIcSSwAfbwSZEpQCoaeqVaoABJi4rDwfRpCKAwSUFbPIkBQ1zx0htTC1aWPWJ9o7K9beABEAIfkEBQgADQAsBQAMAA0ACQAABDUQhACqlaWEY8ag0iAQxyYIgCgMwGkARIBSUxqwYnqKOEi0FZRAdiGwgLlLaEBAWRgKpTIRAQAh+QQFCAANACwAAAAAAQABAAAEArBFADs=";
var smiley20 = "images/smileys/smiley20.gif";

View File

@ -1,6 +1,4 @@
var Toolbar = (function (my) {
var INITIAL_TOOLBAR_TIMEOUT = 20000;
var TOOLBAR_TIMEOUT = INITIAL_TOOLBAR_TIMEOUT;
/**
* Opens the lock room dialog.
@ -199,61 +197,6 @@ var Toolbar = (function (my) {
}
}
};
/**
* Shows the main toolbar.
*/
my.showToolbar = function() {
if (!$('#header').is(':visible')) {
$('#header').show("slide", { direction: "up", duration: 300});
$('#subject').animate({top: "+=40"}, 300);
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
TOOLBAR_TIMEOUT = 4000;
}
if (focus != null)
{
// 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
*/
my.dockToolbar = function(isDock) {
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {
Toolbar.showToolbar();
}
// Then clear the time out, to dock the toolbar.
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
}
else {
if (!$('#header').is(':visible')) {
Toolbar.showToolbar();
}
else {
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
}
}
};
/**
* Updates the lock button state.
*/
@ -261,30 +204,6 @@ var Toolbar = (function (my) {
buttonClick("#lockIcon", "icon-security icon-security-locked");
};
/**
* Hides the toolbar.
*/
var hideToolbar = function () {
var isToolbarHover = false;
$('#header').find('*').each(function () {
var id = $(this).attr('id');
if ($("#" + id + ":hover").length > 0) {
isToolbarHover = true;
}
});
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
if (!isToolbarHover) {
$('#header').hide("slide", { direction: "up", duration: 300});
$('#subject').animate({top: "-=40"}, 300);
}
else {
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
}
};
// Shows or hides the 'recording' button.
my.showRecordingButton = function (show) {
if (!config.enableRecording) {

100
toolbar_toggler.js Normal file
View File

@ -0,0 +1,100 @@
var ToolbarToggler = (function(my) {
var INITIAL_TOOLBAR_TIMEOUT = 20000;
var TOOLBAR_TIMEOUT = INITIAL_TOOLBAR_TIMEOUT;
var toolbarTimeout;
/**
* Shows the main toolbar.
*/
my.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",cduration: 300});
}
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
TOOLBAR_TIMEOUT = 4000;
}
if (focus != null)
{
// TODO: Enable settings functionality. Need to uncomment the settings button in index.html.
// $('#settingsButton').css({visibility:"visible"});
}
// Show/hide desktop sharing button
showDesktopSharingButton();
};
/**
* Hides the toolbar.
*/
var hideToolbar = function () {
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(toolbarTimeout);
toolbarTimeout = null;
if (!isToolbarHover) {
header.hide("slide", { direction: "up", duration: 300});
$('#subject').animate({top: "-=40"}, 300);
if(!$("#remoteVideos").is(":visible")) {
bottomToolbar.hide("slide", {direction: "right", cduration: 300});
}
}
else {
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
}
};
/**
* Docks/undocks the toolbar.
*
* @param isDock indicates what operation to perform
*/
my.dockToolbar = function(isDock) {
if (isDock) {
// First make sure the toolbar is shown.
if (!$('#header').is(':visible')) {
ToolbarToggler.showToolbar();
}
// Then clear the time out, to dock the toolbar.
if (toolbarTimeout) {
clearTimeout(toolbarTimeout);
toolbarTimeout = null;
}
}
else {
if (!$('#header').is(':visible')) {
ToolbarToggler.showToolbar();
}
else {
toolbarTimeout = setTimeout(hideToolbar, TOOLBAR_TIMEOUT);
}
}
};
return my;
}(ToolbarToggler || {}));

View File

@ -381,7 +381,9 @@ var VideoLayout = (function (my) {
// If the container is currently visible we attach the stream.
if (!isVideo
|| (container.offsetParent !== null && isVideo)) {
RTC.attachMediaStream(sel, stream);
var simulcast = new Simulcast();
var videoStream = simulcast.getReceivingVideoStream(stream);
RTC.attachMediaStream(sel, videoStream);
if (isVideo)
waitForRemoteVideo(sel, thessrc, stream);
@ -1248,7 +1250,9 @@ var VideoLayout = (function (my) {
&& mediaStream.type === mediaStream.VIDEO_TYPE) {
var sel = $('#participant_' + resourceJid + '>video');
RTC.attachMediaStream(sel, mediaStream.stream);
var simulcast = new Simulcast();
var videoStream = simulcast.getReceivingVideoStream(mediaStream.stream);
RTC.attachMediaStream(sel, videoStream);
waitForRemoteVideo(
sel,
mediaStream.ssrc,
@ -1288,5 +1292,84 @@ var VideoLayout = (function (my) {
}
});
/**
* On simulcast layers changed event.
*/
$(document).bind('simulcastlayerschanged', function (event, endpointSimulcastLayers) {
var simulcast = new Simulcast();
endpointSimulcastLayers.forEach(function (esl) {
var primarySSRC = esl.simulcastLayer.primarySSRC;
simulcast.setReceivingVideoStream(primarySSRC);
var msid = simulcast.getRemoteVideoStreamIdBySSRC(primarySSRC);
// Get session and stream from msid.
var session, electedStream;
var i, j, k;
if (connection.jingle) {
var keys = Object.keys(connection.jingle.sessions);
for (i = 0; i < keys.length; i++) {
var sid = keys[i];
if (electedStream) {
// stream found, stop.
break;
}
session = connection.jingle.sessions[sid];
if (session.remoteStreams) {
for (j = 0; j < session.remoteStreams.length; j++) {
var remoteStream = session.remoteStreams[j];
if (electedStream) {
// stream found, stop.
break;
}
var tracks = remoteStream.getVideoTracks();
if (tracks) {
for (k = 0; k < tracks.length; k++) {
var track = tracks[k];
if (msid === [remoteStream.id, track.id].join(' ')) {
electedStream = new webkitMediaStream([track]);
// stream found, stop.
break;
}
}
}
}
}
}
}
if (session && electedStream) {
console.info('Switching simulcast substream.');
console.info([esl, primarySSRC, msid, session, electedStream]);
var msidParts = msid.split(' ');
var selRemoteVideo = $(['#', 'remoteVideo_', session.sid, '_', msidParts[0]].join(''));
var updateLargeVideo = (ssrc2jid[videoSrcToSsrc[selRemoteVideo.attr('src')]]
== ssrc2jid[videoSrcToSsrc[$('#largeVideo').attr('src')]]);
var updateFocusedVideoSrc = (selRemoteVideo.attr('src') == focusedVideoSrc);
var electedStreamUrl = webkitURL.createObjectURL(electedStream);
selRemoteVideo.attr('src', electedStreamUrl);
videoSrcToSsrc[selRemoteVideo.attr('src')] = primarySSRC;
if (updateLargeVideo) {
VideoLayout.updateLargeVideo(electedStreamUrl);
}
if (updateFocusedVideoSrc) {
focusedVideoSrc = electedStreamUrl;
}
} else {
console.error('Could not find a stream or a session.');
}
});
});
return my;
}(VideoLayout || {}));