From ccdcc879d04dcda960f6e7dcb89ad374d556c9ea Mon Sep 17 00:00:00 2001 From: hristoterezov Date: Fri, 14 Aug 2015 14:40:51 -0500 Subject: [PATCH] Removes some files that won't be used in jitsi meet library. --- analytics.js | 8 - app.js | 55 - bump-js-versions.sh | 34 - config.js | 43 - css/chat.css | 239 - css/contact_list.css | 51 - css/font.css | 126 - css/jitsi_popover.css | 105 - css/jquery-impromptu.css | 128 - css/login_menu.css | 61 - css/main.css | 377 - css/modaldialog.css | 34 - css/notice.css | 11 - css/popover.css | 124 - css/popup_menu.css | 72 - css/settingsmenu.css | 68 - css/toastr.css | 180 - css/unsupported_browser.css | 118 - css/videolayout_default.css | 474 - css/welcome_page.css | 205 - debian/changelog | 5 - debian/compat | 1 - debian/control | 35 - debian/copyright | 31 - debian/jitsi-meet-prosody.README.Debian | 7 - debian/jitsi-meet-prosody.config | 4 - debian/jitsi-meet-prosody.docs | 2 - debian/jitsi-meet-prosody.postinst | 131 - debian/jitsi-meet-prosody.postrm | 57 - debian/jitsi-meet-prosody.templates | 4 - debian/jitsi-meet.README.Debian | 8 - debian/jitsi-meet.README.source | 24 - debian/jitsi-meet.config | 8 - debian/jitsi-meet.dirs | 1 - debian/jitsi-meet.docs | 4 - debian/jitsi-meet.install | 11 - debian/jitsi-meet.links | 3 - debian/jitsi-meet.postinst | 192 - debian/jitsi-meet.postrm | 56 - debian/jitsi-meet.templates | 32 - debian/missing-source/libs/strophe/sha1.js | 202 - .../libs/strophe/strophe.caps.jsonly.js | 240 - .../libs/strophe/strophe.disco.js | 232 - debian/patches/jquery-package | 22 - debian/patches/series | 1 - debian/po/POTFILES.in | 1 - debian/po/templates.pot | 98 - debian/rules | 19 - debian/source/format | 1 - debian/watch | 2 - doc/adding-an-icon.md | 12 - doc/api.md | 174 - doc/debian/jitsi-meet-prosody/README | 1 - .../prosody.cfg.lua-jvb.example | 28 - doc/debian/jitsi-meet/README | 13 - doc/debian/jitsi-meet/jitsi-meet.example | 36 - doc/example-config-files/config.js.example | 9 - .../jitsi.example.com.example | 33 - doc/example-config-files/nginx.conf.example | 54 - .../prosody.cfg.lua.example | 205 - doc/influxdb.md | 29 - doc/manual-install.md | 223 - doc/quick-install.md | 74 - external_api.js | 355 - favicon.ico | Bin 3638 -> 0 bytes fonts/jitsi.eot | Bin 9108 -> 0 bytes fonts/jitsi.svg | 39 - fonts/jitsi.ttf | Bin 8952 -> 0 bytes fonts/jitsi.woff | Bin 9028 -> 0 bytes fonts/selection.json | 916 - images/avatar1.png | Bin 4278 -> 0 bytes images/avatar2.png | Bin 1607 -> 0 bytes images/avatarprezi.png | Bin 8664 -> 0 bytes images/chatArrow.svg | 12 - images/chrome.png | Bin 8255 -> 0 bytes images/chromium.png | Bin 8052 -> 0 bytes images/dropdownPointer.png | Bin 234 -> 0 bytes images/estoslogo.png | Bin 2947 -> 0 bytes images/favicon.ico | Bin 4286 -> 0 bytes images/firefox-nightly.png | Bin 9723 -> 0 bytes images/firefox.png | Bin 15611 -> 0 bytes images/jitsilogo.png | Bin 3376 -> 0 bytes images/noMic.png | Bin 2880 -> 0 bytes images/noVideo.png | Bin 2911 -> 0 bytes images/opera.png | Bin 8154 -> 0 bytes images/popupPointer.png | Bin 1078 -> 0 bytes images/smile.svg | 25 - images/smileys/smiley1.svg | 34 - images/smileys/smiley10.svg | 61 - images/smileys/smiley11.svg | 60 - images/smileys/smiley12.svg | 99 - images/smileys/smiley13.svg | 19 - images/smileys/smiley14.svg | 98 - images/smileys/smiley15.svg | 54 - images/smileys/smiley16.svg | 195 - images/smileys/smiley17.svg | 152 - images/smileys/smiley18.svg | 102 - images/smileys/smiley19.svg | 27 - images/smileys/smiley2.svg | 59 - images/smileys/smiley20.svg | 172 - images/smileys/smiley21.svg | 58 - images/smileys/smiley3.svg | 68 - images/smileys/smiley4.svg | 60 - images/smileys/smiley5.svg | 75 - images/smileys/smiley6.svg | 116 - images/smileys/smiley7.svg | 80 - images/smileys/smiley8.svg | 68 - images/smileys/smiley9.svg | 46 - images/videomask.svg | 64 - images/watermark.png | Bin 33340 -> 0 bytes images/welcome_page/bubble.png | Bin 8013 -> 0 bytes .../welcome_page/disable-welcome-selected.png | Bin 3845 -> 0 bytes images/welcome_page/disable-welcome.png | Bin 3559 -> 0 bytes images/welcome_page/header-big.png | Bin 794 -> 0 bytes images/welcome_page/pattern-body.png | Bin 3234 -> 0 bytes images/welcome_page/pattern-header.png | Bin 10047 -> 0 bytes index.html | 249 - interface_config.js | 23 - lang/Translation.md | 57 - lang/languages-bg.json | 7 - lang/languages-de.json | 8 - lang/languages-fr.json | 7 - lang/languages-it.json | 7 - lang/languages-tr.json | 5 - lang/languages.json | 8 - lang/main-bg.json | 224 - lang/main-de.json | 243 - lang/main-fr.json | 227 - lang/main-it.json | 227 - lang/main-tr.json | 173 - lang/main.json | 250 - libs/app.bundle.js | 38194 ---------------- libs/jquery-impromptu.js | 863 - libs/toastr.js | 342 - modules/API/API.js | 219 - modules/UI/UI.js | 877 - modules/UI/audio_levels/AudioLevels.js | 265 - modules/UI/audio_levels/CanvasUtils.js | 111 - modules/UI/authentication/Authentication.js | 124 - modules/UI/authentication/LoginDialog.js | 228 - modules/UI/avatar/Avatar.js | 67 - modules/UI/etherpad/Etherpad.js | 130 - modules/UI/prezi/Prezi.js | 343 - modules/UI/prezi/PreziPlayer.js | 294 - modules/UI/side_pannels/SidePanelToggler.js | 174 - modules/UI/side_pannels/chat/Chat.js | 350 - modules/UI/side_pannels/chat/Commands.js | 86 - modules/UI/side_pannels/chat/Replacement.js | 62 - modules/UI/side_pannels/chat/smileys.json | 48 - .../side_pannels/contactlist/ContactList.js | 194 - .../UI/side_pannels/settings/SettingsMenu.js | 108 - modules/UI/toolbars/BottomToolbar.js | 44 - modules/UI/toolbars/Toolbar.js | 678 - modules/UI/toolbars/ToolbarToggler.js | 121 - modules/UI/util/JitsiPopover.js | 128 - modules/UI/util/MessageHandler.js | 241 - modules/UI/util/NicknameHandler.js | 30 - modules/UI/util/UIUtil.js | 96 - modules/UI/videolayout/ConnectionIndicator.js | 382 - modules/UI/videolayout/LargeVideo.js | 672 - modules/UI/videolayout/LocalVideo.js | 244 - modules/UI/videolayout/RemoteVideo.js | 417 - modules/UI/videolayout/SmallVideo.js | 369 - modules/UI/videolayout/VideoLayout.js | 989 - modules/UI/welcome_page/RoomnameGenerator.js | 179 - modules/UI/welcome_page/WelcomePage.js | 96 - modules/URLProcessor/URLProcessor.js | 50 - modules/keyboardshortcut/keyboardshortcut.js | 96 - modules/translation/translation.js | 136 - plugin.head.html | 0 plugin.header.text.html | 0 plugin.welcomepage.footer.html | 0 service/translation/languages.js | 16 - sounds/incomingMessage.wav | Bin 100452 -> 0 bytes sounds/joined.wav | Bin 42156 -> 0 bytes sounds/left.wav | Bin 42156 -> 0 bytes title.html | 1 - unsupported_browser.html | 49 - 178 files changed, 57780 deletions(-) delete mode 100644 analytics.js delete mode 100644 app.js delete mode 100755 bump-js-versions.sh delete mode 100644 config.js delete mode 100644 css/chat.css delete mode 100644 css/contact_list.css delete mode 100644 css/font.css delete mode 100644 css/jitsi_popover.css delete mode 100644 css/jquery-impromptu.css delete mode 100644 css/login_menu.css delete mode 100644 css/main.css delete mode 100644 css/modaldialog.css delete mode 100644 css/notice.css delete mode 100644 css/popover.css delete mode 100644 css/popup_menu.css delete mode 100644 css/settingsmenu.css delete mode 100644 css/toastr.css delete mode 100644 css/unsupported_browser.css delete mode 100644 css/videolayout_default.css delete mode 100644 css/welcome_page.css delete mode 100644 debian/changelog delete mode 100644 debian/compat delete mode 100644 debian/control delete mode 100644 debian/copyright delete mode 100644 debian/jitsi-meet-prosody.README.Debian delete mode 100644 debian/jitsi-meet-prosody.config delete mode 100644 debian/jitsi-meet-prosody.docs delete mode 100644 debian/jitsi-meet-prosody.postinst delete mode 100644 debian/jitsi-meet-prosody.postrm delete mode 100644 debian/jitsi-meet-prosody.templates delete mode 100644 debian/jitsi-meet.README.Debian delete mode 100644 debian/jitsi-meet.README.source delete mode 100644 debian/jitsi-meet.config delete mode 100644 debian/jitsi-meet.dirs delete mode 100644 debian/jitsi-meet.docs delete mode 100644 debian/jitsi-meet.install delete mode 100644 debian/jitsi-meet.links delete mode 100644 debian/jitsi-meet.postinst delete mode 100644 debian/jitsi-meet.postrm delete mode 100644 debian/jitsi-meet.templates delete mode 100644 debian/missing-source/libs/strophe/sha1.js delete mode 100644 debian/missing-source/libs/strophe/strophe.caps.jsonly.js delete mode 100644 debian/missing-source/libs/strophe/strophe.disco.js delete mode 100644 debian/patches/jquery-package delete mode 100644 debian/patches/series delete mode 100644 debian/po/POTFILES.in delete mode 100644 debian/po/templates.pot delete mode 100755 debian/rules delete mode 100644 debian/source/format delete mode 100644 debian/watch delete mode 100644 doc/adding-an-icon.md delete mode 100644 doc/api.md delete mode 100644 doc/debian/jitsi-meet-prosody/README delete mode 100644 doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example delete mode 100644 doc/debian/jitsi-meet/README delete mode 100644 doc/debian/jitsi-meet/jitsi-meet.example delete mode 100755 doc/example-config-files/config.js.example delete mode 100755 doc/example-config-files/jitsi.example.com.example delete mode 100755 doc/example-config-files/nginx.conf.example delete mode 100755 doc/example-config-files/prosody.cfg.lua.example delete mode 100644 doc/influxdb.md delete mode 100644 doc/manual-install.md delete mode 100644 doc/quick-install.md delete mode 100644 external_api.js delete mode 100644 favicon.ico delete mode 100644 fonts/jitsi.eot delete mode 100644 fonts/jitsi.svg delete mode 100644 fonts/jitsi.ttf delete mode 100644 fonts/jitsi.woff delete mode 100644 fonts/selection.json delete mode 100644 images/avatar1.png delete mode 100644 images/avatar2.png delete mode 100644 images/avatarprezi.png delete mode 100644 images/chatArrow.svg delete mode 100644 images/chrome.png delete mode 100644 images/chromium.png delete mode 100644 images/dropdownPointer.png delete mode 100644 images/estoslogo.png delete mode 100644 images/favicon.ico delete mode 100644 images/firefox-nightly.png delete mode 100644 images/firefox.png delete mode 100644 images/jitsilogo.png delete mode 100644 images/noMic.png delete mode 100644 images/noVideo.png delete mode 100644 images/opera.png delete mode 100644 images/popupPointer.png delete mode 100644 images/smile.svg delete mode 100644 images/smileys/smiley1.svg delete mode 100644 images/smileys/smiley10.svg delete mode 100644 images/smileys/smiley11.svg delete mode 100644 images/smileys/smiley12.svg delete mode 100644 images/smileys/smiley13.svg delete mode 100644 images/smileys/smiley14.svg delete mode 100644 images/smileys/smiley15.svg delete mode 100644 images/smileys/smiley16.svg delete mode 100644 images/smileys/smiley17.svg delete mode 100644 images/smileys/smiley18.svg delete mode 100644 images/smileys/smiley19.svg delete mode 100644 images/smileys/smiley2.svg delete mode 100644 images/smileys/smiley20.svg delete mode 100644 images/smileys/smiley21.svg delete mode 100644 images/smileys/smiley3.svg delete mode 100644 images/smileys/smiley4.svg delete mode 100644 images/smileys/smiley5.svg delete mode 100644 images/smileys/smiley6.svg delete mode 100644 images/smileys/smiley7.svg delete mode 100644 images/smileys/smiley8.svg delete mode 100644 images/smileys/smiley9.svg delete mode 100644 images/videomask.svg delete mode 100644 images/watermark.png delete mode 100644 images/welcome_page/bubble.png delete mode 100644 images/welcome_page/disable-welcome-selected.png delete mode 100644 images/welcome_page/disable-welcome.png delete mode 100644 images/welcome_page/header-big.png delete mode 100644 images/welcome_page/pattern-body.png delete mode 100644 images/welcome_page/pattern-header.png delete mode 100644 index.html delete mode 100644 interface_config.js delete mode 100644 lang/Translation.md delete mode 100644 lang/languages-bg.json delete mode 100644 lang/languages-de.json delete mode 100644 lang/languages-fr.json delete mode 100644 lang/languages-it.json delete mode 100644 lang/languages-tr.json delete mode 100644 lang/languages.json delete mode 100644 lang/main-bg.json delete mode 100644 lang/main-de.json delete mode 100644 lang/main-fr.json delete mode 100644 lang/main-it.json delete mode 100644 lang/main-tr.json delete mode 100644 lang/main.json delete mode 100644 libs/app.bundle.js delete mode 100644 libs/jquery-impromptu.js delete mode 100644 libs/toastr.js delete mode 100644 modules/API/API.js delete mode 100644 modules/UI/UI.js delete mode 100644 modules/UI/audio_levels/AudioLevels.js delete mode 100644 modules/UI/audio_levels/CanvasUtils.js delete mode 100644 modules/UI/authentication/Authentication.js delete mode 100644 modules/UI/authentication/LoginDialog.js delete mode 100644 modules/UI/avatar/Avatar.js delete mode 100644 modules/UI/etherpad/Etherpad.js delete mode 100644 modules/UI/prezi/Prezi.js delete mode 100644 modules/UI/prezi/PreziPlayer.js delete mode 100644 modules/UI/side_pannels/SidePanelToggler.js delete mode 100644 modules/UI/side_pannels/chat/Chat.js delete mode 100644 modules/UI/side_pannels/chat/Commands.js delete mode 100644 modules/UI/side_pannels/chat/Replacement.js delete mode 100644 modules/UI/side_pannels/chat/smileys.json delete mode 100644 modules/UI/side_pannels/contactlist/ContactList.js delete mode 100644 modules/UI/side_pannels/settings/SettingsMenu.js delete mode 100644 modules/UI/toolbars/BottomToolbar.js delete mode 100644 modules/UI/toolbars/Toolbar.js delete mode 100644 modules/UI/toolbars/ToolbarToggler.js delete mode 100644 modules/UI/util/JitsiPopover.js delete mode 100644 modules/UI/util/MessageHandler.js delete mode 100644 modules/UI/util/NicknameHandler.js delete mode 100644 modules/UI/util/UIUtil.js delete mode 100644 modules/UI/videolayout/ConnectionIndicator.js delete mode 100644 modules/UI/videolayout/LargeVideo.js delete mode 100644 modules/UI/videolayout/LocalVideo.js delete mode 100644 modules/UI/videolayout/RemoteVideo.js delete mode 100644 modules/UI/videolayout/SmallVideo.js delete mode 100644 modules/UI/videolayout/VideoLayout.js delete mode 100644 modules/UI/welcome_page/RoomnameGenerator.js delete mode 100644 modules/UI/welcome_page/WelcomePage.js delete mode 100644 modules/URLProcessor/URLProcessor.js delete mode 100644 modules/keyboardshortcut/keyboardshortcut.js delete mode 100644 modules/translation/translation.js delete mode 100644 plugin.head.html delete mode 100644 plugin.header.text.html delete mode 100644 plugin.welcomepage.footer.html delete mode 100644 service/translation/languages.js delete mode 100644 sounds/incomingMessage.wav delete mode 100644 sounds/joined.wav delete mode 100644 sounds/left.wav delete mode 100644 title.html delete mode 100644 unsupported_browser.html diff --git a/analytics.js b/analytics.js deleted file mode 100644 index 07ea7184f..000000000 --- a/analytics.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Google Analytics - */ -(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ - (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - ga('create', 'UA-319188-14', 'jit.si'); - ga('send', 'pageview'); \ No newline at end of file diff --git a/app.js b/app.js deleted file mode 100644 index 01beff590..000000000 --- a/app.js +++ /dev/null @@ -1,55 +0,0 @@ -/* jshint -W117 */ -/* application specific logic */ - -var APP = -{ - init: function () { - this.UI = require("./modules/UI/UI"); - this.API = require("./modules/API/API"); - this.connectionquality = require("./modules/connectionquality/connectionquality"); - this.statistics = require("./modules/statistics/statistics"); - this.RTC = require("./modules/RTC/RTC"); - this.desktopsharing = require("./modules/desktopsharing/desktopsharing"); - this.xmpp = require("./modules/xmpp/xmpp"); - this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut"); - this.translation = require("./modules/translation/translation"); - this.settings = require("./modules/settings/Settings"); - this.DTMF = require("./modules/DTMF/DTMF"); - this.members = require("./modules/members/MemberList"); - } -}; - -function init() { - - APP.desktopsharing.init(); - APP.RTC.start(); - APP.xmpp.start(); - APP.statistics.start(); - APP.connectionquality.init(); - APP.keyboardshortcut.init(); - APP.members.start(); -} - - -$(document).ready(function () { - - var URLProcessor = require("./modules/URLProcessor/URLProcessor"); - URLProcessor.setConfigParametersFromUrl(); - APP.init(); - - APP.translation.init(); - - if(APP.API.isEnabled()) - APP.API.init(); - - APP.UI.start(init); - -}); - -$(window).bind('beforeunload', function () { - if(APP.API.isEnabled()) - APP.API.dispose(); -}); - -module.exports = APP; - diff --git a/bump-js-versions.sh b/bump-js-versions.sh deleted file mode 100755 index e7d9aee53..000000000 --- a/bump-js-versions.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/sh - -if ! which git > /dev/null 2>&1 ;then - echo "Cannot find git executable, not bumping js versions." - exit -fi -if ! git status > /dev/null 2>&1 ;then - echo "Not a git repository, not bumping js versions." - exit -fi - -# This script finds all js files included from index.html which have been -# modified and bumps their version (the value of the "v" parameter used -# in index.html) - -# contents of index.html at HEAD (excluding not-committed changes) -index=`git show HEAD:index.html` - -# js files included from index.html. The sort needed for comm -jsfiles=.bump-js-versions-jsfiles.tmp -echo "$index" | grep ' -- -+ - - - - - - -- -+ - - - diff --git a/debian/patches/series b/debian/patches/series deleted file mode 100644 index 097ce197d..000000000 --- a/debian/patches/series +++ /dev/null @@ -1 +0,0 @@ -jquery-package diff --git a/debian/po/POTFILES.in b/debian/po/POTFILES.in deleted file mode 100644 index 3458b92cf..000000000 --- a/debian/po/POTFILES.in +++ /dev/null @@ -1 +0,0 @@ -[type: gettext/rfc822deb] jitsi-meet.templates diff --git a/debian/po/templates.pot b/debian/po/templates.pot deleted file mode 100644 index d3933e730..000000000 --- a/debian/po/templates.pot +++ /dev/null @@ -1,98 +0,0 @@ -# -#, 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 \n" -"Language-Team: LANGUAGE \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 "" - -#. Type: string -#. Description -#: ../jitsi-meet.templates:4001 -msgid "The hostname of the current installation:" -msgstr "" - -#. Type: string -#. Description -#: ../jitsi-meet.templates:4001 -msgid "" -"The value for the hostname that is set in Jitsi Videobridge installation." -msgstr "" - - -#. Type: string -#. Description -#: ../jitsi-meet.templates:5001 -msgid "for internal use" -msgstr "" - -#. Type: string -#. Description -#: ../jitsi-meet.templates:5001 -msgid "" -"Jitsi Videobridge installation can use its internal jetty to serve static meet pages." -msgstr "" diff --git a/debian/rules b/debian/rules deleted file mode 100755 index 71e2450b9..000000000 --- a/debian/rules +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/make -f - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -%: - dh $@ - -# we skip making Makefile exists for updating browserify modules when developing -override_dh_auto_build: - -override_dh_install: - dh_installdirs - dh_install -X/config.js - yui-compressor -o debian/jitsi-meet/usr/share/jitsi-meet/libs/strophe/strophe.caps.jsonly.min.js \ - debian/missing-source/libs/strophe/strophe.caps.jsonly.js - yui-compressor -o debian/jitsi-meet/usr/share/jitsi-meet/libs/strophe/strophe.disco.min.js \ - debian/missing-source/libs/strophe/sha1.js \ - debian/missing-source/libs/strophe/strophe.disco.js diff --git a/debian/source/format b/debian/source/format deleted file mode 100644 index 163aaf8d8..000000000 --- a/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/debian/watch b/debian/watch deleted file mode 100644 index e33c057c7..000000000 --- a/debian/watch +++ /dev/null @@ -1,2 +0,0 @@ -version=3 -opts="uversionmangle=s/^/1.0./" https://github.com/jitsi/jitsi-meet/releases/ /jitsi/jitsi-meet/archive/(\S+)\.tar\.gz \ No newline at end of file diff --git a/doc/adding-an-icon.md b/doc/adding-an-icon.md deleted file mode 100644 index be19a6b5f..000000000 --- a/doc/adding-an-icon.md +++ /dev/null @@ -1,12 +0,0 @@ -### Adding an icon to the font file (e.g. for the floating menu) -1. Go to https://icomoon.io/app/ -2. Go to "Manage Projects" from the menu on the top left. -3. Use "Import project" and select fonts/selection.json from Jitsi Meet. -4. Import icons (e.g. svg files) using the "import items" button. -5. Go to "generate font" and make sure the identifiers for the new icons are correct. -6. Download the result in a zip file using the "download" button. -7. Copy selection.json and fonts/jitsi.* from the zip file to fonts/ in Jitsi Meet -8. Copy the class for the new icon from style.css in the zip file to css/font.css in Jitsi Meet (do *not* copy the whole file) - -Sample commit: https://github.com/jitsi/jitsi-meet/commit/68bc819b89aec12364fcf07b81efa83a1900eed6 - diff --git a/doc/api.md b/doc/api.md deleted file mode 100644 index 139729904..000000000 --- a/doc/api.md +++ /dev/null @@ -1,174 +0,0 @@ -Jitsi Meet API -============ - -You can use Jitsi Meet API to embed Jitsi Meet in to your application. - -Installation -========== - -To embed Jitsi Meet in your application you need to add Jitsi Meet API library -```javascript - -``` - -The next step for embedding Jitsi Meet is to create the Jitsi Meet API object -```javascript - -``` -You can paste that lines in your html code where you want to be placed the Jitsi Meet conference -or you can specify the parent HTML element for the Jitsi Meet conference in the JitsiMeetExternalAPI -constructor. -```javascript - var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement); -``` -If you don't specify room the user will enter in new conference with random room name. - -You can enable the "film strip only" mode(only the small videos are visible) by setting 6th parameter to ```true```: -```javascript - var api = new JitsiMeetExternalAPI(domain, room, width, height, htmlElement, true); -``` - -Controlling embedded Jitsi Meet Conference -========= - -You can control the embedded Jitsi Meet conference using the JitsiMeetExternalAPI object. - -You can send command to Jitsi Meet conference using ```executeCommand```. -``` -api.executeCommand(command, arguments) -``` -The ```command``` parameter is String object with the name of the command. -The ```arguments``` parameter is array with the arguments required by the command. -If no arguments are required by the command this parameter can be omitted or you can pass empty array. -Currently we support the following commands: - - -* **displayName** - sets the display name of the local participant. This command requires one argument - -the new display name to be set -``` -api.executeCommand('displayName', ['New Nickname']); -``` -* **toggleAudio** - mutes / unmutes the audio for the local participant. No arguments are required. -``` -api.executeCommand('toggleAudio', []) -``` -* **toggleVideo** - mutes / unmutes the video for the local participant. No arguments are required. -``` -api.executeCommand('toggleVideo', []) -``` -* **toggleFilmStrip** - hides / shows the film strip. No arguments are required. -``` -api.executeCommand('filmStrip', []) -``` -* **toggleChat** - hides / shows the chat. No arguments are required. -``` -api.executeCommand('toggleChat', []) -``` -* **toggleContactList** - hides / shows the contact list. No arguments are required. -``` -api.executeCommand('toggleContactList', []) -``` - -You can also execute multiple commands using the method ```executeCommands```. -``` -api.executeCommands(commands) -``` -The ```commands``` parameter is object with keys the names of the commands and values the arguments for the -commands. - -``` -api.executeCommands({displayName: ['nickname'], toggleAudio: []}); -``` - -You can add event listeners to the embedded Jitsi Meet using ```addEventListener``` method. -``` -api.addEventListener(event, listener) -``` -The ```event``` parameter is String object with the name of the event. -The ```listener``` paramenter is Function object with one argument that will be notified when the event occurs -with data related to the event. - -Currently we support the following events: - -* **incomingMessage** - event notifications about incoming -messages. The listener will receive object with the following structure: -``` -{ -"from": from,//JID of the user that sent the message -"nick": nick,//the nickname of the user that sent the message -"message": txt//the text of the message -} -``` -* **outgoingMessage** - event notifications about outgoing -messages. The listener will receive object with the following structure: -``` -{ -"message": txt//the text of the message -} -``` -* **displayNameChanged** - event notifications about display name -change. The listener will receive object with the following structure: -``` -{ -jid: jid,//the JID of the participant that changed his display name -displayname: displayName //the new display name -} -``` -* **participantJoined** - event notifications about new participant. -The listener will receive object with the following structure: -``` -{ -jid: jid //the jid of the participant -} -``` -* **participantLeft** - event notifications about participant that left room. -The listener will receive object with the following structure: -``` -{ -jid: jid //the jid of the participant -} -``` - -You can also add multiple event listeners by using ```addEventListeners```. -This method requires one argument of type Object. The object argument must -have keys with the names of the events and values the listeners of the events. - -``` -function incomingMessageListener(object) -{ -... -} - -function outgoingMessageListener(object) -{ -... -} - -api.addEventListeners({ - incomingMessage: incomingMessageListener, - outgoingMessage: outgoingMessageListener}) -``` - -If you want to remove a listener you can use ```removeEventListener``` method with argument the name of the event. -``` -api.removeEventListener("incomingMessage"); -``` - -If you want to remove more than one event you can use ```removeEventListeners``` method with argument - array with the names of the events. -``` -api.removeEventListeners(["incomingMessage", "outgoingMessageListener"]); -``` - -You can remove the embedded Jitsi Meet Conference with the following code: -``` -api.dispose() -``` - -It is a good practice to remove the conference before the page is unloaded. \ No newline at end of file diff --git a/doc/debian/jitsi-meet-prosody/README b/doc/debian/jitsi-meet-prosody/README deleted file mode 100644 index d141f6b71..000000000 --- a/doc/debian/jitsi-meet-prosody/README +++ /dev/null @@ -1 +0,0 @@ -Prosody configuration for Jitsi Meet diff --git a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example b/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example deleted file mode 100644 index 9874f3940..000000000 --- a/doc/debian/jitsi-meet-prosody/prosody.cfg.lua-jvb.example +++ /dev/null @@ -1,28 +0,0 @@ -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"; - } - -- we need bosh - modules_enabled = { - "bosh"; - "pubsub"; - } - -Component "conference.jitmeet.example.com" "muc" -admins = { "focusUser@auth.jitmeet.example.com" } - -Component "jitsi-videobridge.jitmeet.example.com" - component_secret = "jitmeetSecret" - -VirtualHost "auth.jitmeet.example.com" - authentication = "internal_plain" - -Component "focus.jitmeet.example.com" - component_secret = "focusSecret" diff --git a/doc/debian/jitsi-meet/README b/doc/debian/jitsi-meet/README deleted file mode 100644 index a198dcae3..000000000 --- a/doc/debian/jitsi-meet/README +++ /dev/null @@ -1,13 +0,0 @@ -Jitsi Meet - -==== - -A WebRTC-powered multi-user videochat. For a live demo, check out either -https://meet.estos.de/ or https://meet.jit.si/. - -Built using colibri.js[0] and strophe.jingle[1], powered by the jitsi-videobridge[2] - - -[0] https://github.com/ESTOS/colibri.js -[1] https://github.com/ESTOS/strophe.jingle -[3] https://github.com/jitsi/jitsi-videobridge diff --git a/doc/debian/jitsi-meet/jitsi-meet.example b/doc/debian/jitsi-meet/jitsi-meet.example deleted file mode 100644 index 0ab36315d..000000000 --- a/doc/debian/jitsi-meet/jitsi-meet.example +++ /dev/null @@ -1,36 +0,0 @@ -server_names_hash_bucket_size 64; - -server { - listen 80; - server_name jitsi-meet.example.com; - return 301 https://$host$request_uri; -} -server { - listen 443 ssl; - server_name jitsi-meet.example.com; - - ssl_certificate /var/lib/prosody/jitsi-meet.example.com.crt; - ssl_certificate_key /var/lib/prosody/jitsi-meet.example.com.key; - - root /usr/share/jitsi-meet; - index index.html index.htm; - - location /config.js { - alias /etc/jitsi/meet/jitsi-meet.example.com-config.js; - } - - location ~ ^/([a-zA-Z0-9=\?]+)$ { - rewrite ^/(.*)$ / break; - } - - location / { - ssi on; - } - - # BOSH - location /http-bind { - proxy_pass http://localhost:5280/http-bind; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $http_host; - } -} diff --git a/doc/example-config-files/config.js.example b/doc/example-config-files/config.js.example deleted file mode 100755 index 92f71cfed..000000000 --- a/doc/example-config-files/config.js.example +++ /dev/null @@ -1,9 +0,0 @@ -var config = { - hosts: { - domain: 'jitsi.example.com', - muc: 'conference.jitsi.example.com', // FIXME: use XEP-0030 - bridge: 'jitsi-videobridge.jitsi.example.com' // FIXME: use XEP-0030 - }, - useNicks: false, - bosh: '//jitsi.example.com/http-bind' // FIXME: use xep-0156 for that -}; diff --git a/doc/example-config-files/jitsi.example.com.example b/doc/example-config-files/jitsi.example.com.example deleted file mode 100755 index 96ae864f9..000000000 --- a/doc/example-config-files/jitsi.example.com.example +++ /dev/null @@ -1,33 +0,0 @@ -server { - listen 80; - - server_name jitsi.example.com; - # set the root - root /srv/jitsi.example.com; - index index.html; - - location ~ ^/([a-zA-Z0-9=\?]+)$ { - rewrite ^/(.*)$ / break; - } - - location / { - ssi on; - } - - # BOSH - location /http-bind { - proxy_pass http://localhost:5280/http-bind; - 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; - } -} diff --git a/doc/example-config-files/nginx.conf.example b/doc/example-config-files/nginx.conf.example deleted file mode 100755 index 017b34ee6..000000000 --- a/doc/example-config-files/nginx.conf.example +++ /dev/null @@ -1,54 +0,0 @@ -user www-data; -worker_processes 1; - -error_log /var/log/nginx/error.log; -pid /var/run/nginx.pid; - -events { - worker_connections 1024; - # multi_accept on; -} - -http { - include /etc/nginx/mime.types; - - access_log /var/log/nginx/access.log; - - sendfile on; - #tcp_nopush on; - - #keepalive_timeout 0; - keepalive_timeout 65; - tcp_nodelay on; - - tcp_nopush on; - types_hash_max_size 2048; - server_names_hash_bucket_size 64; - - gzip on; - gzip_disable "MSIE [1-6]\.(?!.*SV1)"; - - include /etc/nginx/conf.d/*.conf; - include /etc/nginx/sites-enabled/*; -} - -# mail { -# # See sample authentication script at: -# # http://wiki.nginx.org/NginxImapAuthenticateWithApachePhpScript -# -# # auth_http localhost/auth.php; -# # pop3_capabilities "TOP" "USER"; -# # imap_capabilities "IMAP4rev1" "UIDPLUS"; -# -# server { -# listen localhost:110; -# protocol pop3; -# proxy on; -# } -# -# server { -# listen localhost:143; -# protocol imap; -# proxy on; -# } -# } diff --git a/doc/example-config-files/prosody.cfg.lua.example b/doc/example-config-files/prosody.cfg.lua.example deleted file mode 100755 index 6aca907ff..000000000 --- a/doc/example-config-files/prosody.cfg.lua.example +++ /dev/null @@ -1,205 +0,0 @@ --- 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 } ---component_interface = "192.168.0.10" - --- 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. - -- jitsi - "smacks"; - "carbons"; - "mam"; - "lastactivity"; - "offline"; - "pubsub"; - "adhoc"; - "websocket"; - "http_altconnect"; -} - --- 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" } - --- 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 "jitsi.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 = "/var/lib/prosody/jitsi.example.com.key"; - certificate = "/var/lib/prosody/jitsi.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.jitsi.example.com" "muc" - -Component "jitsi-videobridge.jitsi.example.com" - component_secret = "IfGaish6" diff --git a/doc/influxdb.md b/doc/influxdb.md deleted file mode 100644 index b6cdf5ef3..000000000 --- a/doc/influxdb.md +++ /dev/null @@ -1,29 +0,0 @@ -# Overview -Jitsi Meet supports logging to an [InfluxDB](http://influxdb.com/) database. - -# Configuration -The following needs to be done to enable this functionality. - -## Install InfluxDB -The details are outside the scope of the document, see http://influxdb.com/download/ . - -## Create an InfluxDB database -Use the InfluxDB admin interface (running on port 8083) and create a database. In this example we name it jitsi_database - -## Enable logging for Jitsi Videobridge -Add the following properties to /usr/share/jitsi-videobridge/.sip-communicator/sip-communicator.properties. - -- org.jitsi.videobridge.log.INFLUX_DB_ENABLED=true -- org.jitsi.videobridge.log.INFLUX_URL_BASE=http://influxdb.example.com:8086 -- org.jitsi.videobridge.log.INFLUX_DATABASE=jitsi_database -- org.jitsi.videobridge.log.INFLUX_USER=user -- org.jitsi.videobridge.log.INFLUX_PASS=pass - -## Enable logging for Jicofo -Add the same properties as above to /usr/share/jicofo/.sip-communicator/sip-communicator.properties. - -## Enable logging for Jitsi Meet itself -Change "logStats" to "true" in /etc/jitsi/meet/you-domain.config.js or the config.js file used in your installation. - -# User interface -You can explore the database using the [Jiloin](https://github.com/jitsi/jiloin) web interface. diff --git a/doc/manual-install.md b/doc/manual-install.md deleted file mode 100644 index 782951f7d..000000000 --- a/doc/manual-install.md +++ /dev/null @@ -1,223 +0,0 @@ -# Server Installation for Jitsi Meet - -This describes configuring a server `jitsi.example.com` running Debian or a Debian Derivative. You will need to -change references to that to match your host, and generate some passwords for -`YOURSECRET1`, `YOURSECRET2`, `YOURSECRET3` and `YOURSECRET4`. - -There are also some complete [example config files](https://github.com/jitsi/jitsi-meet/tree/master/doc/example-config-files/) available, mentioned in each section. - -## Install prosody -```sh -apt-get install prosody -``` - -## Configure prosody -Add config file in `/etc/prosody/conf.avail/jitsi.example.com.cfg.lua` : - -- add your domain virtual host section: - -``` -VirtualHost "jitsi.example.com" - authentication = "anonymous" - ssl = { - key = "/var/lib/prosody/jitsi.example.com.key"; - certificate = "/var/lib/prosody/jitsi.example.com.crt"; - } - modules_enabled = { - "bosh"; - "pubsub"; - } -``` -- add domain with authentication for conference focus user: -``` -VirtualHost "auth.jitsi.example.com" - authentication = "internal_plain" -``` -- add focus user to server admins: -``` -admins = { "focus@auth.jitsi.example.com" } -``` -- and finally configure components: -``` -Component "conference.jitsi.example.com" "muc" -Component "jitsi-videobridge.jitsi.example.com" - component_secret = "YOURSECRET1" -Component "focus.jitsi.example.com" - component_secret = "YOURSECRET2" -``` - -Add link for the added configuration -```sh -ln -s /etc/prosody/conf.avail/jitsi.example.com.cfg.lua /etc/prosody/conf.d/jitsi.example.com.cfg.lua -``` - -Generate certs for the domain: -```sh -prosodyctl cert generate jitsi.example.com -``` - -Create conference focus user: -```sh -prosodyctl register focus auth.jitsi.example.com YOURSECRET3 -``` - -Restart prosody XMPP server with the new config -```sh -prosodyctl restart -``` - -## Install nginx -```sh -apt-get install nginx -``` - -Add a new file `jitsi.example.com` in `/etc/nginx/sites-available` (see also the example config file): -``` -server_names_hash_bucket_size 64; - -server { - listen 80; - server_name jitsi.example.com; - # set the root - root /srv/jitsi.example.com; - index index.html; - location ~ ^/([a-zA-Z0-9=\?]+)$ { - rewrite ^/(.*)$ / break; - } - location / { - ssi on; - } - # BOSH - location /http-bind { - proxy_pass http://localhost:5280/http-bind; - proxy_set_header X-Forwarded-For $remote_addr; - proxy_set_header Host $http_host; - } -} -``` - -Add link for the added configuration -```sh -cd /etc/nginx/sites-enabled -ln -s ../sites-available/jitsi.example.com jitsi.example.com -``` - -## Install Jitsi Videobridge -```sh -wget https://download.jitsi.org/jitsi-videobridge/linux/jitsi-videobridge-linux-{arch-buildnum}.zip -unzip jitsi-videobridge-linux-{arch-buildnum}.zip -``` - -Install JRE if missing: -``` -apt-get install default-jre -``` - -_NOTE: When installing on older Debian releases keep in mind that you need JRE >= 1.7._ - -In the user home that will be starting Jitsi Videobridge create `.sip-communicator` folder and add the file `sip-communicator.properties` with one line in it: -``` -org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false -``` - -Start the videobridge with: -```sh -./jvb.sh --host=localhost --domain=jitsi.example.com --port=5347 --secret=YOURSECRET1 & -``` -Or autostart it by adding the line in `/etc/rc.local`: -```sh -/bin/bash /root/jitsi-videobridge-linux-{arch-buildnum}/jvb.sh --host=localhost --domain=jitsi.example.com --port=5347 --secret=YOURSECRET1 > /var/log/jvb.log 2>&1 -``` - -## Install Jitsi Conference Focus (jicofo) - -Install JDK and Ant if missing: -``` -apt-get install default-jdk ant -``` - -_NOTE: When installing on older Debian releases keep in mind that you need JDK >= 1.7._ - -Clone source from Github repo: -```sh -git clone https://github.com/jitsi/jicofo.git -``` -Build distribution package. Replace {os-name} with one of: 'lin', 'lin64', 'macosx', 'win', 'win64'. -```sh -cd jicofo -ant dist.{os-name} -``` -Run jicofo: -```sh -cd dist/{os-name}' -./jicofo.sh --domain=jitsi.example.com --secret=YOURSECRET2 --user_domain=auth.jitsi.example.com --user_name=focus --user_password=YOURSECRET3 -``` - -## Deploy Jitsi Meet -Checkout and configure Jitsi Meet: -```sh -cd /srv -git clone https://github.com/jitsi/jitsi-meet.git -mv jitsi-meet/ jitsi.example.com -``` - -Edit host names in `/srv/jitsi.example.com/config.js` (see also the example config file): -``` -var config = { - hosts: { - domain: 'jitsi.example.com', - muc: 'conference.jitsi.example.com', - 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. - //chromeExtensionId: 'diibjkoicjeejcmhdnailmkgecihlobk', // Id of desktop streamer Chrome extension - //minChromeExtVersion: '0.1' // Required version of Chrome extension -}; -``` - -Restart nginx to get the new configuration: -```sh -invoke-rc.d nginx restart -``` - -## Running behind NAT -In case of videobridge being installed on a machine behind NAT, add the following extra lines to the file `~/.sip-communicator/sip-communicator.properties` (in the home of user running the videobridge): -``` -org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS= -org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS= -``` - -So the file should look like this at the end: -``` -org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false -org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS= -org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS= -``` - -# Hold your first conference -You are now all set and ready to have your first meet by going to http://jitsi.example.com - - -## Enabling recording -Currently recording is only supported for linux-64 and macos. To enable it, add -the following properties to sip-communicator.properties: -``` -org.jitsi.videobridge.ENABLE_MEDIA_RECORDING=true -org.jitsi.videobridge.MEDIA_RECORDING_PATH=/path/to/recordings/dir -org.jitsi.videobridge.MEDIA_RECORDING_TOKEN=secret -``` - -where /path/to/recordings/dir is the path to a pre-existing directory where recordings -will be stored (needs to be writeable by the user running jitsi-videobridge), -and "secret" is a string which will be used for authentication. - -Then, edit the Jitsi-Meet config.js file and set: -``` -enableRecording: true -``` - -Restart jitsi-videobridge and start a new conference (making sure that the page -is reloaded with the new config.js) -- the organizer of the conference should -now have a "recording" button in the floating menu, near the "mute" button. diff --git a/doc/quick-install.md b/doc/quick-install.md deleted file mode 100644 index 3868daa1e..000000000 --- a/doc/quick-install.md +++ /dev/null @@ -1,74 +0,0 @@ -# Jitsi Meet quick install - -This document describes the required steps for a quick Jitsi Meet installation on a Debian based GNU/Linux system. - -N.B.: All commands are supposed to be run by root. If you are logged in as a regular user with sudo rights, please prepend ___sudo___ to each of the commands. - -## Basic Jitsi Meet install - -### Add the repository -```sh -echo 'deb http://download.jitsi.org/nightly/deb unstable/' >> /etc/apt/sources.list -wget -qO - https://download.jitsi.org/nightly/deb/unstable/archive.key | apt-key add - -``` - -### Update the package lists - -```sh -apt-get update -``` - -### Install Jitsi Meet - -```sh -apt-get -y install jitsi-meet -``` - -During the installation, you will be asked to enter the hostname of the Jitsi Meet instance. If you have a FQDN hostname for the instance already set up in DNS, enter it there. If you don't have a resolvable hostname, you can enter the IP address of the machine (if it is static or doesn't change). - -This hostname (or IP address) will be used for virtualhost configuration inside the Jitsi Meet and also, you and your correspondents will be using it to access the web conferences. - -### Open a conference - -Launch a web browser (Chrome, Chromium or latest Opera) and enter in the URL bar the hostname (or IP address) you used in the previous step. - -Confirm that you trust the self-signed certificate of the newly installed Jitsi Meet. - -Enjoy! - -## Adding sip-gateway to Jitsi Meet - -### Install Jigasi - -```sh -apt-get -y install jigasi -``` -or - -```sh -wget https://download.jitsi.org/jigasi_1.0-1_amd64.deb -dpkg -i jigasi_1.0-1_amd64.deb -``` - -During the installation, you will be asked to enter your SIP account and password. This account will be used to invite the other SIP participants. - -### Reload Jitsi Meet - -Launch again a browser with the Jitsi Meet URL and you'll see a telephone icon on the right end of the toolbar. Use it to invite SIP accounts to join the current conference. - -Enjoy! - -## Uninstall - -```sh -apt-get purge jigasi jitsi-meet jicofo jitsi-videobridge -``` - -Sometimes the following packages will fail to uninstall properly: - -- jigasi -- jitsi-videobridge - -When this happens, just run the uninstall command a second time and it should be ok. - -The reason for failure is that sometimes, the uninstall script is faster than the process that stops the daemons. The second run of the uninstall command fixes this, as by then the jigasi or jvb daemons are already stopped. diff --git a/external_api.js b/external_api.js deleted file mode 100644 index 5d23daeb7..000000000 --- a/external_api.js +++ /dev/null @@ -1,355 +0,0 @@ -/** - * Implements API class that embeds Jitsi Meet in external applications. - */ -var JitsiMeetExternalAPI = (function() -{ - /** - * The minimum width for the Jitsi Meet frame - * @type {number} - */ - var MIN_WIDTH = 790; - - /** - * The minimum height for the Jitsi Meet frame - * @type {number} - */ - var MIN_HEIGHT = 300; - - /** - * Constructs new API instance. Creates iframe element that loads - * Jitsi Meet. - * @param domain the domain name of the server that hosts the conference - * @param room_name the name of the room to join - * @param width width of the iframe - * @param height height of the iframe - * @param parent_node the node that will contain the iframe - * @param filmStripOnly if the value is true only the small videos will be - * visible. - * @constructor - */ - function JitsiMeetExternalAPI(domain, room_name, width, height, parentNode, - filmStripOnly) { - if(!width || width < MIN_WIDTH) - width = MIN_WIDTH; - if(!height || height < MIN_HEIGHT) - height = MIN_HEIGHT; - - this.parentNode = null; - if (parentNode) { - this.parentNode = parentNode; - } else { - var scriptTag = document.scripts[document.scripts.length - 1]; - this.parentNode = scriptTag.parentNode; - } - - this.iframeHolder = - this.parentNode.appendChild(document.createElement("div")); - this.iframeHolder.id = "jitsiConference" + JitsiMeetExternalAPI.id; - this.iframeHolder.style.width = width + "px"; - this.iframeHolder.style.height = height + "px"; - this.frameName = "jitsiConferenceFrame" + JitsiMeetExternalAPI.id; - this.url = "//" + domain + "/"; - if(room_name) - this.url += room_name; - this.url += "#external=true"; - if(filmStripOnly) - this.url += "&interfaceConfig.filmStripOnly=true"; - - JitsiMeetExternalAPI.id++; - - this.frame = document.createElement("iframe"); - this.frame.src = this.url; - this.frame.name = this.frameName; - this.frame.id = this.frameName; - this.frame.width = "100%"; - this.frame.height = "100%"; - this.frame.setAttribute("allowFullScreen","true"); - this.frame = this.iframeHolder.appendChild(this.frame); - - - this.frameLoaded = false; - this.initialCommands = []; - this.eventHandlers = {}; - this.initListeners(); - } - - /** - * Last id of api object - * @type {number} - */ - JitsiMeetExternalAPI.id = 0; - - /** - * Sends the passed object to Jitsi Meet - * @param object the object to be sent - */ - JitsiMeetExternalAPI.prototype.sendMessage = function(object) { - if (this.frameLoaded) { - this.frame.contentWindow.postMessage( - JSON.stringify(object), this.frame.src); - } - else { - this.initialCommands.push(object); - } - - }; - - /** - * Executes command. The available commands are: - * displayName - sets the display name of the local participant to the value - * passed in the arguments array. - * toggleAudio - mutes / unmutes audio with no arguments - * toggleVideo - mutes / unmutes video with no arguments - * filmStrip - hides / shows the film strip with no arguments - * If the command doesn't require any arguments the parameter should be set - * to empty array or it may be omitted. - * @param name the name of the command - * @param arguments array of arguments - */ - JitsiMeetExternalAPI.prototype.executeCommand = function(name, - argumentsList) { - var argumentsArray = argumentsList; - if (!argumentsArray) - argumentsArray = []; - var object = {type: "command", action: "execute"}; - object[name] = argumentsArray; - this.sendMessage(object); - }; - - /** - * Executes commands. The available commands are: - * displayName - sets the display name of the local participant to the value - * passed in the arguments array. - * toggleAudio - mutes / unmutes audio with no arguments - * toggleVideo - mutes / unmutes video with no arguments - * filmStrip - hides / shows the film strip with no arguments - * @param object the object with commands to be executed. The keys of the - * object are the commands that will be executed and the values are the - * arguments for the command. - */ - JitsiMeetExternalAPI.prototype.executeCommands = function (object) { - object.type = "command"; - object.action = "execute"; - this.sendMessage(object); - }; - - /** - * Adds event listeners to Meet Jitsi. The object key should be the name of - * the event and value - the listener. - * Currently we support the following - * events: - * incomingMessage - receives event notifications about incoming - * messages. The listener will receive object with the following structure: - * {{ - * "from": from,//JID of the user that sent the message - * "nick": nick,//the nickname of the user that sent the message - * "message": txt//the text of the message - * }} - * outgoingMessage - receives event notifications about outgoing - * messages. The listener will receive object with the following structure: - * {{ - * "message": txt//the text of the message - * }} - * displayNameChanged - receives event notifications about display name - * change. The listener will receive object with the following structure: - * {{ - * jid: jid,//the JID of the participant that changed his display name - * displayname: displayName //the new display name - * }} - * participantJoined - receives event notifications about new participant. - * The listener will receive object with the following structure: - * {{ - * jid: jid //the jid of the participant - * }} - * participantLeft - receives event notifications about participant that left room. - * The listener will receive object with the following structure: - * {{ - * jid: jid //the jid of the participant - * }} - * @param object - */ - JitsiMeetExternalAPI.prototype.addEventListeners - = function (object) { - - var message = {type: "event", action: "add", events: []}; - for(var i in object) - { - message.events.push(i); - this.eventHandlers[i] = object[i]; - } - this.sendMessage(message); - }; - - /** - * Adds event listeners to Meet Jitsi. Currently we support the following - * events: - * incomingMessage - receives event notifications about incoming - * messages. The listener will receive object with the following structure: - * {{ - * "from": from,//JID of the user that sent the message - * "nick": nick,//the nickname of the user that sent the message - * "message": txt//the text of the message - * }} - * outgoingMessage - receives event notifications about outgoing - * messages. The listener will receive object with the following structure: - * {{ - * "message": txt//the text of the message - * }} - * displayNameChanged - receives event notifications about display name - * change. The listener will receive object with the following structure: - * {{ - * jid: jid,//the JID of the participant that changed his display name - * displayname: displayName //the new display name - * }} - * participantJoined - receives event notifications about new participant. - * The listener will receive object with the following structure: - * {{ - * jid: jid //the jid of the participant - * }} - * participantLeft - receives event notifications about participant that left room. - * The listener will receive object with the following structure: - * {{ - * jid: jid //the jid of the participant - * }} - * @param event the name of the event - * @param listener the listener - */ - JitsiMeetExternalAPI.prototype.addEventListener - = function (event, listener) { - - var message = {type: "event", action: "add", events: [event]}; - this.eventHandlers[event] = listener; - this.sendMessage(message); - }; - - /** - * Removes event listener. - * @param event the name of the event. - */ - JitsiMeetExternalAPI.prototype.removeEventListener - = function (event) { - if(!this.eventHandlers[event]) - { - console.error("The event " + event + " is not registered."); - return; - } - var message = {type: "event", action: "remove", events: [event]}; - delete this.eventHandlers[event]; - this.sendMessage(message); - }; - - /** - * Removes event listeners. - * @param events array with the names of the events. - */ - JitsiMeetExternalAPI.prototype.removeEventListeners - = function (events) { - var eventsArray = []; - for(var i = 0; i < events.length; i++) - { - var event = events[i]; - if(!this.eventHandlers[event]) - { - console.error("The event " + event + " is not registered."); - continue; - } - delete this.eventHandlers[event]; - eventsArray.push(event); - } - - if(eventsArray.length > 0) - { - this.sendMessage( - {type: "event", action: "remove", events: eventsArray}); - } - - }; - - /** - * Processes message events sent from Jitsi Meet - * @param event the event - */ - JitsiMeetExternalAPI.prototype.processMessage = function(event) { - var message; - try { - message = JSON.parse(event.data); - } catch (e) {} - - if(!message.type) { - console.error("Message without type is received."); - return; - } - switch (message.type) { - case "system": - if(message.loaded) { - this.onFrameLoaded(); - } - break; - case "event": - if(message.action != "result" || - !message.event || !this.eventHandlers[message.event]) { - console.warn("The received event cannot be parsed."); - return; - } - this.eventHandlers[message.event](message.result); - break; - default : - console.error("Unknown message type."); - return; - } - }; - - /** - * That method is called when the Jitsi Meet is loaded. Executes saved - * commands that are send before the frame was loaded. - */ - JitsiMeetExternalAPI.prototype.onFrameLoaded = function () { - this.frameLoaded = true; - for (var i = 0; i < this.initialCommands.length; i++) { - this.sendMessage(this.initialCommands[i]); - } - this.initialCommands = null; - }; - - /** - * Setups the listener for message events from Jitsi Meet. - */ - JitsiMeetExternalAPI.prototype.initListeners = function () { - var self = this; - this.eventListener = function (event) { - self.processMessage(event); - }; - if (window.addEventListener) { - window.addEventListener('message', - this.eventListener, false); - } - else { - window.attachEvent('onmessage', this.eventListener); - } - }; - - /** - * Removes the listeners and removes the Jitsi Meet frame. - */ - JitsiMeetExternalAPI.prototype.dispose = function () { - if (window.removeEventListener) { - window.removeEventListener('message', - this.eventListener, false); - } - else { - window.detachEvent('onmessage', - this.eventListener); - } - var frame = document.getElementById(this.frameName); - if(frame) - frame.src = 'about:blank'; - var self = this; - window.setTimeout(function () { - self.iframeHolder.removeChild(self.frame); - self.iframeHolder.parentNode.removeChild(self.iframeHolder); - }, 10); - }; - - return JitsiMeetExternalAPI; - -})(); \ No newline at end of file diff --git a/favicon.ico b/favicon.ico deleted file mode 100644 index bb34caf1ffdb68de313a75db9e9f8ff87ece3ec4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3638 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 diff --git a/fonts/jitsi.svg b/fonts/jitsi.svg deleted file mode 100644 index b78a3ebb0..000000000 --- a/fonts/jitsi.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - -Generated by IcoMoon - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fonts/jitsi.ttf b/fonts/jitsi.ttf deleted file mode 100644 index bb740209893c1c1fd0f439c0a0841352b1e00901..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8952 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 m#fU@JqzkC;Cbyw&k$e*AI+%Qi$ws_ diff --git a/fonts/jitsi.woff b/fonts/jitsi.woff deleted file mode 100644 index 0551381e128a9e44e6d5ddbb7fa04b99ebd56838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9028 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 diff --git a/images/avatar2.png b/images/avatar2.png deleted file mode 100644 index f59231c872de648d6bfbd688a58dd62039318af5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1607 zcmV-N2Dtf&P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2i^!3 z5jZ;tC#MSl000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000G|NklvY`nTe*$<9;12-51IR0-KAZAa82}Om z@I8R<09+Pm@(qAz0De_U{f1@}$uP+T$pZDN;Pw-41}I}BGi;19B*#1l#c`Z4$uzy8 zwBtBouZ)%%dQ&rvgfdDpOK&Sna?~`K7)giCA%|ql6qp2?QwiN*me|}{(sBqSnFeqZ z&97gSQa@DlhgP*C<{JS605_{0K~^xNxCf*CHfmlu|!dn{E(SGMTJ-v{bT(4yx15(w|DDP%4#-1xuw;38|Dwq!V;`{5xtMNQVcZP{`P*IkZqH#Qpt! ziLdbBOE_zy=kaXTIG97rW<{c&H2|>OE85GFL;U5-mkrBB2=#rL>2=?s< zNn1K>C=?1OrapClwd=Lu_EgV!-i7FTjA{vbvA2rvnUzeO#omQn(sRSU?x^(H1 zWOJ<+4r=hk<@5QB!x8SYtYyt1$<578`u%=m>y6*<=jP_7XgUfc-|4!7SzKH+24`_` zQQG}?TJNWJcXt^K2DO6;27}z)-4(yjvT#u6t3~bY?MNn*+Si(7GKu!~cIjr{8>#7= zpP#2?S>hwcvMkQe&+DrxBzeO`)zHeyis->mDaDnQ6`d~M0LcMUWmS<#L<~+O64CAM zfaSg=cw^!mGBPsa$+nKs8>Q6e!|UK@Cg8+kaz&l-cwDEe&%VqdM@>zw@$vDxr_}iP zxNbM2TJNnYX=7ufu7z1C#f^;(oi2WTZ{_NViK!I`1TZ)_SbKVIXb6EoK&OjOj)U{F zsQ~f(`SUe@wyUd4w|mEPpn>N=dwYBA>guX_TJ`kwuv{){tH}G3$I;@YpLG9QGMP+W z*C)1ZTe8UtLZGKr>QM5{U%+`uen`EBgBSm`Eh*g><%uYxU%LOh6HYls)sx zR7(YWu~_87!UFsI`x{neaR_N=XGdQJy9+2*O%L1K+q{4OzR{6#nss+~b9#FEw7hbC z!QM%=f}cKp;?&d>J3Bi)1LdflJ9m!v?%g}?vQOsa9*?}xTVG#iJRWCTTbpO09JRK# zHpXHxuCK43`Ndu}fYQZck$3LgVM|L((?B_DEiEl+Eyu^<;o+rbh2q*0G%L}hU=lE- z7yze9pg69-W=mlJWIYZh>-yO+&3F{d3_PMRfV4-Tq+NeJr!fGT20_WV{d-|B3b;>ZPx_|8rfU#eD`v;9p6Ysf;cp3lz002ovPDHLk FV1j*K_I3aO diff --git a/images/avatarprezi.png b/images/avatarprezi.png deleted file mode 100644 index 949c49ec66d4ba9b65635e7bddbab099e759467c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8664 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 diff --git a/images/chatArrow.svg b/images/chatArrow.svg deleted file mode 100644 index f698c28ee..000000000 --- a/images/chatArrow.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - diff --git a/images/chrome.png b/images/chrome.png deleted file mode 100644 index 774be6e89a0b3ab45ab0cea56721464b11dd1b0b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8255 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 diff --git a/images/chromium.png b/images/chromium.png deleted file mode 100644 index ef4516f9d391d45e9e3a33a16993e4b885bdda8b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8052 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 diff --git a/images/dropdownPointer.png b/images/dropdownPointer.png deleted file mode 100644 index fcc7d03963c75be5a82da3d85c80e5c5f780132b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^d_c^}!3HFmd}T9%6k~CayA#8@b22Z19JVBHcNd2L zAh=-f^2s2z1s;*b3=G`DAk4@xYmNj^kiEpy*OmP~6R&_dV~oKbQ=m|mr;B5V#`&qI zH}WJKHDMA3l8~wI}%e z<`su}bWI=s;}?~%4=`C4UG|q>gTe~DWM4fQ07gH diff --git a/images/estoslogo.png b/images/estoslogo.png deleted file mode 100644 index d8e78a831e52ac3649f04b646f31fc2f0a57f207..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2947 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 diff --git a/images/favicon.ico b/images/favicon.ico deleted file mode 100644 index 2197701f74e3dda21e41691d03d3865700e68a57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4286 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 JPDHLkV1gK09GU*E&+5QB% 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 P*^Uepl?7A@>-+yN3un=R diff --git a/images/jitsilogo.png b/images/jitsilogo.png deleted file mode 100644 index 51a385452f026dd3536ee0cfa9b409941982bbf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3376 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 BW%2+3 diff --git a/images/noMic.png b/images/noMic.png deleted file mode 100644 index 287fd286bb4fe8153e815314a8318342a9e6a1d0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2880 zcmbVOdpwi-AD>GlDRKNLg_*nGYM$lNM4kVZeo6o7r0ek_g!WbV#g2$;pNfVdfv z5#&Hyray}q&SScSJ5uT4$LOXEh?OPSJQO1$U^4|Ya40)~!^ebLK)&%}MDz7!I0XF7 zL~zUk@?DfW*$HgRTxoss6l4Hj5HL`uJhzz!sn|03q_efx)g0T$)%pB=3miqv5C^H`#y&;oE=dN7;G5s+{e z5YZbLgT=rAw#IlvBnnRekS1so05Cx!u~;JjXNa;jHAWbtH#q(dYlAh!+lq2-YXgWX zfj2ZknF3f7V;iKQjS#n|$gGy#`K<#GcyI>5=F zE8z0|xq)C?7c^LxOrx_n>kE481^ON@j>%(%Fc}0Mmks`wVGQeU6d+J0BGtx*2xBDT zTjU566agUM0R#?@MjIg@Kd_AdCu?w#82Ea7{7<`VEQxw>efn4Piyr>kK1_~i&+tT> zz?E>1#Ap%aw4=k@%w77s0S>X>~|)&qDc1 zIis_d_G*>yZm;TS`L%5y*%gV~Z0ZQt>Pb7$SOWrFTw0;deNIZtC%-1I9-bSnU!1LI z&Rv>#VX;`(@HKzUYHg_#^JZpduVY#7D0gU9ikkqHBZ~V(R#+=^iTg|TiTg!rSns~B z^hH_MA&{CkFN`R&!XcJ+GHfFsHWCYdJ;#B8!WxwyVP~3xHN?fqDW#z5(~e$O`azk{ zv-1vVCB1U~6Qz&UkLaarvN^RSQ;Mjmaa`hem-!i4aCUaPuo3HU$+gU4`+zDDly)$U zZP-60QBruqt1d)|C%NXkpkm-jCxPv$jhQ=r%{rx^)+`!I1(Qr*m9_fD_U@O= zBH20n>e%s!c(%1YOn*y@Pa|dDPWhHSNpJbi0%`x|P6lH3{!=q3Nir|QItxx*Awpy! z@z33Ls2G_~k~cnbs|4qrD$lbZmm?(J;LKn{TV*8Ue{$84ijNg%S))^A8RE{Ei52d?=}h~GHTtW-cY5X*aa0~=*2Up{%fo@z=?9~uoX0xiWBMQ9*Y z=>x11f@DJE#9>33;D)YDsXKeQ2}P4{Q%3q;MM{=V=nQhEVprJCu43j_wC<&;qb|S_ zT&tv~VC7q_p;kYg9{jWG)vn^~i!;@U`l=VvYRR;wYw37%b{90UezRT8P4cZ6si)oZ zhr6fr1T!C^iE;Pt)a8x7Gh?+~@~M+o`zk}yw^evNae%rXB5`J|M&evUkfe@^{&Y7t zv3LM=Frz;9!?4jUR@$bQ9aazh_7rMC%)C@J<(2kd^D>)^U`;0${rtI92l~0%#=PvX zdQ;N#T{JS!z~l0;^;Rbt&CsjDX=?waq^I0*ll+91z!ng?2#6$iJqw`>kq1q2kxdVm&XYzZ{|s& z`}ZEKfmZvjt)AumJk>nkX7l7@vG;UX>zO$At9xQmRgp&*M_)~hSzVZ%!jA*LZlsm-yrNOcmQIQr9Iw>AGYwRInUp&V!WO zp1KPBh8&hzKpPKb_QsjdydLUmRc|tViAz-syY8WJ5#^8;*MG*BICyD(w^o+K?!xex zqy<_L`936i=bIWGT*9qcmkJr}-0^7KHrK98O#;gM5h(G@hp@tP$*4b-i=%V0gN|b= z2=5-2aIfq;Gl%p_Y4!9k6GPX#S1M+D-)upT19^lcwy)Uwkbl8WF7Hn_&H2nCD zd*ywZ;TILUW|$1@jUYu!o_vWstqL~nqedI94%nys!Sb|3`y+VqNQigXVyvaChd{X5 zX9wQ0qjDF!D(=Vyojq9py1zT&)PT^m`8cO_dmI)v@=$C7Za6(fSt$?AJwXC z&C&CXdWw1`EW(|xF`o^n2M3ud@KuOssz&4FX8S%J>nOOHsM?^WrvO90bZdJPSqlEJ z5}bmA-C53m#*1mowJN4|9L?#9UnOYD1mpa6 z79aoYW@59n*5EfNbD?9n;xna+wla@J<;JXeD{L=}`Ew2|p|ae$TDg!6k^l`7+KXU^ S#yi*lX-RlTT!qb%sQ&;<_QqrY diff --git a/images/noVideo.png b/images/noVideo.png deleted file mode 100644 index 91cd97b19cd1d362256ecd51ce0274ba13cc9e25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2911 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 diff --git a/images/smile.svg b/images/smile.svg deleted file mode 100644 index cd254bd31..000000000 --- a/images/smile.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley1.svg b/images/smileys/smiley1.svg deleted file mode 100644 index b48703bd6..000000000 --- a/images/smileys/smiley1.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - diff --git a/images/smileys/smiley10.svg b/images/smileys/smiley10.svg deleted file mode 100644 index de0974726..000000000 --- a/images/smileys/smiley10.svg +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - - - - - - - diff --git a/images/smileys/smiley11.svg b/images/smileys/smiley11.svg deleted file mode 100644 index adfd0588f..000000000 --- a/images/smileys/smiley11.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley12.svg b/images/smileys/smiley12.svg deleted file mode 100644 index 94a04dddd..000000000 --- a/images/smileys/smiley12.svg +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley13.svg b/images/smileys/smiley13.svg deleted file mode 100644 index aa873fa9a..000000000 --- a/images/smileys/smiley13.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - diff --git a/images/smileys/smiley14.svg b/images/smileys/smiley14.svg deleted file mode 100644 index 33c5826bc..000000000 --- a/images/smileys/smiley14.svg +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley15.svg b/images/smileys/smiley15.svg deleted file mode 100644 index 48970df1a..000000000 --- a/images/smileys/smiley15.svg +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - - - - - - diff --git a/images/smileys/smiley16.svg b/images/smileys/smiley16.svg deleted file mode 100644 index 1e369e086..000000000 --- a/images/smileys/smiley16.svg +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley17.svg b/images/smileys/smiley17.svg deleted file mode 100644 index 75ebedddb..000000000 --- a/images/smileys/smiley17.svg +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley18.svg b/images/smileys/smiley18.svg deleted file mode 100644 index dffc3440d..000000000 --- a/images/smileys/smiley18.svg +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley19.svg b/images/smileys/smiley19.svg deleted file mode 100644 index 1b143b8b1..000000000 --- a/images/smileys/smiley19.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - diff --git a/images/smileys/smiley2.svg b/images/smileys/smiley2.svg deleted file mode 100644 index 2b48e0703..000000000 --- a/images/smileys/smiley2.svg +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - diff --git a/images/smileys/smiley20.svg b/images/smileys/smiley20.svg deleted file mode 100644 index dc8832950..000000000 --- a/images/smileys/smiley20.svg +++ /dev/null @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley21.svg b/images/smileys/smiley21.svg deleted file mode 100644 index 634dd3ac8..000000000 --- a/images/smileys/smiley21.svg +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley3.svg b/images/smileys/smiley3.svg deleted file mode 100644 index 5f29de639..000000000 --- a/images/smileys/smiley3.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley4.svg b/images/smileys/smiley4.svg deleted file mode 100644 index aa6082b8b..000000000 --- a/images/smileys/smiley4.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley5.svg b/images/smileys/smiley5.svg deleted file mode 100644 index 5e2c2b2cc..000000000 --- a/images/smileys/smiley5.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - diff --git a/images/smileys/smiley6.svg b/images/smileys/smiley6.svg deleted file mode 100644 index df46cfbbb..000000000 --- a/images/smileys/smiley6.svg +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley7.svg b/images/smileys/smiley7.svg deleted file mode 100644 index 8eb166b7e..000000000 --- a/images/smileys/smiley7.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley8.svg b/images/smileys/smiley8.svg deleted file mode 100644 index 1e8897cdc..000000000 --- a/images/smileys/smiley8.svg +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/images/smileys/smiley9.svg b/images/smileys/smiley9.svg deleted file mode 100644 index 31fe3cbda..000000000 --- a/images/smileys/smiley9.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - diff --git a/images/videomask.svg b/images/videomask.svg deleted file mode 100644 index 76d67ae80..000000000 --- a/images/videomask.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - diff --git a/images/watermark.png b/images/watermark.png deleted file mode 100644 index 6fa553d3bcacae15a7610911d8c0ef31a76e400c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33340 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 diff --git a/images/welcome_page/bubble.png b/images/welcome_page/bubble.png deleted file mode 100644 index 9a3edc6cc387c4aa87599b72fd84df261dcf7fca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8013 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 X^xT(2Vs_+Cz8*C4wpO`@b?E;9fZTit diff --git a/images/welcome_page/header-big.png b/images/welcome_page/header-big.png deleted file mode 100644 index d845c9d9a5464c49feb4ba2e84cb847e80e78c13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 794 zcmeAS@N?(olHy`uVBq!ia0y~yU~*?*U=-kB1B&cYxS$H8*pj^6T^Rm@;DWu&Cj&(| z3p^r=85p>QL70(Y)*K0-AbW|YuPggK4oMzi!QV$@^npUMnIRD+&iT2ysd*(pE(3#e zQEFmIYKlU6W=V#EyQgnJie4%^(9Swf7srr_TW=2zw@Xg3VhLc}tKDCJ-UmKT~WjvVc2+h7*qW8%RwHPIa% zL8wg~=D}SmB0arGzoTk>85Uw+|53Kl2LIYVb6UEnQW_YTQ8iEsqapsK&NuVaDAe$m9oh7M&lWh^gJ>s$0AMg zvs2z(RtKsnIs=&AI5RM?`N#l}7f846orUJOMJwnEh%QHeX9n0(+EiY9^TckcIA;NV z1x`~jplCF{fPW;7F#Wa5aeKAOWD9>zCZ+W%{SQ9_+FCW&eR<)K3eN|+Op-YFbmzpS zGV!G)-0+O^R^#Zzi6E0bMAw}ivsLIKElZ1>g`03&dtWfBt?FneU?rINPq`*S@|lZuGZJ|6kG+c1 zPrNn7ayTl-HcdpRLv%6~w_5?rltN<(ZRKu73 zJpaQuxwh*z@isRrDbHBm+b&)b=jm(`{UYuaR4Zbk;GtX&s^J|W*M}^$a?_!!ey`7t z3#^3kHv4?T-#yj95iA~jVj})zN262%yE(`;jQ7+7u!Nh0DW4eIEZbfJ6HCh~Mm@I&fln}lqA)Q_wYKnG&SV9p{>Z#;zReO}T2u{-qQ!bGW zwwB~g(lAOn0~Nqr7T47tHB`C$O4DD{ANk}Ocag^+wj`}C(;P$93A`K#M ztjF2AsgP91RHoGU5(6{OA%UUH6893!pu7vz#nxriWoHOh8b>Rnk+Nu+)=K+RyHWF% zb)f%|$seIgPeiws~~B(tOi zCbTj-6P<*P>%MC8esJ8>rjAxk<8qyGhANdR*LUVte!}6N>J2|uX;tgGo9F* zi;c|`8WXaXU6M7CTaZ18lg1swx>wIvZnu7D%W$={3+#+^nY63wD;S%3QC2KB;oGVv%YQaZDa2d zqkFYGGQ3xK6H&39DTT?iVl+;xKPVM95^q%0ByHeVTviIzRn?=_0Q$9{}_o82DS!GJD;p0a=C$Oq|xuCuChDzG0pbQ{##%+y>Tb`t*1TEb#bh*^6V z=^LTOuFPdAxa}hXlT~^OUG%Rt^9%ALf#mSII>@$q{rIkTU8vYNp({cpy>3*y?hxv# zZh$UD_X-6oi>sQ_Bb$@K1qh*&#{>f;e7%b|-i=*}nq2(CF}Vy=w0Kap_`&Ov!l71{fP!=d~lBSY4?8_{M08)Je0hLqmZt}Rby9v52jX748xqPHRtoncAd%nbc!AO2e zTA08E-q;%JT@PQPK3RXVL;CE?M*1{1SN8*(j>lPzzAgW3SvKAKJ@$W#< z+3*tL?c=Kzik?$1SGb^t3g`3ZL&f4VW~lQ4HA|TvIzNsyPPhjTFvYR7v#OsE5Pu!q zwPEC1rsdvl`-%Rvm9~&OU)SgqY#pq#q`#U-Lq(vTbhgCb9=bF*a-rx#%K4VKq>g8k zw>mxdKL11p94(zQQp|g`3A%VnW%Wzv=E;>TF11nrn81=bslDv=mv7Bu%yWi64M)Gs zKTilS2$+})8OS`|cU(^6;r`t<@_Kqk)f=ZPw8TEaM-Nr9NkQMWJ0ezNqlKeXZxGe% zs;Ay%mRFXK_k>a^tX5xn7k{Mg?3z(Zoyt`#F9l`>QkNq4;)YMuXFaf93?j_ESaGM$ zc~$K_m@K>Ba42wg+j0j!zFrqNeSbgm7)$Ko?PXX)z`oTd#9ZL`UO#Iwua!pcor`;9omEF;LQ*dZ4YjzuJD)o|1niT%Y_gC8XRABFg{%D+)YnbkSe;D5JFokNBA(uL7QC|XC$l w28)2aMq_JJc<+`>F*85V9dZ%OI54pTz%N7Yr~u&@$AiYBbq#gOwQa)w2~pVN{{R30 diff --git a/images/welcome_page/pattern-header.png b/images/welcome_page/pattern-header.png deleted file mode 100644 index 83ff8eec398c8b023916568ca05ab7c0bb2d062c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10047 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 NDhitNm9MNq{||=${G9** diff --git a/index.html b/index.html deleted file mode 100644 index 988756984..000000000 --- a/index.html +++ /dev/null @@ -1,249 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

-
- -
-
- -
-
- jitsi.org - -
-
-
-
- -
- - -
-
-
-
- - -
- -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
-
- -
-
-
-
-
- - - - - - - - - - -
- - - - - - - - -
- - - - - - - -
- - - - - -
-
-
-
- -
- -
-
- - -
- - -
-
- -
-
-
-
-
- -
-
    -
    -
    -
    - -
    - - -
    - - -
    - -
    - -
    - - diff --git a/interface_config.js b/interface_config.js deleted file mode 100644 index 0317683e8..000000000 --- a/interface_config.js +++ /dev/null @@ -1,23 +0,0 @@ -var interfaceConfig = { - CANVAS_EXTRA: 104, - CANVAS_RADIUS: 7, - SHADOW_COLOR: '#ffffff', - INITIAL_TOOLBAR_TIMEOUT: 20000, - TOOLBAR_TIMEOUT: 4000, - DEFAULT_REMOTE_DISPLAY_NAME: "Fellow Jitster", - DEFAULT_DOMINANT_SPEAKER_DISPLAY_NAME: "speaker", - DEFAULT_LOCAL_DISPLAY_NAME: "me", - SHOW_JITSI_WATERMARK: true, - JITSI_WATERMARK_LINK: "https://jitsi.org", - SHOW_BRAND_WATERMARK: false, - BRAND_WATERMARK_LINK: "", - SHOW_POWERED_BY: false, - GENERATE_ROOMNAMES_ON_WELCOME_PAGE: true, - APP_NAME: "Jitsi Meet", - INVITATION_POWERED_BY: true, - ACTIVE_SPEAKER_AVATAR_SIZE: 100, - /** - * Whether to only show the filmstrip (and hide the toolbar). - */ - filmStripOnly: false -}; diff --git a/lang/Translation.md b/lang/Translation.md deleted file mode 100644 index 2911c3412..000000000 --- a/lang/Translation.md +++ /dev/null @@ -1,57 +0,0 @@ -Jitsi Meet Translation -========================== -Jitsi Meet uses [i18next](http://i18next.com) library for translation. -i18next uses separate json files for each language. - - -Translating Jitsi Meet -====================== -The translation of Jitsi Meet is integrated with Pootle. You can translate Jitsi Meet via our Pootle user interface on -[http://translate.jitsi.org](http://translate.jitsi.org). - -**WARNING: Please don't create or edit manually the language files! Please use our Pootle user interface!** - -Development -=========== -If you want to add new functionality for Jitsi Meet and you have texts that need to be translated please use our translation module. -It is located in modules/translation. You must add key and value in main.json file in English for each translatable text. -Than you can use the key to get the translated text for the current language. - -**WARNING: Please don't change the other language files except main.json! They must be updated and translated via our Pootle user interface!** - -You can add translatable text in the HTML: - - -* **via attribute on HTML element** - add **data-i18n** attribute with value the key of the translatable text. - - - ``` - OK - ``` - - - You can also use APP.translation.generateTranslationHTML(key, options) to get this HTML code as Javascript string. - - - ``` - APP.translation.generateTranslationHTML("dialog.OK") // returns OK - ``` - - The value in the options parameter will be added in data-i18n-options attribute of the element. - - **Note:** If you dynamically add HTML elements don't forget to call APP.translation.translateElement(jquery_selector) to translate the text initially. - -* **via Javascript string** - call APP.translation.translateString(key, options). You can use that method to get the translated string in Javascript and then attach it in the HTML. - - ``` - APP.translation.translateString("dialog.OK") // returns the value for the key of the current language file. "OK" for example. - ``` - -For the available values of ``options`` parameter for the above methods of translation module see [i18next documentation](http://i18next.com/pages/doc_features). - -**Note:** It is useful to add attributes in the HTML for persistent HTML elements because when the language is changed the text will be automatically translated. - Otherwise you should call ``APP.translation.translateString`` and manually change the text every time the language is changed. - - - - diff --git a/lang/languages-bg.json b/lang/languages-bg.json deleted file mode 100644 index cff4ab662..000000000 --- a/lang/languages-bg.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "en": "Английски", - "bg": "Български", - "de": "Немски", - "tr": "Турски", - "it": "Италиански" -} \ No newline at end of file diff --git a/lang/languages-de.json b/lang/languages-de.json deleted file mode 100644 index 149a29e5b..000000000 --- a/lang/languages-de.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "en": "Englisch", - "bg": "Bulgarisch", - "de": "Deutsch", - "tr": "Türkisch", - "it": "Italienisch", - "fr": "Französisch" -} \ No newline at end of file diff --git a/lang/languages-fr.json b/lang/languages-fr.json deleted file mode 100644 index f50dd22d2..000000000 --- a/lang/languages-fr.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "en": "Anglais", - "bg": "Bulgare", - "de": "Allemand", - "tr": "Turc", - "it": "Italien" -} \ No newline at end of file diff --git a/lang/languages-it.json b/lang/languages-it.json deleted file mode 100644 index a9831eaed..000000000 --- a/lang/languages-it.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "en": "Inglese", - "bg": "Bulgaro", - "de": "Tedesco", - "tr": "Turco", - "it": "Italiano" -} \ No newline at end of file diff --git a/lang/languages-tr.json b/lang/languages-tr.json deleted file mode 100644 index 2d089e711..000000000 --- a/lang/languages-tr.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "en": "İngilizce", - "bg": "Bulgarca", - "de": "Almanca" -} \ No newline at end of file diff --git a/lang/languages.json b/lang/languages.json deleted file mode 100644 index b76ea1a15..000000000 --- a/lang/languages.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "en": "English", - "bg": "Bulgarian", - "de": "German", - "tr": "Turkish", - "it": "Italian", - "fr": "French" -} diff --git a/lang/main-bg.json b/lang/main-bg.json deleted file mode 100644 index da2cfebab..000000000 --- a/lang/main-bg.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "contactlist": "СПИСЪК С КОНТАКТИ", - "connectionsettings": "Настройки на връзката", - "poweredby": "powered by", - "downloadlogs": "Изтегли логовете", - "roomUrlDefaultMsg": "Конференцията се създава...", - "participant": "Участник", - "me": "аз", - "speaker": "Говорител", - "defaultNickname": "например __name__", - "defaultPreziLink": "например __url__", - "welcomepage": { - "go": "Влез", - "roomname": "Въведете име на стаята", - "disable": "Не показвай страницата следващия път", - "feature1": { - "title": "Лесен за употреба", - "content": "Не е необходимо да сваляте нищо. _app_ работи директно във вашия браузър. Просто споделете адреса на вашата конференция с другите за да започнете." - }, - "feature2": { - "title": "", - "content": "Видео конференциите могат да работят с по-малко от 128Kbps, а аудио конференциите и конференциите с споделен екран дори с по-малко." - }, - "feature3": { - "title": "Отворен код", - "content": "__app__ е лицензиран под MIT лиценз. Можете свободно да го изтеглите, използвате, променяте и споделяте според тези лицензи." - }, - "feature4": { - "title": "Неограничен брой потребители", - "content": "Няма изкуствени ограничения за броя на потребителите или участниците в конференция. Единствените ограничения са мощността на вашия сървър и качеството на интернет връзката му." - }, - "feature5": { - "title": "Споделяне на екрана", - "content": "Лесно е да споделите екрана си с другите. __app__ е идеален за онлайн презентации, лекции и техническа подръжка." - }, - "feature6": { - "title": "Сигурни стаи", - "content": "Нуждаете се от уединение? _app__ конферентните стай могат да бъдат защитени от парола за да се препазите от нежелани гости или прекъсвания." - }, - "feature7": { - "title": "Споделени бележки", - "content": "__app__ използва Etherpad, с който можете да редактирате текст в реално време заедно." - }, - "feature8": { - "title": "Статистики за използване", - "content": "Научете повече за своите потребители като интегрирате лесно Piwik, Google Analytics и други статистики за изполването." - } - }, - "toolbar": { - "mute": "Включи / Изключи микрофона", - "videomute": "Спри / пусни камерата", - "authenticate": "", - "record": "Запис", - "lock": "Заключи / отключи стаята", - "invite": "Поканване на други", - "chat": "", - "prezi": "Сподели Prezi", - "etherpad": "Споделяне на документ", - "sharescreen": "Споделяне на екрана", - "fullscreen": "Влез / Излез от Пълен екран", - "sip": "Обади се на SIP номер", - "Settings": "Настройки", - "hangup": "Затвори", - "login": "Влез", - "logout": "" - }, - "bottomtoolbar": { - "chat": "Отвори / затвори чат", - "filmstrip": "Покажи / скрий лентата с видеа", - "contactlist": "Отвори / затвори контакт листа" - }, - "chat": { - "nickname": { - "title": "Въведете име в полето", - "popover": "Избор на име" - }, - "messagebox": "Въведете текст..." - }, - "settings": { - "title": "НАСТРОЙКИ", - "update": "Актуализиране", - "name": "Име" - }, - "videothumbnail": { - "editnickname": "Натиснете за да
    промените името", - "moderator": "Създателя на
    конференцията", - "videomute": "Учасника е спрял
    камерата си.", - "mute": "Учасника е с изключен микрофон", - "kick": "Изгони", - "muted": "Изключен микрофон", - "domute": "Изключи микрофона" - }, - "connectionindicator": { - "bitrate": "", - "packetloss": "Загуба на пакети:", - "resolution": "Резолюция:", - "less": "Скрий", - "more": "Покажи", - "address": "Адрес:", - "remoteport": "Отдалечен порт:", - "remoteport_plural": "Отдалечени портове:", - "localport": "Локален порт:", - "localport_plural": "Локални портове:", - "localaddress": "Локален адрес:", - "localaddress_plural": "Локални адреси:", - "remoteaddress": "Отдалечен адрес:", - "remoteaddress_plural": "Отдалечени адреси:", - "transport": "Транспорт:", - "bandwidth": "", - "na": "Върнете се тук за информацията относно вашата връзка, когато започне конференцията" - }, - "notify": { - "disconnected": "връзката е прекъсната", - "moderator": "Придобихте права на модератор!", - "connected": "свързан", - "somebody": "Някой", - "me": "Аз", - "focus": "Конферентен фокус", - "focusFail": "__component__ не е на раположения - следващ опит след __ms__ секунди", - "grantedTo": "Даване на роля модератор на __to__!", - "grantedToUnknown": "Даване на роля модератор на $t(somebody)!" - }, - "dialog": { - "kickMessage": "Бяхте изгонен от срещата!", - "popupError": "Вашия браузър блокира попъп прозорците от този сайт. Моля позволете попъп прозорците от настройките на браузъра и опитайте пак.", - "passwordError": "Тази конференция е защитена с парола. Само създателя и може да промени паролата.", - "passwordError2": "Тази конференция не е защитена с парола. Само създателя и може да сложи парола.", - "joinError": "Не можете да се присъедините към конференцията. Може би имате проблем с конфигурацията на сифурността. Моля обърнете се към администратора на услугата.", - "connectError": "Опа! Нещо се обърка и не успяхме да се свържем с конференцията.", - "connecting": "", - "error": "", - "detectext": "Възникна грешка при опит да бъде намерено разширението за споделяне на екран.", - "failtoinstall": "Неуспешна инсталация на разширението за споделяне на екрана.", - "failedpermissions": "Неуспешен опит за получаване на права за използване на микрофон и/или камера.", - "bridgeUnavailable": "Jitsi Videobridge не е наличен. Моля опитайте пак!", - "lockTitle": "Неуспешно заключване", - "lockMessage": "Неуспешно заключване на конференцията.", - "warning": "Внимание", - "passwordNotSupported": "В момента не поддържаме стаи с пароли.", - "sorry": "Съжаляваме", - "internalError": "Вътрешна грешка [setRemoteDescription]", - "unableToSwitch": "Неуспешен опит за смяна на видеото.", - "SLDFailure": "Опа! Нещо се обърка и не успяхме да спрем микрофона! (SLD Failure)", - "SRDFailure": "Опа! Нещо се обърка и не успяхме да спрем камерата! (SRD Failure)", - "oops": "Опа!", - "defaultError": "Възникна грешка", - "passwordRequired": "Изисква се парола", - "Ok": "ОК", - "removePreziTitle": "Премахни Prezi", - "removePreziMsg": "Сигурни ли сте, че искате да премахнете Prezi?", - "sharePreziTitle": "Сподели Prezi", - "sharePreziMsg": "Друг участник вече е споделил Prezi. Тази конференция позволява само да се споделя само едно Prezi.", - "Remove": "Премахване", - "Stop": "Спиране", - "AuthMsg": "Нужна е идентификация, за да създадете стая:
    __room__
    Може да се идентифицирате, за да създадете стая или да изчакате някой друг да го направи.", - "Authenticate": "Идентификация", - "Cancel": "Отказ", - "retry": "Повторен опит", - "logoutTitle": "Изход", - "logoutQuestion": "Сигурни ли сте, че искате да излезете и да прекъснете конференцията?", - "sessTerminated": "Сесията е прекъсната.", - "hungUp": "Вие затворихте обаждането.", - "joinAgain": "Песъединете се отново", - "Share": "Споделяне", - "preziLinkError": "Моля въведете правилен Prezi линк.", - "Save": "Запазване", - "recordingToken": "Въведете код за достъп за запис на конференцията", - "Dial": "Набиране", - "sipMsg": "Въведете SIP номер", - "passwordCheck": "Сигурни ли сте, че искате да махнете паролата?", - "passwordMsg": "Въведете парола, за да заключите стаята", - "Invite": "Покани", - "shareLink": "Сподели този линк с всеки, който искаш да поканиш", - "settings1": "Конфигурирай конференцията", - "settings2": "Участниците се присъединиха с изключен микрофон.", - "settings3": "Изисквай имена

    Въведете парола за да заключите стаята:", - "yourPassword": "вашата парола", - "Back": "Назад", - "serviceUnavailable": "Услугата не е налична", - "gracefulShutdown": "Услугата временно не е достъпна поради профилактика. Моля опитайте по-късно.", - "Yes": "Да", - "reservationError": "Грешка в системата за резервации", - "reservationErrorMsg": "Грешка номер: __code__, съобщение: __msg__", - "password": "парола", - "userPassword": "потребителска парола", - "token": "код за достъп" - }, - "email": { - "sharedKey": [ - "Тази конференция е защитена с парола. Моля използвайте следния код за да се присъедините:", - "", - "", - "__sharedKey__", - "", - "" - ], - "subject": "Покана за __appName__ (__conferenceName__)", - "body": [ - "Здравей, Бих искал да те поканя в една __appName__ конференция, която създадох.", - "", - "", - "Кликни на следния линк за да се присъединиш в конференцията.", - "", - "", - "__sharedKeyText__", - "__appName__ поддържа __supportedBrowsers__, така че трябва да използваш един от тези браузъри.", - "", - "", - "Ще се видим след секунда!" - ], - "and": "и" - }, - "connection": { - "ERROR": "Грешка", - "CONNECTING": "Свързване", - "CONNFAIL": "Връзката е неуспешна", - "AUTHENTICATING": "Идентификация", - "AUTHFAIL": "Неуспешна идентификация", - "CONNECTED": "Свързан", - "DISCONNECTED": "Изключен", - "DISCONNECTING": "Прекъсване на връзката", - "ATTACHED": "Прикрепен" - } -} \ No newline at end of file diff --git a/lang/main-de.json b/lang/main-de.json deleted file mode 100644 index d15d3e06b..000000000 --- a/lang/main-de.json +++ /dev/null @@ -1,243 +0,0 @@ -{ - "contactlist": "Kontaktliste", - "connectionsettings": "Verbindungseinstellungen", - "poweredby": "Betrieben von", - "downloadlogs": "Log herunterladen", - "roomUrlDefaultMsg": "Die Konferenz wird erstellt...", - "participant": "Teilnehmer", - "me": "ich", - "speaker": "Sprecher", - "defaultNickname": "Bsp.: __name__", - "defaultPreziLink": "Bsp.: __url__", - "welcomepage": { - "go": "Los", - "roomname": "Raumnamen eingeben", - "disable": "Diese Seite beim nächsten Betreten nicht mehr anzeigen", - "feature1": { - "title": "Einfach zu benutzen", - "content": "Kein Download nötig. __app__ läuft direkt im Browser. Einfach die Konferenzadresse teilen und los geht's." - }, - "feature2": { - "title": "Niedrige Bandbreite", - "content": "Videokonferenzen mit mehreren Teilnehmen mit weniger als 128Kpbs. Bildschirmfreigaben und Telefonkonferenzen kommen sogar mit noch weniger Bandbreite aus." - }, - "feature3": { - "title": "Open Source", - "content": "__app__ steht unter der MIT Lizenz. __app__ kann gemäss der Lizenz heruntergeladen, verwendet, verändert und weitergegeben werden." - }, - "feature4": { - "title": "Unbegrenzte Anzahl Benutzer", - "content": "Es gibt keine künstliche Beschränkung der Anzahl der Benutzer oder Konferenzteilnehmer. Die Leistung des Servers und die Bandbreite sind die einzigen limitierenden Faktoren." - }, - "feature5": { - "title": "Bildschirmfreigabe", - "content": "Es ist ganz einfach den Bildschirm zu teilen. __app__ ist ideal für Online-Präsentationen, Vorlesungen und Fernwartungsanfragen." - }, - "feature6": { - "title": "Sichere Konferenzen", - "content": "Privatsphäre gewünscht? __app__ Konferenzen können mit einem Passwort geschützt werden um ungebetene Gäste fernzuhalten und Unterbrechungen zu vermeiden." - }, - "feature7": { - "title": "Freigegebene Notizen", - "content": "__app__ verwendent Etherpad, ein Editor zur kollaborativen Bearbeitung von Texten." - }, - "feature8": { - "title": "Benutzungsstatistiken", - "content": "Die Verwendung kann durch die Integration mit Piwik, Google Analytics und anderen Überwachungs- und Statistikprogrammen protokolliert werden." - } - }, - "toolbar": { - "mute": "Stummschaltung aktivieren / deaktivieren", - "videomute": "Kamera starten / stoppen", - "authenticate": "Anmelden", - "record": "Aufnehmen", - "lock": "Raum schützen / Schutz aufheben", - "invite": "Andere einladen", - "chat": "Chat öffnen / schliessen", - "prezi": "Prezi freigeben", - "etherpad": "Geteiltes Dokument", - "sharescreen": "Bildschirm freigeben", - "fullscreen": "Vollbildmodus aktivieren / deaktivieren", - "sip": "SIP Nummer anrufen", - "Settings": "Einstellungen", - "hangup": "Auflegen", - "login": "Anmelden", - "logout": "Abmelden", - "dialpad": "Tastenblock anzeigen" - }, - "bottomtoolbar": { - "chat": "Chat öffnen / schliessen", - "filmstrip": "Videovorschauen anzeigen / verstecken", - "contactlist": "Kontaktliste öffnen / schliessen" - }, - "chat": { - "nickname": { - "title": "Nickname im Eingabefeld eingeben", - "popover": "Einen Namen auswählen" - }, - "messagebox": "Text eingeben..." - }, - "settings": { - "title": "Einstellungen", - "update": "Aktualisieren", - "name": "Name", - "startAudioMuted": "Stumm beitreten", - "startVideoMuted": "Ohne Video beitreten" - }, - "videothumbnail": { - "editnickname": "Klicken um den Anzeigenamen zu bearbeiten", - "moderator": "Besitzer dieser Konferenz", - "videomute": "Teilnehmer hat die Kamera pausiert.", - "mute": "Teilnehmer ist stumm geschaltet", - "kick": "Hinauswerfen", - "muted": "Stummgeschaltet", - "domute": "Stummschalten" - }, - "connectionindicator": { - "bitrate": "Bitrate:", - "packetloss": "Paketverlust:", - "resolution": "Auflösung:", - "less": "Weniger anzeigen", - "more": "Mehr anzeigen", - "address": "Adresse:", - "remoteport": "Entfernter Port:", - "remoteport_plural": "Entfernte Ports:", - "localport": "Lokaler Port:", - "localport_plural": "Lokale Ports:", - "localaddress": "Lokale Adresse:", - "localaddress_plural": "Lokale Adressen:", - "remoteaddress": "Entfernte Adresse:", - "remoteaddress_plural": "Entfernte Adressen:", - "transport": "Protokoll:", - "bandwidth": "Geschätzte Bandbreite:", - "na": "Verbindungsdaten erneut anzeigen wenn die Konferenz begonnen hat" - }, - "notify": { - "disconnected": "getrennt", - "moderator": "Moderatorenrechte vergeben", - "connected": "verbunden", - "somebody": "Jemand", - "me": "Ich", - "focus": "Konferenz-Organisator", - "focusFail": "__component__ ist im Moment nicht verfügbar - wiederholen in __ms__ Sekunden", - "grantedTo": "Moderatorenrechte an __to__ vergeben.", - "grantedToUnknown": "Moderatorenrechte an $t(somebody) vergeben.", - "muted": "Der Konferenz wurde stumm beigetreten.", - "mutedTitle": "Stummschaltung aktiv." - }, - "dialog": { - "kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.", - "popupError": "Ihr Browser blockiert Popups von dieser Webseite. Bitte erlauben Sie Popups in den Sicherheitseinstellungen und versuchen Sie es erneut.", - "passwordError": "Diese Konferenz ist mit einem Paswort geschützt. Nur der Besitzer der Konferenz kann ein Passwort vergeben.", - "passwordError2": "Diese Konferenzt ist nicht mit einem Passwort geschützt. Nur der Besitzer der Konferenz kann ein Passwort vergeben.", - "joinError": "Oh! Der Konferenz konnte nicht beigetreten werden. Diese könnte ein Problem mit den Sicherheitseinstellungen sein. Bitte kontaktieren Sie den Administrator des Dienstes.", - "connectError": "Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden.", - "connectErrorWithMsg": "Oh! Es hat etwas nicht geklappt und der Konferenz konnte nicht beigetreten werden: __msg__", - "connecting": "Verbindung wird hergestellt", - "error": "Fehler", - "detectext": "Fehler bei der Erkennung der Bildschirmfreigabeerweiterung.", - "failtoinstall": "Die Bildschirmfreigabeerweiterung konnte nicht installiert werden.", - "failedpermissions": "Die Zugriffsberechtigungen auf das Mikrofon und/oder die Kamera konnte nicht eingeholt werden.", - "bridgeUnavailable": "Die Jitsi Videobridge ist momentan nicht erreichbar. Bitte versuchen Sie es später noch einmal.", - "lockTitle": "Sperren fehlgeschlagen", - "lockMessage": "Die Konferenz konnte nicht gesperrt werden.", - "warning": "Warnung", - "passwordNotSupported": "Passwörter für Räume werden nicht unterstützt.", - "sorry": "Entschuldigung", - "internalError": "Interner Anwendungsfehler [setRemoteDescription]", - "unableToSwitch": "Der Videodatenstrom kann nicht gewechselt werden.", - "SLDFailure": "Oh! Die Stummschaltung konnte nicht aktiviert werden. (SLD Fehler)", - "SRDFailure": "Oh! Das Video konnte nicht gestoppt werden. (SRD Fehler)", - "oops": "Oh!", - "defaultError": "Es ist ein Fehler aufgetreten", - "passwordRequired": "Passwort erforderlich", - "Ok": "OK", - "removePreziTitle": "Prezi entfernen", - "removePreziMsg": "Sind Sie sich sicher dass sie Prezi entfernen möchten?", - "sharePreziTitle": "Ein Prezi teilen", - "sharePreziMsg": "Ein anderer Teilnehmer teilt bereits ein Prezi. Diese Konferenz kann nur eine Prezi auf einmal anzeigen.", - "Remove": "Entfernen", - "WaitingForHost": "Warten auf den Organisator...", - "WaitForHostMsg": "Die Konferenz __room__ hat noch nicht begonnen. Wenn Sie der Organisator sind, melden Sie sich bitte an. Anderenfalls warten Sie bitte bis der Organisator beigetreten ist.", - "IamHost": "Ich bin der Organisator", - "Cancel": "Abbrechen", - "retry": "Wiederholen", - "logoutTitle": "Abmelden", - "logoutQuestion": "Sind Sie sicher dass Sie sich abmelden und die Konferenz verlassen möchten?", - "sessTerminated": "Sitzung beendet", - "hungUp": "Anruf beendet", - "joinAgain": "Erneut beitreten", - "Share": "Teilen", - "preziLinkError": "Bitte einen gültigen Prezi-Link angeben.", - "Save": "Speichern", - "recordingToken": "Aufnahme-Token eingeben", - "Dial": "Wählen", - "sipMsg": "Geben Sie eine SIP Nummer ein", - "passwordCheck": "Sind Sie sicher dass Sie das Passwort entfernen möchten?", - "passwordMsg": "Passwort setzen um den Raum zu schützen", - "Invite": "Einladen", - "shareLink": "Teilen Sie diesen Link mit jedem den Sie einladen möchten", - "settings1": "Konferenz einrichten", - "settings2": "Teilnehmer treten stummgeschaltet bei", - "settings3": "Nickname erforderlich

    Setzen Sie ein Passwort um den Raum zu schützen:", - "yourPassword": "Ihr Passwort", - "Back": "Zurück", - "serviceUnavailable": "Dienst nicht verfügbar", - "gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.", - "Yes": "Ja", - "reservationError": "Fehler im Reservationssystem", - "reservationErrorMsg": "Fehler, Nummer: __code__, Nachricht: __msg__", - "password": "Passwort", - "userPassword": "Benutzerpasswort", - "token": "Token", - "displayNameRequired": "Geben Sie Ihren Anzeigenamen ein:" - }, - "email": { - "sharedKey": [ - "Diese Konferenz ist Passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:", - "", - "", - "__sharedKey__", - "", - "" - ], - "subject": "Einladung zu einer __appName__ (__conferenceName__)", - "body": [ - "Hallo!", - "", - "", - "Ich möchte dich zu einer eben erstellten __appName__-Konferenz einladen.", - "", - "", - "Bitte klicke auf den folgenden Link um der Konferenz ebenfalls beizutreten:", - "", - "", - "__roomUrl__", - "", - "", - "__sharedKeyText__", - " Bitte beachte, dass __appName__ momentan nur mit einem der Browser __supportedBrowsers__ verwendet werden kann.", - "", - "", - "Bis gleich!" - ], - "and": "und" - }, - "connection": { - "ERROR": "Fehler", - "CONNECTING": "Verbindung wird hergestellt", - "RECONNECTING": "Es ist ein Netzwerkproblem aufgetreten. Verbinde...", - "CONNFAIL": "Verbindungsaufbau gescheitert", - "AUTHENTICATING": "Anmeldung läuft", - "AUTHFAIL": "Authentifizierung fehlgeschlagen", - "CONNECTED": "Verbunden", - "DISCONNECTED": "Getrennt", - "DISCONNECTING": "Verbindung wird getrennt", - "ATTACHED": "Angehängt" - }, - "recording": { - "toaster": "Wird aufgezeichnet", - "pending": "Die Aufzeichnung wird gestartet sobald ein weiterer Teilnehmer beitritt", - "on": "Aufzeichnung wurde gestartet" - } -} \ No newline at end of file diff --git a/lang/main-fr.json b/lang/main-fr.json deleted file mode 100644 index 8d2d8ff0a..000000000 --- a/lang/main-fr.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "contactlist": "Liste de contacts", - "connectionsettings": "Paramètres de connexion", - "poweredby": "propulsé par", - "downloadlogs": "Téléchargement des logs", - "roomUrlDefaultMsg": "Votre conférence est en cours de création...", - "participant": "Participant", - "me": "moi", - "speaker": "Haut-parleur", - "defaultNickname": "ex: __name__", - "defaultPreziLink": "e.g. __url__", - "welcomepage": { - "go": "Créer", - "roomname": "Saisissez un nom de salle", - "disable": "Ne pas afficher cette page lors de ma prochaine visite", - "feature1": { - "title": "Simple à utiliser", - "content": "Aucun téléchargement requis. __app__ s'utilise directement depuis votre navigateur. Partager simplement l'URL de votre conférence avec les autres pour commencer." - }, - "feature2": { - "title": "Faible bande passante", - "content": "Les vidéo conférences à plusieurs participants nécessitent moins de 128 kbps. Le partage d'écran et les conférences avec seulement de l'audio sont possibles avec beaucoup moins de débit." - }, - "feature3": { - "title": "Open source", - "content": "__app__ est sous licence MIT. Vous êtes libre de le télécharger, l'utiliser, le modifier et le partager sous cette licence." - }, - "feature4": { - "title": "Nombre d'utilisateurs illimité", - "content": "Il n'y a pas de restrictions artificielles concernant le nombre d'utilisateurs ou de participants à une conférence. La puissance du serveur et la bande passante sont les seuls facteurs limitants." - }, - "feature5": { - "title": "Partage d'écan", - "content": "C'est facile de partager votre écran avec d'autres personnes. __app__ est idéal pour les présentations en ligne, les cours, et les sessions de support technique." - }, - "feature6": { - "title": "Salles sécurisées", - "content": "Besoin de confidentialité? Les salles de conférence __app__ peuvent être sécurisées par un mot de passe pour exclure des invités non désirées, et prévenir des interruptions. " - }, - "feature7": { - "title": "Notes partagées", - "content": "__app__ propose Etherpad, un éditeur de texte collaboratif en temps réel qui est parfait pour les procès-verbaux, l'édition d'articles et plus encore." - }, - "feature8": { - "title": "Statistiques d'utilisation", - "content": "Connaissez mieux vos utilisateurs avec une intégration facile de Piwik, Google Analytics et d'autres systèmes de statistiques et supervision d'utilisation." - } - }, - "toolbar": { - "mute": "Muet / Actif", - "videomute": "Démarrer / Arrêter la caméra", - "authenticate": "", - "record": "Enregistrer", - "lock": "Verrouiller / déverrouiller la salle", - "invite": "Inviter des participants", - "chat": "", - "prezi": "Partager une présentation Prezi", - "etherpad": "Partager un document", - "sharescreen": "Partager mon écran", - "fullscreen": "Activer / Désactiver le plein écran", - "sip": "Appeler un numéro SIP", - "Settings": "Paramètres", - "hangup": "Raccrocher", - "login": "Connexion", - "logout": "" - }, - "bottomtoolbar": { - "chat": "Ouvrir / fermer le chat", - "filmstrip": "Montrer / cacher ma vidéo miniature", - "contactlist": "Ouvrir / fermer ma liste de contacts" - }, - "chat": { - "nickname": { - "title": "Saisissez un pseudonyme dans le champ ci-dessous", - "popover": "Choisissez un pseudonyme" - }, - "messagebox": "Saisissez votre texte..." - }, - "settings": { - "title": "PARAMÈTRES", - "update": "Mise à jour", - "name": "Nom" - }, - "videothumbnail": { - "editnickname": "Cliquez pour modifier
    votre nom", - "moderator": "Le propriétaire de
    cette conférence", - "videomute": "Un participant a
    arrêté sa caméra.", - "mute": "Un participant a coupé son micro", - "kick": "Exclure", - "muted": "Coupé", - "domute": "Couper le son" - }, - "connectionindicator": { - "bitrate": "Débit", - "packetloss": "Perte de paquets:", - "resolution": "Résolution:", - "less": "Cacher le détail", - "more": "Montrer le détail", - "address": "Adresse:", - "remoteport": "Port distant:", - "remoteport_plural": "Ports distants:", - "localport": "Port local:", - "localport_plural": "Ports locaux:", - "localaddress": "Adresse locale:", - "localaddress_plural": "Adresses locales:", - "remoteaddress": "Adresse distante:", - "remoteaddress_plural": "Adresses distantes:", - "transport": "Transport:", - "bandwidth": "Bande passante estimée:", - "na": "Revenez ici pour afficher les informations de connexion une fois la conférence démarrée" - }, - "notify": { - "disconnected": "Déconnecté", - "moderator": "Droits modérateur accordés!", - "connected": "Connecté", - "somebody": "Quelqu'un", - "me": "Moi", - "focus": "", - "focusFail": "__component__ n'est pas disponible - réessayez dans __ms__ sec", - "grantedTo": "Droits modérateur accordés à __to__!", - "grantedToUnknown": "Droits modérateur accordés à $t(somebody)!" - }, - "dialog": { - "kickMessage": "Oups! Vous avez été renvoyé de la réunion!", - "popupError": "Votre navigateur bloque les popups pour ce site. Activez les popups pour ce site dans vos paramètres de sécurité et réessayez.", - "passwordError": "Cette conversation est protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.", - "passwordError2": "Cette conversation n'est pas protégée par un mot de passe. Seul le propriétaire de cette conférence peut paramétrer un mot de passe.", - "joinError": "Oups! La conférence ne peut être rejointe. Il y a peut-être un souci avec les paramètres de sécurité. Contactez l'administrateur.", - "connectError": "Oups! Un problème est survenu et la connexion à la conférence est impossible.", - "connecting": "", - "error": "", - "detectext": "Une erreur est survenue pendant la détection de l'extension de partage d'écran.", - "failtoinstall": "Échec de l'installation de l'extension de partage d'écran", - "failedpermissions": "Échec d'obtention des permissions pour utiliser le micro et/ou la caméra local(e)", - "bridgeUnavailable": "Le pont de visioconférence Jitsi est indisponible pour le moment. Réessayez plus tard!", - "lockTitle": "Échec du verrouillage", - "lockMessage": "Impossible de verrouiller la conférence.", - "warning": "Avertissement", - "passwordNotSupported": "Les mots de passe de conférence ne sont pas supportés.", - "sorry": "Désolé", - "internalError": "Une erreur interne de l'application est survenue [setRemoteDescription]", - "unableToSwitch": "Impossible de passer le flux vidéo.", - "SLDFailure": "Oups! Un problème est survenu et le micro n'a pas été coupé! (Échec SLD)", - "SRDFailure": "Oups! Un problème est survenu et la caméra n'a pas été coupée! (Échec SRD)", - "oops": "Oups!", - "defaultError": "Une erreur est survenue", - "passwordRequired": "Mot de passe requis", - "Ok": "Ok", - "removePreziTitle": "Supprimer la présentation Prezi", - "removePreziMsg": "Voulez-vous vraiment supprimer votre présentation Prezi?", - "sharePreziTitle": "Partager une présentation Prezi", - "sharePreziMsg": "Un autre participant partage déjà une présentation Prezi. Cette conférence autorise une seule présentation Prezi à la fois.", - "Remove": "Supprimer", - "Stop": "Arrêter", - "AuthMsg": "L'authentification est requise pour créer la conférence:
    __room__
    Vous pouvez vous authentifier pour créer la conférence ou attendre que quelqu'un le fasse pour vous.", - "Authenticate": "Authentifiez-vous", - "Cancel": "Annuler", - "retry": "Réessayer", - "logoutTitle": "Déconnexion", - "logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence?", - "sessTerminated": "Session terminée", - "hungUp": "Vous avez raccroché et quitté la conférence", - "joinAgain": "Rejoignez à nouveau la conférence", - "Share": "Partager", - "preziLinkError": "Fournissez s'il vous plaît un lien prezi fonctionnel.", - "Save": "Sauvegarder", - "recordingToken": "Saisissez un jeton d'enregistrement", - "Dial": "Composer", - "sipMsg": "Saisissez un numéro SIP", - "passwordCheck": "Voulez-vous vraiment supprimer votre mot de passe?", - "passwordMsg": "Saisissez un mot de passe pour verrouiller la conférence", - "Invite": "Inviter", - "shareLink": "Partagez ce lien avec toutes les personnes que vous voulez inviter", - "settings1": "Configurez votre conférence", - "settings2": "Les participants rejoignent la conférence en étant muets.", - "settings3": "Pseudonymes requis

    Saisissez un mot de passe pour verrouiller la conférence:", - "yourPassword": "Votre mot de passe", - "Back": "Retour", - "serviceUnavailable": "Service indisponible", - "gracefulShutdown": "Le service est actuellement en maintenance. Réessayez plus tard.", - "Yes": "Oui", - "reservationError": "Erreur du système de réservation", - "reservationErrorMsg": "Code d'erreur: __code__, message: __msg__", - "password": "mot de passe", - "userPassword": "mot de passe utilisateur", - "token": "jeton" - }, - "email": { - "sharedKey": [ - "Cette conférence est protégée par un mot de passe. Utilisez le code suivant pour la rejoindre:", - "", - "", - "__sharedKey__", - "", - "" - ], - "subject": "Invitation à la conférence __appName__ : __conferenceName__", - "body": [ - "Bonjour, je vous invite à la conférence __appName__ que je viens de créer.", - "", - "", - "Cliquez sur le lien suivant pour rejoindre la conférence.", - "", - "", - "__roomUrl__", - "", - "", - "__sharedKeyText__", - " Notez que __appName__ est actuellement seulement supporté par __supportedBrowsers__, vous devez donc utiliser un de ces navigateurs.", - "", - "", - "À tout de suite dans la conférence!" - ], - "and": "et" - }, - "connection": { - "ERROR": "Erreur", - "CONNECTING": "Connexion en cours...", - "CONNFAIL": "Échec de la Connexion", - "AUTHENTICATING": "Authentification en cours...", - "AUTHFAIL": "Échec de l'authentification", - "CONNECTED": "Connecté", - "DISCONNECTED": "Déconnecté", - "DISCONNECTING": "Déconnexion en cours...", - "ATTACHED": "Attachée" - } -} \ No newline at end of file diff --git a/lang/main-it.json b/lang/main-it.json deleted file mode 100644 index ff3b07404..000000000 --- a/lang/main-it.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "contactlist": "LISTA CONTATTI", - "connectionsettings": "Impostazioni Connessione", - "poweredby": "powered by", - "downloadlogs": "Scarica logs", - "roomUrlDefaultMsg": "La tua conferenza sta per essere creata...", - "participant": "Partecipante", - "me": "io", - "speaker": "Relatore", - "defaultNickname": "es. __nome__", - "defaultPreziLink": "es. __url__", - "welcomepage": { - "go": "VAI", - "roomname": "Inserisci Nome Stanza", - "disable": "Non visualizzare questa pagina la prossima volta", - "feature1": { - "title": "Semplice da usare", - "content": "Nessun download richiesto. __app__ funziona direttamente nel tuo browser. Condividi semplicemente l'URL della tua conferenza con altri per iniziare." - }, - "feature2": { - "title": "Poca banda", - "content": "Conferenze video multi utente funzionano con appena 128Kbps. La condivisione dello schermo ed conferenze solo audio sono possibili con molto meno." - }, - "feature3": { - "title": "Open source", - "content": "__app__ è sotto licenza MIT. Sei libero di scaricarla, usarla, modificarla e condividerla con la medesima licenza." - }, - "feature4": { - "title": "Utenti illimitati", - "content": "Non ci sono restrizioni sul numero di utenti per una conferenza. La potenza del server e la banda a disposizione sono gli unici fattori limitanti." - }, - "feature5": { - "title": "Condivisione Schermo", - "content": "é facile condividere il tuo schermo con altri. __app__ è l'ideale per presentazioni online, letture, e sessioni di supporto tecnico." - }, - "feature6": { - "title": "Stanze sicure", - "content": "Hai bisogno di più privacy? Le conferenze di __app__ possono essere rese sicure con una password per escludere ospiti non desiderati e prevenire interruzioni." - }, - "feature7": { - "title": "Note condivise", - "content": "__app__ utilizza Etherpad, un editor di testo real-time e collaborativo che è ottimo per meeting, scrivere articoli e tanto altro." - }, - "feature8": { - "title": "Statistiche di utilizzo", - "content": "Impara come i tuoi utenti lo utilizzano con la facile integrazione con PiWik, Google Analytics, e altri sistemi di statistica e monitor dell'utilizzo." - } - }, - "toolbar": { - "mute": "Microfono Attiva / Disattiva", - "videomute": "Abilita / Disabilita video", - "authenticate": "", - "record": "Registra", - "lock": "Blocca / Sblocca Stanza", - "invite": "Invita altri", - "chat": "", - "prezi": "Condividi con Prezi", - "etherpad": "Documento condiviso", - "sharescreen": "Condividi schermo", - "fullscreen": "Entra / Esci da schermo intero", - "sip": "Chiama numero SIP", - "Settings": "Impostazioni", - "hangup": "Termina", - "login": "Login", - "logout": "" - }, - "bottomtoolbar": { - "chat": "Apri / Chiudi chat", - "filmstrip": "Mostra / Nascondi miniature", - "contactlist": "Apri / Chiudi la lista contatti" - }, - "chat": { - "nickname": { - "title": "Scegli un nickname nel box qui sotto", - "popover": "Scegli un nickname" - }, - "messagebox": "Inserisci testo..." - }, - "settings": { - "title": "IMPOSTAZIONI", - "update": "Aggiorna", - "name": "Nome" - }, - "videothumbnail": { - "editnickname": "Clicca per modificare il tuo
    nome visualizzato", - "moderator": "Il proprietario
    della conferenza", - "videomute": "Il partecipante ha
    fermato il video.", - "mute": "Il partecipante è in muto", - "kick": "Espelli", - "muted": "Audio disattivato", - "domute": "Disattiva audio" - }, - "connectionindicator": { - "bitrate": "Bitrate:", - "packetloss": "Perdita pacchetti:", - "resolution": "Risoluzione:", - "less": "Mostra meno", - "more": "Mostra di più", - "address": "Indirizzo:", - "remoteport": "Porta remota:", - "remoteport_plural": "Porte remote:", - "localport": "Porta locale:", - "localport_plural": "Porte locali:", - "localaddress": "Indirizzo locale:", - "localaddress_plural": "Indirizzi locali:", - "remoteaddress": "Indirizzo remoto:", - "remoteaddress_plural": "Indirizzi remoti:", - "transport": "Trasporto:", - "bandwidth": "Banda stimata:", - "na": "Ritorna qui per informazioni sulla connessione una volta che la conferenza inizia" - }, - "notify": { - "disconnected": "disconnesso", - "moderator": "Impostati i permessi di moderatore!", - "connected": "connesso", - "somebody": "Qualcuno", - "me": "io", - "focus": "Focus su conferenza", - "focusFail": "__component__ non disponibile - riprova in __ms__ sec", - "grantedTo": "Permessi di moderatore garantiti a __to__!", - "grantedToUnknown": "Permessi di moderatore garantiti a $t(somebody)!" - }, - "dialog": { - "kickMessage": "Accidenti! Sei stato espulso dalla conferenza !", - "popupError": "Il tuo browser sta bloccando le finestre popup da questo sito. Abilita i popup tra le impostazioni di sicurezza del tuo browser e riprova.", - "passwordError": "Questa conversazione è protetta da una password. Solo il proprietario della conferenza può impostare una password.", - "passwordError2": "Questa conversazione non è al momento protetta da una password. Solo il proprietario può impostare la password.", - "joinError": "Oops! Non puoi entrare nella conferenza. Ci deve essere qualche problema con la configurazione di sicurezza. Contattare l'amministratore di sistema.", - "connectError": "Oops! Qualcosa è andato storto e non ti puoi collegare alla conferenza.", - "connecting": "", - "error": "", - "detectext": "Errore durante il rilevamento dell'estensione per il desktopsharing.", - "failtoinstall": "Impossibile installare l'estensione per il desktop sharing", - "failedpermissions": "Impossibile ottenere i permessi per usare il microfono e/o il video locale.", - "bridgeUnavailable": "Il Videobridge non è al momento disponibile. Si prega di riprovare più tardi!", - "lockTitle": "Blocco fallito", - "lockMessage": "Impossibile bloccare la conferenza.", - "warning": "Attenzione", - "passwordNotSupported": "Le password sulla stanza non sono al momento supportate.", - "sorry": "Spiacente", - "internalError": "Errore interno dell'applicazione [setRemoteDescription]", - "unableToSwitch": "Impossibile cambiare lo stream video.", - "SLDFailure": "Oops! Qualcosa è andato storto e non è possibile silenziare il microfono! (Errore SLD)", - "SRDFailure": "Oops! Qualcosa è andato storto e non è possibile fermare il video! (Errore SRD)", - "oops": "Oops!", - "defaultError": "C'è stato qualche tipo di errore", - "passwordRequired": "Richiesta password ", - "Ok": "Ok", - "removePreziTitle": "Rimuovi Prezi", - "removePreziMsg": "Sei sicuro di voler rimuovere il tuo Prezi?", - "sharePreziTitle": "Condividi un Prezi", - "sharePreziMsg": "Un altro partecipante sta già condividendo un Prezi. Questa conferenza permette un solo Prezi alla volta.", - "Remove": "Rimuovi", - "Stop": "Ferma", - "AuthMsg": "Autenticazione richiesta per creare la stanza:
    __room__
    Puoi autenticarti per creare la stanza o aspettare che qualcun altro lo faccia per te.", - "Authenticate": "Autenticazione", - "Cancel": "Annulla", - "retry": "Riprova", - "logoutTitle": "Logout", - "logoutQuestion": "Vuoi disconnetterti e interrompere la conferenza ?", - "sessTerminated": "Sessione Terminata", - "hungUp": "Hai terminato la conversazione", - "joinAgain": "Entra ancora", - "Share": "Condividi", - "preziLinkError": "Fornire un link Prezi esatto.", - "Save": "Salva", - "recordingToken": "Inserire token di registrazione", - "Dial": "Componi", - "sipMsg": "Inserire numero SIP", - "passwordCheck": "Confermi la rimozione della password?", - "passwordMsg": "Imposta una password per bloccare la stanza", - "Invite": "Invita", - "shareLink": "Condividi questo link con tutte le persone che vuoi invitare", - "settings1": "Configura la tua conferenza", - "settings2": "Partecipanti connessi in muto", - "settings3": "Richiedi nicknames

    Imposta una password per bloccare la tua stanza:", - "yourPassword": "la tua password", - "Back": "Indietro", - "serviceUnavailable": "Servizio non disponibile", - "gracefulShutdown": "Il nostro servizio è al momento spento per manutenzione. Si prega di riprovare più tardi.", - "Yes": "Sì", - "reservationError": "Errore di sistema in prenotazione", - "reservationErrorMsg": "Codice di errore: __code__, messaggio: __msg__", - "password": "password", - "userPassword": "password utente", - "token": "token" - }, - "email": { - "sharedKey": [ - "Questa conferenza è protetta da password. Utilizzare il seguente PIN alla connessione:", - "", - "", - "__sharedKey__", - "", - "" - ], - "subject": "Invito su __appName__ (__conferenceName__)", - "body": [ - "Ciao, Vorrei invitarti alla conferenza che ho appena creato su __appName__ .", - "", - "", - "Cliccare sul seguente link per entrare nella conferenza.", - "", - "", - "__roomUrl__", - "", - "", - "__sharedKeyText__", - "NOTA: __appName__ è al momento supportato da questi browsers: __supportedBrowsers__, è necessario utilizzare uno di questi programmi per poter entrare.", - "", - "", - "Ci sentiamo tra un secondo!" - ], - "and": "e" - }, - "connection": { - "ERROR": "Errore", - "CONNECTING": "Connessione", - "CONNFAIL": "Connessione non riuscita", - "AUTHENTICATING": "Autenticazione", - "AUTHFAIL": "Autenticazione fallita", - "CONNECTED": "Connesso", - "DISCONNECTED": "Disconnesso", - "DISCONNECTING": "Disconnessione in corso", - "ATTACHED": "Collegato" - } -} \ No newline at end of file diff --git a/lang/main-tr.json b/lang/main-tr.json deleted file mode 100644 index 89a4a7e3b..000000000 --- a/lang/main-tr.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "contactlist": "KİŞİ LİSTESİ", - "connectionsettings": "Bağlantı Ayarları", - "poweredby": "Gücünün kaynağı", - "downloadlogs": "Günlükleri indir", - "welcomepage": { - "go": "GİT", - "roomname": "Oda adı girin", - "disable": "Sonraki girişimde bu sayfayı gösterme", - "feature1": { - "title": "Kullanımı kolay", - "content": "İndirmeye gerek yok. __app__ tarayıcınızda doğrudan çalışır. Başlamak için görüşme bağlantısını URL diğerleri ile paylaşın." - }, - "feature2": { - "title": "Düşük bant genişliği ihtiyacı", - "content": "Ekran paylaşımı ve sadece ses ile çok katılımcılı video görüşmeleri, 128Kbps bağlantı ile mümkündür." - }, - "feature3": { - "title": "Açık kaynak kodlu", - "content": "__app__ MIT ile lisanslanmıştır. Bu lisansa uygun olarak indirmek, kullanmak, değiştirmek ve paylaşmakta özgürsün." - }, - "feature4": { - "title": "Sınırsız sayıda kullanıcı", - "content": "Kullanıcılar veya konferans katılımcılarının sayısında hiçbir yapay kısıtlama yoktur. Sadece sunucun güç ve bant genişliği, sınırlayıcı unsurdur." - }, - "feature5": { - "title": "Ekran paylaşımı", - "content": "Diğerlerinle ekranınızı kolayca paylaşın. __app__ çevrimiçi sunumlar, dersler ve teknik destek oturumları için idealdir." - }, - "feature6": { - "title": "Güvenli odalar", - "content": "Biraz gizliliğe ihtiyacınız var? __app__ görüşme odaları, istemeyen misafirleri uzak tutmak ve kesinleri önlemek için bir parola ile güvence altına alınabilir." - }, - "feature7": { - "title": "Paylaşımlı notlar", - "content": "__app__ Etherpad içerir, gerçek zamanlı bir ortak çalışma metin düzenleyicisidir. Görüşme tutanakları, makale yazımı ve daha fazlası için biçilmiş kaftandır." - }, - "feature8": { - "title": "Kullanım istatistikleri", - "content": "Piwik, Google Analytics ve diğer kullanım izleme ve istatistik sistemleri ile kolay tümleştirmeyle kullanıcılar hakkında bilgi edinin." - } - }, - "toolbar": { - "mute": "Sessiz / Sesli", - "videomute": "Kamera başlat / durdur", - "authenticate": "", - "record": "Kaydet", - "lock": "Odayı kilitle / kilit aç", - "invite": "Arkadaşlarını davet et", - "chat": "", - "prezi": "Prezi paylaş", - "etherpad": "Paylaşımlı belge", - "sharescreen": "Ekran paylaş", - "fullscreen": "Tam Ekrana Gir / Çık", - "sip": "SIP numara ara", - "Settings": "Ayarlar", - "hangup": "Kapat", - "login": "Oturum aç", - "logout": "" - }, - "bottomtoolbar": { - "chat": "Sohbeti aç / kapa", - "filmstrip": "Kişi listesi aç / kapa", - "contactlist": "Film şeridini göster / gizle" - }, - "chat": { - "nickname": { - "title": "Aşağıdaki kutuya bir takma ad girin", - "popover": "Bir takma ad seçin" - }, - "messagebox": "Metin girin..." - }, - "settings": { - "title": "AYARLAR", - "update": "Güncelle", - "name": "Ad" - }, - "videothumbnail": { - "editnickname": "Görünür adınızı değiştirmek
    için tıkla", - "moderator": "Bu görüşmenin
    sahibi", - "videomute": "Katılımcı
    kamera durdurdu.", - "mute": "Katılımcı sessiz", - "kick": "Kovuldu", - "muted": "Sessiz", - "domute": "Sustur" - }, - "connectionindicator": { - "bitrate": "Bit hızı:", - "packetloss": "Paket kaybı:", - "resolution": "Çözünürlük:", - "less": "Daha az göster", - "more": "Daha fazla göster", - "address": "Adres:", - "remoteport": "Uzak port:Uzak portlar:", - "localport": "Yerel port:Yerel portlar:", - "localaddress": "Yerel adres:Yerel adresler:", - "remoteaddress": "Uzak adres:Uzak adresler:", - "transport": "Transport:", - "bandwidth": "Tahmini bant genişliği:", - "na": "Görüşme başladıktan sonra bağlantı bilgileri için buraya gel" - }, - "notify": { - "disconnected": "bağlantı kesildi", - "moderator": "Görüşme yöneticisi hakları verildi!", - "connected": "bağlandı", - "somebody": "Birisi", - "me": "Bana", - "focus": "Görüşme odağı", - "focusFail": "__component__ uygun değil - __ms__ saniye içinde tekrar deneyin", - "grantedTo": "__to__, görüşme yöneticisi hakları verildi!", - "grantedToUnknown": "$t(somebody), görüşme yöneticisi hakları verildi!" - }, - "dialog": { - "kickMessage": "Ahhh! Görüşmeden, kavuldun!", - "popupError": "Tarayıcınız bu siteden açılır pencereleri engelliyor. Lütfen, tarayıcınızın güvenlik ayarlarında pop-up etkinleştirin ve tekrar deneyin.", - "passwordError": "Bu görüşme şu anda bir parola ile korunmaktadır. Sadece görüşmenin sahibi bir parola ayarlayabilir.", - "passwordError2": "Bu görüşme şu anda bir parola ile korunmamaktadır. Sadece görüşmenin sahibi bir parola ayarlayabilir.", - "joinError": "Amanin boo! Görüşmeye katılamadık. Güvenlik yapılandırması ile ilgili bir sorun olabilir. Hizmet yöneticisi ile bağlantı kurun.", - "connectError": "Amanin boo! Birşeyler ters gitti ve görüşmeye bağlanamadık.", - "error": "Hata", - "detectext": "Ekran paylaşımı eklentisi tespit edilirken hata.", - "failtoinstall": "Masaüstü paylaşım eklentisi yüklenemedi", - "failedpermissions": "Yerel mikrofon ve/veya kamerayı kullanmak için izinler alınamadı.", - "bridgeUnavailable": "Jitsi Videobridge şu anda kullanılamıyor. Daha sonra tekrar deneyiniz!", - "lockTitle": "Kilitlenemedi", - "lockMessage": "Görüşme kilitlenemedi.", - "warning": "Uyarı", - "passwordNotSupported": "Oda parolaları şu anda desteklenmemekte.", - "sorry": "Üzgünüz", - "internalError": "İç uygulama hatası [setRemoteDescription]", - "unableToSwitch": "Video akışı açılamıyor.", - "SLDFailure": "Amanin boo! Birşeyler ters gitti ve sessize alamadık! (SLD Başarısız)", - "SRDFailure": "Amanin boo! Birşeyler ters gitti ve videoyu durduramadık! (SRD Başarısız)", - "oops": "Amanin boo!", - "defaultError": "Bir tür hata var", - "passwordRequired": "Parola gerekli", - "Ok": "Tamam", - "removePreziTitle": "Prezi kaldır", - "removePreziMsg": "Prezi kaldırmak istediğinizden emin misiniz?", - "sharePreziTitle": "Bir Prezi paylaşın", - "sharePreziMsg": "Diğer katılımcı hala bir Prezi paylaşıyor.Bu görüşme aynı zamanda sadece bir Prezi izin verir.", - "Remove": "Kaldır", - "Stop": "Durdur", - "AuthMsg": "Oda oluşturmak için kimlik doğrulama gerekli:
    __room__
    Oda oluşturmak için ya kimlik doğrulamalı ya da bunu yapması için bir başkasını beklemelisiniz.", - "Authenticate": "Kimlik doğrula", - "Cancel": "İptal", - "logoutTitle": "Oturum kapat", - "logoutQuestion": "Oturumu ve görüşmeyi sonlandırmak istediğinizden emin misiniz?", - "sessTerminated": "Oturum sonlandırıldı", - "hungUp": "Görüşmeyi bitirdiniz", - "joinAgain": "Yeniden katıl", - "Share": "Paylaş", - "preziLinkError": "Lütfen doğru prezi bağlantısı verin.", - "Save": "Kaydet", - "recordingToken": "Kayıt jetonu girin", - "Dial": "Ara", - "sipMsg": "SIP numarası gir", - "passwordCheck": "Parolanızı kaldırmak istediğinizden emin misiniz?", - "passwordMsg": "Odanızı kilitlemek için bir parola koyun", - "Invite": "Davet et", - "shareLink": "Davet etmek istediğiniz herkesle bu bağlantıyı paylaşın", - "settings1": "Görüşmenizi yapılandır", - "settings2": "Katılımcılar sessiz katılsın", - "settings3": "Takma adlar gerekli

    Odanızı kitlemek için bir parola ayarlayın:", - "yourPassword": "parolanız", - "Back": "Geri", - "serviceUnavailable": "Hizmet kullanım dışı", - "gracefulShutdown": "Hizmetimiz bakıp için durduruldu. Daha sonra tekrar deneyiniz.", - "Yes": "Evet", - "reservationError": "Rezervasyon sistemi hatası", - "reservationErrorMsg": "Hata kodu: __code__, mesaj: __msg__" - } -} \ No newline at end of file diff --git a/lang/main.json b/lang/main.json deleted file mode 100644 index 29df8de17..000000000 --- a/lang/main.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "contactlist": "CONTACT LIST", - "connectionsettings": "Connection Settings", - "poweredby": "powered by", - "downloadlogs": "Download logs", - "roomUrlDefaultMsg": "Your conference is currently being created...", - "participant": "Participant", - "me": "me", - "speaker": "Speaker", - "defaultNickname": "ex. __name__", - "defaultPreziLink": "e.g. __url__", - "welcomepage":{ - "go": "GO", - "roomname": "Enter room name", - "disable": "Don't show this page next time I enter", - "feature1": { - "title": "Simple to use", - "content": "No downloads required. __app__ works directly within your browser. Simply share your conference URL with others to get started." - }, - "feature2": { - "title": "Low bandwidth", - "content": "Multi-party video conferences work with as little as 128Kbps. Screen-sharing and audio-only conferences are possible with far less." - }, - "feature3": { - "title": "Open source", - "content": "__app__ is licensed under MIT. You are free to download, use, modify, and share them as per these licenses." - }, - "feature4": { - "title": "Unlimited users", - "content": "There are no artificial restrictions on the number of users or conference participants. Server power and bandwidth are the only limiting factors." - }, - "feature5": { - "title": "Screen sharing", - "content": "It's easy to share your screen with others. __app__ is ideal for on-line presentations, lectures, and tech support sessions." - }, - "feature6": { - "title": "Secure rooms", - "content": "Need some privacy? __app__ conference rooms can be secured with a password in order to exclude unwanted guests and prevent interruptions." - }, - "feature7": { - "title": "Shared notes", - "content": "__app__ features Etherpad, a real-time collaborative text editor that's great for meeting minutes, writing articles, and more." - }, - "feature8": { - "title": "Usage statistics", - "content": "Learn about your users through easy integration with Piwik, Google Analytics, and other usage monitoring and statistics systems." - } - }, - "toolbar": { - "mute": "Mute / Unmute", - "videomute": "Start / stop camera", - "authenticate": "Authenticate", - "record": "Record", - "lock": "Lock / unlock room", - "invite": "Invite others", - "chat": "Open / close chat", - "prezi": "Share Prezi", - "etherpad": "Shared document", - "sharescreen": "Share screen", - "fullscreen": "Enter / Exit Full Screen", - "sip": "Call SIP number", - "Settings": "Settings", - "hangup": "Hang Up", - "login": "Login", - "logout": "Logout", - "dialpad": "Show dialpad" - }, - "bottomtoolbar": { - "chat": "Open / close chat", - "filmstrip": "Show / hide film strip", - "contactlist": "Open / close contact list" - }, - "chat":{ - "nickname": { - "title": "Enter a nickname in the box below", - "popover": "Choose a nickname" - }, - "messagebox": "Enter text..." - }, - "settings": - { - "title": "SETTINGS", - "update": "Update", - "name": "Name", - "startAudioMuted": "start without audio", - "startVideoMuted": "start without video" - }, - "videothumbnail": - { - "editnickname": "Click to edit your
    display name", - "moderator": "The owner of
    this conference", - "videomute": "Participant has
    stopped the camera.", - "mute": "Participant is muted", - "kick": "Kick out", - "muted": "Muted", - "domute": "Mute" - - }, - "connectionindicator": - { - "bitrate": "Bitrate:", - "packetloss": "Packet loss:", - "resolution": "Resolution:", - "less": "Show less", - "more": "Show more", - "address": "Address:", - "remoteport_plural": "Remote ports:", - "localport_plural": "Local ports:", - "remoteport": "Remote port:", - "localport": "Local port:", - "localaddress": "Local address:", - "localaddress_plural": "Local addresses:", - "remoteaddress": "Remote address:", - "remoteaddress_plural": "Remote addresses:", - "transport": "Transport:", - "bandwidth": "Estimated bandwidth:", - "na": "Come back here for connection information once the conference starts" - }, - "notify": { - "disconnected": "disconnected", - "moderator": "Moderator rights granted!", - "connected": "connected", - "somebody": "Somebody", - "me": "Me", - "focus": "Conference focus", - "focusFail": "__component__ not available - retry in __ms__ sec", - "grantedTo": "Moderator rights granted to __to__!", - "grantedToUnknown": "Moderator rights granted to $t(somebody)!", - "muted": "You have started the conversation muted.", - "mutedTitle": "You're muted!" - }, - "dialog": { - "kickMessage": "Ouch! You have been kicked out of the meet!", - "popupError": "Your browser is blocking popup windows from this site. Please enable popups in your browser security settings and try again.", - "passwordError": "This conversation is currently protected by a password. Only the owner of the conference could set a password.", - "passwordError2": "This conversation isn't currently protected by a password. Only the owner of the conference could set a password.", - "joinError": "Oops! We couldn't join the conference. There might be some problem with security configuration. Please contact service administrator.", - "connectError": "Oops! Something went wrong and we couldn't connect to the conference.", - "connectErrorWithMsg": "Oops! Something went wrong and we couldn't connect to the conference: __msg__", - "connecting": "Connecting", - "error": "Error", - "detectext": "Error when trying to detect desktopsharing extension.", - "failtoinstall": "Failed to install desktop sharing extension", - "failedpermissions": "Failed to obtain permissions to use the local microphone and/or camera.", - "bridgeUnavailable": "Jitsi Videobridge is currently unavailable. Please try again later!", - "lockTitle": "Lock failed", - "lockMessage": "Failed to lock conference.", - "warning": "Warning", - "passwordNotSupported": "Room passwords are currently not supported.", - "sorry": "Sorry", - "internalError": "Internal application error [setRemoteDescription]", - "unableToSwitch": "Unable to switch video stream.", - "SLDFailure": "Oops! Something went wrong and we failed to mute! (SLD Failure)", - "SRDFailure": "Oops! Something went wrong and we failed to stop video! (SRD Failure)", - "oops": "Oops!", - "defaultError": "There was some kind of error", - "passwordRequired": "Password required", - "Ok": "Ok", - "removePreziTitle": "Remove Prezi", - "removePreziMsg": "Are you sure you would like to remove your Prezi?", - "sharePreziTitle": "Share a Prezi", - "sharePreziMsg": "Another participant is already sharing a Prezi.This conference allows only one Prezi at a time.", - "Remove": "Remove", - "WaitingForHost": "Waiting for the host ...", - "WaitForHostMsg": "The conference __room__ has not yet started. If you are the host then please authenticate. Otherwise, please wait for the host to arrive.", - "IamHost": "I am the host", - "Cancel": "Cancel", - "retry": "Retry", - "logoutTitle" : "Logout", - "logoutQuestion" : "Are you sure you want to logout and stop the conference?", - "sessTerminated": "Session Terminated", - "hungUp": "You hung up the call", - "joinAgain": "Join again", - "Share": "Share", - "preziLinkError": "Please provide a correct prezi link.", - "Save": "Save", - "recordingToken": "Enter recording token", - "Dial": "Dial", - "sipMsg": "Enter SIP number", - "passwordCheck": "Are you sure you would like to remove your password?", - "Remove": "Remove", - "passwordMsg": "Set a password to lock your room", - "Invite": "Invite", - "shareLink": "Share this link with everyone you want to invite", - "settings1": "Configure your conference", - "settings2": "Participants join muted", - "settings3": "Require nicknames

    Set a password to lock your room:", - "yourPassword": "your password", - "Back": "Back", - "serviceUnavailable": "Service unavailable", - "gracefulShutdown": "Our service is currently down for maintenance. Please try again later.", - "Yes": "Yes", - "reservationError": "Reservation system error", - "reservationErrorMsg": "Error code: __code__, message: __msg__", - "password": "password", - "userPassword": "user password", - "token": "token", - "displayNameRequired": "Please enter your display name:" - }, - "email": - { - "sharedKey": [ - "This conference is password protected. Please use the following pin when joining:", - "", - "", - "__sharedKey__", - "", - ""], - "subject": "Invitation to a __appName__ (__conferenceName__)", - "body": [ - "Hey there, I%27d like to invite you to a __appName__ conference I%27ve just set up.", - "", - "", - "Please click on the following link in order to join the conference.", - "", - "", - "__roomUrl__", - "", - "", - "__sharedKeyText__", - " Note that __appName__ is currently only supported by __supportedBrowsers__, so you need to be using one of these browsers.", - "", - "", - "Talk to you in a sec!" - ], - "and": "and" - }, - "connection": - { - "ERROR": "Error", - "CONNECTING": "Connecting", - "RECONNECTING": "A network problem occurred. Reconnecting...", - "CONNFAIL": "Connection failed", - "AUTHENTICATING": "Authenticating", - "AUTHFAIL": "Authentication failed", - "CONNECTED": "Connected", - "DISCONNECTED": "Disconnected", - "DISCONNECTING": "Disconnecting", - "ATTACHED": "Attached", - "FETCH_SESSION_ID": "Obtaining session-id...", - "GOT_SESSION_ID": "Obtaining session-id... Done", - "GET_SESSION_ID_ERROR": "Get session-id error: " - }, - "recording": - { - "toaster": "Currently recording!", - "pending": "Your recording will start as soon as another participant joins", - "on": "Recording has been started" - } -} diff --git a/libs/app.bundle.js b/libs/app.bundle.js deleted file mode 100644 index 7367b804e..000000000 --- a/libs/app.bundle.js +++ /dev/null @@ -1,38194 +0,0 @@ -(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.APP = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && this._events[type].length > m) { - this._events[type].warned = true; - console.error('(node) warning: possible EventEmitter memory ' + - 'leak detected. %d listeners added. ' + - 'Use emitter.setMaxListeners() to increase limit.', - this._events[type].length); - if (typeof console.trace === 'function') { - // not supported in IE 10 - console.trace(); - } - } - } - - return this; -}; - -EventEmitter.prototype.on = EventEmitter.prototype.addListener; - -EventEmitter.prototype.once = function(type, listener) { - if (!isFunction(listener)) - throw TypeError('listener must be a function'); - - var fired = false; - - function g() { - this.removeListener(type, g); - - if (!fired) { - fired = true; - listener.apply(this, arguments); - } - } - - g.listener = listener; - this.on(type, g); - - return this; -}; - -// emits a 'removeListener' event iff the listener was removed -EventEmitter.prototype.removeListener = function(type, listener) { - var list, position, length, i; - - if (!isFunction(listener)) - throw TypeError('listener must be a function'); - - if (!this._events || !this._events[type]) - return this; - - list = this._events[type]; - length = list.length; - position = -1; - - if (list === listener || - (isFunction(list.listener) && list.listener === listener)) { - delete this._events[type]; - if (this._events.removeListener) - this.emit('removeListener', type, listener); - - } else if (isObject(list)) { - for (i = length; i-- > 0;) { - if (list[i] === listener || - (list[i].listener && list[i].listener === listener)) { - position = i; - break; - } - } - - if (position < 0) - return this; - - if (list.length === 1) { - list.length = 0; - delete this._events[type]; - } else { - list.splice(position, 1); - } - - if (this._events.removeListener) - this.emit('removeListener', type, listener); - } - - return this; -}; - -EventEmitter.prototype.removeAllListeners = function(type) { - var key, listeners; - - if (!this._events) - return this; - - // not listening for removeListener, no need to emit - if (!this._events.removeListener) { - if (arguments.length === 0) - this._events = {}; - else if (this._events[type]) - delete this._events[type]; - return this; - } - - // emit removeListener for all listeners on all events - if (arguments.length === 0) { - for (key in this._events) { - if (key === 'removeListener') continue; - this.removeAllListeners(key); - } - this.removeAllListeners('removeListener'); - this._events = {}; - return this; - } - - listeners = this._events[type]; - - if (isFunction(listeners)) { - this.removeListener(type, listeners); - } else { - // LIFO order - while (listeners.length) - this.removeListener(type, listeners[listeners.length - 1]); - } - delete this._events[type]; - - return this; -}; - -EventEmitter.prototype.listeners = function(type) { - var ret; - if (!this._events || !this._events[type]) - ret = []; - else if (isFunction(this._events[type])) - ret = [this._events[type]]; - else - ret = this._events[type].slice(); - return ret; -}; - -EventEmitter.listenerCount = function(emitter, type) { - var ret; - if (!emitter._events || !emitter._events[type]) - ret = 0; - else if (isFunction(emitter._events[type])) - ret = 1; - else - ret = emitter._events[type].length; - return ret; -}; - -function isFunction(arg) { - return typeof arg === 'function'; -} - -function isNumber(arg) { - return typeof arg === 'number'; -} - -function isObject(arg) { - return typeof arg === 'object' && arg !== null; -} - -function isUndefined(arg) { - return arg === void 0; -} - -},{}],2:[function(require,module,exports){ -// shim for using process in browser - -var process = module.exports = {}; -var queue = []; -var draining = false; - -function drainQueue() { - if (draining) { - return; - } - draining = true; - var currentQueue; - var len = queue.length; - while(len) { - currentQueue = queue; - queue = []; - var i = -1; - while (++i < len) { - currentQueue[i](); - } - len = queue.length; - } - draining = false; -} -process.nextTick = function (fun) { - queue.push(fun); - if (!draining) { - setTimeout(drainQueue, 0); - } -}; - -process.title = 'browser'; -process.browser = true; -process.env = {}; -process.argv = []; -process.version = ''; // empty string to avoid regexp issues -process.versions = {}; - -function noop() {} - -process.on = noop; -process.addListener = noop; -process.once = noop; -process.off = noop; -process.removeListener = noop; -process.removeAllListeners = noop; -process.emit = noop; - -process.binding = function (name) { - throw new Error('process.binding is not supported'); -}; - -// TODO(shtylman) -process.cwd = function () { return '/' }; -process.chdir = function (dir) { - throw new Error('process.chdir is not supported'); -}; -process.umask = function() { return 0; }; - -},{}],3:[function(require,module,exports){ -/* jshint -W117 */ -/* application specific logic */ - -var APP = -{ - init: function () { - this.UI = require("./modules/UI/UI"); - this.API = require("./modules/API/API"); - this.connectionquality = require("./modules/connectionquality/connectionquality"); - this.statistics = require("./modules/statistics/statistics"); - this.RTC = require("./modules/RTC/RTC"); - this.desktopsharing = require("./modules/desktopsharing/desktopsharing"); - this.xmpp = require("./modules/xmpp/xmpp"); - this.keyboardshortcut = require("./modules/keyboardshortcut/keyboardshortcut"); - this.translation = require("./modules/translation/translation"); - this.settings = require("./modules/settings/Settings"); - this.DTMF = require("./modules/DTMF/DTMF"); - this.members = require("./modules/members/MemberList"); - } -}; - -function init() { - - APP.desktopsharing.init(); - APP.RTC.start(); - APP.xmpp.start(); - APP.statistics.start(); - APP.connectionquality.init(); - APP.keyboardshortcut.init(); - APP.members.start(); -} - - -$(document).ready(function () { - - var URLProcessor = require("./modules/URLProcessor/URLProcessor"); - URLProcessor.setConfigParametersFromUrl(); - APP.init(); - - APP.translation.init(); - - if(APP.API.isEnabled()) - APP.API.init(); - - APP.UI.start(init); - -}); - -$(window).bind('beforeunload', function () { - if(APP.API.isEnabled()) - APP.API.dispose(); -}); - -module.exports = APP; - - -},{"./modules/API/API":4,"./modules/DTMF/DTMF":5,"./modules/RTC/RTC":9,"./modules/UI/UI":13,"./modules/URLProcessor/URLProcessor":44,"./modules/connectionquality/connectionquality":45,"./modules/desktopsharing/desktopsharing":46,"./modules/keyboardshortcut/keyboardshortcut":47,"./modules/members/MemberList":48,"./modules/settings/Settings":49,"./modules/statistics/statistics":53,"./modules/translation/translation":54,"./modules/xmpp/xmpp":70}],4:[function(require,module,exports){ -/* global APP */ -/** - * Implements API class that communicates with external api class - * and provides interface to access Jitsi Meet features by external - * applications that embed Jitsi Meet - */ - -var XMPPEvents = require("../../service/xmpp/XMPPEvents"); - -/** - * List of the available commands. - * @type {{ - * displayName: inputDisplayNameHandler, - * toggleAudio: toggleAudio, - * toggleVideo: toggleVideo, - * toggleFilmStrip: toggleFilmStrip, - * toggleChat: toggleChat, - * toggleContactList: toggleContactList - * }} - */ -var commands = {}; - -function initCommands() { - commands = { - displayName: APP.UI.inputDisplayNameHandler, - toggleAudio: APP.UI.toggleAudio, - toggleVideo: APP.UI.toggleVideo, - toggleFilmStrip: APP.UI.toggleFilmStrip, - toggleChat: APP.UI.toggleChat, - toggleContactList: APP.UI.toggleContactList - }; -} - - -/** - * Maps the supported events and their status - * (true it the event is enabled and false if it is disabled) - * @type {{ - * incomingMessage: boolean, - * outgoingMessage: boolean, - * displayNameChange: boolean, - * participantJoined: boolean, - * participantLeft: boolean - * }} - */ -var events = { - incomingMessage: false, - outgoingMessage:false, - displayNameChange: false, - participantJoined: false, - participantLeft: false -}; - -var displayName = {}; - -/** - * Processes commands from external application. - * @param message the object with the command - */ -function processCommand(message) { - if (message.action != "execute") { - console.error("Unknown action of the message"); - return; - } - for (var key in message) { - if(commands[key]) - commands[key].apply(null, message[key]); - } -} - -/** - * Processes events objects from external applications - * @param event the event - */ -function processEvent(event) { - if (!event.action) { - console.error("Event with no action is received."); - return; - } - - var i = 0; - switch(event.action) { - case "add": - for (; i < event.events.length; i++) { - events[event.events[i]] = true; - } - break; - case "remove": - for (; i < event.events.length; i++) { - events[event.events[i]] = false; - } - break; - default: - console.error("Unknown action for event."); - } -} - -/** - * Sends message to the external application. - * @param object - */ -function sendMessage(object) { - window.parent.postMessage(JSON.stringify(object), "*"); -} - -/** - * Processes a message event from the external application - * @param event the message event - */ -function processMessage(event) { - var message; - try { - message = JSON.parse(event.data); - } catch (e) {} - - if(!message.type) - return; - switch (message.type) { - case "command": - processCommand(message); - break; - case "event": - processEvent(message); - break; - default: - console.error("Unknown type of the message"); - return; - } -} - -function setupListeners() { - APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, function (from) { - API.triggerEvent("participantJoined", {jid: from}); - }); - APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, function (from, nick, txt, myjid, stamp) { - if (from != myjid) - API.triggerEvent("incomingMessage", - {"from": from, "nick": nick, "message": txt, "stamp": stamp}); - }); - APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, function (jid) { - API.triggerEvent("participantLeft", {jid: jid}); - }); - APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, function (jid, newDisplayName) { - var name = displayName[jid]; - if(!name || name != newDisplayName) { - API.triggerEvent("displayNameChange", {jid: jid, displayname: newDisplayName}); - displayName[jid] = newDisplayName; - } - }); - APP.xmpp.addListener(XMPPEvents.SENDING_CHAT_MESSAGE, function (body) { - APP.API.triggerEvent("outgoingMessage", {"message": body}); - }); -} - -var API = { - /** - * Check whether the API should be enabled or not. - * @returns {boolean} - */ - isEnabled: function () { - var hash = location.hash; - if (hash && hash.indexOf("external") > -1 && window.postMessage) - return true; - return false; - }, - /** - * Initializes the APIConnector. Setups message event listeners that will - * receive information from external applications that embed Jitsi Meet. - * It also sends a message to the external application that APIConnector - * is initialized. - */ - init: function () { - initCommands(); - if (window.addEventListener) { - window.addEventListener('message', - processMessage, false); - } - else { - window.attachEvent('onmessage', processMessage); - } - sendMessage({type: "system", loaded: true}); - setupListeners(); - }, - /** - * Checks whether the event is enabled ot not. - * @param name the name of the event. - * @returns {*} - */ - isEventEnabled: function (name) { - return events[name]; - }, - - /** - * Sends event object to the external application that has been subscribed - * for that event. - * @param name the name event - * @param object data associated with the event - */ - triggerEvent: function (name, object) { - if(this.isEnabled() && this.isEventEnabled(name)) - sendMessage({ - type: "event", action: "result", event: name, result: object}); - }, - - /** - * Removes the listeners. - */ - dispose: function () { - if(window.removeEventListener) { - window.removeEventListener("message", - processMessage, false); - } - else { - window.detachEvent('onmessage', processMessage); - } - } -}; - -module.exports = API; -},{"../../service/xmpp/XMPPEvents":166}],5:[function(require,module,exports){ -/* global APP */ - -/** - * A module for sending DTMF tones. - */ -var DTMFSender; -var initDtmfSender = function() { - // TODO: This needs to reset this if the peerconnection changes - // (e.g. the call is re-made) - if (DTMFSender) - return; - - var localAudio = APP.RTC.localAudio; - if (localAudio && localAudio.getTracks().length > 0) - { - var peerconnection - = APP.xmpp.getConnection().jingle.activecall.peerconnection; - if (peerconnection) { - DTMFSender = - peerconnection.peerconnection - .createDTMFSender(localAudio.getTracks()[0]); - console.log("Initialized DTMFSender"); - } - else { - console.log("Failed to initialize DTMFSender: no PeerConnection."); - } - } - else { - console.log("Failed to initialize DTMFSender: no audio track."); - } -}; - -var DTMF = { - sendTones: function (tones, duration, pause) { - if (!DTMFSender) - initDtmfSender(); - - if (DTMFSender){ - DTMFSender.insertDTMF(tones, - (duration || 200), - (pause || 200)); - } - } -}; - -module.exports = DTMF; - - -},{}],6:[function(require,module,exports){ -/* global config, APP, Strophe */ - -// cache datachannels to avoid garbage collection -// https://code.google.com/p/chromium/issues/detail?id=405545 -var RTCEvents = require("../../service/RTC/RTCEvents"); - -var _dataChannels = []; -var eventEmitter = null; - - -var DataChannels = { - /** - * Callback triggered by PeerConnection when new data channel is opened - * on the bridge. - * @param event the event info object. - */ - onDataChannel: function (event) { - var dataChannel = event.channel; - - dataChannel.onopen = function () { - console.info("Data channel opened by the Videobridge!", dataChannel); - - // Code sample for sending string and/or binary data - // Sends String message to the bridge - //dataChannel.send("Hello bridge!"); - // Sends 12 bytes binary message to the bridge - //dataChannel.send(new ArrayBuffer(12)); - - eventEmitter.emit(RTCEvents.DATA_CHANNEL_OPEN); - }; - - dataChannel.onerror = function (error) { - console.error("Data Channel Error:", error, dataChannel); - }; - - dataChannel.onmessage = function (event) { - var data = event.data; - // JSON - var obj; - - try { - obj = JSON.parse(data); - } - catch (e) { - console.error( - "Failed to parse data channel message as JSON: ", - data, - dataChannel); - } - if (('undefined' !== typeof(obj)) && (null !== obj)) { - var colibriClass = obj.colibriClass; - - if ("DominantSpeakerEndpointChangeEvent" === colibriClass) { - // Endpoint ID from the Videobridge. - var dominantSpeakerEndpoint = obj.dominantSpeakerEndpoint; - - console.info( - "Data channel new dominant speaker event: ", - dominantSpeakerEndpoint); - eventEmitter.emit(RTCEvents.DOMINANTSPEAKER_CHANGED, dominantSpeakerEndpoint); - } - else if ("InLastNChangeEvent" === colibriClass) { - var oldValue = obj.oldValue; - var newValue = obj.newValue; - // Make sure that oldValue and newValue are of type boolean. - var type; - - if ((type = typeof oldValue) !== 'boolean') { - if (type === 'string') { - oldValue = (oldValue == "true"); - } else { - oldValue = new Boolean(oldValue).valueOf(); - } - } - if ((type = typeof newValue) !== 'boolean') { - if (type === 'string') { - newValue = (newValue == "true"); - } else { - newValue = new Boolean(newValue).valueOf(); - } - } - - eventEmitter.emit(RTCEvents.LASTN_CHANGED, oldValue, newValue); - } - else if ("LastNEndpointsChangeEvent" === colibriClass) { - // The new/latest list of last-n endpoint IDs. - var lastNEndpoints = obj.lastNEndpoints; - // The list of endpoint IDs which are entering the list of - // last-n at this time i.e. were not in the old list of last-n - // endpoint IDs. - var endpointsEnteringLastN = obj.endpointsEnteringLastN; - - console.log( - "Data channel new last-n event: ", - lastNEndpoints, endpointsEnteringLastN, obj); - eventEmitter.emit(RTCEvents.LASTN_ENDPOINT_CHANGED, - lastNEndpoints, endpointsEnteringLastN, obj); - } - else { - console.debug("Data channel JSON-formatted message: ", obj); - } - } - }; - - dataChannel.onclose = function () { - console.info("The Data Channel closed", dataChannel); - var idx = _dataChannels.indexOf(dataChannel); - if (idx > -1) - _dataChannels = _dataChannels.splice(idx, 1); - }; - _dataChannels.push(dataChannel); - }, - - /** - * Binds "ondatachannel" event listener to given PeerConnection instance. - * @param peerConnection WebRTC peer connection instance. - */ - init: function (peerConnection, emitter) { - if(!config.openSctp) - return; - - peerConnection.ondatachannel = this.onDataChannel; - eventEmitter = emitter; - - // Sample code for opening new data channel from Jitsi Meet to the bridge. - // Although it's not a requirement to open separate channels from both bridge - // and peer as single channel can be used for sending and receiving data. - // So either channel opened by the bridge or the one opened here is enough - // for communication with the bridge. - /*var dataChannelOptions = - { - reliable: true - }; - var dataChannel - = peerConnection.createDataChannel("myChannel", dataChannelOptions); - - // Can be used only when is in open state - dataChannel.onopen = function () - { - dataChannel.send("My channel !!!"); - }; - dataChannel.onmessage = function (event) - { - var msgData = event.data; - console.info("Got My Data Channel Message:", msgData, dataChannel); - };*/ - }, - handleSelectedEndpointEvent: onSelectedEndpointChanged, - handlePinnedEndpointEvent: onPinnedEndpointChanged -}; - -function onSelectedEndpointChanged(userResource) { - console.log('selected endpoint changed: ', userResource); - if (_dataChannels && _dataChannels.length != 0) { - _dataChannels.some(function (dataChannel) { - if (dataChannel.readyState == 'open') { - console.log('sending selected endpoint changed ' + - 'notification to the bridge: ', userResource); - dataChannel.send(JSON.stringify({ - 'colibriClass': 'SelectedEndpointChangedEvent', - 'selectedEndpoint': - (!userResource || userResource === null)? - null : userResource - })); - - return true; - } - }); - } -} - -function onPinnedEndpointChanged(userResource) { - console.log('pinned endpoint changed: ', userResource); - if (_dataChannels && _dataChannels.length != 0) { - _dataChannels.some(function (dataChannel) { - if (dataChannel.readyState == 'open') { - dataChannel.send(JSON.stringify({ - 'colibriClass': 'PinnedEndpointChangedEvent', - 'pinnedEndpoint': - (!userResource || userResource == null)? - null : userResource - })); - - return true; - } - }); - } -} - -module.exports = DataChannels; - - -},{"../../service/RTC/RTCEvents":157}],7:[function(require,module,exports){ -/* global APP */ -var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js"); -var RTCEvents = require("../../service/RTC/RTCEvents"); -var RTCBrowserType = require("./RTCBrowserType"); - -/** - * This implements 'onended' callback normally fired by WebRTC after the stream - * is stopped. There is no such behaviour yet in FF, so we have to add it. - * @param stream original WebRTC stream object to which 'onended' handling - * will be added. - */ -function implementOnEndedHandling(stream) { - var originalStop = stream.stop; - stream.stop = function () { - originalStop.apply(stream); - if (!stream.ended) { - stream.ended = true; - stream.onended(); - } - }; -} - -function LocalStream(stream, type, eventEmitter, videoType, isGUMStream) { - this.stream = stream; - this.eventEmitter = eventEmitter; - this.type = type; - this.videoType = videoType; - this.isGUMStream = true; - if(isGUMStream === false) - this.isGUMStream = isGUMStream; - var self = this; - if(type == "audio") { - this.getTracks = function () { - return self.stream.getAudioTracks(); - }; - } else { - this.getTracks = function () { - return self.stream.getVideoTracks(); - }; - } - - this.stream.onended = function () { - self.streamEnded(); - }; - if (RTCBrowserType.isFirefox()) { - implementOnEndedHandling(this.stream); - } -} - -LocalStream.prototype.streamEnded = function () { - this.eventEmitter.emit(StreamEventTypes.EVENT_TYPE_LOCAL_ENDED, this); -}; - -LocalStream.prototype.getOriginalStream = function() -{ - return this.stream; -}; - -LocalStream.prototype.isAudioStream = function () { - return this.type === "audio"; -}; - -LocalStream.prototype.setMute = function (mute) -{ - var isAudio = this.isAudioStream(); - var eventType = isAudio ? RTCEvents.AUDIO_MUTE : RTCEvents.VIDEO_MUTE; - - if ((window.location.protocol != "https:" && this.isGUMStream) || - (isAudio && this.isGUMStream) || this.videoType === "screen" || - // FIXME FF does not support 'removeStream' method used to mute - RTCBrowserType.isFirefox()) { - - var tracks = this.getTracks(); - for (var idx = 0; idx < tracks.length; idx++) { - tracks[idx].enabled = !mute; - } - this.eventEmitter.emit(eventType, mute); - } else { - if (mute) { - APP.xmpp.removeStream(this.stream); - this.stream.stop(); - this.eventEmitter.emit(eventType, true); - } else { - var self = this; - APP.RTC.rtcUtils.obtainAudioAndVideoPermissions( - (this.isAudioStream() ? ["audio"] : ["video"]), - function (stream) { - if (isAudio) { - APP.RTC.changeLocalAudio(stream, - function () { - self.eventEmitter.emit(eventType, false); - }); - } else { - APP.RTC.changeLocalVideo(stream, false, - function () { - self.eventEmitter.emit(eventType, false); - }); - } - }); - } - } -}; - -LocalStream.prototype.isMuted = function () { - var tracks = []; - if (this.isAudioStream()) { - tracks = this.stream.getAudioTracks(); - } else { - if (this.stream.ended) - return true; - tracks = this.stream.getVideoTracks(); - } - for (var idx = 0; idx < tracks.length; idx++) { - if(tracks[idx].enabled) - return false; - } - return true; -}; - -LocalStream.prototype.getId = function () { - return this.stream.getTracks()[0].id; -}; - -module.exports = LocalStream; - -},{"../../service/RTC/RTCEvents":157,"../../service/RTC/StreamEventTypes.js":159,"./RTCBrowserType":10}],8:[function(require,module,exports){ -var MediaStreamType = require("../../service/RTC/MediaStreamTypes"); - -/** - * Creates a MediaStream object for the given data, session id and ssrc. - * It is a wrapper class for the MediaStream. - * - * @param data the data object from which we obtain the stream, - * the peerjid, etc. - * @param sid the session id - * @param ssrc the ssrc corresponding to this MediaStream - * - * @constructor - */ -function MediaStream(data, sid, ssrc, browser, eventEmitter) { - - // XXX(gp) to minimize headaches in the future, we should build our - // abstractions around tracks and not streams. ORTC is track based API. - // Mozilla expects m-lines to represent media tracks. - // - // Practically, what I'm saying is that we should have a MediaTrack class - // and not a MediaStream class. - // - // Also, we should be able to associate multiple SSRCs with a MediaTrack as - // a track might have an associated RTX and FEC sources. - - this.sid = sid; - this.stream = data.stream; - this.peerjid = data.peerjid; - this.videoType = data.videoType; - this.ssrc = ssrc; - this.type = (this.stream.getVideoTracks().length > 0)? - MediaStreamType.VIDEO_TYPE : MediaStreamType.AUDIO_TYPE; - this.muted = false; - this.eventEmitter = eventEmitter; -} - - -MediaStream.prototype.getOriginalStream = function() { - return this.stream; -}; - -MediaStream.prototype.setMute = function (value) { - this.stream.muted = value; - this.muted = value; -}; - -module.exports = MediaStream; - -},{"../../service/RTC/MediaStreamTypes":156}],9:[function(require,module,exports){ -/* global APP */ -var EventEmitter = require("events"); -var RTCBrowserType = require("./RTCBrowserType"); -var RTCUtils = require("./RTCUtils.js"); -var LocalStream = require("./LocalStream.js"); -var DataChannels = require("./DataChannels"); -var MediaStream = require("./MediaStream.js"); -var DesktopSharingEventTypes - = require("../../service/desktopsharing/DesktopSharingEventTypes"); -var MediaStreamType = require("../../service/RTC/MediaStreamTypes"); -var StreamEventTypes = require("../../service/RTC/StreamEventTypes.js"); -var RTCEvents = require("../../service/RTC/RTCEvents.js"); -var XMPPEvents = require("../../service/xmpp/XMPPEvents"); -var UIEvents = require("../../service/UI/UIEvents"); - -var eventEmitter = new EventEmitter(); - - -function getMediaStreamUsage() -{ - var result = { - audio: true, - video: true - }; - - /** There are some issues with the desktop sharing - * when this property is enabled. - * WARNING: We must change the implementation to start video/audio if we - * receive from the focus that the peer is not muted. - - var isSecureConnection = window.location.protocol == "https:"; - - if(config.disableEarlyMediaPermissionRequests || !isSecureConnection) - { - result = { - audio: false, - video: false - }; - - } - **/ - - return result; -} - -var RTC = { - rtcUtils: null, - devices: { - audio: true, - video: true - }, - localStreams: [], - remoteStreams: {}, - localAudio: null, - localVideo: null, - addStreamListener: function (listener, eventType) { - eventEmitter.on(eventType, listener); - }, - addListener: function (type, listener) { - eventEmitter.on(type, listener); - }, - removeStreamListener: function (listener, eventType) { - if(!(eventType instanceof StreamEventTypes)) - throw "Illegal argument"; - - eventEmitter.removeListener(eventType, listener); - }, - createLocalStream: function (stream, type, change, videoType, isMuted, isGUMStream) { - - var localStream = new LocalStream(stream, type, eventEmitter, videoType, isGUMStream); - //in firefox we have only one stream object - if(this.localStreams.length == 0 || - this.localStreams[0].getOriginalStream() != stream) - this.localStreams.push(localStream); - if(isMuted === true) - localStream.setMute(true); - - if(type == "audio") { - this.localAudio = localStream; - } else { - this.localVideo = localStream; - } - var eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CREATED; - if(change) - eventType = StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED; - - eventEmitter.emit(eventType, localStream, isMuted); - return localStream; - }, - removeLocalStream: function (stream) { - for(var i = 0; i < this.localStreams.length; i++) { - if(this.localStreams[i].getOriginalStream() === stream) { - delete this.localStreams[i]; - return; - } - } - }, - createRemoteStream: function (data, sid, thessrc) { - var remoteStream = new MediaStream(data, sid, thessrc, - RTCBrowserType.getBrowserType(), eventEmitter); - var jid = data.peerjid || APP.xmpp.myJid(); - if(!this.remoteStreams[jid]) { - this.remoteStreams[jid] = {}; - } - this.remoteStreams[jid][remoteStream.type]= remoteStream; - eventEmitter.emit(StreamEventTypes.EVENT_TYPE_REMOTE_CREATED, remoteStream); - return remoteStream; - }, - getPCConstraints: function () { - return this.rtcUtils.pc_constraints; - }, - getUserMediaWithConstraints:function(um, success_callback, - failure_callback, resolution, - bandwidth, fps, desktopStream) - { - return this.rtcUtils.getUserMediaWithConstraints(um, success_callback, - failure_callback, resolution, bandwidth, fps, desktopStream); - }, - attachMediaStream: function (elSelector, stream) { - this.rtcUtils.attachMediaStream(elSelector, stream); - }, - getStreamID: function (stream) { - return this.rtcUtils.getStreamID(stream); - }, - getVideoSrc: function (element) { - return this.rtcUtils.getVideoSrc(element); - }, - setVideoSrc: function (element, src) { - this.rtcUtils.setVideoSrc(element, src); - }, - getVideoElementName: function () { - return RTCBrowserType.isTemasysPluginUsed() ? 'object' : 'video'; - }, - dispose: function() { - if (this.rtcUtils) { - this.rtcUtils = null; - } - }, - stop: function () { - this.dispose(); - }, - start: function () { - var self = this; - APP.desktopsharing.addListener( - function (stream, isUsingScreenStream, callback) { - self.changeLocalVideo(stream, isUsingScreenStream, callback); - }, DesktopSharingEventTypes.NEW_STREAM_CREATED); - APP.xmpp.addListener(XMPPEvents.CALL_INCOMING, function(event) { - DataChannels.init(event.peerconnection, eventEmitter); - }); - APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, - DataChannels.handleSelectedEndpointEvent); - APP.UI.addListener(UIEvents.PINNED_ENDPOINT, - DataChannels.handlePinnedEndpointEvent); - - // In case of IE we continue from 'onReady' callback - // passed to RTCUtils constructor. It will be invoked by Temasys plugin - // once it is initialized. - var onReady = function () { - eventEmitter.emit(RTCEvents.RTC_READY, true); - self.rtcUtils.obtainAudioAndVideoPermissions( - null, null, getMediaStreamUsage()); - }; - - this.rtcUtils = new RTCUtils(this, onReady); - - // Call onReady() if Temasys plugin is not used - if (!RTCBrowserType.isTemasysPluginUsed()) { - onReady(); - } - }, - muteRemoteVideoStream: function (jid, value) { - var stream; - - if(this.remoteStreams[jid] && - this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) { - stream = this.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]; - } - - if(!stream) - return true; - - if (value != stream.muted) { - stream.setMute(value); - return true; - } - return false; - }, - switchVideoStreams: function (new_stream) { - this.localVideo.stream = new_stream; - - this.localStreams = []; - - //in firefox we have only one stream object - if (this.localAudio.getOriginalStream() != new_stream) - this.localStreams.push(this.localAudio); - this.localStreams.push(this.localVideo); - }, - changeLocalVideo: function (stream, isUsingScreenStream, callback) { - var oldStream = this.localVideo.getOriginalStream(); - var type = (isUsingScreenStream ? "screen" : "camera"); - var localCallback = callback; - if(this.localVideo.isMuted() && this.localVideo.videoType !== type) { - localCallback = function() { - APP.xmpp.setVideoMute(false, function(mute) { - eventEmitter.emit(RTCEvents.VIDEO_MUTE, mute); - }); - - callback(); - }; - } - // FIXME: Workaround for FF/IE/Safari - if (stream && stream.videoStream) { - stream = stream.videoStream; - } - var videoStream = this.rtcUtils.createStream(stream, true); - this.localVideo = this.createLocalStream(videoStream, "video", true, type); - // Stop the stream to trigger onended event for old stream - oldStream.stop(); - - this.switchVideoStreams(videoStream, oldStream); - - APP.xmpp.switchStreams(videoStream, oldStream,localCallback); - }, - changeLocalAudio: function (stream, callback) { - var oldStream = this.localAudio.getOriginalStream(); - var newStream = this.rtcUtils.createStream(stream); - this.localAudio = this.createLocalStream(newStream, "audio", true); - // Stop the stream to trigger onended event for old stream - oldStream.stop(); - APP.xmpp.switchStreams(newStream, oldStream, callback, true); - }, - isVideoMuted: function (jid) { - if (jid === APP.xmpp.myJid()) { - var localVideo = APP.RTC.localVideo; - return (!localVideo || localVideo.isMuted()); - } else { - if (!APP.RTC.remoteStreams[jid] || - !APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE]) { - return null; - } - return APP.RTC.remoteStreams[jid][MediaStreamType.VIDEO_TYPE].muted; - } - }, - setVideoMute: function (mute, callback, options) { - if (!this.localVideo) - return; - - if (mute == APP.RTC.localVideo.isMuted()) - { - APP.xmpp.sendVideoInfoPresence(mute); - if (callback) - callback(mute); - } - else - { - APP.RTC.localVideo.setMute(mute); - APP.xmpp.setVideoMute( - mute, - callback, - options); - } - }, - setDeviceAvailability: function (devices) { - if(!devices) - return; - if(devices.audio === true || devices.audio === false) - this.devices.audio = devices.audio; - if(devices.video === true || devices.video === false) - this.devices.video = devices.video; - eventEmitter.emit(RTCEvents.AVAILABLE_DEVICES_CHANGED, this.devices); - } -}; - -module.exports = RTC; - -},{"../../service/RTC/MediaStreamTypes":156,"../../service/RTC/RTCEvents.js":157,"../../service/RTC/StreamEventTypes.js":159,"../../service/UI/UIEvents":160,"../../service/desktopsharing/DesktopSharingEventTypes":163,"../../service/xmpp/XMPPEvents":166,"./DataChannels":6,"./LocalStream.js":7,"./MediaStream.js":8,"./RTCBrowserType":10,"./RTCUtils.js":11,"events":1}],10:[function(require,module,exports){ - -var currentBrowser; - -var browserVersion; - -var RTCBrowserType = { - - RTC_BROWSER_CHROME: "rtc_browser.chrome", - - RTC_BROWSER_OPERA: "rtc_browser.opera", - - RTC_BROWSER_FIREFOX: "rtc_browser.firefox", - - RTC_BROWSER_IEXPLORER: "rtc_browser.iexplorer", - - RTC_BROWSER_SAFARI: "rtc_browser.safari", - - getBrowserType: function () { - return currentBrowser; - }, - - isChrome: function () { - return currentBrowser === RTCBrowserType.RTC_BROWSER_CHROME; - }, - - isOpera: function () { - return currentBrowser === RTCBrowserType.RTC_BROWSER_OPERA; - }, - isFirefox: function () { - return currentBrowser === RTCBrowserType.RTC_BROWSER_FIREFOX; - }, - - isIExplorer: function () { - return currentBrowser === RTCBrowserType.RTC_BROWSER_IEXPLORER; - }, - - isSafari: function () { - return currentBrowser === RTCBrowserType.RTC_BROWSER_SAFARI; - }, - isTemasysPluginUsed: function () { - return RTCBrowserType.isIExplorer() || RTCBrowserType.isSafari(); - }, - getFirefoxVersion: function () { - return RTCBrowserType.isFirefox() ? browserVersion : null; - }, - - getChromeVersion: function () { - return RTCBrowserType.isChrome() ? browserVersion : null; - }, - - usesPlanB: function() { - return RTCBrowserType.isChrome() || RTCBrowserType.isOpera() || - RTCBrowserType.isTemasysPluginUsed(); - }, - - usesUnifiedPlan: function() { - return RTCBrowserType.isFirefox(); - } - - // Add version getters for other browsers when needed -}; - -// detectOpera() must be called before detectChrome() !!! -// otherwise Opera wil be detected as Chrome -function detectChrome() { - if (navigator.webkitGetUserMedia) { - currentBrowser = RTCBrowserType.RTC_BROWSER_CHROME; - var userAgent = navigator.userAgent.toLowerCase(); - // We can assume that user agent is chrome, because it's - // enforced when 'ext' streaming method is set - var ver = parseInt(userAgent.match(/chrome\/(\d+)\./)[1], 10); - console.log("This appears to be Chrome, ver: " + ver); - return ver; - } - return null; -} - -function detectOpera() { - var userAgent = navigator.userAgent; - if (userAgent.match(/Opera|OPR/)) { - currentBrowser = RTCBrowserType.RTC_BROWSER_OPERA; - var version = userAgent.match(/(Opera|OPR) ?\/?(\d+)\.?/)[2]; - console.info("This appears to be Opera, ver: " + version); - return version; - } - return null; -} - -function detectFirefox() { - if (navigator.mozGetUserMedia) { - currentBrowser = RTCBrowserType.RTC_BROWSER_FIREFOX; - var version = parseInt( - navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); - console.log('This appears to be Firefox, ver: ' + version); - return version; - } - return null; -} - -function detectSafari() { - if ((/^((?!chrome).)*safari/i.test(navigator.userAgent))) { - currentBrowser = RTCBrowserType.RTC_BROWSER_SAFARI; - console.info("This appears to be Safari"); - // FIXME detect Safari version when needed - return 1; - } - return null; -} - -function detectIE() { - var version; - var ua = window.navigator.userAgent; - - var msie = ua.indexOf('MSIE '); - if (msie > 0) { - // IE 10 or older => return version number - version = parseInt(ua.substring(msie + 5, ua.indexOf('.', msie)), 10); - } - - var trident = ua.indexOf('Trident/'); - if (!version && trident > 0) { - // IE 11 => return version number - var rv = ua.indexOf('rv:'); - version = parseInt(ua.substring(rv + 3, ua.indexOf('.', rv)), 10); - } - - var edge = ua.indexOf('Edge/'); - if (!version && edge > 0) { - // IE 12 => return version number - version = parseInt(ua.substring(edge + 5, ua.indexOf('.', edge)), 10); - } - - if (version) { - currentBrowser = RTCBrowserType.RTC_BROWSER_IEXPLORER; - console.info("This appears to be IExplorer, ver: " + version); - } - return version; -} - -function detectBrowser() { - var version; - var detectors = [ - detectOpera, - detectChrome, - detectFirefox, - detectIE, - detectSafari - ]; - // Try all browser detectors - for (var i = 0; i < detectors.length; i++) { - version = detectors[i](); - if (version) - return version; - } - console.error("Failed to detect browser type"); - return undefined; -} - -browserVersion = detectBrowser(); - -module.exports = RTCBrowserType; -},{}],11:[function(require,module,exports){ -/* global config, require, attachMediaStream, getUserMedia */ -var RTCBrowserType = require("./RTCBrowserType"); -var Resolutions = require("../../service/RTC/Resolutions"); -var AdapterJS = require("./adapter.screenshare"); -var SDPUtil = require("../xmpp/SDPUtil"); - -var currentResolution = null; - -function getPreviousResolution(resolution) { - if(!Resolutions[resolution]) - return null; - var order = Resolutions[resolution].order; - var res = null; - var resName = null; - for(var i in Resolutions) { - var tmp = Resolutions[i]; - if(res == null || (res.order < tmp.order && tmp.order < order)) { - resName = i; - res = tmp; - } - } - return resName; -} - -function setResolutionConstraints(constraints, resolution, isAndroid) { - - if (Resolutions[resolution]) { - constraints.video.mandatory.minWidth = Resolutions[resolution].width; - constraints.video.mandatory.minHeight = Resolutions[resolution].height; - } - else if (isAndroid) { - // FIXME can't remember if the purpose of this was to always request - // low resolution on Android ? if yes it should be moved up front - constraints.video.mandatory.minWidth = 320; - constraints.video.mandatory.minHeight = 240; - constraints.video.mandatory.maxFrameRate = 15; - } - - if (constraints.video.mandatory.minWidth) - constraints.video.mandatory.maxWidth = - constraints.video.mandatory.minWidth; - if (constraints.video.mandatory.minHeight) - constraints.video.mandatory.maxHeight = - constraints.video.mandatory.minHeight; -} - -function getConstraints(um, resolution, bandwidth, fps, desktopStream, isAndroid) -{ - var constraints = {audio: false, video: false}; - - if (um.indexOf('video') >= 0) { - // same behaviour as true - constraints.video = { mandatory: {}, optional: [] }; - - constraints.video.optional.push({ googLeakyBucket: true }); - - setResolutionConstraints(constraints, resolution, isAndroid); - } - if (um.indexOf('audio') >= 0) { - if (!RTCBrowserType.isFirefox()) { - // same behaviour as true - constraints.audio = { mandatory: {}, optional: []}; - // if it is good enough for hangouts... - constraints.audio.optional.push( - {googEchoCancellation: true}, - {googAutoGainControl: true}, - {googNoiseSupression: true}, - {googHighpassFilter: true}, - {googNoisesuppression2: true}, - {googEchoCancellation2: true}, - {googAutoGainControl2: true} - ); - } else { - constraints.audio = true; - } - } - if (um.indexOf('screen') >= 0) { - if (RTCBrowserType.isChrome()) { - constraints.video = { - mandatory: { - chromeMediaSource: "screen", - googLeakyBucket: true, - maxWidth: window.screen.width, - maxHeight: window.screen.height, - maxFrameRate: 3 - }, - optional: [] - }; - } else if (RTCBrowserType.isTemasysPluginUsed()) { - constraints.video = { - optional: [ - { - sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey - } - ] - }; - } else { - console.error( - "'screen' WebRTC media source is supported only in Chrome" + - " and with Temasys plugin"); - } - } - if (um.indexOf('desktop') >= 0) { - constraints.video = { - mandatory: { - chromeMediaSource: "desktop", - chromeMediaSourceId: desktopStream, - googLeakyBucket: true, - maxWidth: window.screen.width, - maxHeight: window.screen.height, - maxFrameRate: 3 - }, - optional: [] - }; - } - - if (bandwidth) { - if (!constraints.video) { - //same behaviour as true - constraints.video = {mandatory: {}, optional: []}; - } - constraints.video.optional.push({bandwidth: bandwidth}); - } - if (fps) { - // for some cameras it might be necessary to request 30fps - // so they choose 30fps mjpg over 10fps yuy2 - if (!constraints.video) { - // same behaviour as true; - constraints.video = {mandatory: {}, optional: []}; - } - constraints.video.mandatory.minFrameRate = fps; - } - - return constraints; -} - - -function RTCUtils(RTCService, onTemasysPluginReady) -{ - var self = this; - this.service = RTCService; - if (RTCBrowserType.isFirefox()) { - var FFversion = RTCBrowserType.getFirefoxVersion(); - if (FFversion >= 40) { - this.peerconnection = mozRTCPeerConnection; - this.getUserMedia = navigator.mozGetUserMedia.bind(navigator); - this.pc_constraints = {}; - this.attachMediaStream = function (element, stream) { - // srcObject is being standardized and FF will eventually - // support that unprefixed. FF also supports the - // "element.src = URL.createObjectURL(...)" combo, but that - // will be deprecated in favour of srcObject. - // - // https://groups.google.com/forum/#!topic/mozilla.dev.media/pKOiioXonJg - // https://github.com/webrtc/samples/issues/302 - if(!element[0]) - return; - element[0].mozSrcObject = stream; - element[0].play(); - }; - this.getStreamID = function (stream) { - var id = stream.id; - if (!id) { - var tracks = stream.getVideoTracks(); - if (!tracks || tracks.length === 0) { - tracks = stream.getAudioTracks(); - } - id = tracks[0].id; - } - return SDPUtil.filter_special_chars(id); - }; - this.getVideoSrc = function (element) { - if(!element) - return null; - return element.mozSrcObject; - }; - this.setVideoSrc = function (element, src) { - if(element) - element.mozSrcObject = src; - }; - RTCSessionDescription = mozRTCSessionDescription; - RTCIceCandidate = mozRTCIceCandidate; - } else { - console.error( - "Firefox version too old: " + FFversion + ". Required >= 40."); - window.location.href = 'unsupported_browser.html'; - return; - } - - } else if (RTCBrowserType.isChrome() || RTCBrowserType.isOpera()) { - this.peerconnection = webkitRTCPeerConnection; - this.getUserMedia = navigator.webkitGetUserMedia.bind(navigator); - this.attachMediaStream = function (element, stream) { - element.attr('src', webkitURL.createObjectURL(stream)); - }; - this.getStreamID = function (stream) { - // streams from FF endpoints have the characters '{' and '}' - // that make jQuery choke. - return SDPUtil.filter_special_chars(stream.id); - }; - this.getVideoSrc = function (element) { - if(!element) - return null; - return element.getAttribute("src"); - }; - this.setVideoSrc = function (element, src) { - if(element) - element.setAttribute("src", src); - }; - // DTLS should now be enabled by default but.. - this.pc_constraints = {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}; - if (navigator.userAgent.indexOf('Android') != -1) { - this.pc_constraints = {}; // disable DTLS on Android - } - if (!webkitMediaStream.prototype.getVideoTracks) { - webkitMediaStream.prototype.getVideoTracks = function () { - return this.videoTracks; - }; - } - if (!webkitMediaStream.prototype.getAudioTracks) { - webkitMediaStream.prototype.getAudioTracks = function () { - return this.audioTracks; - }; - } - } - // Detect IE/Safari - else if (RTCBrowserType.isTemasysPluginUsed()) { - - //AdapterJS.WebRTCPlugin.setLogLevel( - // AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS.VERBOSE); - - AdapterJS.webRTCReady(function (isPlugin) { - - self.peerconnection = RTCPeerConnection; - self.getUserMedia = getUserMedia; - self.attachMediaStream = function (elSel, stream) { - - if (stream.id === "dummyAudio" || stream.id === "dummyVideo") { - return; - } - - attachMediaStream(elSel[0], stream); - }; - self.getStreamID = function (stream) { - var id = SDPUtil.filter_special_chars(stream.label); - return id; - }; - self.getVideoSrc = function (element) { - if (!element) { - console.warn("Attempt to get video SRC of null element"); - return null; - } - var children = element.children; - for (var i = 0; i !== children.length; ++i) { - if (children[i].name === 'streamId') { - return children[i].value; - } - } - //console.info(element.id + " SRC: " + src); - return null; - }; - self.setVideoSrc = function (element, src) { - //console.info("Set video src: ", element, src); - if (!src) { - console.warn("Not attaching video stream, 'src' is null"); - return; - } - AdapterJS.WebRTCPlugin.WaitForPluginReady(); - var stream = AdapterJS.WebRTCPlugin.plugin - .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, src); - attachMediaStream(element, stream); - }; - - onTemasysPluginReady(isPlugin); - }); - } else { - try { - console.log('Browser does not appear to be WebRTC-capable'); - } catch (e) { } - window.location.href = 'unsupported_browser.html'; - } -} - - -RTCUtils.prototype.getUserMediaWithConstraints = function( - um, success_callback, failure_callback, resolution,bandwidth, fps, - desktopStream) { - currentResolution = resolution; - // Check if we are running on Android device - var isAndroid = navigator.userAgent.indexOf('Android') != -1; - - var constraints = getConstraints( - um, resolution, bandwidth, fps, desktopStream, isAndroid); - - console.info("Get media constraints", constraints); - - var self = this; - - try { - this.getUserMedia(constraints, - function (stream) { - console.log('onUserMediaSuccess'); - self.setAvailableDevices(um, true); - success_callback(stream); - }, - function (error) { - self.setAvailableDevices(um, false); - console.warn('Failed to get access to local media. Error ', - error, constraints); - if (failure_callback) { - failure_callback(error); - } - }); - } catch (e) { - console.error('GUM failed: ', e); - if(failure_callback) { - failure_callback(e); - } - } -}; - -RTCUtils.prototype.setAvailableDevices = function (um, available) { - var devices = {}; - if(um.indexOf("video") != -1) { - devices.video = available; - } - if(um.indexOf("audio") != -1) { - devices.audio = available; - } - this.service.setDeviceAvailability(devices); -}; - -/** - * We ask for audio and video combined stream in order to get permissions and - * not to ask twice. - */ -RTCUtils.prototype.obtainAudioAndVideoPermissions = - function(devices, callback, usageOptions) -{ - var self = this; - // Get AV - - var successCallback = function (stream) { - if(callback) - callback(stream, usageOptions); - else - self.successCallback(stream, usageOptions); - }; - - if(!devices) - devices = ['audio', 'video']; - - var newDevices = []; - - - if(usageOptions) - for(var i = 0; i < devices.length; i++) { - var device = devices[i]; - if(usageOptions[device] === true) - newDevices.push(device); - } - else - newDevices = devices; - - if(newDevices.length === 0) { - successCallback(); - return; - } - - if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) { - - // With FF/IE we can't split the stream into audio and video because FF - // doesn't support media stream constructors. So, we need to get the - // audio stream separately from the video stream using two distinct GUM - // calls. Not very user friendly :-( but we don't have many other - // options neither. - // - // Note that we pack those 2 streams in a single object and pass it to - // the successCallback method. - var obtainVideo = function (audioStream) { - self.getUserMediaWithConstraints( - ['video'], - function (videoStream) { - return successCallback({ - audioStream: audioStream, - videoStream: videoStream - }); - }, - function (error) { - console.error( - 'failed to obtain video stream - stop', error); - self.errorCallback(error); - }, - config.resolution || '360'); - }; - var obtainAudio = function () { - self.getUserMediaWithConstraints( - ['audio'], - function (audioStream) { - if (newDevices.indexOf('video') !== -1) - obtainVideo(audioStream); - }, - function (error) { - console.error( - 'failed to obtain audio stream - stop', error); - self.errorCallback(error); - } - ); - }; - if (newDevices.indexOf('audio') !== -1) { - obtainAudio(); - } else { - obtainVideo(null); - } - } else { - this.getUserMediaWithConstraints( - newDevices, - function (stream) { - successCallback(stream); - }, - function (error) { - self.errorCallback(error); - }, - config.resolution || '360'); - } -}; - -RTCUtils.prototype.successCallback = function (stream, usageOptions) { - // If this is FF or IE, the stream parameter is *not* a MediaStream object, - // it's an object with two properties: audioStream, videoStream. - if (stream && stream.getAudioTracks && stream.getVideoTracks) - console.log('got', stream, stream.getAudioTracks().length, - stream.getVideoTracks().length); - this.handleLocalStream(stream, usageOptions); -}; - -RTCUtils.prototype.errorCallback = function (error) { - var self = this; - console.error('failed to obtain audio/video stream - trying audio only', error); - var resolution = getPreviousResolution(currentResolution); - if(typeof error == "object" && error.constraintName && error.name - && (error.name == "ConstraintNotSatisfiedError" || - error.name == "OverconstrainedError") && - (error.constraintName == "minWidth" || error.constraintName == "maxWidth" || - error.constraintName == "minHeight" || error.constraintName == "maxHeight") - && resolution != null) - { - self.getUserMediaWithConstraints(['audio', 'video'], - function (stream) { - return self.successCallback(stream); - }, function (error) { - return self.errorCallback(error); - }, resolution); - } - else { - self.getUserMediaWithConstraints( - ['audio'], - function (stream) { - return self.successCallback(stream); - }, - function (error) { - console.error('failed to obtain audio/video stream - stop', - error); - return self.successCallback(null); - } - ); - } -}; - -RTCUtils.prototype.handleLocalStream = function(stream, usageOptions) { - // If this is FF, the stream parameter is *not* a MediaStream object, it's - // an object with two properties: audioStream, videoStream. - var audioStream, videoStream; - if(window.webkitMediaStream) - { - audioStream = new webkitMediaStream(); - videoStream = new webkitMediaStream(); - if(stream) { - var audioTracks = stream.getAudioTracks(); - - for (var i = 0; i < audioTracks.length; i++) { - audioStream.addTrack(audioTracks[i]); - } - - var videoTracks = stream.getVideoTracks(); - - for (i = 0; i < videoTracks.length; i++) { - videoStream.addTrack(videoTracks[i]); - } - } - } - else if (RTCBrowserType.isFirefox() || RTCBrowserType.isTemasysPluginUsed()) - { // Firefox and Temasys plugin - if (stream && stream.audioStream) - audioStream = stream.audioStream; - else - audioStream = new DummyMediaStream("dummyAudio"); - - if (stream && stream.videoStream) - videoStream = stream.videoStream; - else - videoStream = new DummyMediaStream("dummyVideo"); - } - - var audioMuted = (usageOptions && usageOptions.audio === false), - videoMuted = (usageOptions && usageOptions.video === false); - - var audioGUM = (!usageOptions || usageOptions.audio !== false), - videoGUM = (!usageOptions || usageOptions.video !== false); - - - this.service.createLocalStream(audioStream, "audio", null, null, - audioMuted, audioGUM); - - this.service.createLocalStream(videoStream, "video", null, 'camera', - videoMuted, videoGUM); -}; - -function DummyMediaStream(id) { - this.id = id; - this.label = id; - this.stop = function() { }; - this.getAudioTracks = function() { return []; }; - this.getVideoTracks = function() { return []; }; -} - -RTCUtils.prototype.createStream = function(stream, isVideo) { - var newStream = null; - if (window.webkitMediaStream) { - newStream = new webkitMediaStream(); - if (newStream) { - var tracks = (isVideo ? stream.getVideoTracks() : stream.getAudioTracks()); - - for (var i = 0; i < tracks.length; i++) { - newStream.addTrack(tracks[i]); - } - } - - } - else { - // FIXME: this is duplicated with 'handleLocalStream' !!! - if (stream) { - newStream = stream; - } else { - newStream = - new DummyMediaStream(isVideo ? "dummyVideo" : "dummyAudio"); - } - } - - return newStream; -}; - -module.exports = RTCUtils; - -},{"../../service/RTC/Resolutions":158,"../xmpp/SDPUtil":60,"./RTCBrowserType":10,"./adapter.screenshare":12}],12:[function(require,module,exports){ -/*! adapterjs - custom version from - 2015-08-12 */ - -// Adapter's interface. -var AdapterJS = AdapterJS || {}; - -// Browserify compatibility -if(typeof exports !== 'undefined') { - module.exports = AdapterJS; -} - -AdapterJS.options = AdapterJS.options || {}; - -// uncomment to get virtual webcams -// AdapterJS.options.getAllCams = true; - -// uncomment to prevent the install prompt when the plugin in not yet installed -// AdapterJS.options.hidePluginInstallPrompt = true; - -// AdapterJS version -AdapterJS.VERSION = '0.11.1'; - -// This function will be called when the WebRTC API is ready to be used -// Whether it is the native implementation (Chrome, Firefox, Opera) or -// the plugin -// You may Override this function to synchronise the start of your application -// with the WebRTC API being ready. -// If you decide not to override use this synchronisation, it may result in -// an extensive CPU usage on the plugin start (once per tab loaded) -// Params: -// - isUsingPlugin: true is the WebRTC plugin is being used, false otherwise -// -AdapterJS.onwebrtcready = AdapterJS.onwebrtcready || function(isUsingPlugin) { - // The WebRTC API is ready. - // Override me and do whatever you want here -}; - -// Sets a callback function to be called when the WebRTC interface is ready. -// The first argument is the function to callback.\ -// Throws an error if the first argument is not a function -AdapterJS.webRTCReady = function (callback) { - if (typeof callback !== 'function') { - throw new Error('Callback provided is not a function'); - } - - if (true === AdapterJS.onwebrtcreadyDone) { - // All WebRTC interfaces are ready, just call the callback - callback(null !== AdapterJS.WebRTCPlugin.plugin); - } else { - // will be triggered automatically when your browser/plugin is ready. - AdapterJS.onwebrtcready = callback; - } -}; - -// Plugin namespace -AdapterJS.WebRTCPlugin = AdapterJS.WebRTCPlugin || {}; - -// The object to store plugin information -AdapterJS.WebRTCPlugin.pluginInfo = { - prefix : 'Tem', - plugName : 'TemWebRTCPlugin', - pluginId : 'plugin0', - type : 'application/x-temwebrtcplugin', - onload : '__TemWebRTCReady0', - portalLink : 'http://skylink.io/plugin/', - downloadLink : null, //set below - companyName: 'Temasys' -}; -if(!!navigator.platform.match(/^Mac/i)) { - AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1n77hco'; -} -else if(!!navigator.platform.match(/^Win/i)) { - AdapterJS.WebRTCPlugin.pluginInfo.downloadLink = 'http://bit.ly/1kkS4FN'; -} - -// Unique identifier of each opened page -AdapterJS.WebRTCPlugin.pageId = Math.random().toString(36).slice(2); - -// Use this whenever you want to call the plugin. -AdapterJS.WebRTCPlugin.plugin = null; - -// Set log level for the plugin once it is ready. -// The different values are -// This is an asynchronous function that will run when the plugin is ready -AdapterJS.WebRTCPlugin.setLogLevel = null; - -// Defines webrtc's JS interface according to the plugin's implementation. -// Define plugin Browsers as WebRTC Interface. -AdapterJS.WebRTCPlugin.defineWebRTCInterface = null; - -// This function detects whether or not a plugin is installed. -// Checks if Not IE (firefox, for example), else if it's IE, -// we're running IE and do something. If not it is not supported. -AdapterJS.WebRTCPlugin.isPluginInstalled = null; - - // Lets adapter.js wait until the the document is ready before injecting the plugin -AdapterJS.WebRTCPlugin.pluginInjectionInterval = null; - -// Inject the HTML DOM object element into the page. -AdapterJS.WebRTCPlugin.injectPlugin = null; - -// States of readiness that the plugin goes through when -// being injected and stated -AdapterJS.WebRTCPlugin.PLUGIN_STATES = { - NONE : 0, // no plugin use - INITIALIZING : 1, // Detected need for plugin - INJECTING : 2, // Injecting plugin - INJECTED: 3, // Plugin element injected but not usable yet - READY: 4 // Plugin ready to be used -}; - -// Current state of the plugin. You cannot use the plugin before this is -// equal to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY -AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.NONE; - -// True is AdapterJS.onwebrtcready was already called, false otherwise -// Used to make sure AdapterJS.onwebrtcready is only called once -AdapterJS.onwebrtcreadyDone = false; - -// Log levels for the plugin. -// To be set by calling AdapterJS.WebRTCPlugin.setLogLevel -/* -Log outputs are prefixed in some cases. - INFO: Information reported by the plugin. - ERROR: Errors originating from within the plugin. - WEBRTC: Error originating from within the libWebRTC library -*/ -// From the least verbose to the most verbose -AdapterJS.WebRTCPlugin.PLUGIN_LOG_LEVELS = { - NONE : 'NONE', - ERROR : 'ERROR', - WARNING : 'WARNING', - INFO: 'INFO', - VERBOSE: 'VERBOSE', - SENSITIVE: 'SENSITIVE' -}; - -// Does a waiting check before proceeding to load the plugin. -AdapterJS.WebRTCPlugin.WaitForPluginReady = null; - -// This methid will use an interval to wait for the plugin to be ready. -AdapterJS.WebRTCPlugin.callWhenPluginReady = null; - -// !!!! WARNING: DO NOT OVERRIDE THIS FUNCTION. !!! -// This function will be called when plugin is ready. It sends necessary -// details to the plugin. -// The function will wait for the document to be ready and the set the -// plugin state to AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY, -// indicating that it can start being requested. -// This function is not in the IE/Safari condition brackets so that -// TemPluginLoaded function might be called on Chrome/Firefox. -// This function is the only private function that is not encapsulated to -// allow the plugin method to be called. -__TemWebRTCReady0 = function () { - if (document.readyState === 'complete') { - AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY; - - AdapterJS.maybeThroughWebRTCReady(); - } else { - AdapterJS.WebRTCPlugin.documentReadyInterval = setInterval(function () { - if (document.readyState === 'complete') { - // TODO: update comments, we wait for the document to be ready - clearInterval(AdapterJS.WebRTCPlugin.documentReadyInterval); - AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY; - - AdapterJS.maybeThroughWebRTCReady(); - } - }, 100); - } -}; - -AdapterJS.maybeThroughWebRTCReady = function() { - if (!AdapterJS.onwebrtcreadyDone) { - AdapterJS.onwebrtcreadyDone = true; - - if (typeof(AdapterJS.onwebrtcready) === 'function') { - AdapterJS.onwebrtcready(AdapterJS.WebRTCPlugin.plugin !== null); - } - } -}; - -// Text namespace -AdapterJS.TEXT = { - PLUGIN: { - REQUIRE_INSTALLATION: 'This website requires you to install a WebRTC-enabling plugin ' + - 'to work on this browser.', - NOT_SUPPORTED: 'Your browser does not support WebRTC.', - BUTTON: 'Install Now' - }, - REFRESH: { - REQUIRE_REFRESH: 'Please refresh page', - BUTTON: 'Refresh Page' - } -}; - -// The result of ice connection states. -// - starting: Ice connection is starting. -// - checking: Ice connection is checking. -// - connected Ice connection is connected. -// - completed Ice connection is connected. -// - done Ice connection has been completed. -// - disconnected Ice connection has been disconnected. -// - failed Ice connection has failed. -// - closed Ice connection is closed. -AdapterJS._iceConnectionStates = { - starting : 'starting', - checking : 'checking', - connected : 'connected', - completed : 'connected', - done : 'completed', - disconnected : 'disconnected', - failed : 'failed', - closed : 'closed' -}; - -//The IceConnection states that has been fired for each peer. -AdapterJS._iceConnectionFiredStates = []; - - -// Check if WebRTC Interface is defined. -AdapterJS.isDefined = null; - -// This function helps to retrieve the webrtc detected browser information. -// This sets: -// - webrtcDetectedBrowser: The browser agent name. -// - webrtcDetectedVersion: The browser version. -// - webrtcDetectedType: The types of webRTC support. -// - 'moz': Mozilla implementation of webRTC. -// - 'webkit': WebKit implementation of webRTC. -// - 'plugin': Using the plugin implementation. -AdapterJS.parseWebrtcDetectedBrowser = function () { - var hasMatch, checkMatch = navigator.userAgent.match( - /(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; - if (/trident/i.test(checkMatch[1])) { - hasMatch = /\brv[ :]+(\d+)/g.exec(navigator.userAgent) || []; - webrtcDetectedBrowser = 'IE'; - webrtcDetectedVersion = parseInt(hasMatch[1] || '0', 10); - } else if (checkMatch[1] === 'Chrome') { - hasMatch = navigator.userAgent.match(/\bOPR\/(\d+)/); - if (hasMatch !== null) { - webrtcDetectedBrowser = 'opera'; - webrtcDetectedVersion = parseInt(hasMatch[1], 10); - } - } - if (navigator.userAgent.indexOf('Safari')) { - if (typeof InstallTrigger !== 'undefined') { - webrtcDetectedBrowser = 'firefox'; - } else if (/*@cc_on!@*/ false || !!document.documentMode) { - webrtcDetectedBrowser = 'IE'; - } else if ( - Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0) { - webrtcDetectedBrowser = 'safari'; - } else if (!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0) { - webrtcDetectedBrowser = 'opera'; - } else if (!!window.chrome) { - webrtcDetectedBrowser = 'chrome'; - } - } - if (!webrtcDetectedBrowser) { - webrtcDetectedVersion = checkMatch[1]; - } - if (!webrtcDetectedVersion) { - try { - checkMatch = (checkMatch[2]) ? [checkMatch[1], checkMatch[2]] : - [navigator.appName, navigator.appVersion, '-?']; - if ((hasMatch = navigator.userAgent.match(/version\/(\d+)/i)) !== null) { - checkMatch.splice(1, 1, hasMatch[1]); - } - webrtcDetectedVersion = parseInt(checkMatch[1], 10); - } catch (error) { } - } -}; - -// To fix configuration as some browsers does not support -// the 'urls' attribute. -AdapterJS.maybeFixConfiguration = function (pcConfig) { - if (pcConfig === null) { - return; - } - for (var i = 0; i < pcConfig.iceServers.length; i++) { - if (pcConfig.iceServers[i].hasOwnProperty('urls')) { - pcConfig.iceServers[i].url = pcConfig.iceServers[i].urls; - delete pcConfig.iceServers[i].urls; - } - } -}; - -AdapterJS.addEvent = function(elem, evnt, func) { - if (elem.addEventListener) { // W3C DOM - elem.addEventListener(evnt, func, false); - } else if (elem.attachEvent) {// OLD IE DOM - elem.attachEvent('on'+evnt, func); - } else { // No much to do - elem[evnt] = func; - } -}; - -AdapterJS.renderNotificationBar = function (text, buttonText, buttonLink, openNewTab, displayRefreshBar) { - // only inject once the page is ready - if (document.readyState !== 'complete') { - return; - } - - var w = window; - var i = document.createElement('iframe'); - i.style.position = 'fixed'; - i.style.top = '-41px'; - i.style.left = 0; - i.style.right = 0; - i.style.width = '100%'; - i.style.height = '40px'; - i.style.backgroundColor = '#ffffe1'; - i.style.border = 'none'; - i.style.borderBottom = '1px solid #888888'; - i.style.zIndex = '9999999'; - if(typeof i.style.webkitTransition === 'string') { - i.style.webkitTransition = 'all .5s ease-out'; - } else if(typeof i.style.transition === 'string') { - i.style.transition = 'all .5s ease-out'; - } - document.body.appendChild(i); - c = (i.contentWindow) ? i.contentWindow : - (i.contentDocument.document) ? i.contentDocument.document : i.contentDocument; - c.document.open(); - c.document.write('' + text + ''); - if(buttonText && buttonLink) { - c.document.write(''); - c.document.close(); - - AdapterJS.addEvent(c.document.getElementById('okay'), 'click', function(e) { - if (!!displayRefreshBar) { - AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION ? - AdapterJS.TEXT.EXTENSION.REQUIRE_REFRESH : AdapterJS.TEXT.REFRESH.REQUIRE_REFRESH, - AdapterJS.TEXT.REFRESH.BUTTON, 'javascript:location.reload()'); - } - window.open(buttonLink, !!openNewTab ? '_blank' : '_top'); - - e.preventDefault(); - try { - event.cancelBubble = true; - } catch(error) { } - - var pluginInstallInterval = setInterval(function(){ - if(! isIE) { - navigator.plugins.refresh(false); - } - AdapterJS.WebRTCPlugin.isPluginInstalled( - AdapterJS.WebRTCPlugin.pluginInfo.prefix, - AdapterJS.WebRTCPlugin.pluginInfo.plugName, - function() { - clearInterval(pluginInstallInterval); - AdapterJS.WebRTCPlugin.defineWebRTCInterface(); - }, - function() { //Does nothing because not used here - }); - } , 500); - }); - - }else { - c.document.close(); - } - AdapterJS.addEvent(c.document, 'click', function() { - w.document.body.removeChild(i); - }); - setTimeout(function() { - if(typeof i.style.webkitTransform === 'string') { - i.style.webkitTransform = 'translateY(40px)'; - } else if(typeof i.style.transform === 'string') { - i.style.transform = 'translateY(40px)'; - } else { - i.style.top = '0px'; - } - }, 300); -}; - -// ----------------------------------------------------------- -// Detected webrtc implementation. Types are: -// - 'moz': Mozilla implementation of webRTC. -// - 'webkit': WebKit implementation of webRTC. -// - 'plugin': Using the plugin implementation. -webrtcDetectedType = null; - -// Detected webrtc datachannel support. Types are: -// - 'SCTP': SCTP datachannel support. -// - 'RTP': RTP datachannel support. -webrtcDetectedDCSupport = null; - -// Set the settings for creating DataChannels, MediaStream for -// Cross-browser compability. -// - This is only for SCTP based support browsers. -// the 'urls' attribute. -checkMediaDataChannelSettings = - function (peerBrowserAgent, peerBrowserVersion, callback, constraints) { - if (typeof callback !== 'function') { - return; - } - var beOfferer = true; - var isLocalFirefox = webrtcDetectedBrowser === 'firefox'; - // Nightly version does not require MozDontOfferDataChannel for interop - var isLocalFirefoxInterop = webrtcDetectedType === 'moz' && webrtcDetectedVersion > 30; - var isPeerFirefox = peerBrowserAgent === 'firefox'; - var isPeerFirefoxInterop = peerBrowserAgent === 'firefox' && - ((peerBrowserVersion) ? (peerBrowserVersion > 30) : false); - - // Resends an updated version of constraints for MozDataChannel to work - // If other userAgent is firefox and user is firefox, remove MozDataChannel - if ((isLocalFirefox && isPeerFirefox) || (isLocalFirefoxInterop)) { - try { - delete constraints.mandatory.MozDontOfferDataChannel; - } catch (error) { - console.error('Failed deleting MozDontOfferDataChannel'); - console.error(error); - } - } else if ((isLocalFirefox && !isPeerFirefox)) { - constraints.mandatory.MozDontOfferDataChannel = true; - } - if (!isLocalFirefox) { - // temporary measure to remove Moz* constraints in non Firefox browsers - for (var prop in constraints.mandatory) { - if (constraints.mandatory.hasOwnProperty(prop)) { - if (prop.indexOf('Moz') !== -1) { - delete constraints.mandatory[prop]; - } - } - } - } - // Firefox (not interopable) cannot offer DataChannel as it will cause problems to the - // interopability of the media stream - if (isLocalFirefox && !isPeerFirefox && !isLocalFirefoxInterop) { - beOfferer = false; - } - callback(beOfferer, constraints); -}; - -// Handles the differences for all browsers ice connection state output. -// - Tested outcomes are: -// - Chrome (offerer) : 'checking' > 'completed' > 'completed' -// - Chrome (answerer) : 'checking' > 'connected' -// - Firefox (offerer) : 'checking' > 'connected' -// - Firefox (answerer): 'checking' > 'connected' -checkIceConnectionState = function (peerId, iceConnectionState, callback) { - if (typeof callback !== 'function') { - console.warn('No callback specified in checkIceConnectionState. Aborted.'); - return; - } - peerId = (peerId) ? peerId : 'peer'; - - if (!AdapterJS._iceConnectionFiredStates[peerId] || - iceConnectionState === AdapterJS._iceConnectionStates.disconnected || - iceConnectionState === AdapterJS._iceConnectionStates.failed || - iceConnectionState === AdapterJS._iceConnectionStates.closed) { - AdapterJS._iceConnectionFiredStates[peerId] = []; - } - iceConnectionState = AdapterJS._iceConnectionStates[iceConnectionState]; - if (AdapterJS._iceConnectionFiredStates[peerId].indexOf(iceConnectionState) < 0) { - AdapterJS._iceConnectionFiredStates[peerId].push(iceConnectionState); - if (iceConnectionState === AdapterJS._iceConnectionStates.connected) { - setTimeout(function () { - AdapterJS._iceConnectionFiredStates[peerId] - .push(AdapterJS._iceConnectionStates.done); - callback(AdapterJS._iceConnectionStates.done); - }, 1000); - } - callback(iceConnectionState); - } - return; -}; - -// Firefox: -// - Creates iceServer from the url for Firefox. -// - Create iceServer with stun url. -// - Create iceServer with turn url. -// - Ignore the transport parameter from TURN url for FF version <=27. -// - Return null for createIceServer if transport=tcp. -// - FF 27 and above supports transport parameters in TURN url, -// - So passing in the full url to create iceServer. -// Chrome: -// - Creates iceServer from the url for Chrome M33 and earlier. -// - Create iceServer with stun url. -// - Chrome M28 & above uses below TURN format. -// Plugin: -// - Creates Ice Server for Plugin Browsers -// - If Stun - Create iceServer with stun url. -// - Else - Create iceServer with turn url -// - This is a WebRTC Function -createIceServer = null; - -// Firefox: -// - Creates IceServers for Firefox -// - Use .url for FireFox. -// - Multiple Urls support -// Chrome: -// - Creates iceServers from the urls for Chrome M34 and above. -// - .urls is supported since Chrome M34. -// - Multiple Urls support -// Plugin: -// - Creates Ice Servers for Plugin Browsers -// - Multiple Urls support -// - This is a WebRTC Function -createIceServers = null; -//------------------------------------------------------------ - -//The RTCPeerConnection object. -RTCPeerConnection = null; - -// Creates RTCSessionDescription object for Plugin Browsers -RTCSessionDescription = (typeof RTCSessionDescription === 'function') ? - RTCSessionDescription : null; - -// Creates RTCIceCandidate object for Plugin Browsers -RTCIceCandidate = (typeof RTCIceCandidate === 'function') ? - RTCIceCandidate : null; - -// Get UserMedia (only difference is the prefix). -// Code from Adam Barth. -getUserMedia = null; - -// Attach a media stream to an element. -attachMediaStream = null; - -// Re-attach a media stream to an element. -reattachMediaStream = null; - - -// Detected browser agent name. Types are: -// - 'firefox': Firefox browser. -// - 'chrome': Chrome browser. -// - 'opera': Opera browser. -// - 'safari': Safari browser. -// - 'IE' - Internet Explorer browser. -webrtcDetectedBrowser = null; - -// Detected browser version. -webrtcDetectedVersion = null; - -// Check for browser types and react accordingly -if (navigator.mozGetUserMedia) { - webrtcDetectedBrowser = 'firefox'; - webrtcDetectedVersion = parseInt(navigator - .userAgent.match(/Firefox\/([0-9]+)\./)[1], 10); - webrtcDetectedType = 'moz'; - webrtcDetectedDCSupport = 'SCTP'; - - RTCPeerConnection = function (pcConfig, pcConstraints) { - AdapterJS.maybeFixConfiguration(pcConfig); - return new mozRTCPeerConnection(pcConfig, pcConstraints); - }; - - // The RTCSessionDescription object. - RTCSessionDescription = mozRTCSessionDescription; - window.RTCSessionDescription = RTCSessionDescription; - - // The RTCIceCandidate object. - RTCIceCandidate = mozRTCIceCandidate; - window.RTCIceCandidate = RTCIceCandidate; - - window.getUserMedia = navigator.mozGetUserMedia.bind(navigator); - navigator.getUserMedia = window.getUserMedia; - - // Shim for MediaStreamTrack.getSources. - MediaStreamTrack.getSources = function(successCb) { - setTimeout(function() { - var infos = [ - { kind: 'audio', id: 'default', label:'', facing:'' }, - { kind: 'video', id: 'default', label:'', facing:'' } - ]; - successCb(infos); - }, 0); - }; - - createIceServer = function (url, username, password) { - var iceServer = null; - var url_parts = url.split(':'); - if (url_parts[0].indexOf('stun') === 0) { - iceServer = { url : url }; - } else if (url_parts[0].indexOf('turn') === 0) { - if (webrtcDetectedVersion < 27) { - var turn_url_parts = url.split('?'); - if (turn_url_parts.length === 1 || - turn_url_parts[1].indexOf('transport=udp') === 0) { - iceServer = { - url : turn_url_parts[0], - credential : password, - username : username - }; - } - } else { - iceServer = { - url : url, - credential : password, - username : username - }; - } - } - return iceServer; - }; - - createIceServers = function (urls, username, password) { - var iceServers = []; - for (i = 0; i < urls.length; i++) { - var iceServer = createIceServer(urls[i], username, password); - if (iceServer !== null) { - iceServers.push(iceServer); - } - } - return iceServers; - }; - - attachMediaStream = function (element, stream) { - element.mozSrcObject = stream; - if (stream !== null) - element.play(); - - return element; - }; - - reattachMediaStream = function (to, from) { - to.mozSrcObject = from.mozSrcObject; - to.play(); - return to; - }; - - MediaStreamTrack.getSources = MediaStreamTrack.getSources || function (callback) { - if (!callback) { - throw new TypeError('Failed to execute \'getSources\' on \'MediaStreamTrack\'' + - ': 1 argument required, but only 0 present.'); - } - return callback([]); - }; - - // Fake get{Video,Audio}Tracks - if (!MediaStream.prototype.getVideoTracks) { - MediaStream.prototype.getVideoTracks = function () { - return []; - }; - } - if (!MediaStream.prototype.getAudioTracks) { - MediaStream.prototype.getAudioTracks = function () { - return []; - }; - } - - AdapterJS.maybeThroughWebRTCReady(); -} else if (navigator.webkitGetUserMedia) { - webrtcDetectedBrowser = 'chrome'; - webrtcDetectedType = 'webkit'; - webrtcDetectedVersion = parseInt(navigator - .userAgent.match(/Chrom(e|ium)\/([0-9]+)\./)[2], 10); - // check if browser is opera 20+ - var checkIfOpera = navigator.userAgent.match(/\bOPR\/(\d+)/); - if (checkIfOpera !== null) { - webrtcDetectedBrowser = 'opera'; - webrtcDetectedVersion = parseInt(checkIfOpera[1], 10); - } - // check browser datachannel support - if ((webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion >= 31) || - (webrtcDetectedBrowser === 'opera' && webrtcDetectedVersion >= 20)) { - webrtcDetectedDCSupport = 'SCTP'; - } else if (webrtcDetectedBrowser === 'chrome' && webrtcDetectedVersion < 30 && - webrtcDetectedVersion > 24) { - webrtcDetectedDCSupport = 'RTP'; - } else { - webrtcDetectedDCSupport = ''; - } - - createIceServer = function (url, username, password) { - var iceServer = null; - var url_parts = url.split(':'); - if (url_parts[0].indexOf('stun') === 0) { - iceServer = { 'url' : url }; - } else if (url_parts[0].indexOf('turn') === 0) { - iceServer = { - 'url' : url, - 'credential' : password, - 'username' : username - }; - } - return iceServer; - }; - - createIceServers = function (urls, username, password) { - var iceServers = []; - if (webrtcDetectedVersion >= 34) { - iceServers = { - 'urls' : urls, - 'credential' : password, - 'username' : username - }; - } else { - for (i = 0; i < urls.length; i++) { - var iceServer = createIceServer(urls[i], username, password); - if (iceServer !== null) { - iceServers.push(iceServer); - } - } - } - return iceServers; - }; - - RTCPeerConnection = function (pcConfig, pcConstraints) { - if (webrtcDetectedVersion < 34) { - AdapterJS.maybeFixConfiguration(pcConfig); - } - return new webkitRTCPeerConnection(pcConfig, pcConstraints); - }; - - window.getUserMedia = navigator.webkitGetUserMedia.bind(navigator); - navigator.getUserMedia = window.getUserMedia; - - attachMediaStream = function (element, stream) { - if (typeof element.srcObject !== 'undefined') { - element.srcObject = stream; - } else if (typeof element.mozSrcObject !== 'undefined') { - element.mozSrcObject = stream; - } else if (typeof element.src !== 'undefined') { - element.src = (stream === null ? '' : URL.createObjectURL(stream)); - } else { - console.log('Error attaching stream to element.'); - } - return element; - }; - - reattachMediaStream = function (to, from) { - to.src = from.src; - return to; - }; - - AdapterJS.maybeThroughWebRTCReady(); -} else { // TRY TO USE PLUGIN - // IE 9 is not offering an implementation of console.log until you open a console - if (typeof console !== 'object' || typeof console.log !== 'function') { - /* jshint -W020 */ - console = {} || console; - // Implemented based on console specs from MDN - // You may override these functions - console.log = function (arg) {}; - console.info = function (arg) {}; - console.error = function (arg) {}; - console.dir = function (arg) {}; - console.exception = function (arg) {}; - console.trace = function (arg) {}; - console.warn = function (arg) {}; - console.count = function (arg) {}; - console.debug = function (arg) {}; - console.count = function (arg) {}; - console.time = function (arg) {}; - console.timeEnd = function (arg) {}; - console.group = function (arg) {}; - console.groupCollapsed = function (arg) {}; - console.groupEnd = function (arg) {}; - /* jshint +W020 */ - } - webrtcDetectedType = 'plugin'; - webrtcDetectedDCSupport = 'plugin'; - AdapterJS.parseWebrtcDetectedBrowser(); - isIE = webrtcDetectedBrowser === 'IE'; - - /* jshint -W035 */ - AdapterJS.WebRTCPlugin.WaitForPluginReady = function() { - while (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { - /* empty because it needs to prevent the function from running. */ - } - }; - /* jshint +W035 */ - - AdapterJS.WebRTCPlugin.callWhenPluginReady = function (callback) { - if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { - // Call immediately if possible - // Once the plugin is set, the code will always take this path - callback(); - } else { - // otherwise start a 100ms interval - var checkPluginReadyState = setInterval(function () { - if (AdapterJS.WebRTCPlugin.pluginState === AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { - clearInterval(checkPluginReadyState); - callback(); - } - }, 100); - } - }; - - AdapterJS.WebRTCPlugin.setLogLevel = function(logLevel) { - AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { - AdapterJS.WebRTCPlugin.plugin.setLogLevel(logLevel); - }); - }; - - AdapterJS.WebRTCPlugin.injectPlugin = function () { - // only inject once the page is ready - if (document.readyState !== 'complete') { - return; - } - - // Prevent multiple injections - if (AdapterJS.WebRTCPlugin.pluginState !== AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING) { - return; - } - - AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTING; - - if (webrtcDetectedBrowser === 'IE' && webrtcDetectedVersion <= 10) { - var frag = document.createDocumentFragment(); - AdapterJS.WebRTCPlugin.plugin = document.createElement('div'); - AdapterJS.WebRTCPlugin.plugin.innerHTML = '' + - ' ' + - ' ' + - ' ' + - '' + - // uncomment to be able to use virtual cams - (AdapterJS.options.getAllCams ? '':'') + - - ''; - while (AdapterJS.WebRTCPlugin.plugin.firstChild) { - frag.appendChild(AdapterJS.WebRTCPlugin.plugin.firstChild); - } - document.body.appendChild(frag); - - // Need to re-fetch the plugin - AdapterJS.WebRTCPlugin.plugin = - document.getElementById(AdapterJS.WebRTCPlugin.pluginInfo.pluginId); - } else { - // Load Plugin - AdapterJS.WebRTCPlugin.plugin = document.createElement('object'); - AdapterJS.WebRTCPlugin.plugin.id = - AdapterJS.WebRTCPlugin.pluginInfo.pluginId; - // IE will only start the plugin if it's ACTUALLY visible - if (isIE) { - AdapterJS.WebRTCPlugin.plugin.width = '1px'; - AdapterJS.WebRTCPlugin.plugin.height = '1px'; - } else { // The size of the plugin on Safari should be 0x0px - // so that the autorisation prompt is at the top - AdapterJS.WebRTCPlugin.plugin.width = '0px'; - AdapterJS.WebRTCPlugin.plugin.height = '0px'; - } - AdapterJS.WebRTCPlugin.plugin.type = AdapterJS.WebRTCPlugin.pluginInfo.type; - AdapterJS.WebRTCPlugin.plugin.innerHTML = '' + - '' + - ' ' + - (AdapterJS.options.getAllCams ? '':'') + - ''; - document.body.appendChild(AdapterJS.WebRTCPlugin.plugin); - } - - - AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INJECTED; - }; - - AdapterJS.WebRTCPlugin.isPluginInstalled = - function (comName, plugName, installedCb, notInstalledCb) { - if (!isIE) { - var pluginArray = navigator.plugins; - for (var i = 0; i < pluginArray.length; i++) { - if (pluginArray[i].name.indexOf(plugName) >= 0) { - installedCb(); - return; - } - } - notInstalledCb(); - } else { - try { - var axo = new ActiveXObject(comName + '.' + plugName); - } catch (e) { - notInstalledCb(); - return; - } - installedCb(); - } - }; - - AdapterJS.WebRTCPlugin.defineWebRTCInterface = function () { - if (AdapterJS.WebRTCPlugin.pluginState === - AdapterJS.WebRTCPlugin.PLUGIN_STATES.READY) { - console.error("WebRTC interface has been defined already"); - return; - } - AdapterJS.WebRTCPlugin.pluginState = AdapterJS.WebRTCPlugin.PLUGIN_STATES.INITIALIZING; - - AdapterJS.isDefined = function (variable) { - return variable !== null && variable !== undefined; - }; - - createIceServer = function (url, username, password) { - var iceServer = null; - var url_parts = url.split(':'); - if (url_parts[0].indexOf('stun') === 0) { - iceServer = { - 'url' : url, - 'hasCredentials' : false - }; - } else if (url_parts[0].indexOf('turn') === 0) { - iceServer = { - 'url' : url, - 'hasCredentials' : true, - 'credential' : password, - 'username' : username - }; - } - return iceServer; - }; - - createIceServers = function (urls, username, password) { - var iceServers = []; - for (var i = 0; i < urls.length; ++i) { - iceServers.push(createIceServer(urls[i], username, password)); - } - return iceServers; - }; - - RTCSessionDescription = function (info) { - AdapterJS.WebRTCPlugin.WaitForPluginReady(); - return AdapterJS.WebRTCPlugin.plugin. - ConstructSessionDescription(info.type, info.sdp); - }; - - RTCPeerConnection = function (servers, constraints) { - var iceServers = null; - if (servers) { - iceServers = servers.iceServers; - for (var i = 0; i < iceServers.length; i++) { - if (iceServers[i].urls && !iceServers[i].url) { - iceServers[i].url = iceServers[i].urls; - } - iceServers[i].hasCredentials = AdapterJS. - isDefined(iceServers[i].username) && - AdapterJS.isDefined(iceServers[i].credential); - } - } - var mandatory = (constraints && constraints.mandatory) ? - constraints.mandatory : null; - var optional = (constraints && constraints.optional) ? - constraints.optional : null; - - AdapterJS.WebRTCPlugin.WaitForPluginReady(); - return AdapterJS.WebRTCPlugin.plugin. - PeerConnection(AdapterJS.WebRTCPlugin.pageId, - iceServers, mandatory, optional); - }; - - MediaStreamTrack = {}; - MediaStreamTrack.getSources = function (callback) { - AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { - AdapterJS.WebRTCPlugin.plugin.GetSources(callback); - }); - }; - - window.getUserMedia = function (constraints, successCallback, failureCallback) { - constraints.audio = constraints.audio || false; - constraints.video = constraints.video || false; - - AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { - AdapterJS.WebRTCPlugin.plugin. - getUserMedia(constraints, successCallback, failureCallback); - }); - }; - window.navigator.getUserMedia = window.getUserMedia; - - attachMediaStream = function (element, stream) { - if (!element || !element.parentNode) { - return; - } - - var streamId - if (stream === null) { - streamId = ''; - } - else { - stream.enableSoundTracks(true); - streamId = stream.id; - } - - if (element.nodeName.toLowerCase() !== 'audio') { - var elementId = element.id.length === 0 ? Math.random().toString(36).slice(2) : element.id; - if (!element.isWebRTCPlugin || !element.isWebRTCPlugin()) { - var frag = document.createDocumentFragment(); - var temp = document.createElement('div'); - var classHTML = ''; - if (element.className) { - classHTML = 'class="' + element.className + '" '; - } else if (element.attributes && element.attributes['class']) { - classHTML = 'class="' + element.attributes['class'].value + '" '; - } - - temp.innerHTML = '' + - ' ' + - ' ' + - ' ' + - ' ' + - ''; - while (temp.firstChild) { - frag.appendChild(temp.firstChild); - } - - var height = ''; - var width = ''; - if (element.getBoundingClientRect) { - var rectObject = element.getBoundingClientRect(); - width = rectObject.width + 'px'; - height = rectObject.height + 'px'; - } - else if (element.width) { - width = element.width; - height = element.height; - } else { - // TODO: What scenario could bring us here? - } - - element.parentNode.insertBefore(frag, element); - frag = document.getElementById(elementId); - frag.width = width; - frag.height = height; - element.parentNode.removeChild(element); - } else { - var children = element.children; - for (var i = 0; i !== children.length; ++i) { - if (children[i].name === 'streamId') { - children[i].value = streamId; - break; - } - } - element.setStreamId(streamId); - } - var newElement = document.getElementById(elementId); - newElement.onplaying = (element.onplaying) ? element.onplaying : function (arg) {}; - if (isIE) { // on IE the event needs to be plugged manually - newElement.attachEvent('onplaying', newElement.onplaying); - newElement.onclick = (element.onclick) ? element.onclick : function (arg) {}; - newElement._TemOnClick = function (id) { - var arg = { - srcElement : document.getElementById(id) - }; - newElement.onclick(arg); - }; - } - return newElement; - } else { - return element; - } - }; - - reattachMediaStream = function (to, from) { - var stream = null; - var children = from.children; - for (var i = 0; i !== children.length; ++i) { - if (children[i].name === 'streamId') { - AdapterJS.WebRTCPlugin.WaitForPluginReady(); - stream = AdapterJS.WebRTCPlugin.plugin - .getStreamWithId(AdapterJS.WebRTCPlugin.pageId, children[i].value); - break; - } - } - if (stream !== null) { - return attachMediaStream(to, stream); - } else { - console.log('Could not find the stream associated with this element'); - } - }; - - RTCIceCandidate = function (candidate) { - if (!candidate.sdpMid) { - candidate.sdpMid = ''; - } - - AdapterJS.WebRTCPlugin.WaitForPluginReady(); - return AdapterJS.WebRTCPlugin.plugin.ConstructIceCandidate( - candidate.sdpMid, candidate.sdpMLineIndex, candidate.candidate - ); - }; - - // inject plugin - AdapterJS.addEvent(document, 'readystatechange', AdapterJS.WebRTCPlugin.injectPlugin); - AdapterJS.WebRTCPlugin.injectPlugin(); - }; - - // This function will be called if the plugin is needed (browser different - // from Chrome or Firefox), but the plugin is not installed. - AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb = AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb || - function() { - AdapterJS.addEvent(document, - 'readystatechange', - AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv); - AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv(); - }; - - AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCbPriv = function () { - if (AdapterJS.options.hidePluginInstallPrompt) { - return; - } - - var downloadLink = AdapterJS.WebRTCPlugin.pluginInfo.downloadLink; - if(downloadLink) { // if download link - var popupString; - if (AdapterJS.WebRTCPlugin.pluginInfo.portalLink) { // is portal link - popupString = 'This website requires you to install the ' + - ' ' + AdapterJS.WebRTCPlugin.pluginInfo.companyName + - ' WebRTC Plugin' + - ' to work on this browser.'; - } else { // no portal link, just print a generic explanation - popupString = AdapterJS.TEXT.PLUGIN.REQUIRE_INSTALLATION; - } - - AdapterJS.renderNotificationBar(popupString, AdapterJS.TEXT.PLUGIN.BUTTON, downloadLink); - } else { // no download link, just print a generic explanation - AdapterJS.renderNotificationBar(AdapterJS.TEXT.PLUGIN.NOT_SUPPORTED); - } - }; - - // Try to detect the plugin and act accordingly - AdapterJS.WebRTCPlugin.isPluginInstalled( - AdapterJS.WebRTCPlugin.pluginInfo.prefix, - AdapterJS.WebRTCPlugin.pluginInfo.plugName, - AdapterJS.WebRTCPlugin.defineWebRTCInterface, - AdapterJS.WebRTCPlugin.pluginNeededButNotInstalledCb); -} - - - -(function () { - - 'use strict'; - - var baseGetUserMedia = null; - - AdapterJS.TEXT.EXTENSION = { - REQUIRE_INSTALLATION_FF: 'To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.', - REQUIRE_INSTALLATION_CHROME: 'To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.', - REQUIRE_REFRESH: 'Please refresh this page after the Skylink WebRTC tools extension has been installed.', - BUTTON_FF: 'Install Now', - BUTTON_CHROME: 'Go to Chrome Web Store' - }; - - var clone = function(obj) { - if (null == obj || "object" != typeof obj) return obj; - var copy = obj.constructor(); - for (var attr in obj) { - if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr]; - } - return copy; - }; - - if (window.navigator.mozGetUserMedia) { - baseGetUserMedia = window.navigator.getUserMedia; - - navigator.getUserMedia = function (constraints, successCb, failureCb) { - - if (constraints && constraints.video && !!constraints.video.mediaSource) { - // intercepting screensharing requests - - if (constraints.video.mediaSource !== 'screen' && constraints.video.mediaSource !== 'window') { - throw new Error('Only "screen" and "window" option is available as mediaSource'); - } - - var updatedConstraints = clone(constraints); - - //constraints.video.mediaSource = constraints.video.mediaSource; - updatedConstraints.video.mozMediaSource = updatedConstraints.video.mediaSource; - - // so generally, it requires for document.readyState to be completed before the getUserMedia could be invoked. - // strange but this works anyway - var checkIfReady = setInterval(function () { - if (document.readyState === 'complete') { - clearInterval(checkIfReady); - - baseGetUserMedia(updatedConstraints, successCb, function (error) { - if (error.name === 'PermissionDeniedError' && window.parent.location.protocol === 'https:') { - AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF, - AdapterJS.TEXT.EXTENSION.BUTTON_FF, - 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname, false, true); - //window.location.href = 'http://skylink.io/screensharing/ff_addon.php?domain=' + window.location.hostname; - } else { - failureCb(error); - } - }); - } - }, 1); - - } else { // regular GetUserMediaRequest - baseGetUserMedia(constraints, successCb, failureCb); - } - }; - - getUserMedia = navigator.getUserMedia; - - } else if (window.navigator.webkitGetUserMedia) { - baseGetUserMedia = window.navigator.getUserMedia; - - navigator.getUserMedia = function (constraints, successCb, failureCb) { - - if (constraints && constraints.video && !!constraints.video.mediaSource) { - if (window.webrtcDetectedBrowser !== 'chrome') { - throw new Error('Current browser does not support screensharing'); - } - - // would be fine since no methods - var updatedConstraints = clone(constraints); - - var chromeCallback = function(error, sourceId) { - if(!error) { - updatedConstraints.video.mandatory = updatedConstraints.video.mandatory || {}; - updatedConstraints.video.mandatory.chromeMediaSource = 'desktop'; - updatedConstraints.video.mandatory.maxWidth = window.screen.width > 1920 ? window.screen.width : 1920; - updatedConstraints.video.mandatory.maxHeight = window.screen.height > 1080 ? window.screen.height : 1080; - - if (sourceId) { - updatedConstraints.video.mandatory.chromeMediaSourceId = sourceId; - } - - delete updatedConstraints.video.mediaSource; - - baseGetUserMedia(updatedConstraints, successCb, failureCb); - - } else { - if (error === 'permission-denied') { - throw new Error('Permission denied for screen retrieval'); - } else { - throw new Error('Failed retrieving selected screen'); - } - } - }; - - var onIFrameCallback = function (event) { - if (!event.data) { - return; - } - - if (event.data.chromeMediaSourceId) { - if (event.data.chromeMediaSourceId === 'PermissionDeniedError') { - chromeCallback('permission-denied'); - } else { - chromeCallback(null, event.data.chromeMediaSourceId); - } - } - - if (event.data.chromeExtensionStatus) { - if (event.data.chromeExtensionStatus === 'not-installed') { - AdapterJS.renderNotificationBar(AdapterJS.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME, - AdapterJS.TEXT.EXTENSION.BUTTON_CHROME, - event.data.data, true, true); - } else { - chromeCallback(event.data.chromeExtensionStatus, null); - } - } - - // this event listener is no more needed - window.removeEventListener('message', onIFrameCallback); - }; - - window.addEventListener('message', onIFrameCallback); - - postFrameMessage({ - captureSourceId: true - }); - - } else { - baseGetUserMedia(constraints, successCb, failureCb); - } - }; - - getUserMedia = navigator.getUserMedia; - - } else { - baseGetUserMedia = window.navigator.getUserMedia; - - navigator.getUserMedia = function (constraints, successCb, failureCb) { - if (constraints && constraints.video && !!constraints.video.mediaSource) { - // would be fine since no methods - var updatedConstraints = clone(constraints); - - // wait for plugin to be ready - AdapterJS.WebRTCPlugin.callWhenPluginReady(function() { - // check if screensharing feature is available - if (!!AdapterJS.WebRTCPlugin.plugin.HasScreensharingFeature && - !!AdapterJS.WebRTCPlugin.plugin.isScreensharingAvailable) { - - - // set the constraints - updatedConstraints.video.optional = updatedConstraints.video.optional || []; - updatedConstraints.video.optional.push({ - sourceId: AdapterJS.WebRTCPlugin.plugin.screensharingKey || 'Screensharing' - }); - - delete updatedConstraints.video.mediaSource; - } else { - throw new Error('Your WebRTC plugin does not support screensharing'); - } - baseGetUserMedia(updatedConstraints, successCb, failureCb); - }); - } else { - baseGetUserMedia(constraints, successCb, failureCb); - } - }; - - getUserMedia = window.navigator.getUserMedia; - } - - if (window.webrtcDetectedBrowser === 'chrome') { - var iframe = document.createElement('iframe'); - - iframe.onload = function() { - iframe.isLoaded = true; - }; - - iframe.src = 'https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html'; - //'https://temasys-cdn.s3.amazonaws.com/skylink/extensions/detection-script-dev/detectRTC.html'; - iframe.style.display = 'none'; - - (document.body || document.documentElement).appendChild(iframe); - - var postFrameMessage = function (object) { - object = object || {}; - - if (!iframe.isLoaded) { - setTimeout(function () { - iframe.contentWindow.postMessage(object, '*'); - }, 100); - return; - } - - iframe.contentWindow.postMessage(object, '*'); - }; - } -})(); -},{}],13:[function(require,module,exports){ -/* global Strophe, APP, $, config, interfaceConfig, toastr */ -var UI = {}; - -var VideoLayout = require("./videolayout/VideoLayout.js"); -var AudioLevels = require("./audio_levels/AudioLevels.js"); -var Prezi = require("./prezi/Prezi.js"); -var Etherpad = require("./etherpad/Etherpad.js"); -var Chat = require("./side_pannels/chat/Chat.js"); -var Toolbar = require("./toolbars/Toolbar"); -var ToolbarToggler = require("./toolbars/ToolbarToggler"); -var BottomToolbar = require("./toolbars/BottomToolbar"); -var ContactList = require("./side_pannels/contactlist/ContactList"); -var Avatar = require("./avatar/Avatar"); -var EventEmitter = require("events"); -var SettingsMenu = require("./side_pannels/settings/SettingsMenu"); -var Settings = require("./../settings/Settings"); -var PanelToggler = require("./side_pannels/SidePanelToggler"); -var RoomNameGenerator = require("./welcome_page/RoomnameGenerator"); -UI.messageHandler = require("./util/MessageHandler"); -var messageHandler = UI.messageHandler; -var Authentication = require("./authentication/Authentication"); -var UIUtil = require("./util/UIUtil"); -var NicknameHandler = require("./util/NicknameHandler"); -var JitsiPopover = require("./util/JitsiPopover"); -var CQEvents = require("../../service/connectionquality/CQEvents"); -var DesktopSharingEventTypes - = require("../../service/desktopsharing/DesktopSharingEventTypes"); -var RTCEvents = require("../../service/RTC/RTCEvents"); -var RTCBrowserType = require("../RTC/RTCBrowserType"); -var StreamEventTypes = require("../../service/RTC/StreamEventTypes"); -var XMPPEvents = require("../../service/xmpp/XMPPEvents"); -var UIEvents = require("../../service/UI/UIEvents"); -var MemberEvents = require("../../service/members/Events"); - -var eventEmitter = new EventEmitter(); -var roomName = null; - - -function promptDisplayName() { - var message = '

    '; - message += APP.translation.translateString( - "dialog.displayNameRequired"); - message += '

    ' + - ''; - - var buttonTxt - = APP.translation.generateTranslationHTML("dialog.Ok"); - var buttons = []; - buttons.push({title: buttonTxt, value: "ok"}); - - messageHandler.openDialog(null, message, - true, - buttons, - function (e, v, m, f) { - if (v == "ok") { - var displayName = f.displayName; - if (displayName) { - VideoLayout.inputDisplayNameHandler(displayName); - return true; - } - } - e.preventDefault(); - }, - function () { - var form = $.prompt.getPrompt(); - var input = form.find("input[name='displayName']"); - input.focus(); - var button = form.find("button"); - button.attr("disabled", "disabled"); - input.keyup(function () { - if(!input.val()) - button.attr("disabled", "disabled"); - else - button.removeAttr("disabled"); - }); - } - ); -} - -function notifyForInitialMute() { - messageHandler.notify(null, "notify.mutedTitle", "connected", - "notify.muted", null, {timeOut: 120000}); -} - -function setupPrezi() { - $("#reloadPresentationLink").click(function() { - Prezi.reloadPresentation(); - }); -} - -function setupChat() { - Chat.init(); - $("#toggle_smileys").click(function() { - Chat.toggleSmileys(); - }); -} - -function setupToolbars() { - Toolbar.init(UI); - Toolbar.setupButtonsFromConfig(); - BottomToolbar.init(); -} - -function streamHandler(stream, isMuted) { - switch (stream.type) { - case "audio": - VideoLayout.changeLocalAudio(stream, isMuted); - break; - case "video": - VideoLayout.changeLocalVideo(stream, isMuted); - break; - case "stream": - VideoLayout.changeLocalStream(stream, isMuted); - break; - } -} - -function onXmppConnectionFailed(stropheErrorMsg) { - - var title = APP.translation.generateTranslationHTML( - "dialog.error"); - - var message; - if (stropheErrorMsg) { - message = APP.translation.generateTranslationHTML( - "dialog.connectErrorWithMsg", {msg: stropheErrorMsg}); - } else { - message = APP.translation.generateTranslationHTML( - "dialog.connectError"); - } - - messageHandler.openDialog( - title, message, true, {}, function (e, v, m, f) { return false; }); -} - -function onDisposeConference(unload) { - Toolbar.showAuthenticateButton(false); -} - -function onDisplayNameChanged(jid, displayName) { - ContactList.onDisplayNameChange(jid, displayName); - SettingsMenu.onDisplayNameChange(jid, displayName); - VideoLayout.onDisplayNameChanged(jid, displayName); -} - -function registerListeners() { - APP.RTC.addStreamListener(streamHandler, - StreamEventTypes.EVENT_TYPE_LOCAL_CREATED); - APP.RTC.addStreamListener(streamHandler, - StreamEventTypes.EVENT_TYPE_LOCAL_CHANGED); - APP.RTC.addStreamListener(function (stream) { - VideoLayout.onRemoteStreamAdded(stream); - }, StreamEventTypes.EVENT_TYPE_REMOTE_CREATED); - APP.RTC.addListener(RTCEvents.LASTN_CHANGED, onLastNChanged); - APP.RTC.addListener(RTCEvents.DOMINANTSPEAKER_CHANGED, - function (resourceJid) { - VideoLayout.onDominantSpeakerChanged(resourceJid); - }); - APP.RTC.addListener(RTCEvents.LASTN_ENDPOINT_CHANGED, - function (lastNEndpoints, endpointsEnteringLastN, stream) { - VideoLayout.onLastNEndpointsChanged(lastNEndpoints, - endpointsEnteringLastN, stream); - }); - APP.RTC.addListener(RTCEvents.AVAILABLE_DEVICES_CHANGED, - function (devices) { - VideoLayout.setDeviceAvailabilityIcons(null, devices); - }); - APP.RTC.addListener(RTCEvents.VIDEO_MUTE, UI.setVideoMuteButtonsState); - APP.RTC.addListener(RTCEvents.DATA_CHANNEL_OPEN, function () { - // when the data channel becomes available, tell the bridge about video - // selections so that it can do adaptive simulcast, - // we want the notification to trigger even if userJid is undefined, - // or null. - var userResource = APP.UI.getLargeVideoResource(); - eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, userResource); - }); - APP.statistics.addAudioLevelListener(function(jid, audioLevel) { - var resourceJid; - if(jid === APP.statistics.LOCAL_JID) { - resourceJid = AudioLevels.LOCAL_LEVEL; - if(APP.RTC.localAudio.isMuted()) { - audioLevel = 0; - } - } else { - resourceJid = Strophe.getResourceFromJid(jid); - } - - AudioLevels.updateAudioLevel(resourceJid, audioLevel, - UI.getLargeVideoResource()); - }); - APP.desktopsharing.addListener(function () { - ToolbarToggler.showDesktopSharingButton(); - }, DesktopSharingEventTypes.INIT); - APP.desktopsharing.addListener( - Toolbar.changeDesktopSharingButtonState, - DesktopSharingEventTypes.SWITCHING_DONE); - APP.connectionquality.addListener(CQEvents.LOCALSTATS_UPDATED, - VideoLayout.updateLocalConnectionStats); - APP.connectionquality.addListener(CQEvents.REMOTESTATS_UPDATED, - VideoLayout.updateConnectionStats); - APP.connectionquality.addListener(CQEvents.STOP, - VideoLayout.onStatsStop); - APP.xmpp.addListener(XMPPEvents.CONNECTION_FAILED, onXmppConnectionFailed); - APP.xmpp.addListener(XMPPEvents.DISPOSE_CONFERENCE, onDisposeConference); - APP.xmpp.addListener(XMPPEvents.GRACEFUL_SHUTDOWN, function () { - messageHandler.openMessageDialog( - 'dialog.serviceUnavailable', - 'dialog.gracefulShutdown' - ); - }); - APP.xmpp.addListener(XMPPEvents.RESERVATION_ERROR, function (code, msg) { - var title = APP.translation.generateTranslationHTML( - "dialog.reservationError"); - var message = APP.translation.generateTranslationHTML( - "dialog.reservationErrorMsg", {code: code, msg: msg}); - messageHandler.openDialog( - title, - message, - true, {}, - function (event, value, message, formVals) { - return false; - } - ); - }); - APP.xmpp.addListener(XMPPEvents.KICKED, function () { - messageHandler.openMessageDialog("dialog.sessTerminated", - "dialog.kickMessage"); - }); - APP.xmpp.addListener(XMPPEvents.MUC_DESTROYED, function (reason) { - //FIXME: use Session Terminated from translation, but - // 'reason' text comes from XMPP packet and is not translated - var title = APP.translation.generateTranslationHTML("dialog.sessTerminated"); - messageHandler.openDialog( - title, reason, true, {}, - function (event, value, message, formVals) { - return false; - } - ); - }); - APP.xmpp.addListener(XMPPEvents.BRIDGE_DOWN, function () { - messageHandler.showError("dialog.error", - "dialog.bridgeUnavailable"); - }); - APP.xmpp.addListener(XMPPEvents.USER_ID_CHANGED, function (from, id) { - Avatar.setUserAvatar(from, id); - }); - APP.xmpp.addListener(XMPPEvents.DISPLAY_NAME_CHANGED, onDisplayNameChanged); - APP.xmpp.addListener(XMPPEvents.MUC_JOINED, onMucJoined); - APP.xmpp.addListener(XMPPEvents.LOCAL_ROLE_CHANGED, onLocalRoleChanged); - APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_JOINED, onMucMemberJoined); - APP.xmpp.addListener(XMPPEvents.MUC_ROLE_CHANGED, onMucRoleChanged); - APP.xmpp.addListener(XMPPEvents.PRESENCE_STATUS, onMucPresenceStatus); - APP.xmpp.addListener(XMPPEvents.SUBJECT_CHANGED, chatSetSubject); - APP.xmpp.addListener(XMPPEvents.MUC_MEMBER_LEFT, onMucMemberLeft); - APP.xmpp.addListener(XMPPEvents.PASSWORD_REQUIRED, onPasswordRequired); - APP.xmpp.addListener(XMPPEvents.ETHERPAD, initEtherpad); - APP.xmpp.addListener(XMPPEvents.AUTHENTICATION_REQUIRED, - onAuthenticationRequired); - APP.xmpp.addListener(XMPPEvents.VIDEO_TYPE, onPeerVideoTypeChanged); - APP.xmpp.addListener(XMPPEvents.DEVICE_AVAILABLE, - function (resource, devices) { - VideoLayout.setDeviceAvailabilityIcons(resource, devices); - }); - - APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED, VideoLayout.onAudioMute); - APP.xmpp.addListener(XMPPEvents.VIDEO_MUTED, VideoLayout.onVideoMute); - APP.xmpp.addListener(XMPPEvents.AUDIO_MUTED_BY_FOCUS, function (doMuteAudio) { - UI.setAudioMuted(doMuteAudio); - }); - APP.members.addListener(MemberEvents.DTMF_SUPPORT_CHANGED, - onDtmfSupportChanged); - APP.xmpp.addListener(XMPPEvents.START_MUTED_SETTING_CHANGED, function (audio, video) { - SettingsMenu.setStartMuted(audio, video); - }); - APP.xmpp.addListener(XMPPEvents.START_MUTED_FROM_FOCUS, function (audio, video) { - UI.setInitialMuteFromFocus(audio, video); - }); - - APP.xmpp.addListener(XMPPEvents.JINGLE_FATAL_ERROR, function (session, error) { - UI.messageHandler.showError("dialog.sorry", - "dialog.internalError"); - }); - - APP.xmpp.addListener(XMPPEvents.SET_LOCAL_DESCRIPTION_ERROR, function () { - messageHandler.showError("dialog.error", - "dialog.SLDFailure"); - }); - APP.xmpp.addListener(XMPPEvents.SET_REMOTE_DESCRIPTION_ERROR, function () { - messageHandler.showError("dialog.error", - "dialog.SRDFailure"); - }); - APP.xmpp.addListener(XMPPEvents.CREATE_ANSWER_ERROR, function () { - messageHandler.showError(); - }); - APP.xmpp.addListener(XMPPEvents.PROMPT_FOR_LOGIN, function () { - // FIXME: re-use LoginDialog which supports retries - UI.showLoginPopup(connect); - }); - - APP.xmpp.addListener(XMPPEvents.FOCUS_DISCONNECTED, function (focusComponent, retrySec) { - UI.messageHandler.notify( - null, "notify.focus", - 'disconnected', "notify.focusFail", - {component: focusComponent, ms: retrySec}); - }); - - APP.xmpp.addListener(XMPPEvents.ROOM_JOIN_ERROR, function (pres) { - UI.messageHandler.openReportDialog(null, - "dialog.joinError", pres); - }); - APP.xmpp.addListener(XMPPEvents.ROOM_CONNECT_ERROR, function (pres) { - UI.messageHandler.openReportDialog(null, - "dialog.connectError", pres); - }); - - APP.xmpp.addListener(XMPPEvents.READY_TO_JOIN, function () { - var roomName = UI.generateRoomName(); - APP.xmpp.allocateConferenceFocus(roomName, UI.checkForNicknameAndJoin); - }); - - //NicknameHandler emits this event - UI.addListener(UIEvents.NICKNAME_CHANGED, function (nickname) { - APP.xmpp.addToPresence("displayName", nickname); - }); - - UI.addListener(UIEvents.LARGEVIDEO_INIT, function () { - AudioLevels.init(); - }); - - if (!interfaceConfig.filmStripOnly) { - APP.xmpp.addListener(XMPPEvents.MESSAGE_RECEIVED, updateChatConversation); - APP.xmpp.addListener(XMPPEvents.CHAT_ERROR_RECEIVED, chatAddError); - // Listens for video interruption events. - APP.xmpp.addListener(XMPPEvents.CONNECTION_INTERRUPTED, VideoLayout.onVideoInterrupted); - // Listens for video restores events. - APP.xmpp.addListener(XMPPEvents.CONNECTION_RESTORED, VideoLayout.onVideoRestored); - } -} - - -/** - * Mutes/unmutes the local video. - * - * @param mute true to mute the local video; otherwise, false - * @param options an object which specifies optional arguments such as the - * boolean key byUser with default value true which - * specifies whether the method was initiated in response to a user command (in - * contrast to an automatic decision taken by the application logic) - */ -function setVideoMute(mute, options) { - APP.RTC.setVideoMute(mute, - UI.setVideoMuteButtonsState, - options); -} - -function onResize() { - Chat.resizeChat(); - VideoLayout.resizeLargeVideoContainer(); -} - -function bindEvents() { - /** - * Resizes and repositions videos in full screen mode. - */ - $(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange', - onResize); - - $(window).resize(onResize); -} - -UI.start = function (init) { - document.title = interfaceConfig.APP_NAME; - var setupWelcomePage = null; - if(config.enableWelcomePage && window.location.pathname == "/" && - (!window.localStorage.welcomePageDisabled || - window.localStorage.welcomePageDisabled == "false")) { - $("#videoconference_page").hide(); - if (!setupWelcomePage) - setupWelcomePage = require("./welcome_page/WelcomePage"); - setupWelcomePage(); - - return; - } - - $("#welcome_page").hide(); - - // Set the defaults for prompt dialogs. - $.prompt.setDefaults({persistent: false}); - - - registerListeners(); - - VideoLayout.init(eventEmitter); - NicknameHandler.init(eventEmitter); - - bindEvents(); - setupPrezi(); - if (!interfaceConfig.filmStripOnly) { - $("#videospace").mousemove(function () { - return ToolbarToggler.showToolbar(); - }); - setupToolbars(); - setupChat(); - // Display notice message at the top of the toolbar - if (config.noticeMessage) { - $('#noticeText').text(config.noticeMessage); - $('#notice').css({display: 'block'}); - } - $("#downloadlog").click(function (event) { - dump(event.target); - }); - } - else - { - $("#header").css("display", "none"); - $("#bottomToolbar").css("display", "none"); - $("#downloadlog").css("display", "none"); - $("#remoteVideos").css("padding", "0px 0px 18px 0px"); - $("#remoteVideos").css("right", "0px"); - messageHandler.disableNotifications(); - $('body').popover("disable"); -// $("[data-toggle=popover]").popover("disable"); - JitsiPopover.enabled = false; - } - - document.title = interfaceConfig.APP_NAME; - - - - - - if(config.requireDisplayName) { - var currentSettings = Settings.getSettings(); - if (!currentSettings.displayName) { - promptDisplayName(); - } - } - - init(); - - if (!interfaceConfig.filmStripOnly) { - toastr.options = { - "closeButton": true, - "debug": false, - "positionClass": "notification-bottom-right", - "onclick": null, - "showDuration": "300", - "hideDuration": "1000", - "timeOut": "2000", - "extendedTimeOut": "1000", - "showEasing": "swing", - "hideEasing": "linear", - "showMethod": "fadeIn", - "hideMethod": "fadeOut", - "reposition": function () { - if (PanelToggler.isVisible()) { - $("#toast-container").addClass("notification-bottom-right-center"); - } else { - $("#toast-container").removeClass("notification-bottom-right-center"); - } - }, - "newestOnTop": false - }; - - - SettingsMenu.init(); - } - -}; - -function chatAddError(errorMessage, originalText) { - return Chat.chatAddError(errorMessage, originalText); -} - -function chatSetSubject(text) { - return Chat.chatSetSubject(text); -} - -function updateChatConversation(from, displayName, message, myjid, stamp) { - return Chat.updateChatConversation(from, displayName, message, myjid, stamp); -} - -function onMucJoined(jid, info) { - Toolbar.updateRoomUrl(window.location.href); - var meHTML = APP.translation.generateTranslationHTML("me"); - $("#localNick").html(Strophe.getResourceFromJid(jid) + " (" + meHTML + ")"); - - var settings = Settings.getSettings(); - - // Make sure we configure our avatar id, before creating avatar for us - Avatar.setUserAvatar(jid, settings.email || settings.uid); - - // Add myself to the contact list. - ContactList.addContact(jid); - - // Once we've joined the muc show the toolbar - ToolbarToggler.showToolbar(); - - var displayName = - config.displayJids ? Strophe.getResourceFromJid(jid) : info.displayName; - - if (displayName) - onDisplayNameChanged('localVideoContainer', displayName); - - - VideoLayout.mucJoined(); -} - -function initEtherpad(name) { - Etherpad.init(name); -} - -function onMucMemberLeft(jid) { - console.log('left.muc', jid); - var displayName = $('#participant_' + Strophe.getResourceFromJid(jid) + - '>.displayname').html(); - messageHandler.notify(displayName,'notify.somebody', - 'disconnected', - 'notify.disconnected'); - if (!config.startAudioMuted || - config.startAudioMuted > APP.members.size()) { - UIUtil.playSoundNotification('userLeft'); - } - - ContactList.removeContact(jid); - - VideoLayout.participantLeft(jid); -} - -function onLocalRoleChanged(jid, info, pres, isModerator) { - console.info("My role changed, new role: " + info.role); - onModeratorStatusChanged(isModerator); - VideoLayout.showModeratorIndicator(); - SettingsMenu.onRoleChanged(); - - if (isModerator) { - Authentication.closeAuthenticationWindow(); - messageHandler.notify(null, "notify.me", - 'connected', "notify.moderator"); - - Toolbar.checkAutoRecord(); - } -} - -function onModeratorStatusChanged(isModerator) { - Toolbar.showSipCallButton(isModerator); - Toolbar.showRecordingButton( - isModerator); //&& - // FIXME: - // Recording visible if - // there are at least 2(+ 1 focus) participants - //Object.keys(connection.emuc.members).length >= 3); -} - -function onPasswordRequired(callback) { - // password is required - Toolbar.lockLockButton(); - var message = '

    '; - message += APP.translation.translateString( - "dialog.passwordRequired"); - message += '

    ' + - ''; - - messageHandler.openTwoButtonDialog(null, null, null, message, - true, - "dialog.Ok", - function (e, v, m, f) {}, - null, - function (e, v, m, f) { - if (v) { - var lockKey = f.lockKey; - if (lockKey) { - Toolbar.setSharedKey(lockKey); - callback(lockKey); - } - } - }, - ':input:first' - ); -} - -/** - * The dialpad button is shown iff there is at least one member that supports - * DTMF (e.g. jigasi). - */ -function onDtmfSupportChanged(dtmfSupport) { - //TODO: enable when the UI is ready - //Toolbar.showDialPadButton(dtmfSupport); -} - -function onMucMemberJoined(jid, id, displayName) { - messageHandler.notify(displayName,'notify.somebody', - 'connected', - 'notify.connected'); - - if (!config.startAudioMuted || - config.startAudioMuted > APP.members.size()) - UIUtil.playSoundNotification('userJoined'); - - // Configure avatar - Avatar.setUserAvatar(jid, id); - - // Add Peer's container - VideoLayout.ensurePeerContainerExists(jid); -} - -function onMucPresenceStatus(jid, info) { - VideoLayout.setPresenceStatus(Strophe.getResourceFromJid(jid), info.status); -} - -function onPeerVideoTypeChanged(resourceJid, newVideoType) { - VideoLayout.onVideoTypeChanged(resourceJid, newVideoType); -} - -function onMucRoleChanged(role, displayName) { - VideoLayout.showModeratorIndicator(); - - if (role === 'moderator') { - var messageKey, messageOptions = {}; - if (!displayName) { - messageKey = "notify.grantedToUnknown"; - } - else { - messageKey = "notify.grantedTo"; - messageOptions = {to: displayName}; - } - messageHandler.notify( - displayName,'notify.somebody', - 'connected', messageKey, - messageOptions); - } -} - -function onAuthenticationRequired(intervalCallback) { - Authentication.openAuthenticationDialog( - roomName, intervalCallback, function () { - Toolbar.authenticateClicked(); - }); -} - - -function onLastNChanged(oldValue, newValue) { - if (config.muteLocalVideoIfNotInLastN) { - setVideoMute(!newValue, { 'byUser': false }); - } -} - - -UI.toggleSmileys = function () { - Chat.toggleSmileys(); -}; - -UI.getSettings = function () { - return Settings.getSettings(); -}; - -UI.toggleFilmStrip = function () { - return BottomToolbar.toggleFilmStrip(); -}; - -UI.toggleChat = function () { - return BottomToolbar.toggleChat(); -}; - -UI.toggleContactList = function () { - return BottomToolbar.toggleContactList(); -}; - -UI.inputDisplayNameHandler = function (value) { - VideoLayout.inputDisplayNameHandler(value); -}; - -UI.getLargeVideoResource = function () { - return VideoLayout.getLargeVideoResource(); -}; - -UI.generateRoomName = function() { - if(roomName) - return roomName; - var roomnode = null; - var path = window.location.pathname; - - // determinde the room node from the url - // TODO: just the roomnode or the whole bare jid? - if (config.getroomnode && typeof config.getroomnode === 'function') { - // custom function might be responsible for doing the pushstate - roomnode = config.getroomnode(path); - } else { - /* fall back to default strategy - * this is making assumptions about how the URL->room mapping happens. - * It currently assumes deployment at root, with a rewrite like the - * following one (for nginx): - location ~ ^/([a-zA-Z0-9]+)$ { - rewrite ^/(.*)$ / break; - } - */ - if (path.length > 1) { - roomnode = path.substr(1).toLowerCase(); - } else { - var word = RoomNameGenerator.generateRoomWithoutSeparator(); - roomnode = word.toLowerCase(); - - window.history.pushState('VideoChat', - 'Room: ' + word, window.location.pathname + word); - } - } - - roomName = roomnode + '@' + config.hosts.muc; - return roomName; -}; - - -UI.connectionIndicatorShowMore = function(jid) { - return VideoLayout.showMore(jid); -}; - -UI.showLoginPopup = function(callback) { - console.log('password is required'); - var message = '

    '; - message += APP.translation.translateString( - "dialog.passwordRequired"); - message += '

    ' + - '' + - ''; - UI.messageHandler.openTwoButtonDialog(null, null, null, message, - true, - "dialog.Ok", - function (e, v, m, f) { - if (v) { - if (f.username !== null && f.password != null) { - callback(f.username, f.password); - } - } - }, - null, null, ':input:first' - - ); -}; - -UI.checkForNicknameAndJoin = function () { - - Authentication.closeAuthenticationDialog(); - Authentication.stopInterval(); - - var nick = null; - if (config.useNicks) { - nick = window.prompt('Your nickname (optional)'); - } - APP.xmpp.joinRoom(roomName, config.useNicks, nick); -}; - - -function dump(elem, filename) { - elem = elem.parentNode; - elem.download = filename || 'meetlog.json'; - elem.href = 'data:application/json;charset=utf-8,\n'; - var data = APP.xmpp.getJingleLog(); - var metadata = {}; - metadata.time = new Date(); - metadata.url = window.location.href; - metadata.ua = navigator.userAgent; - var log = APP.xmpp.getXmppLog(); - if (log) { - metadata.xmpp = log; - } - data.metadata = metadata; - elem.href += encodeURIComponent(JSON.stringify(data, null, ' ')); - return false; -} - -UI.getRoomName = function () { - return roomName; -}; - -UI.setInitialMuteFromFocus = function (muteAudio, muteVideo) { - if (muteAudio || muteVideo) - notifyForInitialMute(); - if (muteAudio) - UI.setAudioMuted(true); - if (muteVideo) - UI.setVideoMute(true); -}; - -/** - * Mutes/unmutes the local video. - */ -UI.toggleVideo = function () { - setVideoMute(!APP.RTC.localVideo.isMuted()); -}; - -/** - * Mutes / unmutes audio for the local participant. - */ -UI.toggleAudio = function() { - UI.setAudioMuted(!APP.RTC.localAudio.isMuted()); -}; - -/** - * Sets muted audio state for the local participant. - */ -UI.setAudioMuted = function (mute, earlyMute) { - var audioMute = null; - if (earlyMute) - audioMute = function (mute, cb) { - return APP.xmpp.sendAudioInfoPresence(mute, cb); - }; - else - audioMute = function (mute, cb) { - return APP.xmpp.setAudioMute(mute, cb); - }; - if (!audioMute(mute, function () { - VideoLayout.showLocalAudioIndicator(mute); - - UIUtil.buttonClick("#toolbar_button_mute", "icon-microphone icon-mic-disabled"); - })) { - // We still click the button. - UIUtil.buttonClick("#toolbar_button_mute", "icon-microphone icon-mic-disabled"); - return; - } -}; - -UI.addListener = function (type, listener) { - eventEmitter.on(type, listener); -}; - -UI.clickOnVideo = function (videoNumber) { - var remoteVideos = $(".videocontainer:not(#mixedstream)"); - if (remoteVideos.length > videoNumber) { - remoteVideos[videoNumber].click(); - } -}; - -//Used by torture -UI.showToolbar = function () { - return ToolbarToggler.showToolbar(); -}; - -//Used by torture -UI.dockToolbar = function (isDock) { - return ToolbarToggler.dockToolbar(isDock); -}; - -UI.setVideoMuteButtonsState = function (mute) { - var video = $('#toolbar_button_camera'); - var communicativeClass = "icon-camera"; - var muteClass = "icon-camera icon-camera-disabled"; - - if (mute) { - video.removeClass(communicativeClass); - video.addClass(muteClass); - } else { - video.removeClass(muteClass); - video.addClass(communicativeClass); - } -}; - -UI.userAvatarChanged = function (resourceJid, thumbUrl, contactListUrl) { - VideoLayout.userAvatarChanged(resourceJid, thumbUrl); - ContactList.userAvatarChanged(resourceJid, contactListUrl); - if(resourceJid === APP.xmpp.myResource()) - SettingsMenu.changeAvatar(thumbUrl); -}; - -UI.setVideoMute = setVideoMute; - -module.exports = UI; - - -},{"../../service/RTC/RTCEvents":157,"../../service/RTC/StreamEventTypes":159,"../../service/UI/UIEvents":160,"../../service/connectionquality/CQEvents":162,"../../service/desktopsharing/DesktopSharingEventTypes":163,"../../service/members/Events":164,"../../service/xmpp/XMPPEvents":166,"../RTC/RTCBrowserType":10,"./../settings/Settings":49,"./audio_levels/AudioLevels.js":14,"./authentication/Authentication":16,"./avatar/Avatar":18,"./etherpad/Etherpad.js":19,"./prezi/Prezi.js":20,"./side_pannels/SidePanelToggler":22,"./side_pannels/chat/Chat.js":23,"./side_pannels/contactlist/ContactList":27,"./side_pannels/settings/SettingsMenu":28,"./toolbars/BottomToolbar":29,"./toolbars/Toolbar":30,"./toolbars/ToolbarToggler":31,"./util/JitsiPopover":32,"./util/MessageHandler":33,"./util/NicknameHandler":34,"./util/UIUtil":35,"./videolayout/VideoLayout.js":41,"./welcome_page/RoomnameGenerator":42,"./welcome_page/WelcomePage":43,"events":1}],14:[function(require,module,exports){ -/* global APP, interfaceConfig, $, Strophe */ -var CanvasUtil = require("./CanvasUtils"); - -var ASDrawContext = null; - -function initActiveSpeakerAudioLevels() { - var ASRadius = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE / 2; - var ASCenter = (interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE + ASRadius) / 2; - - // Draw a circle. - ASDrawContext.arc(ASCenter, ASCenter, ASRadius, 0, 2 * Math.PI); - - // Add a shadow around the circle - ASDrawContext.shadowColor = interfaceConfig.SHADOW_COLOR; - ASDrawContext.shadowOffsetX = 0; - ASDrawContext.shadowOffsetY = 0; -} - -/** - * The audio Levels plugin. - */ -var AudioLevels = (function(my) { - var audioLevelCanvasCache = {}; - - my.LOCAL_LEVEL = 'local'; - - my.init = function () { - ASDrawContext = $('#activeSpeakerAudioLevel')[0].getContext('2d'); - initActiveSpeakerAudioLevels(); - }; - - /** - * Updates the audio level canvas for the given peerJid. If the canvas - * didn't exist we create it. - */ - my.updateAudioLevelCanvas = function (peerJid, VideoLayout) { - var resourceJid = null; - var videoSpanId = null; - if (!peerJid) - videoSpanId = 'localVideoContainer'; - else { - resourceJid = Strophe.getResourceFromJid(peerJid); - - videoSpanId = 'participant_' + resourceJid; - } - - var videoSpan = document.getElementById(videoSpanId); - - if (!videoSpan) { - if (resourceJid) - console.error("No video element for jid", resourceJid); - else - console.error("No video element for local video."); - - return; - } - - var audioLevelCanvas = $('#' + videoSpanId + '>canvas'); - - var videoSpaceWidth = $('#remoteVideos').width(); - var thumbnailSize = VideoLayout.calculateThumbnailSize(videoSpaceWidth); - var thumbnailWidth = thumbnailSize[0]; - var thumbnailHeight = thumbnailSize[1]; - - if (!audioLevelCanvas || audioLevelCanvas.length === 0) { - - audioLevelCanvas = document.createElement('canvas'); - audioLevelCanvas.className = "audiolevel"; - audioLevelCanvas.style.bottom = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px"; - audioLevelCanvas.style.left = "-" + interfaceConfig.CANVAS_EXTRA/2 + "px"; - resizeAudioLevelCanvas( audioLevelCanvas, - thumbnailWidth, - thumbnailHeight); - - videoSpan.appendChild(audioLevelCanvas); - } else { - audioLevelCanvas = audioLevelCanvas.get(0); - - resizeAudioLevelCanvas( audioLevelCanvas, - thumbnailWidth, - thumbnailHeight); - } - }; - - /** - * Updates the audio level UI for the given resourceJid. - * - * @param resourceJid the resource jid indicating the video element for - * which we draw the audio level - * @param audioLevel the newAudio level to render - */ - my.updateAudioLevel = function (resourceJid, audioLevel, largeVideoResourceJid) { - drawAudioLevelCanvas(resourceJid, audioLevel); - - var videoSpanId = getVideoSpanId(resourceJid); - - var audioLevelCanvas = $('#' + videoSpanId + '>canvas').get(0); - - if (!audioLevelCanvas) - return; - - var drawContext = audioLevelCanvas.getContext('2d'); - - var canvasCache = audioLevelCanvasCache[resourceJid]; - - drawContext.clearRect (0, 0, - audioLevelCanvas.width, audioLevelCanvas.height); - drawContext.drawImage(canvasCache, 0, 0); - - if(resourceJid === AudioLevels.LOCAL_LEVEL) { - if(!APP.xmpp.myJid()) { - return; - } - resourceJid = APP.xmpp.myResource(); - } - - if(resourceJid === largeVideoResourceJid) { - window.requestAnimationFrame(function () { - AudioLevels.updateActiveSpeakerAudioLevel(audioLevel); - }); - } - }; - - my.updateActiveSpeakerAudioLevel = function(audioLevel) { - if($("#activeSpeaker").css("visibility") == "hidden" || ASDrawContext === null) - return; - - ASDrawContext.clearRect(0, 0, 300, 300); - if(audioLevel == 0) - return; - - ASDrawContext.shadowBlur = getShadowLevel(audioLevel); - - - // Fill the shape. - ASDrawContext.fill(); - }; - - /** - * Resizes the given audio level canvas to match the given thumbnail size. - */ - function resizeAudioLevelCanvas(audioLevelCanvas, - thumbnailWidth, - thumbnailHeight) { - audioLevelCanvas.width = thumbnailWidth + interfaceConfig.CANVAS_EXTRA; - audioLevelCanvas.height = thumbnailHeight + interfaceConfig.CANVAS_EXTRA; - } - - /** - * Draws the audio level canvas into the cached canvas object. - * - * @param resourceJid the resource jid indicating the video element for - * which we draw the audio level - * @param audioLevel the newAudio level to render - */ - function drawAudioLevelCanvas(resourceJid, audioLevel) { - if (!audioLevelCanvasCache[resourceJid]) { - - var videoSpanId = getVideoSpanId(resourceJid); - - var audioLevelCanvasOrig = $('#' + videoSpanId + '>canvas').get(0); - - /* - * FIXME Testing has shown that audioLevelCanvasOrig may not exist. - * In such a case, the method CanvasUtil.cloneCanvas may throw an - * error. Since audio levels are frequently updated, the errors have - * been observed to pile into the console, strain the CPU. - */ - if (audioLevelCanvasOrig) { - audioLevelCanvasCache[resourceJid] = - CanvasUtil.cloneCanvas(audioLevelCanvasOrig); - } - } - - var canvas = audioLevelCanvasCache[resourceJid]; - - if (!canvas) - return; - - var drawContext = canvas.getContext('2d'); - - drawContext.clearRect(0, 0, canvas.width, canvas.height); - - var shadowLevel = getShadowLevel(audioLevel); - - if (shadowLevel > 0) { - // drawContext, x, y, w, h, r, shadowColor, shadowLevel - CanvasUtil.drawRoundRectGlow(drawContext, - interfaceConfig.CANVAS_EXTRA / 2, interfaceConfig.CANVAS_EXTRA / 2, - canvas.width - interfaceConfig.CANVAS_EXTRA, - canvas.height - interfaceConfig.CANVAS_EXTRA, - interfaceConfig.CANVAS_RADIUS, - interfaceConfig.SHADOW_COLOR, - shadowLevel); - } - } - - /** - * Returns the shadow/glow level for the given audio level. - * - * @param audioLevel the audio level from which we determine the shadow - * level - */ - function getShadowLevel (audioLevel) { - var shadowLevel = 0; - - if (audioLevel <= 0.3) { - shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*(audioLevel/0.3)); - } - else if (audioLevel <= 0.6) { - shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.3) / 0.3)); - } - else { - shadowLevel = Math.round(interfaceConfig.CANVAS_EXTRA/2*((audioLevel - 0.6) / 0.4)); - } - return shadowLevel; - } - - /** - * Returns the video span id corresponding to the given resourceJid or local - * user. - */ - function getVideoSpanId(resourceJid) { - var videoSpanId = null; - if (resourceJid === AudioLevels.LOCAL_LEVEL || - (APP.xmpp.myResource() && resourceJid === APP.xmpp.myResource())) - videoSpanId = 'localVideoContainer'; - else - videoSpanId = 'participant_' + resourceJid; - - return videoSpanId; - } - - /** - * Indicates that the remote video has been resized. - */ - $(document).bind('remotevideo.resized', function (event, width, height) { - var resized = false; - $('#remoteVideos>span>canvas').each(function() { - var canvas = $(this).get(0); - if (canvas.width !== width + interfaceConfig.CANVAS_EXTRA) { - canvas.width = width + interfaceConfig.CANVAS_EXTRA; - resized = true; - } - - if (canvas.heigh !== height + interfaceConfig.CANVAS_EXTRA) { - canvas.height = height + interfaceConfig.CANVAS_EXTRA; - resized = true; - } - }); - - if (resized) - Object.keys(audioLevelCanvasCache).forEach(function (resourceJid) { - audioLevelCanvasCache[resourceJid].width = - width + interfaceConfig.CANVAS_EXTRA; - audioLevelCanvasCache[resourceJid].height = - height + interfaceConfig.CANVAS_EXTRA; - }); - }); - - return my; - -})(AudioLevels || {}); - -module.exports = AudioLevels; -},{"./CanvasUtils":15}],15:[function(require,module,exports){ -/** - * Utility class for drawing canvas shapes. - */ -var CanvasUtil = (function(my) { - - /** - * Draws a round rectangle with a glow. The glowWidth indicates the depth - * of the glow. - * - * @param drawContext the context of the canvas to draw to - * @param x the x coordinate of the round rectangle - * @param y the y coordinate of the round rectangle - * @param w the width of the round rectangle - * @param h the height of the round rectangle - * @param glowColor the color of the glow - * @param glowWidth the width of the glow - */ - my.drawRoundRectGlow - = function(drawContext, x, y, w, h, r, glowColor, glowWidth) { - - // Save the previous state of the context. - drawContext.save(); - - if (w < 2 * r) r = w / 2; - if (h < 2 * r) r = h / 2; - - // Draw a round rectangle. - drawContext.beginPath(); - drawContext.moveTo(x+r, y); - drawContext.arcTo(x+w, y, x+w, y+h, r); - drawContext.arcTo(x+w, y+h, x, y+h, r); - drawContext.arcTo(x, y+h, x, y, r); - drawContext.arcTo(x, y, x+w, y, r); - drawContext.closePath(); - - // Add a shadow around the rectangle - drawContext.shadowColor = glowColor; - drawContext.shadowBlur = glowWidth; - drawContext.shadowOffsetX = 0; - drawContext.shadowOffsetY = 0; - - // Fill the shape. - drawContext.fill(); - - drawContext.save(); - - drawContext.restore(); - -// 1) Uncomment this line to use Composite Operation, which is doing the -// same as the clip function below and is also antialiasing the round -// border, but is said to be less fast performance wise. - -// drawContext.globalCompositeOperation='destination-out'; - - drawContext.beginPath(); - drawContext.moveTo(x+r, y); - drawContext.arcTo(x+w, y, x+w, y+h, r); - drawContext.arcTo(x+w, y+h, x, y+h, r); - drawContext.arcTo(x, y+h, x, y, r); - drawContext.arcTo(x, y, x+w, y, r); - drawContext.closePath(); - -// 2) Uncomment this line to use Composite Operation, which is doing the -// same as the clip function below and is also antialiasing the round -// border, but is said to be less fast performance wise. - -// drawContext.fill(); - - // Comment these two lines if choosing to do the same with composite - // operation above 1 and 2. - drawContext.clip(); - drawContext.clearRect(0, 0, 277, 200); - - // Restore the previous context state. - drawContext.restore(); - }; - - /** - * Clones the given canvas. - * - * @return the new cloned canvas. - */ - my.cloneCanvas = function (oldCanvas) { - /* - * FIXME Testing has shown that oldCanvas may not exist. In such a case, - * the method CanvasUtil.cloneCanvas may throw an error. Since audio - * levels are frequently updated, the errors have been observed to pile - * into the console, strain the CPU. - */ - if (!oldCanvas) - return oldCanvas; - - //create a new canvas - var newCanvas = document.createElement('canvas'); - var context = newCanvas.getContext('2d'); - - //set dimensions - newCanvas.width = oldCanvas.width; - newCanvas.height = oldCanvas.height; - - //apply the old canvas to the new one - context.drawImage(oldCanvas, 0, 0); - - //return the new canvas - return newCanvas; - }; - - return my; -})(CanvasUtil || {}); - -module.exports = CanvasUtil; -},{}],16:[function(require,module,exports){ -/* global $, APP*/ - -var LoginDialog = require('./LoginDialog'); -var Moderator = require('../../xmpp/moderator'); - -/* Initial "authentication required" dialog */ -var authDialog = null; -/* Loop retry ID that wits for other user to create the room */ -var authRetryId = null; -var authenticationWindow = null; - -var Authentication = { - openAuthenticationDialog: function (roomName, intervalCallback, callback) { - // This is the loop that will wait for the room to be created by - // someone else. 'auth_required.moderator' will bring us back here. - authRetryId = window.setTimeout(intervalCallback, 5000); - // Show prompt only if it's not open - if (authDialog !== null) { - return; - } - // extract room name from 'room@muc.server.net' - var room = roomName.substr(0, roomName.indexOf('@')); - - var title - = APP.translation.generateTranslationHTML("dialog.WaitingForHost"); - var msg - = APP.translation.generateTranslationHTML( - "dialog.WaitForHostMsg", {room: room}); - - var buttonTxt - = APP.translation.generateTranslationHTML("dialog.IamHost"); - var buttons = []; - buttons.push({title: buttonTxt, value: "authNow"}); - - authDialog = APP.UI.messageHandler.openDialog( - title, - msg, - true, - buttons, - function (onSubmitEvent, submitValue) { - - // Do not close the dialog yet - onSubmitEvent.preventDefault(); - - // Open login popup - if (submitValue === 'authNow') { - callback(); - } - } - ); - }, - closeAuthenticationWindow: function () { - if (authenticationWindow) { - authenticationWindow.close(); - authenticationWindow = null; - } - }, - xmppAuthenticate: function () { - - var loginDialog = LoginDialog.show( - function (connection, state) { - if (!state) { - // User cancelled - loginDialog.close(); - return; - } else if (state == APP.xmpp.Status.CONNECTED) { - - loginDialog.close(); - - Authentication.stopInterval(); - Authentication.closeAuthenticationDialog(); - - // Close the connection as anonymous one will be used - // to create the conference. Session-id will authorize - // the request. - connection.disconnect(); - - var roomName = APP.UI.generateRoomName(); - Moderator.allocateConferenceFocus(roomName, function () { - // If it's not "on the fly" authentication now join - // the conference room - if (!APP.xmpp.isMUCJoined()) { - APP.UI.checkForNicknameAndJoin(); - } - }); - } - }, true); - }, - focusAuthenticationWindow: function () { - // If auth window exists just bring it to the front - if (authenticationWindow) { - authenticationWindow.focus(); - return; - } - }, - closeAuthenticationDialog: function () { - // Close authentication dialog if opened - if (authDialog) { - authDialog.close(); - authDialog = null; - } - }, - createAuthenticationWindow: function (callback, url) { - authenticationWindow = APP.UI.messageHandler.openCenteredPopup( - url, 910, 660, - // On closed - function () { - // Close authentication dialog if opened - Authentication.closeAuthenticationDialog(); - callback(); - authenticationWindow = null; - }); - return authenticationWindow; - }, - stopInterval: function () { - // Clear retry interval, so that we don't call 'doJoinAfterFocus' twice - if (authRetryId) { - window.clearTimeout(authRetryId); - authRetryId = null; - } - } -}; - -module.exports = Authentication; -},{"../../xmpp/moderator":62,"./LoginDialog":17}],17:[function(require,module,exports){ -/* global $, APP, config*/ - -var XMPP = require('../../xmpp/xmpp'); -var Moderator = require('../../xmpp/moderator'); - -//FIXME: use LoginDialog to add retries to XMPP.connect method used when -// anonymous domain is not enabled - -/** - * Creates new Dialog instance. - * @param callback function(Strophe.Connection, Strophe.Status) called - * when we either fail to connect or succeed(check Strophe.Status). - * @param obtainSession true if we want to send ConferenceIQ to Jicofo - * in order to create session-id after the connection is established. - * @constructor - */ -function Dialog(callback, obtainSession) { - - var self = this; - - var stop = false; - - var connection = APP.xmpp.createConnection(); - - var message = '

    '; - message += APP.translation.translateString("dialog.passwordRequired"); - message += '

    ' + - '' + - ''; - - var okButton = APP.translation.generateTranslationHTML("dialog.Ok"); - - var cancelButton = APP.translation.generateTranslationHTML("dialog.Cancel"); - - var states = { - login: { - html: message, - buttons: [ - { title: okButton, value: true}, - { title: cancelButton, value: false} - ], - focus: ':input:first', - submit: function (e, v, m, f) { - e.preventDefault(); - if (v) { - var jid = f.username; - var password = f.password; - if (jid && password) { - stop = false; - connection.reset(); - connDialog.goToState('connecting'); - connection.connect(jid, password, stateHandler); - } - } else { - // User cancelled - stop = true; - callback(); - } - } - }, - connecting: { - title: APP.translation.translateString('dialog.connecting'), - html: '
    ', - buttons: [], - defaultButton: 0 - }, - finished: { - title: APP.translation.translateString('dialog.error'), - html: '
    ', - buttons: [ - { - title: APP.translation.translateString('dialog.retry'), - value: 'retry' - }, - { - title: APP.translation.translateString('dialog.Cancel'), - value: 'cancel' - }, - ], - defaultButton: 0, - submit: function (e, v, m, f) { - e.preventDefault(); - if (v === 'retry') - connDialog.goToState('login'); - else - callback(); - } - } - }; - - var connDialog - = APP.UI.messageHandler.openDialogWithStates(states, - { persistent: true, closeText: '' }, null); - - var stateHandler = function (status, message) { - if (stop) { - return; - } - - var translateKey = "connection." + XMPP.getStatusString(status); - var statusStr = APP.translation.translateString(translateKey); - - // Display current state - var connectionStatus = - connDialog.getState('connecting').find('#connectionStatus'); - - connectionStatus.text(statusStr); - - switch (status) { - case XMPP.Status.CONNECTED: - - stop = true; - if (!obtainSession) { - callback(connection, status); - return; - } - // Obtaining session-id status - connectionStatus.text( - APP.translation.translateString( - 'connection.FETCH_SESSION_ID')); - - // Authenticate with Jicofo and obtain session-id - var roomName = APP.UI.generateRoomName(); - - // Jicofo will return new session-id when connected - // from authenticated domain - connection.sendIQ( - Moderator.createConferenceIq(roomName), - function (result) { - - connectionStatus.text( - APP.translation.translateString( - 'connection.GOT_SESSION_ID')); - - stop = true; - - // Parse session-id - Moderator.parseSessionId(result); - - callback(connection, status); - }, - function (error) { - console.error("Auth on the fly failed", error); - - stop = true; - - var errorMsg = - APP.translation.translateString( - 'connection.GET_SESSION_ID_ERROR') + - $(error).find('>error').attr('code'); - - self.displayError(errorMsg); - - connection.disconnect(); - }); - - break; - case XMPP.Status.AUTHFAIL: - case XMPP.Status.CONNFAIL: - case XMPP.Status.DISCONNECTED: - - stop = true; - - callback(connection, status); - - var errorMessage = statusStr; - - if (message) - { - errorMessage += ': ' + message; - } - self.displayError(errorMessage); - - break; - default: - break; - } - }; - - /** - * Displays error message in 'finished' state which allows either to cancel - * or retry. - * @param message the final message to be displayed. - */ - this.displayError = function (message) { - - var finishedState = connDialog.getState('finished'); - - var errorMessageElem = finishedState.find('#errorMessage'); - errorMessageElem.text(message); - - connDialog.goToState('finished'); - }; - - /** - * Closes LoginDialog. - */ - this.close = function () { - stop = true; - connDialog.close(); - }; -} - -var LoginDialog = { - - /** - * Displays login prompt used to establish new XMPP connection. Given - * callback(Strophe.Connection, Strophe.Status) function will be - * called when we connect successfully(status === CONNECTED) or when we fail - * to do so. On connection failure program can call Dialog.close() method in - * order to cancel or do nothing to let the user retry. - * @param callback function(Strophe.Connection, Strophe.Status) - * called when we either fail to connect or succeed(check - * Strophe.Status). - * @param obtainSession true if we want to send ConferenceIQ to - * Jicofo in order to create session-id after the connection is - * established. - * @returns {Dialog} - */ - show: function (callback, obtainSession) { - return new Dialog(callback, obtainSession); - } -}; - -module.exports = LoginDialog; -},{"../../xmpp/moderator":62,"../../xmpp/xmpp":70}],18:[function(require,module,exports){ -var Settings = require("../../settings/Settings"); - -var users = {}; - -var Avatar = { - - /** - * Sets the user's avatar in the settings menu(if local user), contact list - * and thumbnail - * @param jid jid of the user - * @param id email or userID to be used as a hash - */ - setUserAvatar: function (jid, id) { - if (id) { - if (users[jid] === id) { - return; - } - users[jid] = id; - } - var thumbUrl = this.getThumbUrl(jid); - var contactListUrl = this.getContactListUrl(jid); - var resourceJid = Strophe.getResourceFromJid(jid); - - APP.UI.userAvatarChanged(resourceJid, thumbUrl, contactListUrl); - }, - /** - * Returns image URL for the avatar to be displayed on large video area - * where current active speaker is presented. - * @param jid full MUC jid of the user for whom we want to obtain avatar URL - */ - getActiveSpeakerUrl: function (jid) { - return this.getGravatarUrl(jid, 100); - }, - /** - * Returns image URL for the avatar to be displayed on small video thumbnail - * @param jid full MUC jid of the user for whom we want to obtain avatar URL - */ - getThumbUrl: function (jid) { - return this.getGravatarUrl(jid, 100); - }, - /** - * Returns the URL for the avatar to be displayed as contactlist item - * @param jid full MUC jid of the user for whom we want to obtain avatar URL - */ - getContactListUrl: function (jid) { - return this.getGravatarUrl(jid, 30); - }, - getGravatarUrl: function (jid, size) { - if (!jid) { - console.error("Get gravatar - jid is undefined"); - return null; - } - var id = users[jid]; - if (!id) { - console.warn( - "No avatar stored yet for " + jid + " - using JID as ID"); - id = jid; - } - return 'https://www.gravatar.com/avatar/' + - MD5.hexdigest(id.trim().toLowerCase()) + - "?d=wavatar&size=" + (size || "30"); - } - -}; - - -module.exports = Avatar; -},{"../../settings/Settings":49}],19:[function(require,module,exports){ -/* global $, config, - setLargeVideoVisible, Util */ - -var VideoLayout = require("../videolayout/VideoLayout"); -var Prezi = require("../prezi/Prezi"); -var UIUtil = require("../util/UIUtil"); - -var etherpadName = null; -var etherpadIFrame = null; -var domain = null; -var options = "?showControls=true&showChat=false&showLineNumbers=true&useMonospaceFont=false"; - - -/** - * Resizes the etherpad. - */ -function resize() { - if ($('#etherpad>iframe').length) { - var remoteVideos = $('#remoteVideos'); - var availableHeight - = window.innerHeight - remoteVideos.outerHeight(); - var availableWidth = UIUtil.getAvailableVideoWidth(); - - $('#etherpad>iframe').width(availableWidth); - $('#etherpad>iframe').height(availableHeight); - } -} - -/** - * Creates the Etherpad button and adds it to the toolbar. - */ -function enableEtherpadButton() { - if (!$('#toolbar_button_etherpad').is(":visible")) - $('#toolbar_button_etherpad').css({display: 'inline-block'}); -} - -/** - * Creates the IFrame for the etherpad. - */ -function createIFrame() { - etherpadIFrame = VideoLayout.createEtherpadIframe( - domain + etherpadName + options, function() { - - document.domain = document.domain; - bubbleIframeMouseMove(etherpadIFrame); - setTimeout(function() { - // the iframes inside of the etherpad are - // not yet loaded when the etherpad iframe is loaded - var outer = etherpadIFrame. - contentDocument.getElementsByName("ace_outer")[0]; - bubbleIframeMouseMove(outer); - var inner = outer. - contentDocument.getElementsByName("ace_inner")[0]; - bubbleIframeMouseMove(inner); - }, 2000); - }); -} - -function bubbleIframeMouseMove(iframe){ - var existingOnMouseMove = iframe.contentWindow.onmousemove; - iframe.contentWindow.onmousemove = function(e){ - if(existingOnMouseMove) existingOnMouseMove(e); - var evt = document.createEvent("MouseEvents"); - var boundingClientRect = iframe.getBoundingClientRect(); - evt.initMouseEvent( - "mousemove", - true, // bubbles - false, // not cancelable - window, - e.detail, - e.screenX, - e.screenY, - e.clientX + boundingClientRect.left, - e.clientY + boundingClientRect.top, - e.ctrlKey, - e.altKey, - e.shiftKey, - e.metaKey, - e.button, - null // no related element - ); - iframe.dispatchEvent(evt); - }; -} - - -var Etherpad = { - /** - * Initializes the etherpad. - */ - init: function (name) { - - if (config.etherpad_base && !etherpadName && name) { - - domain = config.etherpad_base; - - etherpadName = name; - - enableEtherpadButton(); - - /** - * Resizes the etherpad, when the window is resized. - */ - $(window).resize(function () { - resize(); - }); - } - }, - - /** - * Opens/hides the Etherpad. - */ - toggleEtherpad: function (isPresentation) { - if (!etherpadIFrame) - createIFrame(); - - - if(VideoLayout.getLargeVideoState() === "etherpad") - { - VideoLayout.setLargeVideoState("video"); - } - else - { - VideoLayout.setLargeVideoState("etherpad"); - } - resize(); - } -}; - -module.exports = Etherpad; - -},{"../prezi/Prezi":20,"../util/UIUtil":35,"../videolayout/VideoLayout":41}],20:[function(require,module,exports){ -var ToolbarToggler = require("../toolbars/ToolbarToggler"); -var UIUtil = require("../util/UIUtil"); -var VideoLayout = require("../videolayout/VideoLayout"); -var messageHandler = require("../util/MessageHandler"); -var PreziPlayer = require("./PreziPlayer"); - -var preziPlayer = null; - - -/** - * Shows/hides a presentation. - */ -function setPresentationVisible(visible) { - - if (visible) { - VideoLayout.setLargeVideoState("prezi"); - } - else { - VideoLayout.setLargeVideoState("video"); - } -} - -var Prezi = { - - - /** - * Reloads the current presentation. - */ - reloadPresentation: function() { - var iframe = document.getElementById(preziPlayer.options.preziId); - iframe.src = iframe.src; - }, - - /** - * Returns true if the presentation is visible, false - - * otherwise. - */ - isPresentationVisible: function () { - return ($('#presentation>iframe') != null - && $('#presentation>iframe').css('opacity') == 1); - }, - - /** - * Opens the Prezi dialog, from which the user could choose a presentation - * to load. - */ - openPreziDialog: function() { - var myprezi = APP.xmpp.getPrezi(); - if (myprezi) { - messageHandler.openTwoButtonDialog("dialog.removePreziTitle", - null, - "dialog.removePreziMsg", - null, - false, - "dialog.Remove", - function(e,v,m,f) { - if(v) { - APP.xmpp.removePreziFromPresence(); - } - } - ); - } - else if (preziPlayer != null) { - messageHandler.openTwoButtonDialog("dialog.sharePreziTitle", - null, "dialog.sharePreziMsg", - null, - false, - "dialog.Ok", - function(e,v,m,f) { - $.prompt.close(); - } - ); - } - else { - var html = APP.translation.generateTranslationHTML( - "dialog.sharePreziTitle"); - var cancelButton = APP.translation.generateTranslationHTML( - "dialog.Cancel"); - var shareButton = APP.translation.generateTranslationHTML( - "dialog.Share"); - var backButton = APP.translation.generateTranslationHTML( - "dialog.Back"); - var buttons = []; - var buttons1 = []; - // Cancel button to both states - buttons.push({title: cancelButton, value: false}); - buttons1.push({title: cancelButton, value: false}); - // Share button - buttons.push({title: shareButton, value: true}); - // Back button - buttons1.push({title: backButton, value: true}); - var linkError = APP.translation.generateTranslationHTML( - "dialog.preziLinkError"); - var defaultUrl = APP.translation.translateString("defaultPreziLink", - {url: "http://prezi.com/wz7vhjycl7e6/my-prezi"}); - var openPreziState = { - state0: { - html: '

    ' + html + '

    ' + - '', - persistent: false, - buttons: buttons, - focus: ':input:first', - defaultButton: 0, - submit: function (e, v, m, f) { - e.preventDefault(); - if(v) - { - var preziUrl = f.preziUrl; - - if (preziUrl) - { - var urlValue - = encodeURI(UIUtil.escapeHtml(preziUrl)); - - if (urlValue.indexOf('http://prezi.com/') != 0 - && urlValue.indexOf('https://prezi.com/') != 0) - { - $.prompt.goToState('state1'); - return false; - } - else { - var presIdTmp = urlValue.substring( - urlValue.indexOf("prezi.com/") + 10); - if (!isAlphanumeric(presIdTmp) - || presIdTmp.indexOf('/') < 2) { - $.prompt.goToState('state1'); - return false; - } - else { - APP.xmpp.addToPresence("prezi", urlValue); - $.prompt.close(); - } - } - } - } - else - $.prompt.close(); - } - }, - state1: { - html: '

    ' + html + '

    ' + - linkError, - persistent: false, - buttons: buttons1, - focus: ':input:first', - defaultButton: 1, - submit: function (e, v, m, f) { - e.preventDefault(); - if (v === 0) - $.prompt.close(); - else - $.prompt.goToState('state0'); - } - } - }; - messageHandler.openDialogWithStates(openPreziState); - } - } - -}; - -/** - * A new presentation has been added. - * - * @param event the event indicating the add of a presentation - * @param jid the jid from which the presentation was added - * @param presUrl url of the presentation - * @param currentSlide the current slide to which we should move - */ -function presentationAdded(event, jid, presUrl, currentSlide) { - console.log("presentation added", presUrl); - - var presId = getPresentationId(presUrl); - - var elementId = 'participant_' - + Strophe.getResourceFromJid(jid) - + '_' + presId; - - VideoLayout.addPreziContainer(elementId); - - var controlsEnabled = false; - if (jid === APP.xmpp.myJid()) - controlsEnabled = true; - - setPresentationVisible(true); - VideoLayout.setLargeVideoHover( - function (event) { - if (Prezi.isPresentationVisible()) { - var reloadButtonRight = window.innerWidth - - $('#presentation>iframe').offset().left - - $('#presentation>iframe').width(); - - $('#reloadPresentation').css({ right: reloadButtonRight, - display:'inline-block'}); - } - }, - function (event) { - if (!Prezi.isPresentationVisible()) - $('#reloadPresentation').css({display:'none'}); - else { - var e = event.toElement || event.relatedTarget; - - if (e && e.id != 'reloadPresentation' && e.id != 'header') - $('#reloadPresentation').css({display:'none'}); - } - }); - - preziPlayer = new PreziPlayer( - 'presentation', - {preziId: presId, - width: getPresentationWidth(), - height: getPresentationHeihgt(), - controls: controlsEnabled, - debug: true - }); - - $('#presentation>iframe').attr('id', preziPlayer.options.preziId); - - preziPlayer.on(PreziPlayer.EVENT_STATUS, function(event) { - console.log("prezi status", event.value); - if (event.value == PreziPlayer.STATUS_CONTENT_READY) { - if (jid != APP.xmpp.myJid()) - preziPlayer.flyToStep(currentSlide); - } - }); - - preziPlayer.on(PreziPlayer.EVENT_CURRENT_STEP, function(event) { - console.log("event value", event.value); - APP.xmpp.addToPresence("preziSlide", event.value); - }); - - $("#" + elementId).css( 'background-image', - 'url(../images/avatarprezi.png)'); - $("#" + elementId).click( - function () { - setPresentationVisible(true); - } - ); -}; - -/** - * A presentation has been removed. - * - * @param event the event indicating the remove of a presentation - * @param jid the jid for which the presentation was removed - * @param the url of the presentation - */ -function presentationRemoved(event, jid, presUrl) { - console.log('presentation removed', presUrl); - var presId = getPresentationId(presUrl); - setPresentationVisible(false); - $('#participant_' - + Strophe.getResourceFromJid(jid) - + '_' + presId).remove(); - $('#presentation>iframe').remove(); - if (preziPlayer != null) { - preziPlayer.destroy(); - preziPlayer = null; - } -}; - -/** - * Indicates if the given string is an alphanumeric string. - * Note that some special characters are also allowed (-, _ , /, &, ?, =, ;) for the - * purpose of checking URIs. - */ -function isAlphanumeric(unsafeText) { - var regex = /^[a-z0-9-_\/&\?=;]+$/i; - return regex.test(unsafeText); -} - -/** - * Returns the presentation id from the given url. - */ -function getPresentationId (presUrl) { - var presIdTmp = presUrl.substring(presUrl.indexOf("prezi.com/") + 10); - return presIdTmp.substring(0, presIdTmp.indexOf('/')); -} - -/** - * Returns the presentation width. - */ -function getPresentationWidth() { - var availableWidth = UIUtil.getAvailableVideoWidth(); - var availableHeight = getPresentationHeihgt(); - - var aspectRatio = 16.0 / 9.0; - if (availableHeight < availableWidth / aspectRatio) { - availableWidth = Math.floor(availableHeight * aspectRatio); - } - return availableWidth; -} - -/** - * Returns the presentation height. - */ -function getPresentationHeihgt() { - var remoteVideos = $('#remoteVideos'); - return window.innerHeight - remoteVideos.outerHeight(); -} - -/** - * Resizes the presentation iframe. - */ -function resize() { - if ($('#presentation>iframe')) { - $('#presentation>iframe').width(getPresentationWidth()); - $('#presentation>iframe').height(getPresentationHeihgt()); - } -} - -/** - * Presentation has been removed. - */ -$(document).bind('presentationremoved.muc', presentationRemoved); - -/** - * Presentation has been added. - */ -$(document).bind('presentationadded.muc', presentationAdded); - -/* - * Indicates presentation slide change. - */ -$(document).bind('gotoslide.muc', function (event, jid, presUrl, current) { - if (preziPlayer && preziPlayer.getCurrentStep() != current) { - preziPlayer.flyToStep(current); - - var animationStepsArray = preziPlayer.getAnimationCountOnSteps(); - for (var i = 0; i < parseInt(animationStepsArray[current]); i++) { - preziPlayer.flyToStep(current, i); - } - } -}); - -$(window).resize(function () { - resize(); -}); - -module.exports = Prezi; - -},{"../toolbars/ToolbarToggler":31,"../util/MessageHandler":33,"../util/UIUtil":35,"../videolayout/VideoLayout":41,"./PreziPlayer":21}],21:[function(require,module,exports){ -(function() { - "use strict"; - var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; - - window.PreziPlayer = (function() { - - PreziPlayer.API_VERSION = 1; - PreziPlayer.CURRENT_STEP = 'currentStep'; - PreziPlayer.CURRENT_ANIMATION_STEP = 'currentAnimationStep'; - PreziPlayer.CURRENT_OBJECT = 'currentObject'; - PreziPlayer.STATUS_LOADING = 'loading'; - PreziPlayer.STATUS_READY = 'ready'; - PreziPlayer.STATUS_CONTENT_READY = 'contentready'; - PreziPlayer.EVENT_CURRENT_STEP = "currentStepChange"; - PreziPlayer.EVENT_CURRENT_ANIMATION_STEP = "currentAnimationStepChange"; - PreziPlayer.EVENT_CURRENT_OBJECT = "currentObjectChange"; - PreziPlayer.EVENT_STATUS = "statusChange"; - PreziPlayer.EVENT_PLAYING = "isAutoPlayingChange"; - PreziPlayer.EVENT_IS_MOVING = "isMovingChange"; - PreziPlayer.domain = "https://prezi.com"; - PreziPlayer.path = "/player/"; - PreziPlayer.players = {}; - PreziPlayer.binded_methods = ['changesHandler']; - - PreziPlayer.createMultiplePlayers = function(optionArray){ - for(var i=0; i 0 && - obj.values.animationCountOnSteps && - obj.values.animationCountOnSteps[step] <= animation_step) { - animation_step = obj.values.animationCountOnSteps[step]; - } - // jump to animation steps by calling flyToNextStep() - function doAnimationSteps() { - if (obj.values.isMoving == true) { - setTimeout(doAnimationSteps, 100); // wait until the flight ends - return; - } - while (animation_step-- > 0) { - obj.flyToNextStep(); // do the animation steps - } - } - setTimeout(doAnimationSteps, 200); // 200ms is the internal "reporting" time - // jump to the step - return this.sendMessage({ - 'action': 'present', - 'data': ['moveToStep', step] - }); - }; - - PreziPlayer.prototype.toObject = /* toObject is DEPRECATED */ - PreziPlayer.prototype.flyToObject = function(objectId) { - return this.sendMessage({ - 'action': 'present', - 'data': ['moveToObject', objectId] - }); - }; - - PreziPlayer.prototype.play = function(defaultDelay) { - return this.sendMessage({ - 'action': 'present', - 'data': ['startAutoPlay', defaultDelay] - }); - }; - - PreziPlayer.prototype.stop = function() { - return this.sendMessage({ - 'action': 'present', - 'data': ['stopAutoPlay'] - }); - }; - - PreziPlayer.prototype.pause = function(defaultDelay) { - return this.sendMessage({ - 'action': 'present', - 'data': ['pauseAutoPlay', defaultDelay] - }); - }; - - PreziPlayer.prototype.getCurrentStep = function() { - return this.values.currentStep; - }; - - PreziPlayer.prototype.getCurrentAnimationStep = function() { - return this.values.currentAnimationStep; - }; - - PreziPlayer.prototype.getCurrentObject = function() { - return this.values.currentObject; - }; - - PreziPlayer.prototype.getStatus = function() { - return this.values.status; - }; - - PreziPlayer.prototype.isPlaying = function() { - return this.values.isAutoPlaying; - }; - - PreziPlayer.prototype.getStepCount = function() { - return this.values.stepCount; - }; - - PreziPlayer.prototype.getAnimationCountOnSteps = function() { - return this.values.animationCountOnSteps; - }; - - PreziPlayer.prototype.getTitle = function() { - return this.values.title; - }; - - PreziPlayer.prototype.setDimensions = function(dims) { - for (var parameter in dims) { - this.iframe[parameter] = dims[parameter]; - } - } - - PreziPlayer.prototype.getDimensions = function() { - return { - width: parseInt(this.iframe.width, 10), - height: parseInt(this.iframe.height, 10) - } - } - - PreziPlayer.prototype.on = function(event, callback) { - this.callbacks.push({ - event: event, - callback: callback - }); - }; - - PreziPlayer.prototype.off = function(event, callback) { - var j, item; - if (event === undefined) { - this.callbacks = []; - } - j = this.callbacks.length; - while (j--) { - item = this.callbacks[j]; - if (item && item.event === event && (callback === undefined || item.callback === callback)){ - this.callbacks.splice(j, 1); - } - } - }; - - if (window.addEventListener) { - window.addEventListener('message', PreziPlayer.messageReceived, false); - } else { - window.attachEvent('onmessage', PreziPlayer.messageReceived); - } - - return PreziPlayer; - - })(); - -})(); - -module.exports = PreziPlayer; - -},{}],22:[function(require,module,exports){ -/* global require, $ */ -var Chat = require("./chat/Chat"); -var ContactList = require("./contactlist/ContactList"); -var Settings = require("./../../settings/Settings"); -var SettingsMenu = require("./settings/SettingsMenu"); -var VideoLayout = require("../videolayout/VideoLayout"); -var ToolbarToggler = require("../toolbars/ToolbarToggler"); -var UIUtil = require("../util/UIUtil"); -var LargeVideo = require("../videolayout/LargeVideo"); - -/** - * Toggler for the chat, contact list, settings menu, etc.. - */ -var PanelToggler = (function(my) { - - var currentlyOpen = null; - var buttons = { - '#chatspace': '#chatBottomButton', - '#contactlist': '#contactListButton', - '#settingsmenu': '#toolbar_button_settings' - }; - - /** - * Toggles the windows in the side panel - * @param object the window that should be shown - * @param selector the selector for the element containing the panel - * @param onOpenComplete function to be called when the panel is opened - * @param onOpen function to be called if the window is going to be opened - * @param onClose function to be called if the window is going to be closed - */ - var toggle = function(object, selector, onOpenComplete, onOpen, onClose) { - UIUtil.buttonClick(buttons[selector], "active"); - - if (object.isVisible()) { - $("#toast-container").animate({ - right: '5px' - }, - { - queue: false, - duration: 500 - }); - $(selector).hide("slide", { - direction: "right", - queue: false, - duration: 500 - }); - if(typeof onClose === "function") { - onClose(); - } - - currentlyOpen = null; - } - else { - // Undock the toolbar when the chat is shown and if we're in a - // video mode. - if (LargeVideo.isLargeVideoVisible()) { - ToolbarToggler.dockToolbar(false); - } - - if(currentlyOpen) { - var current = $(currentlyOpen); - UIUtil.buttonClick(buttons[currentlyOpen], "active"); - current.css('z-index', 4); - setTimeout(function () { - current.css('display', 'none'); - current.css('z-index', 5); - }, 500); - } - - $("#toast-container").animate({ - right: (PanelToggler.getPanelSize()[0] + 5) + 'px' - }, - { - queue: false, - duration: 500 - }); - $(selector).show("slide", { - direction: "right", - queue: false, - duration: 500, - complete: onOpenComplete - }); - if(typeof onOpen === "function") { - onOpen(); - } - - currentlyOpen = selector; - } - }; - - /** - * Opens / closes the chat area. - */ - my.toggleChat = function() { - var chatCompleteFunction = Chat.isVisible() ? - function() {} : function () { - Chat.scrollChatToBottom(); - $('#chatspace').trigger('shown'); - }; - - VideoLayout.resizeVideoArea(!Chat.isVisible(), chatCompleteFunction); - - toggle(Chat, - '#chatspace', - function () { - // Request the focus in the nickname field or the chat input field. - if ($('#nickname').css('visibility') === 'visible') { - $('#nickinput').focus(); - } else { - $('#usermsg').focus(); - } - }, - null, - Chat.resizeChat, - null); - }; - - /** - * Opens / closes the contact list area. - */ - my.toggleContactList = function () { - var completeFunction = ContactList.isVisible() ? - function() {} : function () { $('#contactlist').trigger('shown');}; - VideoLayout.resizeVideoArea(!ContactList.isVisible(), completeFunction); - - toggle(ContactList, - '#contactlist', - null, - function() { - ContactList.setVisualNotification(false); - }, - null); - }; - - /** - * Opens / closes the settings menu - */ - my.toggleSettingsMenu = function() { - VideoLayout.resizeVideoArea(!SettingsMenu.isVisible(), function (){}); - toggle(SettingsMenu, - '#settingsmenu', - null, - function() { - var settings = Settings.getSettings(); - $('#setDisplayName').get(0).value = settings.displayName; - $('#setEmail').get(0).value = settings.email; - }, - null); - }; - - /** - * Returns the size of the side panel. - */ - my.getPanelSize = function () { - var availableHeight = window.innerHeight; - var availableWidth = window.innerWidth; - - var panelWidth = 200; - if (availableWidth * 0.2 < 200) { - panelWidth = availableWidth * 0.2; - } - - return [panelWidth, availableHeight]; - }; - - my.isVisible = function() { - return (Chat.isVisible() || ContactList.isVisible() || SettingsMenu.isVisible()); - }; - - return my; - -}(PanelToggler || {})); - -module.exports = PanelToggler; -},{"../toolbars/ToolbarToggler":31,"../util/UIUtil":35,"../videolayout/LargeVideo":37,"../videolayout/VideoLayout":41,"./../../settings/Settings":49,"./chat/Chat":23,"./contactlist/ContactList":27,"./settings/SettingsMenu":28}],23:[function(require,module,exports){ -/* global APP, $, Util, nickname:true */ -var Replacement = require("./Replacement"); -var CommandsProcessor = require("./Commands"); -var ToolbarToggler = require("../../toolbars/ToolbarToggler"); -var smileys = require("./smileys.json").smileys; -var NicknameHandler = require("../../util/NicknameHandler"); -var UIUtil = require("../../util/UIUtil"); -var UIEvents = require("../../../../service/UI/UIEvents"); - -var notificationInterval = false; -var unreadMessages = 0; - - -/** - * Shows/hides a visual notification, indicating that a message has arrived. - */ -function setVisualNotification(show) { - var unreadMsgElement = document.getElementById('unreadMessages'); - var unreadMsgBottomElement - = document.getElementById('bottomUnreadMessages'); - - var glower = $('#toolbar_button_chat'); - var bottomGlower = $('#chatBottomButton'); - - if (unreadMessages) { - unreadMsgElement.innerHTML = unreadMessages.toString(); - unreadMsgBottomElement.innerHTML = unreadMessages.toString(); - - ToolbarToggler.dockToolbar(true); - - var chatButtonElement - = document.getElementById('toolbar_button_chat'); - var leftIndent = (UIUtil.getTextWidth(chatButtonElement) - - UIUtil.getTextWidth(unreadMsgElement)) / 2; - var topIndent = (UIUtil.getTextHeight(chatButtonElement) - - UIUtil.getTextHeight(unreadMsgElement)) / 2 - 3; - - unreadMsgElement.setAttribute( - 'style', - 'top:' + topIndent + - '; left:' + leftIndent + ';'); - - var chatBottomButtonElement - = document.getElementById('chatBottomButton').parentNode; - var bottomLeftIndent = (UIUtil.getTextWidth(chatBottomButtonElement) - - UIUtil.getTextWidth(unreadMsgBottomElement)) / 2; - var bottomTopIndent = (UIUtil.getTextHeight(chatBottomButtonElement) - - UIUtil.getTextHeight(unreadMsgBottomElement)) / 2 - 2; - - unreadMsgBottomElement.setAttribute( - 'style', - 'top:' + bottomTopIndent + - '; left:' + bottomLeftIndent + ';'); - - - if (!glower.hasClass('icon-chat-simple')) { - glower.removeClass('icon-chat'); - glower.addClass('icon-chat-simple'); - } - } - else { - unreadMsgElement.innerHTML = ''; - unreadMsgBottomElement.innerHTML = ''; - glower.removeClass('icon-chat-simple'); - glower.addClass('icon-chat'); - } - - if (show && !notificationInterval) { - notificationInterval = window.setInterval(function () { - glower.toggleClass('active'); - bottomGlower.toggleClass('active glowing'); - }, 800); - } - else if (!show && notificationInterval) { - window.clearInterval(notificationInterval); - notificationInterval = false; - glower.removeClass('active'); - bottomGlower.removeClass('glowing'); - bottomGlower.addClass('active'); - } -} - - -/** - * Returns the current time in the format it is shown to the user - * @returns {string} - */ -function getCurrentTime(stamp) { - var now = (stamp? new Date(stamp): new Date()); - var hour = now.getHours(); - var minute = now.getMinutes(); - var second = now.getSeconds(); - if(hour.toString().length === 1) { - hour = '0'+hour; - } - if(minute.toString().length === 1) { - minute = '0'+minute; - } - if(second.toString().length === 1) { - second = '0'+second; - } - return hour+':'+minute+':'+second; -} - -function toggleSmileys() { - var smileys = $('#smileysContainer'); - if(!smileys.is(':visible')) { - smileys.show("slide", { direction: "down", duration: 300}); - } else { - smileys.hide("slide", { direction: "down", duration: 300}); - } - $('#usermsg').focus(); -} - -function addClickFunction(smiley, number) { - smiley.onclick = function addSmileyToMessage() { - var usermsg = $('#usermsg'); - var message = usermsg.val(); - message += smileys['smiley' + number]; - usermsg.val(message); - usermsg.get(0).setSelectionRange(message.length, message.length); - toggleSmileys(); - usermsg.focus(); - }; -} - -/** - * Adds the smileys container to the chat - */ -function addSmileys() { - var smileysContainer = document.createElement('div'); - smileysContainer.id = 'smileysContainer'; - for(var i = 1; i <= 21; i++) { - var smileyContainer = document.createElement('div'); - smileyContainer.id = 'smiley' + i; - smileyContainer.className = 'smileyContainer'; - var smiley = document.createElement('img'); - smiley.src = 'images/smileys/smiley' + i + '.svg'; - smiley.className = 'smiley'; - addClickFunction(smiley, i); - smileyContainer.appendChild(smiley); - smileysContainer.appendChild(smileyContainer); - } - - $("#chatspace").append(smileysContainer); -} - -/** - * Resizes the chat conversation. - */ -function resizeChatConversation() { - var msgareaHeight = $('#usermsg').outerHeight(); - var chatspace = $('#chatspace'); - var width = chatspace.width(); - var chat = $('#chatconversation'); - var smileys = $('#smileysarea'); - - smileys.height(msgareaHeight); - $("#smileys").css('bottom', (msgareaHeight - 26) / 2); - $('#smileysContainer').css('bottom', msgareaHeight); - chat.width(width - 10); - chat.height(window.innerHeight - 15 - msgareaHeight); -} - -/** - * Chat related user interface. - */ -var Chat = (function (my) { - /** - * Initializes chat related interface. - */ - my.init = function () { - if(NicknameHandler.getNickname()) - Chat.setChatConversationMode(true); - NicknameHandler.addListener(UIEvents.NICKNAME_CHANGED, - function (nickname) { - Chat.setChatConversationMode(true); - }); - - $('#nickinput').keydown(function (event) { - if (event.keyCode === 13) { - event.preventDefault(); - var val = UIUtil.escapeHtml(this.value); - this.value = ''; - if (!NicknameHandler.getNickname()) { - NicknameHandler.setNickname(val); - - return; - } - } - }); - - var usermsg = $('#usermsg'); - usermsg.keydown(function (event) { - if (event.keyCode === 13) { - event.preventDefault(); - var value = this.value; - usermsg.val('').trigger('autosize.resize'); - this.focus(); - var command = new CommandsProcessor(value); - if(command.isCommand()) { - command.processCommand(); - } - else { - var message = UIUtil.escapeHtml(value); - APP.xmpp.sendChatMessage(message, NicknameHandler.getNickname()); - } - } - }); - - var onTextAreaResize = function () { - resizeChatConversation(); - Chat.scrollChatToBottom(); - }; - usermsg.autosize({callback: onTextAreaResize}); - - $("#chatspace").bind("shown", - function () { - unreadMessages = 0; - setVisualNotification(false); - }); - - addSmileys(); - }; - - /** - * Appends the given message to the chat conversation. - */ - my.updateChatConversation = - function (from, displayName, message, myjid, stamp) { - var divClassName = ''; - - if (APP.xmpp.myJid() === from) { - divClassName = "localuser"; - } - else { - divClassName = "remoteuser"; - - if (!Chat.isVisible()) { - unreadMessages++; - UIUtil.playSoundNotification('chatNotification'); - setVisualNotification(true); - } - } - - // replace links and smileys - // Strophe already escapes special symbols on sending, - // so we escape here only tags to avoid double & - var escMessage = message.replace(//g, '>').replace(/\n/g, '
    '); - var escDisplayName = UIUtil.escapeHtml(displayName); - message = Replacement.processReplacements(escMessage); - - var messageContainer = - '
    '+ - '' + - '
    ' + escDisplayName + - '
    ' + '
    ' + getCurrentTime(stamp) + - '
    ' + '
    ' + message + '
    ' + - '
    '; - - $('#chatconversation').append(messageContainer); - $('#chatconversation').animate( - { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000); - }; - - /** - * Appends error message to the conversation - * @param errorMessage the received error message. - * @param originalText the original message. - */ - my.chatAddError = function(errorMessage, originalText) { - errorMessage = UIUtil.escapeHtml(errorMessage); - originalText = UIUtil.escapeHtml(originalText); - - $('#chatconversation').append( - '
    Error: ' + 'Your message' + - (originalText? (' \"'+ originalText + '\"') : "") + - ' was not sent.' + - (errorMessage? (' Reason: ' + errorMessage) : '') + '
    '); - $('#chatconversation').animate( - { scrollTop: $('#chatconversation')[0].scrollHeight}, 1000); - }; - - /** - * Sets the subject to the UI - * @param subject the subject - */ - my.chatSetSubject = function(subject) { - if (subject) - subject = subject.trim(); - $('#subject').html(Replacement.linkify(UIUtil.escapeHtml(subject))); - if(subject === "") { - $("#subject").css({display: "none"}); - } - else { - $("#subject").css({display: "block"}); - } - }; - - /** - * Sets the chat conversation mode. - */ - my.setChatConversationMode = function (isConversationMode) { - if (isConversationMode) { - $('#nickname').css({visibility: 'hidden'}); - $('#chatconversation').css({visibility: 'visible'}); - $('#usermsg').css({visibility: 'visible'}); - $('#smileysarea').css({visibility: 'visible'}); - $('#usermsg').focus(); - } - }; - - /** - * Resizes the chat area. - */ - my.resizeChat = function () { - var chatSize = require("../SidePanelToggler").getPanelSize(); - - $('#chatspace').width(chatSize[0]); - $('#chatspace').height(chatSize[1]); - - resizeChatConversation(); - }; - - /** - * Indicates if the chat is currently visible. - */ - my.isVisible = function () { - return $('#chatspace').is(":visible"); - }; - /** - * Shows and hides the window with the smileys - */ - my.toggleSmileys = toggleSmileys; - - /** - * Scrolls chat to the bottom. - */ - my.scrollChatToBottom = function() { - setTimeout(function () { - $('#chatconversation').scrollTop( - $('#chatconversation')[0].scrollHeight); - }, 5); - }; - - - return my; -}(Chat || {})); -module.exports = Chat; -},{"../../../../service/UI/UIEvents":160,"../../toolbars/ToolbarToggler":31,"../../util/NicknameHandler":34,"../../util/UIUtil":35,"../SidePanelToggler":22,"./Commands":24,"./Replacement":25,"./smileys.json":26}],24:[function(require,module,exports){ -/* global APP, require */ -var UIUtil = require("../../util/UIUtil"); - -/** - * List with supported commands. The keys are the names of the commands and - * the value is the function that processes the message. - * @type {{String: function}} - */ -var commands = { - "topic" : processTopic -}; - -/** - * Extracts the command from the message. - * @param message the received message - * @returns {string} the command - */ -function getCommand(message) { - if(message) { - for(var command in commands) { - if(message.indexOf("/" + command) == 0) - return command; - } - } - return ""; -} - -/** - * Processes the data for topic command. - * @param commandArguments the arguments of the topic command. - */ -function processTopic(commandArguments) { - var topic = UIUtil.escapeHtml(commandArguments); - APP.xmpp.setSubject(topic); -} - -/** - * Constructs a new CommandProccessor instance from a message that - * handles commands received via chat messages. - * @param message the message - * @constructor - */ -function CommandsProcessor(message) { - var command = getCommand(message); - - /** - * Returns the name of the command. - * @returns {String} the command - */ - this.getCommand = function() { - return command; - }; - - - var messageArgument = message.substr(command.length + 2); - - /** - * Returns the arguments of the command. - * @returns {string} - */ - this.getArgument = function() { - return messageArgument; - }; -} - -/** - * Checks whether this instance is valid command or not. - * @returns {boolean} - */ -CommandsProcessor.prototype.isCommand = function() { - if (this.getCommand()) - return true; - return false; -}; - -/** - * Processes the command. - */ -CommandsProcessor.prototype.processCommand = function() { - if(!this.isCommand()) - return; - - commands[this.getCommand()](this.getArgument()); -}; - -module.exports = CommandsProcessor; -},{"../../util/UIUtil":35}],25:[function(require,module,exports){ -var Smileys = require("./smileys.json"); -/** - * Processes links and smileys in "body" - */ -function processReplacements(body) -{ - //make links clickable - body = linkify(body); - - //add smileys - body = smilify(body); - - return body; -} - -/** - * Finds and replaces all links in the links in "body" - * with their - */ -function linkify(inputText) -{ - var replacedText, replacePattern1, replacePattern2, replacePattern3; - - //URLs starting with http://, https://, or ftp:// - replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim; - replacedText = inputText.replace(replacePattern1, '$1'); - - //URLs starting with "www." (without // before it, or it'd re-link the ones done above). - replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim; - replacedText = replacedText.replace(replacePattern2, '$1$2'); - - //Change email addresses to mailto:: links. - replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim; - replacedText = replacedText.replace(replacePattern3, '$1'); - - return replacedText; -} - -/** - * Replaces common smiley strings with images - */ -function smilify(body) -{ - if(!body) { - return body; - } - - var regexs = Smileys["regexs"]; - for(var smiley in regexs) { - if(regexs.hasOwnProperty(smiley)) { - body = body.replace(regexs[smiley], - ''); - } - } - - return body; -} - -module.exports = { - processReplacements: processReplacements, - linkify: linkify -}; - -},{"./smileys.json":26}],26:[function(require,module,exports){ -module.exports={ - "smileys": { - "smiley1": ":)", - "smiley2": ":(", - "smiley3": ":D", - "smiley4": "(y)", - "smiley5": " :P", - "smiley6": "(wave)", - "smiley7": "(blush)", - "smiley8": "(chuckle)", - "smiley9": "(shocked)", - "smiley10": ":*", - "smiley11": "(n)", - "smiley12": "(search)", - "smiley13": " <3", - "smiley14": "(oops)", - "smiley15": "(angry)", - "smiley16": "(angel)", - "smiley17": "(sick)", - "smiley18": ";(", - "smiley19": "(bomb)", - "smiley20": "(clap)", - "smiley21": " ;)" - }, - "regexs": { - "smiley2": /(:-\(\(|:-\(|:\(\(|:\(|\(sad\))/gi, - "smiley3": /(:-\)\)|:\)\)|\(lol\)|:-D|:D)/gi, - "smiley1": /(:-\)|:\))/gi, - "smiley4": /(\(y\)|\(Y\)|\(ok\))/gi, - "smiley5": /(:-P|:P|:-p|:p)/gi, - "smiley6": /(\(wave\))/gi, - "smiley7": /(\(blush\))/gi, - "smiley8": /(\(chuckle\))/gi, - "smiley9": /(:-0|\(shocked\))/gi, - "smiley10": /(:-\*|:\*|\(kiss\))/gi, - "smiley11": /(\(n\))/gi, - "smiley12": /(\(search\))/g, - "smiley13": /(<3|<3|&lt;3|\(L\)|\(l\)|\(H\)|\(h\))/gi, - "smiley14": /(\(oops\))/gi, - "smiley15": /(\(angry\))/gi, - "smiley16": /(\(angel\))/gi, - "smiley17": /(\(sick\))/gi, - "smiley18": /(;-\(\(|;\(\(|;-\(|;\(|:"\(|:"-\(|:~-\(|:~\(|\(upset\))/gi, - "smiley19": /(\(bomb\))/gi, - "smiley20": /(\(clap\))/gi, - "smiley21": /(;-\)|;\)|;-\)\)|;\)\)|;-D|;D|\(wink\))/gi - } -} - -},{}],27:[function(require,module,exports){ -/* global $, APP, Strophe */ -var Avatar = require('../../avatar/Avatar'); - -var numberOfContacts = 0; -var notificationInterval; - -/** - * Updates the number of participants in the contact list button and sets - * the glow - * @param delta indicates whether a new user has joined (1) or someone has - * left(-1) - */ -function updateNumberOfParticipants(delta) { - numberOfContacts += delta; - if (numberOfContacts === 1) { - // when the user is alone we don't show the number of participants - $("#numberOfParticipants").text(''); - ContactList.setVisualNotification(false); - } else if (numberOfContacts > 1) { - ContactList.setVisualNotification(!ContactList.isVisible()); - $("#numberOfParticipants").text(numberOfContacts); - } else { - console.error("Invalid number of participants: " + numberOfContacts); - } -} - -/** - * Creates the avatar element. - * - * @return {object} the newly created avatar element - */ -function createAvatar(jid) { - var avatar = document.createElement('img'); - avatar.className = "icon-avatar avatar"; - avatar.src = Avatar.getContactListUrl(jid); - - return avatar; -} - -/** - * Creates the display name paragraph. - * - * @param displayName the display name to set - */ -function createDisplayNameParagraph(key, displayName) { - var p = document.createElement('p'); - if(displayName) - p.innerText = displayName; - else if(key) { - p.setAttribute("data-i18n",key); - p.innerText = APP.translation.translateString(key); - } - - return p; -} - - -function stopGlowing(glower) { - window.clearInterval(notificationInterval); - notificationInterval = false; - glower.removeClass('glowing'); - if (!ContactList.isVisible()) { - glower.removeClass('active'); - } -} - -/** - * Contact list. - */ -var ContactList = { - /** - * Indicates if the chat is currently visible. - * - * @return true if the chat is currently visible, false - - * otherwise - */ - isVisible: function () { - return $('#contactlist').is(":visible"); - }, - - /** - * Adds a contact for the given peerJid if such doesn't yet exist. - * - * @param peerJid the peerJid corresponding to the contact - */ - ensureAddContact: function (peerJid) { - var resourceJid = Strophe.getResourceFromJid(peerJid); - - var contact = $('#contacts>li[id="' + resourceJid + '"]'); - - if (!contact || contact.length <= 0) - ContactList.addContact(peerJid); - }, - - /** - * Adds a contact for the given peer jid. - * - * @param peerJid the jid of the contact to add - */ - addContact: function (peerJid) { - var resourceJid = Strophe.getResourceFromJid(peerJid); - - var contactlist = $('#contacts'); - - var newContact = document.createElement('li'); - newContact.id = resourceJid; - newContact.className = "clickable"; - newContact.onclick = function (event) { - if (event.currentTarget.className === "clickable") { - $(ContactList).trigger('contactclicked', [peerJid]); - } - }; - - newContact.appendChild(createAvatar(peerJid)); - newContact.appendChild(createDisplayNameParagraph("participant")); - - if (resourceJid === APP.xmpp.myResource()) { - contactlist.prepend(newContact); - } - else { - contactlist.append(newContact); - } - updateNumberOfParticipants(1); - }, - - /** - * Removes a contact for the given peer jid. - * - * @param peerJid the peerJid corresponding to the contact to remove - */ - removeContact: function (peerJid) { - var resourceJid = Strophe.getResourceFromJid(peerJid); - - var contact = $('#contacts>li[id="' + resourceJid + '"]'); - - if (contact && contact.length > 0) { - var contactlist = $('#contactlist>ul'); - - contactlist.get(0).removeChild(contact.get(0)); - - updateNumberOfParticipants(-1); - } - }, - - setVisualNotification: function (show, stopGlowingIn) { - var glower = $('#contactListButton'); - - if (show && !notificationInterval) { - notificationInterval = window.setInterval(function () { - glower.toggleClass('active glowing'); - }, 800); - } - else if (!show && notificationInterval) { - stopGlowing(glower); - } - if (stopGlowingIn) { - setTimeout(function () { - stopGlowing(glower); - }, stopGlowingIn); - } - }, - - setClickable: function (resourceJid, isClickable) { - var contact = $('#contacts>li[id="' + resourceJid + '"]'); - if (isClickable) { - contact.addClass('clickable'); - } else { - contact.removeClass('clickable'); - } - }, - - onDisplayNameChange: function (peerJid, displayName) { - if (peerJid === 'localVideoContainer') - peerJid = APP.xmpp.myJid(); - - var resourceJid = Strophe.getResourceFromJid(peerJid); - - var contactName = $('#contacts #' + resourceJid + '>p'); - - if (contactName && displayName && displayName.length > 0) - contactName.html(displayName); - }, - - userAvatarChanged: function (resourceJid, contactListUrl) { - // set the avatar in the contact list - var contact = $('#' + resourceJid + '>img'); - if (contact && contact.length > 0) { - contact.get(0).src = contactListUrl; - } - - } -}; - -module.exports = ContactList; -},{"../../avatar/Avatar":18}],28:[function(require,module,exports){ -/* global APP, $ */ -var Avatar = require("../../avatar/Avatar"); -var Settings = require("./../../../settings/Settings"); -var UIUtil = require("../../util/UIUtil"); -var languages = require("../../../../service/translation/languages"); - -function generateLanguagesSelectBox() { - var currentLang = APP.translation.getCurrentLanguage(); - var html = ""; -} - - -var SettingsMenu = { - - init: function () { - var startMutedSelector = $("#startMutedOptions"); - startMutedSelector.before(generateLanguagesSelectBox()); - APP.translation.translateElement($("#languages_selectbox")); - $('#settingsmenu>input').keyup(function(event){ - if(event.keyCode === 13) {//enter - SettingsMenu.update(); - } - }); - - if (APP.xmpp.isModerator()) { - startMutedSelector.css("display", "block"); - } - else { - startMutedSelector.css("display", "none"); - } - - $("#updateSettings").click(function () { - SettingsMenu.update(); - }); - }, - - onRoleChanged: function () { - if(APP.xmpp.isModerator()) { - $("#startMutedOptions").css("display", "block"); - } - else { - $("#startMutedOptions").css("display", "none"); - } - }, - - setStartMuted: function (audio, video) { - $("#startAudioMuted").attr("checked", audio); - $("#startVideoMuted").attr("checked", video); - }, - - update: function() { - var newDisplayName = UIUtil.escapeHtml($('#setDisplayName').get(0).value); - var newEmail = UIUtil.escapeHtml($('#setEmail').get(0).value); - - if(newDisplayName) { - var displayName = Settings.setDisplayName(newDisplayName); - APP.xmpp.addToPresence("displayName", displayName, true); - } - - var language = $("#languages_selectbox").val(); - APP.translation.setLanguage(language); - Settings.setLanguage(language); - - APP.xmpp.addToPresence("email", newEmail); - var email = Settings.setEmail(newEmail); - - var startAudioMuted = ($("#startAudioMuted").is(":checked")); - var startVideoMuted = ($("#startVideoMuted").is(":checked")); - APP.xmpp.addToPresence("startMuted", - [startAudioMuted, startVideoMuted]); - - Avatar.setUserAvatar(APP.xmpp.myJid(), email); - }, - - isVisible: function() { - return $('#settingsmenu').is(':visible'); - }, - - setDisplayName: function(newDisplayName) { - var displayName = Settings.setDisplayName(newDisplayName); - $('#setDisplayName').get(0).value = displayName; - }, - - onDisplayNameChange: function(peerJid, newDisplayName) { - if(peerJid === 'localVideoContainer' || - peerJid === APP.xmpp.myJid()) { - this.setDisplayName(newDisplayName); - } - }, - changeAvatar: function (thumbUrl) { - $('#avatar').get(0).src = thumbUrl; - } -}; - - -module.exports = SettingsMenu; -},{"../../../../service/translation/languages":165,"../../avatar/Avatar":18,"../../util/UIUtil":35,"./../../../settings/Settings":49}],29:[function(require,module,exports){ -/* global $ */ -var PanelToggler = require("../side_pannels/SidePanelToggler"); - -var buttonHandlers = { - "bottom_toolbar_contact_list": function () { - BottomToolbar.toggleContactList(); - }, - "bottom_toolbar_film_strip": function () { - BottomToolbar.toggleFilmStrip(); - }, - "bottom_toolbar_chat": function () { - BottomToolbar.toggleChat(); - } -}; - -var BottomToolbar = (function (my) { - my.init = function () { - for(var k in buttonHandlers) - $("#" + k).click(buttonHandlers[k]); - }; - - my.toggleChat = function() { - PanelToggler.toggleChat(); - }; - - my.toggleContactList = function() { - PanelToggler.toggleContactList(); - }; - - my.toggleFilmStrip = function() { - var filmstrip = $("#remoteVideos"); - filmstrip.toggleClass("hidden"); - }; - - $(document).bind("remotevideo.resized", function (event, width, height) { - var bottom = (height - $('#bottomToolbar').outerHeight())/2 + 18; - - $('#bottomToolbar').css({bottom: bottom + 'px'}); - }); - - return my; -}(BottomToolbar || {})); - -module.exports = BottomToolbar; - -},{"../side_pannels/SidePanelToggler":22}],30:[function(require,module,exports){ -/* global APP, $, buttonClick, config, lockRoom, interfaceConfig, setSharedKey, - Util */ -var messageHandler = require("../util/MessageHandler"); -var BottomToolbar = require("./BottomToolbar"); -var Prezi = require("../prezi/Prezi"); -var Etherpad = require("../etherpad/Etherpad"); -var PanelToggler = require("../side_pannels/SidePanelToggler"); -var Authentication = require("../authentication/Authentication"); -var UIUtil = require("../util/UIUtil"); -var AuthenticationEvents - = require("../../../service/authentication/AuthenticationEvents"); - -var roomUrl = null; -var sharedKey = ''; -var UI = null; -var recordingToaster = null; - -var buttonHandlers = { - "toolbar_button_mute": function () { - return APP.UI.toggleAudio(); - }, - "toolbar_button_camera": function () { - return APP.UI.toggleVideo(); - }, - /*"toolbar_button_authentication": function () { - return Toolbar.authenticateClicked(); - },*/ - "toolbar_button_record": function () { - return toggleRecording(); - }, - "toolbar_button_security": function () { - return Toolbar.openLockDialog(); - }, - "toolbar_button_link": function () { - return Toolbar.openLinkDialog(); - }, - "toolbar_button_chat": function () { - return BottomToolbar.toggleChat(); - }, - "toolbar_button_prezi": function () { - return Prezi.openPreziDialog(); - }, - "toolbar_button_etherpad": function () { - return Etherpad.toggleEtherpad(0); - }, - "toolbar_button_desktopsharing": function () { - return APP.desktopsharing.toggleScreenSharing(); - }, - "toolbar_button_fullScreen": function() { - UIUtil.buttonClick("#toolbar_button_fullScreen", "icon-full-screen icon-exit-full-screen"); - return Toolbar.toggleFullScreen(); - }, - "toolbar_button_sip": function () { - return callSipButtonClicked(); - }, - "toolbar_button_dialpad": function () { - return dialpadButtonClicked(); - }, - "toolbar_button_settings": function () { - PanelToggler.toggleSettingsMenu(); - }, - "toolbar_button_hangup": function () { - return hangup(); - }, - "toolbar_button_login": function () { - Toolbar.authenticateClicked(); - }, - "toolbar_button_logout": function () { - // Ask for confirmation - messageHandler.openTwoButtonDialog( - "dialog.logoutTitle", - null, - "dialog.logoutQuestion", - null, - false, - "dialog.Yes", - function (evt, yes) { - if (yes) { - APP.xmpp.logout(function (url) { - if (url) { - window.location.href = url; - } else { - hangup(); - } - }); - } - }); - } -}; - -function hangup() { - APP.xmpp.disposeConference(); - if(config.enableWelcomePage) { - setTimeout(function() { - window.localStorage.welcomePageDisabled = false; - window.location.pathname = "/"; - }, 10000); - - } - - var title = APP.translation.generateTranslationHTML( - "dialog.sessTerminated"); - var msg = APP.translation.generateTranslationHTML( - "dialog.hungUp"); - var button = APP.translation.generateTranslationHTML( - "dialog.joinAgain"); - var buttons = []; - buttons.push({title: button, value: true}); - - UI.messageHandler.openDialog( - title, - msg, - true, - buttons, - function(event, value, message, formVals) { - window.location.reload(); - return false; - } - ); -} - -/** - * Starts or stops the recording for the conference. - */ - -function toggleRecording(predefinedToken) { - APP.xmpp.toggleRecording(function (callback) { - if (predefinedToken) { - callback(UIUtil.escapeHtml(predefinedToken)); - return; - } - - var msg = APP.translation.generateTranslationHTML( - "dialog.recordingToken"); - var token = APP.translation.translateString("dialog.token"); - APP.UI.messageHandler.openTwoButtonDialog(null, null, null, - '

    ' + msg + '

    ' + - '', - false, - "dialog.Save", - function (e, v, m, f) { - if (v) { - var token = f.recordingToken; - - if (token) { - callback(UIUtil.escapeHtml(token)); - } - } - }, - null, - function () { }, - ':input:first' - ); - }, Toolbar.setRecordingButtonState); -} - -/** - * Locks / unlocks the room. - */ -function lockRoom(lock) { - var currentSharedKey = ''; - if (lock) - currentSharedKey = sharedKey; - - APP.xmpp.lockRoom(currentSharedKey, function (res) { - // password is required - if (sharedKey) { - console.log('set room password'); - Toolbar.lockLockButton(); - } - else { - console.log('removed room password'); - Toolbar.unlockLockButton(); - } - }, function (err) { - console.warn('setting password failed', err); - messageHandler.showError("dialog.lockTitle", - "dialog.lockMessage"); - Toolbar.setSharedKey(''); - }, function () { - console.warn('room passwords not supported'); - messageHandler.showError("dialog.warning", - "dialog.passwordNotSupported"); - Toolbar.setSharedKey(''); - }); -} - -/** - * Invite participants to conference. - */ -function inviteParticipants() { - if (roomUrl === null) - return; - - var sharedKeyText = ""; - if (sharedKey && sharedKey.length > 0) { - sharedKeyText = - APP.translation.translateString("email.sharedKey", - {sharedKey: sharedKey}); - sharedKeyText = sharedKeyText.replace(/\n/g, "%0D%0A"); - } - - var supportedBrowsers = "Chromium, Google Chrome " + - APP.translation.translateString("email.and") + " Opera"; - var conferenceName = roomUrl.substring(roomUrl.lastIndexOf('/') + 1); - var subject = APP.translation.translateString("email.subject", - {appName:interfaceConfig.APP_NAME, conferenceName: conferenceName}); - var body = APP.translation.translateString("email.body", - {appName:interfaceConfig.APP_NAME, sharedKeyText: sharedKeyText, - roomUrl: roomUrl, supportedBrowsers: supportedBrowsers}); - body = body.replace(/\n/g, "%0D%0A"); - - if (window.localStorage.displayname) { - body += "%0D%0A%0D%0A" + window.localStorage.displayname; - } - - if (interfaceConfig.INVITATION_POWERED_BY) { - body += "%0D%0A%0D%0A--%0D%0Apowered by jitsi.org"; - } - - window.open("mailto:?subject=" + subject + "&body=" + body, '_blank'); -} - -function dialpadButtonClicked() { - //TODO show the dialpad box -} - -function callSipButtonClicked() { - var defaultNumber - = config.defaultSipNumber ? config.defaultSipNumber : ''; - - var sipMsg = APP.translation.generateTranslationHTML( - "dialog.sipMsg"); - messageHandler.openTwoButtonDialog(null, null, null, - '

    ' + sipMsg + '

    ' + - '', - false, - "dialog.Dial", - function (e, v, m, f) { - if (v) { - var numberInput = f.sipNumber; - if (numberInput) { - APP.xmpp.dial( - numberInput, 'fromnumber', UI.getRoomName(), sharedKey); - } - } - }, - null, null, ':input:first' - ); -} - -var Toolbar = (function (my) { - - my.init = function (ui) { - for(var k in buttonHandlers) - $("#" + k).click(buttonHandlers[k]); - UI = ui; - // Update login info - APP.xmpp.addListener( - AuthenticationEvents.IDENTITY_UPDATED, - function (authenticationEnabled, userIdentity) { - - var loggedIn = false; - if (userIdentity) { - loggedIn = true; - } - - Toolbar.showAuthenticateButton(authenticationEnabled); - - if (authenticationEnabled) { - Toolbar.setAuthenticatedIdentity(userIdentity); - - Toolbar.showLoginButton(!loggedIn); - Toolbar.showLogoutButton(loggedIn); - } - } - ); - }; - - /** - * Sets shared key - * @param sKey the shared key - */ - my.setSharedKey = function (sKey) { - sharedKey = sKey; - }; - - my.authenticateClicked = function () { - Authentication.focusAuthenticationWindow(); - if (!APP.xmpp.isExternalAuthEnabled()) { - Authentication.xmppAuthenticate(); - return; - } - // Get authentication URL - if (!APP.xmpp.isMUCJoined()) { - APP.xmpp.getLoginUrl(UI.getRoomName(), function (url) { - // If conference has not been started yet - redirect to login page - window.location.href = url; - }); - } else { - APP.xmpp.getPopupLoginUrl(UI.getRoomName(), function (url) { - // Otherwise - open popup with authentication URL - var authenticationWindow = Authentication.createAuthenticationWindow( - function () { - // On popup closed - retry room allocation - APP.xmpp.allocateConferenceFocus( - APP.UI.getRoomName(), - function () { console.info("AUTH DONE"); } - ); - }, url); - if (!authenticationWindow) { - messageHandler.openMessageDialog( - null, "dialog.popupError"); - } - }); - } - }; - - /** - * Updates the room invite url. - */ - my.updateRoomUrl = function (newRoomUrl) { - roomUrl = newRoomUrl; - - // If the invite dialog has been already opened we update the information. - var inviteLink = document.getElementById('inviteLinkRef'); - if (inviteLink) { - inviteLink.value = roomUrl; - inviteLink.select(); - $('#inviteLinkRef').parent() - .find('button[value=true]').prop('disabled', false); - } - }; - - /** - * Disables and enables some of the buttons. - */ - my.setupButtonsFromConfig = function () { - if (config.disablePrezi) { - $("#toolbar_button_prezi").css({display: "none"}); - } - }; - - /** - * Opens the lock room dialog. - */ - my.openLockDialog = function () { - // Only the focus is able to set a shared key. - if (!APP.xmpp.isModerator()) { - if (sharedKey) { - messageHandler.openMessageDialog(null, - "dialog.passwordError"); - } else { - messageHandler.openMessageDialog(null, "dialog.passwordError2"); - } - } else { - if (sharedKey) { - messageHandler.openTwoButtonDialog(null, null, - "dialog.passwordCheck", - null, - false, - "dialog.Remove", - function (e, v) { - if (v) { - Toolbar.setSharedKey(''); - lockRoom(false); - } - }); - } else { - var msg = APP.translation.generateTranslationHTML( - "dialog.passwordMsg"); - var yourPassword = APP.translation.translateString( - "dialog.yourPassword"); - messageHandler.openTwoButtonDialog(null, null, null, - '

    ' + msg + '

    ' + - '', - false, - "dialog.Save", - function (e, v, m, f) { - if (v) { - var lockKey = f.lockKey; - - if (lockKey) { - Toolbar.setSharedKey( - UIUtil.escapeHtml(lockKey)); - lockRoom(true); - } - } - }, - null, null, 'input:first' - ); - } - } - }; - - /** - * Opens the invite link dialog. - */ - my.openLinkDialog = function () { - var inviteAttreibutes; - - if (roomUrl === null) { - inviteAttreibutes = 'data-i18n="[value]roomUrlDefaultMsg" value="' + - APP.translation.translateString("roomUrlDefaultMsg") + '"'; - } else { - inviteAttreibutes = "value=\"" + encodeURI(roomUrl) + "\""; - } - messageHandler.openTwoButtonDialog("dialog.shareLink", - null, null, - '', - false, - "dialog.Invite", - function (e, v) { - if (v) { - if (roomUrl) { - inviteParticipants(); - } - } - }, - function (event) { - if (roomUrl) { - document.getElementById('inviteLinkRef').select(); - } else { - if (event && event.target) - $(event.target) - .find('button[value=true]').prop('disabled', true); - } - } - ); - }; - - /** - * Opens the settings dialog. - * FIXME: not used ? - */ - my.openSettingsDialog = function () { - var settings1 = APP.translation.generateTranslationHTML( - "dialog.settings1"); - var settings2 = APP.translation.generateTranslationHTML( - "dialog.settings2"); - var settings3 = APP.translation.generateTranslationHTML( - "dialog.settings3"); - - var yourPassword = APP.translation.translateString( - "dialog.yourPassword"); - - messageHandler.openTwoButtonDialog(null, - '

    ' + settings1 + '

    ' + - '' + - settings2 + '
    ' + - '' + - settings3 + - '', - null, - null, - false, - "dialog.Save", - function () { - document.getElementById('lockKey').focus(); - }, - function (e, v) { - if (v) { - if ($('#initMuted').is(":checked")) { - // it is checked - } - - if ($('#requireNicknames').is(":checked")) { - // it is checked - } - /* - var lockKey = document.getElementById('lockKey'); - - if (lockKey.value) { - setSharedKey(lockKey.value); - lockRoom(true); - } - */ - } - } - ); - }; - - /** - * Toggles the application in and out of full screen mode - * (a.k.a. presentation mode in Chrome). - */ - my.toggleFullScreen = function () { - var fsElement = document.documentElement; - - if (!document.mozFullScreen && !document.webkitIsFullScreen) { - //Enter Full Screen - if (fsElement.mozRequestFullScreen) { - fsElement.mozRequestFullScreen(); - } - else { - fsElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT); - } - } else { - //Exit Full Screen - if (document.mozCancelFullScreen) { - document.mozCancelFullScreen(); - } else { - document.webkitCancelFullScreen(); - } - } - }; - /** - * Unlocks the lock button state. - */ - my.unlockLockButton = function () { - if ($("#toolbar_button_security").hasClass("icon-security-locked")) - UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked"); - }; - /** - * Updates the lock button state to locked. - */ - my.lockLockButton = function () { - if ($("#toolbar_button_security").hasClass("icon-security")) - UIUtil.buttonClick("#toolbar_button_security", "icon-security icon-security-locked"); - }; - - /** - * Shows or hides authentication button - * @param show true to show or false to hide - */ - my.showAuthenticateButton = function (show) { - if (show) { - $('#authentication').css({display: "inline"}); - } - else { - $('#authentication').css({display: "none"}); - } - }; - - // Shows or hides the 'recording' button. - my.showRecordingButton = function (show) { - if (!config.enableRecording) { - return; - } - - if (show) { - $('#toolbar_button_record').css({display: "inline-block"}); - } - else { - $('#toolbar_button_record').css({display: "none"}); - } - }; - - // Sets the state of the recording button - my.setRecordingButtonState = function (recordingState) { - var selector = $('#toolbar_button_record'); - - if (recordingState === 'on') { - selector.removeClass("icon-recEnable"); - selector.addClass("icon-recEnable active"); - - $("#largeVideo").toggleClass("videoMessageFilter", true); - var recordOnKey = "recording.on"; - $('#videoConnectionMessage').attr("data-i18n", recordOnKey); - $('#videoConnectionMessage').text(APP.translation.translateString(recordOnKey)); - - setTimeout(function(){ - $("#largeVideo").toggleClass("videoMessageFilter", false); - $('#videoConnectionMessage').css({display: "none"}); - }, 1500); - - recordingToaster = messageHandler.notify(null, "recording.toaster", null, - null, null, {timeOut: 0, closeButton: null, tapToDismiss: false}); - } else if (recordingState === 'off') { - selector.removeClass("icon-recEnable active"); - selector.addClass("icon-recEnable"); - - $("#largeVideo").toggleClass("videoMessageFilter", false); - $('#videoConnectionMessage').css({display: "none"}); - - if (recordingToaster) - messageHandler.remove(recordingToaster); - - } else if (recordingState === 'pending') { - selector.removeClass("icon-recEnable active"); - selector.addClass("icon-recEnable"); - - $("#largeVideo").toggleClass("videoMessageFilter", true); - var recordPendingKey = "recording.pending"; - $('#videoConnectionMessage').attr("data-i18n", recordPendingKey); - $('#videoConnectionMessage').text(APP.translation.translateString(recordPendingKey)); - $('#videoConnectionMessage').css({display: "block"}); - } - }; - - // checks whether recording is enabled and whether we have params to start automatically recording - my.checkAutoRecord = function () { - if (config.enableRecording && config.autoRecord) { - toggleRecording(config.autoRecordToken); - } - } - - // Shows or hides SIP calls button - my.showSipCallButton = function (show) { - if (APP.xmpp.isSipGatewayEnabled() && show) { - $('#toolbar_button_sip').css({display: "inline-block"}); - } else { - $('#toolbar_button_sip').css({display: "none"}); - } - }; - - // Shows or hides the dialpad button - my.showDialPadButton = function (show) { - if (show) { - $('#toolbar_button_dialpad').css({display: "inline-block"}); - } else { - $('#toolbar_button_dialpad').css({display: "none"}); - } - }; - - /** - * Displays user authenticated identity name(login). - * @param authIdentity identity name to be displayed. - */ - my.setAuthenticatedIdentity = function (authIdentity) { - if (authIdentity) { - var selector = $('#toolbar_auth_identity'); - selector.css({display: "list-item"}); - selector.text(authIdentity); - } else { - $('#toolbar_auth_identity').css({display: "none"}); - } - }; - - /** - * Shows/hides login button. - * @param show true to show - */ - my.showLoginButton = function (show) { - if (show) { - $('#toolbar_button_login').css({display: "list-item"}); - } else { - $('#toolbar_button_login').css({display: "none"}); - } - }; - - /** - * Shows/hides logout button. - * @param show true to show - */ - my.showLogoutButton = function (show) { - if (show) { - $('#toolbar_button_logout').css({display: "list-item"}); - } else { - $('#toolbar_button_logout').css({display: "none"}); - } - }; - - /** - * Sets the state of the button. The button has blue glow if desktop - * streaming is active. - * @param active the state of the desktop streaming. - */ - my.changeDesktopSharingButtonState = function (active) { - var button = $("#toolbar_button_desktopsharing"); - if (active) { - button.addClass("glow"); - } else { - button.removeClass("glow"); - } - }; - - return my; -}(Toolbar || {})); - -module.exports = Toolbar; -},{"../../../service/authentication/AuthenticationEvents":161,"../authentication/Authentication":16,"../etherpad/Etherpad":19,"../prezi/Prezi":20,"../side_pannels/SidePanelToggler":22,"../util/MessageHandler":33,"../util/UIUtil":35,"./BottomToolbar":29}],31:[function(require,module,exports){ -/* global APP, config, $, interfaceConfig, Moderator, - DesktopStreaming.showDesktopSharingButton */ - -var toolbarTimeoutObject, - toolbarTimeout = interfaceConfig.INITIAL_TOOLBAR_TIMEOUT; - -function showDesktopSharingButton() { - if (APP.desktopsharing.isDesktopSharingEnabled()) { - $('#toolbar_button_desktopsharing').css({display: "inline-block"}); - } else { - $('#toolbar_button_desktopsharing').css({display: "none"}); - } -} - -/** - * Hides the toolbar. - */ -function hideToolbar() { - if(config.alwaysVisibleToolbar) - return; - - var header = $("#header"), - bottomToolbar = $("#bottomToolbar"); - var isToolbarHover = false; - header.find('*').each(function () { - var id = $(this).attr('id'); - if ($("#" + id + ":hover").length > 0) { - isToolbarHover = true; - } - }); - if ($("#bottomToolbar:hover").length > 0) { - isToolbarHover = true; - } - - clearTimeout(toolbarTimeoutObject); - toolbarTimeoutObject = null; - - if (!isToolbarHover) { - header.hide("slide", { direction: "up", duration: 300}); - $('#subject').animate({top: "-=40"}, 300); - if ($("#remoteVideos").hasClass("hidden")) { - bottomToolbar.hide( - "slide", {direction: "right", duration: 300}); - } - } - else { - toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout); - } -} - -var ToolbarToggler = { - /** - * Shows the main toolbar. - */ - showToolbar: function () { - if (interfaceConfig.filmStripOnly) - return; - var header = $("#header"), - bottomToolbar = $("#bottomToolbar"); - if (!header.is(':visible') || !bottomToolbar.is(":visible")) { - header.show("slide", { direction: "up", duration: 300}); - $('#subject').animate({top: "+=40"}, 300); - if (!bottomToolbar.is(":visible")) { - bottomToolbar.show( - "slide", {direction: "right", duration: 300}); - } - - if (toolbarTimeoutObject) { - clearTimeout(toolbarTimeoutObject); - toolbarTimeoutObject = null; - } - toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout); - toolbarTimeout = interfaceConfig.TOOLBAR_TIMEOUT; - } - - if (APP.xmpp.isModerator()) - { -// TODO: Enable settings functionality. -// Need to uncomment the settings button in index.html. -// $('#settingsButton').css({visibility:"visible"}); - } - - // Show/hide desktop sharing button - showDesktopSharingButton(); - }, - - /** - * Docks/undocks the toolbar. - * - * @param isDock indicates what operation to perform - */ - dockToolbar: function (isDock) { - if (interfaceConfig.filmStripOnly) - return; - - if (isDock) { - // First make sure the toolbar is shown. - if (!$('#header').is(':visible')) { - this.showToolbar(); - } - - // Then clear the time out, to dock the toolbar. - if (toolbarTimeoutObject) { - clearTimeout(toolbarTimeoutObject); - toolbarTimeoutObject = null; - } - } - else { - if (!$('#header').is(':visible')) { - this.showToolbar(); - } - else { - toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout); - } - } - }, - - showDesktopSharingButton: showDesktopSharingButton -}; - -module.exports = ToolbarToggler; -},{}],32:[function(require,module,exports){ -/* global $ */ -var JitsiPopover = (function () { - /** - * Constructs new JitsiPopover and attaches it to the element - * @param element jquery selector - * @param options the options for the popover. - * @constructor - */ - function JitsiPopover(element, options) - { - this.options = { - skin: "white", - content: "" - }; - if(options) - { - if(options.skin) - this.options.skin = options.skin; - - if(options.content) - this.options.content = options.content; - } - - this.elementIsHovered = false; - this.popoverIsHovered = false; - this.popoverShown = false; - - element.data("jitsi_popover", this); - this.element = element; - this.template = '
    ' + - '
    '; - var self = this; - this.element.on("mouseenter", function () { - self.elementIsHovered = true; - self.show(); - }).on("mouseleave", function () { - self.elementIsHovered = false; - setTimeout(function () { - self.hide(); - }, 10); - }); - } - - /** - * Shows the popover - */ - JitsiPopover.prototype.show = function () { - if(!JitsiPopover.enabled) - return; - this.createPopover(); - this.popoverShown = true; - }; - - /** - * Hides the popover - */ - JitsiPopover.prototype.hide = function () { - if(!this.elementIsHovered && !this.popoverIsHovered && - this.popoverShown) { - this.forceHide(); - } - }; - - /** - * Hides the popover. - */ - JitsiPopover.prototype.forceHide = function () { - $(".jitsipopover").remove(); - this.popoverShown = false; - }; - - /** - * Creates the popover html. - */ - JitsiPopover.prototype.createPopover = function () { - $("body").append(this.template); - $(".jitsipopover > .jitsipopover-content").html(this.options.content); - var self = this; - $(".jitsipopover").on("mouseenter", function () { - self.popoverIsHovered = true; - }).on("mouseleave", function () { - self.popoverIsHovered = false; - self.hide(); - }); - - this.refreshPosition(); - }; - - /** - * Refreshes the position of the popover. - */ - JitsiPopover.prototype.refreshPosition = function () { - $(".jitsipopover").position({ - my: "bottom", - at: "top", - collision: "fit", - of: this.element, - using: function (position, elements) { - var calcLeft = elements.target.left - elements.element.left + - elements.target.width/2; - $(".jitsipopover").css( - {top: position.top, left: position.left, display: "table"}); - $(".jitsipopover > .arrow").css({left: calcLeft}); - $(".jitsipopover > .jitsiPopupmenuPadding").css( - {left: calcLeft - 50}); - } - }); - }; - - /** - * Updates the content of popover. - * @param content new content - */ - JitsiPopover.prototype.updateContent = function (content) { - this.options.content = content; - if(!this.popoverShown) - return; - $(".jitsipopover").remove(); - this.createPopover(); - }; - - JitsiPopover.enabled = true; - - return JitsiPopover; -})(); - -module.exports = JitsiPopover; -},{}],33:[function(require,module,exports){ -/* global $, APP, jQuery, toastr */ - -/** - * Flag for enable/disable of the notifications. - * @type {boolean} - */ -var notificationsEnabled = true; - -var messageHandler = (function(my) { - - /** - * Shows a message to the user. - * - * @param titleKey the title of the message - * @param messageKey the text of the message - */ - my.openMessageDialog = function(titleKey, messageKey) { - var title = null; - if(titleKey) { - title = APP.translation.generateTranslationHTML(titleKey); - } - var message = APP.translation.generateTranslationHTML(messageKey); - $.prompt(message, - {title: title, persistent: false} - ); - }; - - /** - * Shows a message to the user with two buttons: first is given as a - * parameter and the second is Cancel. - * - * @param titleString the title of the message - * @param msgString the text of the message - * @param persistent boolean value which determines whether the message is - * persistent or not - * @param leftButton the fist button's text - * @param submitFunction function to be called on submit - * @param loadedFunction function to be called after the prompt is fully - * loaded - * @param closeFunction function to be called after the prompt is closed - * @param focus optional focus selector or button index to be focused after - * the dialog is opened - * @param defaultButton index of default button which will be activated when - * the user press 'enter'. Indexed from 0. - */ - my.openTwoButtonDialog = function(titleKey, titleString, msgKey, msgString, - persistent, leftButtonKey, submitFunction, loadedFunction, - closeFunction, focus, defaultButton) { - var buttons = []; - - var leftButton = APP.translation.generateTranslationHTML(leftButtonKey); - buttons.push({ title: leftButton, value: true}); - - var cancelButton - = APP.translation.generateTranslationHTML("dialog.Cancel"); - buttons.push({title: cancelButton, value: false}); - - var message = msgString, title = titleString; - if (titleKey) { - title = APP.translation.generateTranslationHTML(titleKey); - } - if (msgKey) { - message = APP.translation.generateTranslationHTML(msgKey); - } - $.prompt(message, { - title: title, - persistent: false, - buttons: buttons, - defaultButton: defaultButton, - focus: focus, - loaded: loadedFunction, - submit: submitFunction, - close: closeFunction - }); - }; - - /** - * Shows a message to the user with two buttons: first is given as a parameter and the second is Cancel. - * - * @param titleString the title of the message - * @param msgString the text of the message - * @param persistent boolean value which determines whether the message is - * persistent or not - * @param buttons object with the buttons. The keys must be the name of the - * button and value is the value that will be passed to - * submitFunction - * @param submitFunction function to be called on submit - * @param loadedFunction function to be called after the prompt is fully - * loaded - */ - my.openDialog = function (titleString, msgString, persistent, buttons, - submitFunction, loadedFunction) { - var args = { - title: titleString, - persistent: persistent, - buttons: buttons, - defaultButton: 1, - loaded: loadedFunction, - submit: submitFunction - }; - if (persistent) { - args.closeText = ''; - } - return new Impromptu(msgString, args); - }; - - /** - * Closes currently opened dialog. - */ - my.closeDialog = function () { - $.prompt.close(); - }; - - /** - * Shows a dialog with different states to the user. - * - * @param statesObject object containing all the states of the dialog. - */ - my.openDialogWithStates = function (statesObject, options) { - return new Impromptu(statesObject, options); - }; - - /** - * Opens new popup window for given url centered over current - * window. - * - * @param url the URL to be displayed in the popup window - * @param w the width of the popup window - * @param h the height of the popup window - * @param onPopupClosed optional callback function called when popup window - * has been closed. - * - * @returns {object} popup window object if opened successfully or undefined - * in case we failed to open it(popup blocked) - */ - my.openCenteredPopup = function (url, w, h, onPopupClosed) { - var l = window.screenX + (window.innerWidth / 2) - (w / 2); - var t = window.screenY + (window.innerHeight / 2) - (h / 2); - var popup = window.open( - url, '_blank', - 'top=' + t + ', left=' + l + ', width=' + w + ', height=' + h + ''); - if (popup && onPopupClosed) { - var pollTimer = window.setInterval(function () { - if (popup.closed !== false) { - window.clearInterval(pollTimer); - onPopupClosed(); - } - }, 200); - } - return popup; - }; - - /** - * Shows a dialog prompting the user to send an error report. - * - * @param titleKey the title of the message - * @param msgKey the text of the message - * @param error the error that is being reported - */ - my.openReportDialog = function(titleKey, msgKey, error) { - my.openMessageDialog(titleKey, msgKey); - console.log(error); - //FIXME send the error to the server - }; - - /** - * Shows an error dialog to the user. - * @param titleKey the title of the message. - * @param msgKey the text of the message. - */ - my.showError = function(titleKey, msgKey) { - - if (!titleKey) { - titleKey = "dialog.oops"; - } - if (!msgKey) { - msgKey = "dialog.defaultError"; - } - messageHandler.openMessageDialog(titleKey, msgKey); - }; - - /** - * Displayes notification. - * @param displayName display name of the participant that is associated with the notification. - * @param displayNameKey the key from the language file for the display name. - * @param cls css class for the notification - * @param messageKey the key from the language file for the text of the message. - * @param messageArguments object with the arguments for the message. - * @param options object with language options. - */ - my.notify = function(displayName, displayNameKey, - cls, messageKey, messageArguments, options) { - if(!notificationsEnabled) - return; - var displayNameSpan = '" + APP.translation.translateString(displayNameKey); - } - displayNameSpan += ""; - return toastr.info( - displayNameSpan + '
    ' + - '" + - APP.translation.translateString(messageKey, - messageArguments) + - '', null, options); - }; - - /** - * Removes the toaster. - * @param toasterElement - */ - my.remove = function(toasterElement) { - toasterElement.remove(); - }; - - /** - * Disables notifications. - */ - my.disableNotifications = function () { - notificationsEnabled = false; - }; - - /** - * Enables notifications. - */ - my.enableNotifications = function () { - notificationsEnabled = true; - }; - - return my; -}(messageHandler || {})); - -module.exports = messageHandler; - - - -},{}],34:[function(require,module,exports){ -var UIEvents = require("../../../service/UI/UIEvents"); - -var nickname = null; -var eventEmitter = null; - -var NicknameHandler = { - init: function (emitter) { - eventEmitter = emitter; - var storedDisplayName = window.localStorage.displayname; - if (storedDisplayName) { - nickname = storedDisplayName; - } - }, - setNickname: function (newNickname) { - if (!newNickname || nickname === newNickname) - return; - - nickname = newNickname; - window.localStorage.displayname = nickname; - eventEmitter.emit(UIEvents.NICKNAME_CHANGED, newNickname); - }, - getNickname: function () { - return nickname; - }, - addListener: function (type, listener) { - eventEmitter.on(type, listener); - } -}; - -module.exports = NicknameHandler; -},{"../../../service/UI/UIEvents":160}],35:[function(require,module,exports){ -/* global $ */ -/** - * Created by hristo on 12/22/14. - */ -module.exports = { - /** - * Returns the available video width. - */ - getAvailableVideoWidth: function (isVisible) { - var PanelToggler = require("../side_pannels/SidePanelToggler"); - if(typeof isVisible === "undefined" || isVisible === null) - isVisible = PanelToggler.isVisible(); - var rightPanelWidth - = isVisible ? PanelToggler.getPanelSize()[0] : 0; - - return window.innerWidth - rightPanelWidth; - }, - /** - * Changes the style class of the element given by id. - */ - buttonClick: function(id, classname) { - $(id).toggleClass(classname); // add the class to the clicked element - }, - /** - * Returns the text width for the given element. - * - * @param el the element - */ - getTextWidth: function (el) { - return (el.clientWidth + 1); - }, - - /** - * Returns the text height for the given element. - * - * @param el the element - */ - getTextHeight: function (el) { - return (el.clientHeight + 1); - }, - - /** - * Plays the sound given by id. - * - * @param id the identifier of the audio element. - */ - playSoundNotification: function (id) { - document.getElementById(id).play(); - }, - - /** - * Escapes the given text. - */ - escapeHtml: function (unsafeText) { - return $('
    ').text(unsafeText).html(); - }, - - imageToGrayScale: function (canvas) { - var context = canvas.getContext('2d'); - var imgData = context.getImageData(0, 0, canvas.width, canvas.height); - var pixels = imgData.data; - - for (var i = 0, n = pixels.length; i < n; i += 4) { - var grayscale - = pixels[i] * 0.3 + pixels[i+1] * 0.59 + pixels[i+2] * 0.11; - pixels[i ] = grayscale; // red - pixels[i+1] = grayscale; // green - pixels[i+2] = grayscale; // blue - // pixels[i+3] is alpha - } - // redraw the image in black & white - context.putImageData(imgData, 0, 0); - }, - - setTooltip: function (element, key, position) { - element.setAttribute("data-i18n", "[data-content]" + key); - element.setAttribute("data-toggle", "popover"); - element.setAttribute("data-placement", position); - element.setAttribute("data-html", true); - element.setAttribute("data-container", "body"); - }, - - /** - * Inserts given child element as the first one into the container. - * @param container the container to which new child element will be added - * @param newChild the new element that will be inserted into the container - */ - prependChild: function (container, newChild) { - var firstChild = container.childNodes[0]; - if (firstChild) { - container.insertBefore(newChild, firstChild); - } else { - container.appendChild(newChild); - } - } -}; -},{"../side_pannels/SidePanelToggler":22}],36:[function(require,module,exports){ -/* global APP, $ */ -var JitsiPopover = require("../util/JitsiPopover"); - -/** - * Constructs new connection indicator. - * @param videoContainer the video container associated with the indicator. - * @constructor - */ -function ConnectionIndicator(videoContainer, jid) { - this.videoContainer = videoContainer; - this.bandwidth = null; - this.packetLoss = null; - this.bitrate = null; - this.showMoreValue = false; - this.resolution = null; - this.transport = []; - this.popover = null; - this.jid = jid; - this.create(); -} - -/** - * Values for the connection quality - * @type {{98: string, - * 81: string, - * 64: string, - * 47: string, - * 30: string, - * 0: string}} - */ -ConnectionIndicator.connectionQualityValues = { - 98: "18px", //full - 81: "15px",//4 bars - 64: "11px",//3 bars - 47: "7px",//2 bars - 30: "3px",//1 bar - 0: "0px"//empty -}; - -ConnectionIndicator.getIP = function(value) { - return value.substring(0, value.lastIndexOf(":")); -}; - -ConnectionIndicator.getPort = function(value) { - return value.substring(value.lastIndexOf(":") + 1, value.length); -}; - -ConnectionIndicator.getStringFromArray = function (array) { - var res = ""; - for(var i = 0; i < array.length; i++) { - res += (i === 0? "" : ", ") + array[i]; - } - return res; -}; - -/** - * Generates the html content. - * @returns {string} the html content. - */ -ConnectionIndicator.prototype.generateText = function () { - var downloadBitrate, uploadBitrate, packetLoss, resolution, i; - - var translate = APP.translation.translateString; - - if(this.bitrate === null) { - downloadBitrate = "N/A"; - uploadBitrate = "N/A"; - } - else { - downloadBitrate = - this.bitrate.download? this.bitrate.download + " Kbps" : "N/A"; - uploadBitrate = - this.bitrate.upload? this.bitrate.upload + " Kbps" : "N/A"; - } - - if(this.packetLoss === null) { - packetLoss = "N/A"; - } else { - - packetLoss = "" + - (this.packetLoss.download !== null ? - this.packetLoss.download : "N/A") + - "% " + - (this.packetLoss.upload !== null? this.packetLoss.upload : "N/A") + - "%"; - } - - var resolutionValue = null; - if(this.resolution && this.jid != null) { - var keys = Object.keys(this.resolution); - for(var ssrc in this.resolution) { - resolutionValue = this.resolution[ssrc]; - } - } - - if(this.jid === null) { - resolution = ""; - if(this.resolution === null || !Object.keys(this.resolution) || - Object.keys(this.resolution).length === 0) { - resolution = "N/A"; - } else { - for (i in this.resolution) { - resolutionValue = this.resolution[i]; - if (resolutionValue) { - if (resolutionValue.height && - resolutionValue.width) { - resolution += (resolution === "" ? "" : ", ") + - resolutionValue.width + "x" + - resolutionValue.height; - } - } - } - } - } else if(!resolutionValue || - !resolutionValue.height || - !resolutionValue.width) { - resolution = "N/A"; - } else { - resolution = resolutionValue.width + "x" + resolutionValue.height; - } - - var result = "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "
    " + - translate("connectionindicator.bitrate") + "" + - downloadBitrate + " " + - uploadBitrate + "
    " + - translate("connectionindicator.packetloss") + "" + packetLoss + "
    " + - translate("connectionindicator.resolution") + "" + resolution + "
    "; - - if(this.videoContainer.videoSpanId == "localVideoContainer") { - result += "
    " + - translate("connectionindicator." + (this.showMoreValue ? "less" : "more")) + - "

    "; - } - - if (this.showMoreValue) { - var downloadBandwidth, uploadBandwidth, transport; - if (this.bandwidth === null) { - downloadBandwidth = "N/A"; - uploadBandwidth = "N/A"; - } else { - downloadBandwidth = this.bandwidth.download? - this.bandwidth.download + " Kbps" : - "N/A"; - uploadBandwidth = this.bandwidth.upload? - this.bandwidth.upload + " Kbps" : - "N/A"; - } - - if (!this.transport || this.transport.length === 0) { - transport = "" + - "" + - translate("connectionindicator.address") + "" + - " N/A"; - } else { - var data = {remoteIP: [], localIP:[], remotePort:[], localPort:[]}; - for(i = 0; i < this.transport.length; i++) { - var ip = ConnectionIndicator.getIP(this.transport[i].ip); - var port = ConnectionIndicator.getPort(this.transport[i].ip); - var localIP = - ConnectionIndicator.getIP(this.transport[i].localip); - var localPort = - ConnectionIndicator.getPort(this.transport[i].localip); - if(data.remoteIP.indexOf(ip) == -1) { - data.remoteIP.push(ip); - } - - if(data.remotePort.indexOf(port) == -1) { - data.remotePort.push(port); - } - - if(data.localIP.indexOf(localIP) == -1) { - data.localIP.push(localIP); - } - - if(data.localPort.indexOf(localPort) == -1) { - data.localPort.push(localPort); - } - } - - var local_address_key = "connectionindicator.localaddress"; - var remote_address_key = "connectionindicator.remoteaddress"; - var localTransport = - "" + - translate(local_address_key, {count: data.localIP.length}) + - " " + - ConnectionIndicator.getStringFromArray(data.localIP) + - ""; - transport = - "" + - translate(remote_address_key, - {count: data.remoteIP.length}) + - " " + - ConnectionIndicator.getStringFromArray(data.remoteIP) + - ""; - - var key_remote = "connectionindicator.remoteport", - key_local = "connectionindicator.localport"; - - transport += "" + - "" + - "" + - translate(key_remote, {count: this.transport.length}) + - ""; - localTransport += "" + - "" + - "" + - translate(key_local, {count: this.transport.length}) + - ""; - - transport += - ConnectionIndicator.getStringFromArray(data.remotePort); - localTransport += - ConnectionIndicator.getStringFromArray(data.localPort); - transport += ""; - transport += localTransport + ""; - transport +="" + - "" + - translate("connectionindicator.transport") + "" + - "" + this.transport[0].type + ""; - - } - - result += "" + - "" + - ""; - - result += transport + "
    " + - "" + - translate("connectionindicator.bandwidth") + "" + - "" + - "" + - downloadBandwidth + - " " + - uploadBandwidth + "
    "; - } - - return result; -}; - -/** - * Shows or hide the additional information. - */ -ConnectionIndicator.prototype.showMore = function () { - this.showMoreValue = !this.showMoreValue; - this.updatePopoverData(); -}; - - -function createIcon(classes) { - var icon = document.createElement("span"); - for(var i in classes) { - icon.classList.add(classes[i]); - } - icon.appendChild( - document.createElement("i")).classList.add("icon-connection"); - return icon; -} - -/** - * Creates the indicator - */ -ConnectionIndicator.prototype.create = function () { - this.connectionIndicatorContainer = document.createElement("div"); - this.connectionIndicatorContainer.className = "connectionindicator"; - this.connectionIndicatorContainer.style.display = "none"; - this.videoContainer.container.appendChild( - this.connectionIndicatorContainer); - this.popover = new JitsiPopover( - $("#" + this.videoContainer.videoSpanId + " > .connectionindicator"), - {content: "
    " + - APP.translation.translateString("connectionindicator.na") + "
    ", - skin: "black"}); - - this.emptyIcon = this.connectionIndicatorContainer.appendChild( - createIcon(["connection", "connection_empty"])); - this.fullIcon = this.connectionIndicatorContainer.appendChild( - createIcon(["connection", "connection_full"])); -}; - -/** - * Removes the indicator - */ -ConnectionIndicator.prototype.remove = function() { - if (this.connectionIndicatorContainer.parentNode) { - this.connectionIndicatorContainer.parentNode.removeChild( - this.connectionIndicatorContainer); - } - this.popover.forceHide(); -}; - -/** - * Updates the data of the indicator - * @param percent the percent of connection quality - * @param object the statistics data. - */ -ConnectionIndicator.prototype.updateConnectionQuality = - function (percent, object) { - - if (percent === null) { - this.connectionIndicatorContainer.style.display = "none"; - this.popover.forceHide(); - return; - } else { - if(this.connectionIndicatorContainer.style.display == "none") { - this.connectionIndicatorContainer.style.display = "block"; - this.videoContainer.updateIconPositions(); - } - } - this.bandwidth = object.bandwidth; - this.bitrate = object.bitrate; - this.packetLoss = object.packetLoss; - this.transport = object.transport; - if (object.resolution) { - this.resolution = object.resolution; - } - for (var quality in ConnectionIndicator.connectionQualityValues) { - if (percent >= quality) { - this.fullIcon.style.width = - ConnectionIndicator.connectionQualityValues[quality]; - } - } - this.updatePopoverData(); -}; - -/** - * Updates the resolution - * @param resolution the new resolution - */ -ConnectionIndicator.prototype.updateResolution = function (resolution) { - this.resolution = resolution; - this.updatePopoverData(); -}; - -/** - * Updates the content of the popover - */ -ConnectionIndicator.prototype.updatePopoverData = function () { - this.popover.updateContent( - "
    " + this.generateText() + "
    "); - APP.translation.translateElement($(".connection_info")); -}; - -/** - * Hides the popover - */ -ConnectionIndicator.prototype.hide = function () { - this.popover.forceHide(); -}; - -/** - * Hides the indicator - */ -ConnectionIndicator.prototype.hideIndicator = function () { - this.connectionIndicatorContainer.style.display = "none"; - if(this.popover) - this.popover.forceHide(); -}; - -module.exports = ConnectionIndicator; -},{"../util/JitsiPopover":32}],37:[function(require,module,exports){ -/* global $, APP, Strophe, interfaceConfig */ -var Avatar = require("../avatar/Avatar"); -var RTCBrowserType = require("../../RTC/RTCBrowserType"); -var UIUtil = require("../util/UIUtil"); -var UIEvents = require("../../../service/UI/UIEvents"); -var xmpp = require("../../xmpp/xmpp"); -var ToolbarToggler = require("../toolbars/ToolbarToggler"); - -// FIXME: With Temasys we have to re-select everytime -//var video = $('#largeVideo'); - -var currentVideoWidth = null; -var currentVideoHeight = null; -// By default we use camera -var getVideoSize = getCameraVideoSize; -var getVideoPosition = getCameraVideoPosition; -/** - * The small video instance that is displayed in the large video - * @type {SmallVideo} - */ -var currentSmallVideo = null; -/** - * Indicates whether the large video is enabled. - * @type {boolean} - */ -var isEnabled = true; -/** - * Current large video state. - * Possible values - video, prezi or etherpad. - * @type {string} - */ -var state = "video"; - -/** - * Returns the html element associated with the passed state of large video - * @param state the state. - * @returns {JQuery|*|jQuery|HTMLElement} the container. - */ -function getContainerByState(state) -{ - var selector = null; - switch (state) - { - case "video": - selector = "#largeVideo"; - break; - case "etherpad": - selector = "#etherpad>iframe"; - break; - case "prezi": - selector = "#presentation>iframe"; - break; - } - return (selector !== null)? $(selector) : null; -} - -/** - * Sets the size and position of the given video element. - * - * @param video the video element to position - * @param width the desired video width - * @param height the desired video height - * @param horizontalIndent the left and right indent - * @param verticalIndent the top and bottom indent - */ -function positionVideo(video, - width, - height, - horizontalIndent, - verticalIndent, - animate) { - if (animate) { - video.animate({ - width: width, - height: height, - top: verticalIndent, - bottom: verticalIndent, - left: horizontalIndent, - right: horizontalIndent - }, - { - queue: false, - duration: 500 - }); - } else { - video.width(width); - video.height(height); - video.css({ top: verticalIndent + 'px', - bottom: verticalIndent + 'px', - left: horizontalIndent + 'px', - right: horizontalIndent + 'px'}); - } - -} - -/** - * Returns an array of the video dimensions, so that it keeps it's aspect - * ratio and fits available area with it's larger dimension. This method - * ensures that whole video will be visible and can leave empty areas. - * - * @return an array with 2 elements, the video width and the video height - */ -function getDesktopVideoSize(videoWidth, - videoHeight, - videoSpaceWidth, - videoSpaceHeight) { - if (!videoWidth) - videoWidth = currentVideoWidth; - if (!videoHeight) - videoHeight = currentVideoHeight; - - var aspectRatio = videoWidth / videoHeight; - - var availableWidth = Math.max(videoWidth, videoSpaceWidth); - var availableHeight = Math.max(videoHeight, videoSpaceHeight); - - videoSpaceHeight -= $('#remoteVideos').outerHeight(); - - if (availableWidth / aspectRatio >= videoSpaceHeight) - { - availableHeight = videoSpaceHeight; - availableWidth = availableHeight * aspectRatio; - } - - if (availableHeight * aspectRatio >= videoSpaceWidth) - { - availableWidth = videoSpaceWidth; - availableHeight = availableWidth / aspectRatio; - } - - return [availableWidth, availableHeight]; -} - - -/** - * Returns an array of the video horizontal and vertical indents, - * so that if fits its parent. - * - * @return an array with 2 elements, the horizontal indent and the vertical - * indent - */ -function getCameraVideoPosition(videoWidth, - videoHeight, - videoSpaceWidth, - videoSpaceHeight) { - // Parent height isn't completely calculated when we position the video in - // full screen mode and this is why we use the screen height in this case. - // Need to think it further at some point and implement it properly. - var isFullScreen = document.fullScreen || - document.mozFullScreen || - document.webkitIsFullScreen; - if (isFullScreen) - videoSpaceHeight = window.innerHeight; - - var horizontalIndent = (videoSpaceWidth - videoWidth) / 2; - var verticalIndent = (videoSpaceHeight - videoHeight) / 2; - - return [horizontalIndent, verticalIndent]; -} - -/** - * Returns an array of the video horizontal and vertical indents. - * Centers horizontally and top aligns vertically. - * - * @return an array with 2 elements, the horizontal indent and the vertical - * indent - */ -function getDesktopVideoPosition(videoWidth, - videoHeight, - videoSpaceWidth, - videoSpaceHeight) { - - var horizontalIndent = (videoSpaceWidth - videoWidth) / 2; - - var verticalIndent = 0;// Top aligned - - return [horizontalIndent, verticalIndent]; -} - - -/** - * Returns an array of the video dimensions, so that it covers the screen. - * It leaves no empty areas, but some parts of the video might not be visible. - * - * @return an array with 2 elements, the video width and the video height - */ -function getCameraVideoSize(videoWidth, - videoHeight, - videoSpaceWidth, - videoSpaceHeight) { - if (!videoWidth) - videoWidth = currentVideoWidth; - if (!videoHeight) - videoHeight = currentVideoHeight; - - var aspectRatio = videoWidth / videoHeight; - - var availableWidth = Math.max(videoWidth, videoSpaceWidth); - var availableHeight = Math.max(videoHeight, videoSpaceHeight); - - if (availableWidth / aspectRatio < videoSpaceHeight) { - availableHeight = videoSpaceHeight; - availableWidth = availableHeight * aspectRatio; - } - - if (availableHeight * aspectRatio < videoSpaceWidth) { - availableWidth = videoSpaceWidth; - availableHeight = availableWidth / aspectRatio; - } - - return [availableWidth, availableHeight]; -} - -/** - * Updates the src of the active speaker avatar - * @param jid of the current active speaker - */ -function updateActiveSpeakerAvatarSrc() { - var avatar = $("#activeSpeakerAvatar")[0]; - var jid = currentSmallVideo.peerJid; - var url = Avatar.getActiveSpeakerUrl(jid); - if (avatar.src === url) - return; - if (jid) { - avatar.src = url; - currentSmallVideo.showAvatar(); - } -} - -/** - * Change the video source of the large video. - * @param isVisible - */ -function changeVideo(isVisible) { - - if (!currentSmallVideo) { - console.error("Unable to change large video - no 'currentSmallVideo'"); - return; - } - - updateActiveSpeakerAvatarSrc(); - - APP.RTC.setVideoSrc($('#largeVideo')[0], currentSmallVideo.getSrc()); - - var videoTransform = document.getElementById('largeVideo') - .style.webkitTransform; - - var flipX = currentSmallVideo.flipX; - - if (flipX && videoTransform !== 'scaleX(-1)') { - document.getElementById('largeVideo').style.webkitTransform = - "scaleX(-1)"; - } else if (!flipX && videoTransform === 'scaleX(-1)') { - document.getElementById('largeVideo').style.webkitTransform = - "none"; - } - - var isDesktop = currentSmallVideo.getVideoType() === 'screen'; - // Change the way we'll be measuring and positioning large video - - getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize; - getVideoPosition = isDesktop ? getDesktopVideoPosition : - getCameraVideoPosition; - - - // Only if the large video is currently visible. - if (isVisible) { - LargeVideo.VideoLayout.largeVideoUpdated(currentSmallVideo); - - $('#largeVideo').fadeIn(300); - } -} - -/** - * Creates the html elements for the large video. - */ -function createLargeVideoHTML() -{ - var html = '
    '; - html += '
    ' + - '
    ' + - '
    ' + - '
    ' + - '' + - ' jitsi.org' + - ''+ - '
    ' + - '' + - '' + - '
    ' + - '' + - ''; - html += '
    '; - $(html).prependTo("#videospace"); - - if (interfaceConfig.SHOW_JITSI_WATERMARK) { - var leftWatermarkDiv - = $("#largeVideoContainer div[class='watermark leftwatermark']"); - - leftWatermarkDiv.css({display: 'block'}); - leftWatermarkDiv.parent().get(0).href - = interfaceConfig.JITSI_WATERMARK_LINK; - } - - if (interfaceConfig.SHOW_BRAND_WATERMARK) { - var rightWatermarkDiv - = $("#largeVideoContainer div[class='watermark rightwatermark']"); - - rightWatermarkDiv.css({display: 'block'}); - rightWatermarkDiv.parent().get(0).href - = interfaceConfig.BRAND_WATERMARK_LINK; - rightWatermarkDiv.get(0).style.backgroundImage - = "url(images/rightwatermark.png)"; - } - - if (interfaceConfig.SHOW_POWERED_BY) { - $("#largeVideoContainer>a[class='poweredby']").css({display: 'block'}); - } - - if (!RTCBrowserType.isIExplorer()) { - $('#largeVideo').volume = 0; - } -} - -var LargeVideo = { - - init: function (VideoLayout, emitter) { - if(!isEnabled) - return; - createLargeVideoHTML(); - - this.VideoLayout = VideoLayout; - this.eventEmitter = emitter; - this.eventEmitter.emit(UIEvents.LARGEVIDEO_INIT); - var self = this; - // Listen for large video size updates - var largeVideo = $('#largeVideo')[0]; - var onplaying = function (arg1, arg2, arg3) { - // re-select - if (RTCBrowserType.isTemasysPluginUsed()) - largeVideo = $('#largeVideo')[0]; - currentVideoWidth = largeVideo.videoWidth; - currentVideoHeight = largeVideo.videoHeight; - self.position(currentVideoWidth, currentVideoHeight); - }; - largeVideo.onplaying = onplaying; - }, - /** - * Indicates if the large video is currently visible. - * - * @return true if visible, false - otherwise - */ - isLargeVideoVisible: function() { - return $('#largeVideo').is(':visible'); - }, - /** - * Returns true if the user is currently displayed on large video. - */ - isCurrentlyOnLarge: function (resourceJid) { - return currentSmallVideo && resourceJid && - currentSmallVideo.getResourceJid() === resourceJid; - }, - /** - * Updates the large video with the given new video source. - */ - updateLargeVideo: function (resourceJid, forceUpdate) { - if(!isEnabled) - return; - var newSmallVideo = this.VideoLayout.getSmallVideo(resourceJid); - console.log('hover in ' + resourceJid + ', video: ', newSmallVideo); - - if (!LargeVideo.isCurrentlyOnLarge(resourceJid) || forceUpdate) { - $('#activeSpeaker').css('visibility', 'hidden'); - - var oldSmallVideo = null; - if (currentSmallVideo) { - oldSmallVideo = currentSmallVideo; - } - currentSmallVideo = newSmallVideo; - - var oldJid = null; - if (oldSmallVideo) - oldJid = oldSmallVideo.peerJid; - if (oldJid !== resourceJid) { - // we want the notification to trigger even if userJid is undefined, - // or null. - this.eventEmitter.emit(UIEvents.SELECTED_ENDPOINT, resourceJid); - } - if (RTCBrowserType.isSafari()) { - // FIXME In Safari fadeOut works only for the first time - changeVideo(this.isLargeVideoVisible()); - } else { - $('#largeVideo').fadeOut(300, - changeVideo.bind($('#largeVideo'), this.isLargeVideoVisible())); - } - } else { - if (currentSmallVideo) { - currentSmallVideo.showAvatar(); - } - } - - }, - - /** - * Shows/hides the large video. - */ - setLargeVideoVisible: function(isVisible) { - if(!isEnabled) - return; - if (isVisible) { - $('#largeVideo').css({visibility: 'visible'}); - $('.watermark').css({visibility: 'visible'}); - if(currentSmallVideo) - currentSmallVideo.enableDominantSpeaker(true); - } - else { - $('#largeVideo').css({visibility: 'hidden'}); - $('#activeSpeaker').css('visibility', 'hidden'); - $('.watermark').css({visibility: 'hidden'}); - if(currentSmallVideo) - currentSmallVideo.enableDominantSpeaker(false); - } - }, - onVideoTypeChanged: function (resourceJid, newVideoType) { - if (!isEnabled) - return; - if (LargeVideo.isCurrentlyOnLarge(resourceJid)) - { - var isDesktop = newVideoType === 'screen'; - getVideoSize = isDesktop ? getDesktopVideoSize : getCameraVideoSize; - getVideoPosition = isDesktop ? getDesktopVideoPosition - : getCameraVideoPosition; - this.position(null, null); - } - }, - /** - * Positions the large video. - * - * @param videoWidth the stream video width - * @param videoHeight the stream video height - */ - position: function (videoWidth, videoHeight, - videoSpaceWidth, videoSpaceHeight, animate) { - if(!isEnabled) - return; - if(!videoSpaceWidth) - videoSpaceWidth = $('#videospace').width(); - if(!videoSpaceHeight) - videoSpaceHeight = window.innerHeight; - - var videoSize = getVideoSize(videoWidth, - videoHeight, - videoSpaceWidth, - videoSpaceHeight); - - var largeVideoWidth = videoSize[0]; - var largeVideoHeight = videoSize[1]; - - var videoPosition = getVideoPosition(largeVideoWidth, - largeVideoHeight, - videoSpaceWidth, - videoSpaceHeight); - - var horizontalIndent = videoPosition[0]; - var verticalIndent = videoPosition[1]; - - positionVideo($('#largeVideo'), - largeVideoWidth, - largeVideoHeight, - horizontalIndent, verticalIndent, animate); - }, - /** - * Resizes the large html elements. - * @param animate boolean property that indicates whether the resize should be animated or not. - * @param isChatVisible boolean property that indicates whether the chat area is displayed or not. - * If that parameter is null the method will check the chat pannel visibility. - * @param completeFunction a function to be called when the video space is resized - * @returns {*[]} array with the current width and height values of the largeVideo html element. - */ - resize: function (animate, isVisible, completeFunction) { - if(!isEnabled) - return; - var availableHeight = window.innerHeight; - var availableWidth = UIUtil.getAvailableVideoWidth(isVisible); - - if (availableWidth < 0 || availableHeight < 0) return; - - var avatarSize = interfaceConfig.ACTIVE_SPEAKER_AVATAR_SIZE; - var top = availableHeight / 2 - avatarSize / 4 * 3; - $('#activeSpeaker').css('top', top); - - this.VideoLayout.resizeVideoSpace(animate, isVisible, completeFunction); - if(animate) { - $('#largeVideoContainer').animate({ - width: availableWidth, - height: availableHeight - }, - { - queue: false, - duration: 500 - }); - } else { - $('#largeVideoContainer').width(availableWidth); - $('#largeVideoContainer').height(availableHeight); - } - return [availableWidth, availableHeight]; - }, - resizeVideoAreaAnimated: function (isVisible, completeFunction) { - if(!isEnabled) - return; - var size = this.resize(true, isVisible, completeFunction); - this.position(null, null, size[0], size[1], true); - }, - getResourceJid: function () { - return currentSmallVideo ? currentSmallVideo.getResourceJid() : null; - }, - updateAvatar: function (resourceJid) { - if(!isEnabled) - return; - if (resourceJid === this.getResourceJid()) { - updateActiveSpeakerAvatarSrc(); - } - }, - showAvatar: function (resourceJid, show) { - if(!isEnabled) - return; - if(this.getResourceJid() === resourceJid && state === "video") { - $("#largeVideo").css("visibility", show ? "hidden" : "visible"); - $('#activeSpeaker').css("visibility", show ? "visible" : "hidden"); - return true; - } - return false; - }, - /** - * Disables the large video - */ - disable: function () { - isEnabled = false; - }, - /** - * Enables the large video - */ - enable: function () { - isEnabled = true; - }, - /** - * Returns true if the video is enabled. - */ - isEnabled: function () { - return isEnabled; - }, - /** - * Creates the iframe used by the etherpad - * @param src the value for src attribute - * @param onloadHandler handler executed when the iframe loads it content - * @returns {HTMLElement} the iframe - */ - createEtherpadIframe: function (src, onloadHandler) { - if(!isEnabled) - return; - - var etherpadIFrame = document.createElement('iframe'); - etherpadIFrame.src = src; - etherpadIFrame.frameBorder = 0; - etherpadIFrame.scrolling = "no"; - etherpadIFrame.width = $('#largeVideoContainer').width() || 640; - etherpadIFrame.height = $('#largeVideoContainer').height() || 480; - etherpadIFrame.setAttribute('style', 'visibility: hidden;'); - - document.getElementById('etherpad').appendChild(etherpadIFrame); - - etherpadIFrame.onload = onloadHandler; - - return etherpadIFrame; - }, - /** - * Changes the state of the large video. - * Possible values - video, prezi, etherpad. - * @param newState - the new state - */ - setState: function (newState) { - if(state === newState) - return; - var currentContainer = getContainerByState(state); - if(!currentContainer) - return; - - var self = this; - var oldState = state; - - switch (newState) - { - case "etherpad": - $('#activeSpeaker').css('visibility', 'hidden'); - currentContainer.fadeOut(300, function () { - if (oldState === "prezi") { - currentContainer.css({opacity: '0'}); - $('#reloadPresentation').css({display: 'none'}); - } - else { - self.setLargeVideoVisible(false); - } - }); - - $('#etherpad>iframe').fadeIn(300, function () { - document.body.style.background = '#eeeeee'; - $('#etherpad>iframe').css({visibility: 'visible'}); - $('#etherpad').css({zIndex: 2}); - }); - break; - case "prezi": - var prezi = $('#presentation>iframe'); - currentContainer.fadeOut(300, function() { - document.body.style.background = 'black'; - }); - prezi.fadeIn(300, function() { - prezi.css({opacity:'1'}); - ToolbarToggler.dockToolbar(true);//fix that - self.setLargeVideoVisible(false); - $('#etherpad>iframe').css({visibility: 'hidden'}); - $('#etherpad').css({zIndex: 0}); - }); - $('#activeSpeaker').css('visibility', 'hidden'); - break; - - case "video": - currentContainer.fadeOut(300, function () { - $('#presentation>iframe').css({opacity:'0'}); - $('#reloadPresentation').css({display:'none'}); - $('#etherpad>iframe').css({visibility: 'hidden'}); - $('#etherpad').css({zIndex: 0}); - document.body.style.background = 'black'; - ToolbarToggler.dockToolbar(false);//fix that - }); - $('#largeVideo').fadeIn(300, function () { - self.setLargeVideoVisible(true); - }); - break; - } - - state = newState; - - }, - /** - * Returns the current state of the large video. - * @returns {string} the current state - video, prezi or etherpad. - */ - getState: function () { - return state; - }, - /** - * Sets hover handlers for the large video container div. - * - * @param inHandler - * @param outHandler - */ - setHover: function(inHandler, outHandler) - { - $('#largeVideoContainer').hover(inHandler, outHandler); - }, - - /** - * Enables/disables the filter indicating a video problem to the user. - * - * @param enable true to enable, false to disable - */ - enableVideoProblemFilter: function (enable) { - $("#largeVideo").toggleClass("videoProblemFilter", enable); - } -}; - -module.exports = LargeVideo; -},{"../../../service/UI/UIEvents":160,"../../RTC/RTCBrowserType":10,"../../xmpp/xmpp":70,"../avatar/Avatar":18,"../toolbars/ToolbarToggler":31,"../util/UIUtil":35}],38:[function(require,module,exports){ -/* global $, interfaceConfig, APP */ -var SmallVideo = require("./SmallVideo"); -var ConnectionIndicator = require("./ConnectionIndicator"); -var NicknameHandler = require("../util/NicknameHandler"); -var UIUtil = require("../util/UIUtil"); -var LargeVideo = require("./LargeVideo"); -var RTCBrowserType = require("../../RTC/RTCBrowserType"); - -function LocalVideo(VideoLayout) { - this.videoSpanId = "localVideoContainer"; - this.container = $("#localVideoContainer").get(0); - this.VideoLayout = VideoLayout; - this.flipX = true; - this.isLocal = true; - this.peerJid = null; -} - -LocalVideo.prototype = Object.create(SmallVideo.prototype); -LocalVideo.prototype.constructor = LocalVideo; - -/** - * Creates the edit display name button. - * - * @returns {object} the edit button - */ -function createEditDisplayNameButton() { - var editButton = document.createElement('a'); - editButton.className = 'displayname'; - UIUtil.setTooltip(editButton, - "videothumbnail.editnickname", - "top"); - editButton.innerHTML = ''; - - return editButton; -} - -/** - * Sets the display name for the given video span id. - */ -LocalVideo.prototype.setDisplayName = function(displayName, key) { - if (!this.container) { - console.warn( - "Unable to set displayName - " + this.videoSpanId + - " does not exist"); - return; - } - - var nameSpan = $('#' + this.videoSpanId + '>span.displayname'); - var defaultLocalDisplayName = APP.translation.generateTranslationHTML( - interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME); - - var meHTML; - // If we already have a display name for this video. - if (nameSpan.length > 0) { - if (nameSpan.text() !== displayName) { - if (displayName && displayName.length > 0) { - meHTML = APP.translation.generateTranslationHTML("me"); - $('#localDisplayName').html(displayName + ' (' + meHTML + ')'); - } else { - $('#localDisplayName').html(defaultLocalDisplayName); - } - } - } else { - var editButton = createEditDisplayNameButton(); - - nameSpan = document.createElement('span'); - nameSpan.className = 'displayname'; - $('#' + this.videoSpanId)[0].appendChild(nameSpan); - - - if (displayName && displayName.length > 0) { - meHTML = APP.translation.generateTranslationHTML("me"); - nameSpan.innerHTML = displayName + meHTML; - } - else { - nameSpan.innerHTML = defaultLocalDisplayName; - } - - - nameSpan.id = 'localDisplayName'; - this.container.appendChild(editButton); - //translates popover of edit button - APP.translation.translateElement($("a.displayname")); - - var editableText = document.createElement('input'); - editableText.className = 'displayname'; - editableText.type = 'text'; - editableText.id = 'editDisplayName'; - - if (displayName && displayName.length) { - editableText.value = displayName; - } - - var defaultNickname = APP.translation.translateString( - "defaultNickname", {name: "Jane Pink"}); - editableText.setAttribute('style', 'display:none;'); - editableText.setAttribute('data-18n', - '[placeholder]defaultNickname'); - editableText.setAttribute("data-i18n-options", - JSON.stringify({name: "Jane Pink"})); - editableText.setAttribute("placeholder", defaultNickname); - - this.container.appendChild(editableText); - - var self = this; - $('#localVideoContainer .displayname') - .bind("click", function (e) { - - var editDisplayName = $('#editDisplayName'); - e.preventDefault(); - e.stopPropagation(); - $('#localDisplayName').hide(); - editDisplayName.show(); - editDisplayName.focus(); - editDisplayName.select(); - - editDisplayName.one("focusout", function (e) { - self.VideoLayout.inputDisplayNameHandler(this.value); - }); - - editDisplayName.on('keydown', function (e) { - if (e.keyCode === 13) { - e.preventDefault(); - self.VideoLayout.inputDisplayNameHandler(this.value); - } - }); - }); - } -}; - -LocalVideo.prototype.inputDisplayNameHandler = function (name) { - NicknameHandler.setNickname(name); - - var localDisplayName = $('#localDisplayName'); - if (!localDisplayName.is(":visible")) { - if (NicknameHandler.getNickname()) { - var meHTML = APP.translation.generateTranslationHTML("me"); - localDisplayName.html(NicknameHandler.getNickname() + " (" + - meHTML + ")"); - } else { - var defaultHTML = APP.translation.generateTranslationHTML( - interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME); - localDisplayName .html(defaultHTML); - } - localDisplayName.show(); - } - - $('#editDisplayName').hide(); -}; - -LocalVideo.prototype.createConnectionIndicator = function() { - if(this.connectionIndicator) - return; - - this.connectionIndicator = new ConnectionIndicator(this, null); -}; - -LocalVideo.prototype.changeVideo = function (stream, isMuted) { - var self = this; - - function localVideoClick(event) { - // FIXME: with Temasys plugin event arg is not an event, but - // the clicked object itself, so we have to skip this call - if (event.stopPropagation) { - event.stopPropagation(); - } - self.VideoLayout.handleVideoThumbClicked( - false, - APP.xmpp.myResource()); - } - - var localVideoContainerSelector = $('#localVideoContainer'); - localVideoContainerSelector.off('click'); - localVideoContainerSelector.on('click', localVideoClick); - - // Add hover handler - localVideoContainerSelector.hover( - function() { - self.showDisplayName(true); - }, - function() { - if (!LargeVideo.isLargeVideoVisible() || - !LargeVideo.isCurrentlyOnLarge(self.getResourceJid())) { - self.showDisplayName(false); - } - } - ); - - if(isMuted) { - APP.UI.setVideoMute(true); - return; - } - this.flipX = stream.videoType != "screen"; - var localVideo = document.createElement('video'); - localVideo.id = 'localVideo_' + - APP.RTC.getStreamID(stream.getOriginalStream()); - if (!RTCBrowserType.isIExplorer()) { - localVideo.autoplay = true; - localVideo.volume = 0; // is it required if audio is separated ? - } - localVideo.oncontextmenu = function () { return false; }; - - var localVideoContainer = document.getElementById('localVideoWrapper'); - // Put the new video always in front - UIUtil.prependChild(localVideoContainer, localVideo); - - var localVideoSelector = $('#' + localVideo.id); - - // Add click handler to both video and video wrapper elements in case - // there's no video. - - // onclick has to be used with Temasys plugin - localVideo.onclick = localVideoClick; - - if (this.flipX) { - localVideoSelector.addClass("flipVideoX"); - } - - // Attach WebRTC stream - APP.RTC.attachMediaStream(localVideoSelector, stream.getOriginalStream()); - - // Add stream ended handler - stream.getOriginalStream().onended = function () { - // We have to re-select after attach when Temasys plugin is used, - // because