Compare commits

...

149 Commits
1156 ... 1237

Author SHA1 Message Date
Дамян Минков
c18b80c43f Merge pull request #862 from jitsi/make-settings-sections-configurable
Make settings sections configurable
2016-09-13 20:46:03 -05:00
yanas
9ce83250da Fixes console logs 2016-09-13 20:43:44 -05:00
yanas
a9e31a2843 Fixes settings menu for non moderators 2016-09-13 20:43:44 -05:00
yanas
45496b7592 Make settings sections configurable 2016-09-13 20:43:44 -05:00
yanas
4075a5f4f7 Merge pull request #864 from jitsi/origin/ui-redesign-guests-profile
fix(profile_icon): Cursor and background styles
2016-09-13 19:17:45 -05:00
hristoterezov
2d4819257b fix(profile_icon): Cursor and background styles 2016-09-13 18:19:38 -05:00
yanas
406618677b Merge pull request #861 from jitsi/ui-redesign-guests-profile
feat(profile): Don't allow non-guest users to edit their profile
2016-09-13 17:38:51 -05:00
hristoterezov
f10b13328f feat(profile): Don't allow non-guest users to edit their profile 2016-09-13 16:56:05 -05:00
Дамян Минков
c609cd0ace Merge pull request #859 from jitsi/change-ringing-background
Ringing background changed to gradient
2016-09-13 16:53:32 -05:00
yanas
0396a05b38 Merge pull request #860 from jitsi/css-watermark-link-variable
Moves watermark link to be css variable.
2016-09-13 16:46:53 -05:00
damencho
4a460e09b9 Moves watermark link to be css variable. 2016-09-13 16:46:15 -05:00
yanas
d5d81c1bed Merge pull request #858 from jitsi/ui-redesign-feedback
Ui redesign feedback
2016-09-13 16:44:11 -05:00
yanas
dc6e8f99d8 Ringing background change to gradient 2016-09-13 16:32:00 -05:00
hristoterezov
e02a8c9103 fix(feedback): Styles of Feedback dialog 2016-09-13 16:06:53 -05:00
Maxim Voloshin
7b4941bde9 Redesigned feedback window 2016-09-13 16:06:53 -05:00
hristoterezov
c602839e06 Merge pull request #851 from jitsi/ui-redesign-in-progress
Ui redesign in progress
2016-09-13 14:50:59 -05:00
yanas
2aea6c4120 Remove unused dialog 2016-09-13 13:07:08 -05:00
hristoterezov
a937e025b5 fix(index.html): Remove preload attribute on local video tag 2016-09-13 12:59:59 -05:00
yanas
b9fb510006 Adds ringing.scss, which was missing 2016-09-13 10:47:22 -05:00
Дамян Минков
b3f66aea3b Merge pull request #618 from photoninger/add_firefox_supportedBrowsers
Add Firefox in list of supportedBrowsers for invite emails
2016-09-13 08:29:59 -05:00
yanas
8491fbbd95 Reverts change in makefile 2016-09-12 23:29:26 -05:00
yanas
c6ec96aca5 Adds avatar on stage specific background 2016-09-12 23:24:23 -05:00
yanas
d250623fde Ring overlay improvements and enable buttons fix 2016-09-12 23:24:10 -05:00
hristoterezov
c3b4ecbbd8 fix(ext_toolbar): position of authentication popup 2016-09-12 16:22:51 -05:00
hristoterezov
2b84a149fe fix(toolbar): Button popups 2016-09-12 14:43:34 -05:00
hristoterezov
99ca38d13f Merge pull request #852 from jitsi/make-node-sass
Installs node-sass on every update, fixes a problem in some operating…
2016-09-12 12:54:52 -05:00
damencho
bfc174ffdc Installs node-sass on every update, fixes a problem in some operating systems. 2016-09-12 12:02:43 -05:00
yanas
09f7615a35 Changes icon set. 2016-09-11 23:36:15 -05:00
yanas
fdf0e39516 Fix lock room 2016-09-11 20:49:59 -05:00
yanas
775f389e5c Fix watermark and help position related to toolbar 2016-09-11 17:06:08 -05:00
yanas
f811410b45 Settings re-design 2016-09-11 16:55:06 -05:00
yanas
50e803f1a0 Fixes side toolbar container behavior 2016-09-09 21:26:29 -05:00
Paweł Domas
9a0d8616ed Merge pull request #847 from jitsi/cs-custom-scriptUrl
Skips some url parameters that can inject scripts.
2016-09-08 18:09:04 -05:00
yanas
b4c9816d9f Rename side panels and implement toggle/untoggle policy 2016-09-08 17:38:41 -05:00
yanas
17e28069ab Re-designs toolbars, side panels like setting, contact list 2016-09-08 13:22:50 -05:00
yanas
8562d3d55d Remove bottom toolbar and change side panel position and behavior 2016-09-08 13:16:23 -05:00
damencho
d65479abc9 Skips some url parameters that can inject scripts. 2016-09-08 13:12:56 -05:00
yanas
ed29db290e Add mixins for animations and separate toolbar css 2016-09-08 12:19:45 -05:00
yanas
978c0f2f31 Remove unused import 2016-09-08 12:19:45 -05:00
damencho
62c4ff719e Fixes build. 2016-09-08 11:26:57 -05:00
Ilya Daynatovich
e28f646cef Fixed styles for unsupported browser page 2016-09-08 11:15:17 -05:00
yanas
fb63ed92a8 Resets the main font after remove 2016-09-07 22:07:13 -05:00
yanas
c0097d1e2b Merge pull request #833 from BeatC/configurable-text
Set up SASS and change base font
2016-09-07 22:05:58 -05:00
yanas
fe710d8de9 Merge pull request #842 from jitsi/revert-kicks
Revert "Temprorary disable kicks hack."
2016-09-07 10:59:23 -05:00
damencho
83546a899f Revert "Temprorary disable kicks hack."
This reverts commit 76a7c7b426.
2016-09-07 10:48:13 -05:00
hristoterezov
2eca459ff6 Merge pull request #840 from jitsi/moves-analytics-feedback
Moves analytics event for feedback to the library.
2016-09-07 09:42:30 -05:00
hristoterezov
6b23ad54db Merge pull request #839 from jitsi/removes-ds-error-onjoin
Skips selecting local participant when alone in the room.
2016-09-07 09:41:01 -05:00
damencho
5ef7f38bed Moves analytics event for feedback to the library. 2016-09-06 13:16:54 -05:00
damencho
f86056c4f8 Skips selecting local participant when alone in the room. 2016-09-06 12:00:13 -05:00
Emil Ivov
f19b364871 Merge pull request #838 from jitsi/fix_audio_not_working_msg
fix(lang): Change the message in the mic not working error dialog
2016-09-06 11:33:54 -05:00
hristoterezov
29f1d96d87 fix(lang): Change the message in the mic not working error dialog 2016-09-06 11:30:51 -05:00
yanas
2f966b7da4 Merge pull request #829 from BeatC/fix-sharing-resize-after-hiding-filmstrip
Fix problem with resizing
2016-09-04 20:16:08 -05:00
bgrozev
e3f599fbc0 Merge pull request #834 from jitsi/disable-kick-until-fixed-hack
Temprorary disable kicks hack.
2016-09-02 11:44:15 -05:00
yanas
76a7c7b426 Temprorary disable kicks hack. 2016-09-02 11:19:17 -05:00
Ilya Daynatovich
4044e11da2 Set up SASS and change base font 2016-09-02 12:55:33 +03:00
Дамян Минков
a07c21b2a3 Merge pull request #832 from jitsi/remove-avatar-url
Remove avatar url - not used anymore
2016-09-01 16:55:11 -05:00
yanas
109bedba75 Update index.html 2016-09-01 16:37:07 -05:00
Дамян Минков
aced860ba4 Merge pull request #830 from jitsi/detect_local_media_not_working
Shows dialog when lib-jitsi-meet report microphone issues
2016-09-01 14:16:50 -05:00
hristoterezov
d33324c198 Merge pull request #831 from jitsi/analytics-remove-browser-suffix
Adds browser name as parameter.
2016-09-01 14:16:41 -05:00
damencho
adb1b33441 Adds browser name as parameter. 2016-09-01 14:13:17 -05:00
yanas
238e1f1bea Merge pull request #814 from BeatC/fix-video-preview-when-resize
Fix problem with last video preview
2016-09-01 13:45:56 -05:00
hristoterezov
4c9b6ce193 Shows dialog when lib-jitsi-meet report microphone issues 2016-09-01 12:57:21 -05:00
Paweł Domas
5ef547d285 Merge pull request #828 from SamWhited/mod_token_use_proper_path
Use valid path for HTTP token fetches
2016-09-01 10:02:47 -05:00
Ilya Daynatovich
aea99b8ffb Fix problem with resizing 2016-09-01 08:53:51 +03:00
Sam Whited
9ec120d7cb Use valid path for HTTP token fetches 2016-08-31 16:25:49 -05:00
Дамян Минков
e2c5439112 Merge pull request #821 from SamWhited/hql1748_update_lock_icon_on_config_change
Update lock icon when moderator changes state
2016-08-31 15:53:17 -05:00
hristoterezov
19362d1904 Merge pull request #826 from jitsi/fix-random-avatar
Fixes random avatar
2016-08-31 15:46:53 -05:00
hristoterezov
1a69fd8a49 Merge pull request #822 from jitsi/removes-atarURL
Removes avatar url from UI.
2016-08-31 15:39:00 -05:00
yanas
224670ed03 Merge pull request #827 from jitsi/fix-missing-indications-on-no-video-device
Moves local video thumb initializations where they belong.
2016-08-31 14:55:12 -05:00
damencho
bb705e32d9 Moves local video thumb initializations where they belong.
Moves local video thumb initializations where they belong in the local video constructor. Fixes a problem when there is no video device, then audio levels and gsm bars are missing. We were doing this initializations every time a video device is changed.
2016-08-31 14:18:09 -05:00
Sam Whited
3e269978d9 Update lock icon when moderator changes state 2016-08-31 13:59:08 -05:00
Paweł Domas
9ba62c320b Merge pull request #825 from SamWhited/mod_token_improvements
Mod token improvements
2016-08-31 12:48:04 -05:00
damencho
8e6d7d3960 Sends and dispatches avatarId command. 2016-08-31 11:41:17 -05:00
damencho
3138748f57 Uses avatarId from settings.
Removes unused variable bottomToolbarEnabled.
2016-08-31 11:40:06 -05:00
damencho
6f10156bf3 Adds avatarId and respect it with lowest priority. 2016-08-31 11:37:11 -05:00
damencho
3852b34397 Adds avatarId to Settings. 2016-08-31 11:24:51 -05:00
Sam Whited
3128628d09 Populate the token cache 2016-08-31 09:30:07 -05:00
Sam Whited
d8c4c0627a SHA256 hash the kid claim before fetching tokens 2016-08-31 09:24:15 -05:00
hristoterezov
d9559ecf63 Merge pull request #817 from jitsi/fix-shortcut-tooltips
Fix shortcut appearing in tooltip of wrong buttons
2016-08-30 15:30:09 -05:00
yanas
2b492883ca Some code optimisations. 2016-08-30 14:14:52 -05:00
damencho
5ab6c551df Disables storing display name and email when using overlay ring. 2016-08-30 14:10:20 -05:00
damencho
0e27f471f1 Removes avatarURL from settings UI.
Removes storing avatarURL in localstorage and retrieving it.
2016-08-30 14:09:07 -05:00
hristoterezov
ba477ad720 Merge pull request #816 from jitsi/improve-device-errors
Updates string for not found devices.
2016-08-30 13:04:44 -05:00
yanas
7858c157c1 Fix shortcut appearing in tooltip of wrong buttons 2016-08-29 23:56:20 -05:00
damencho
a5d3cc63c3 Updates string for not found devices.
The user can be in case where there is no audio or no video device at all, so removing the requested word from the message.
2016-08-29 16:46:44 -05:00
hristoterezov
cac7ccf176 Merge pull request #812 from jitsi/attach-shortcuts-to-features
Attach keyboard shortcuts to features
2016-08-29 16:05:12 -05:00
yanas
9693cba17a Registers filmstrip shortcut from bottom toolbar 2016-08-29 15:47:24 -05:00
yanas
45e38ae4c9 Fix wrong import 2016-08-29 13:04:55 -05:00
Paweł Domas
ad68d535b4 Merge pull request #815 from SamWhited/mod_auth_token_tweaks
mod_auth_token: Misc minor fixes
2016-08-29 10:23:44 -05:00
Sam Whited
bb56ea4b33 mod_auth_token: Add semicolons
Remove unnecessary cjson config
2016-08-29 09:39:47 -05:00
Ilya Daynatovich
ba822eaeed Fix problem with last video preview 2016-08-29 16:39:57 +03:00
yanas
7076ada6f4 Attach keyboard shortcuts to features 2016-08-28 22:59:23 -05:00
Paweł Domas
84834dc4e6 Merge pull request #808 from SamWhited/auth_token_asap
Add support for fetching public keys from a server to mod_auth_token
2016-08-26 16:51:17 -05:00
hristoterezov
f9b3f34593 Merge pull request #810 from jitsi/fix-hangup-multiple-feedback-windows
Fix hangup triggering multiple feedback windows
2016-08-26 16:30:31 -05:00
Sam Whited
c17576a931 mod_auth_token: Don't timeout finished requests 2016-08-26 16:19:01 -05:00
yanas
570124058c Addresses comment about Feedback dependency 2016-08-26 16:07:20 -05:00
Sam Whited
3793119209 mod_auth_token: Fix cache hit log line 2016-08-26 16:03:08 -05:00
Sam Whited
7fb18d1cb3 Fix broken claims comparison 2016-08-26 14:47:34 -05:00
Sam Whited
4fc86175e1 mod_auth_token: Set room name on session 2016-08-26 14:41:06 -05:00
Sam Whited
c951f7f3e9 Add missing semicolons 2016-08-26 14:11:50 -05:00
yanas
777217bd75 Fixes hangup triggering multiple feedbacks. 2016-08-26 11:42:45 -05:00
yanas
2bb637e140 Fixes hangup triggering several times 2016-08-26 11:42:45 -05:00
Дамян Минков
b5c1c95a15 Merge pull request #807 from jitsi/fix_ss_external_install
Fixes the issue with SS external installation dialog is not closed after the plugin has been installed
2016-08-26 11:40:35 -05:00
Sam Whited
f2e369cfc0 mod_auth_token: Remove broken path.join 2016-08-26 09:48:02 -05:00
yanas
6329271731 Merge pull request #806 from BeatC/fix-help-dialog-behind-filmstrip
Change z-index of keyboard-shortcuts
2016-08-25 14:15:57 -05:00
yanas
1428559546 Merge pull request #805 from jitsi/fix-moderator-mute
Fixes muting particular remote participant.
2016-08-25 14:15:01 -05:00
hristoterezov
5c9a85e928 Fixes the issue with SS external installation dialog is not closed after the plugin has been installed 2016-08-25 13:27:58 -05:00
Illia Daynatovich
00355caf8d HQL-1829 Change z-index of keyboard-shortcuts 2016-08-25 12:12:29 +03:00
Sam Whited
feb1d9d8e1 Add an LRU cache to mod_auth_token 2016-08-24 15:28:13 -05:00
Sam Whited
7f2fa9597c Add basic ASAP support to mod_auth_token
See: http://s2sauth.bitbucket.org/
2016-08-24 14:08:21 -05:00
damencho
246ab88a3e Fixes muting particular remote participant.
There was a bug that the handlers for menuItem for mute and kick are added for all remote participants. When clicked multiple handlers are executed and the action will be executed for all remote participants.
2016-08-23 18:37:41 -05:00
Дамян Минков
49cc4ae087 Merge pull request #802 from jitsi/fix-video-linux-qt
Fix video thumbnails for Qt on Linux.
2016-08-23 15:36:52 -05:00
hristoterezov
8d466ad77f Merge pull request #801 from jitsi/reloads-update
Updates statistics implementation.
2016-08-23 10:20:44 -05:00
yanas
f824f78db9 Fix video thumbnails for Qt on Linux. 2016-08-22 16:31:20 -05:00
damencho
35f592bb2c Adds optional label to statistics implementation. 2016-08-22 15:18:13 -05:00
bgrozev
25a6728acc Merge pull request #663 from cmrd-senya/patch-1
Update manual-install.md
2016-08-21 14:36:06 -05:00
jitsi-pootle
169da33411 New files added from translate.jitsi.org based on templates 2016-08-21 19:30:40 +00:00
George Politis
db70cf4aa9 Merge pull request #795 from jitsi/hide-download-log
Hide download log by default
2016-08-17 16:00:09 -05:00
yanas
dcfab4401f Update main.css 2016-08-17 15:50:26 -05:00
hristoterezov
d85a91ae49 Merge pull request #792 from jitsi/remove-bottom-toolbar-button-separators-1
Remove unused css class
2016-08-16 16:53:49 -04:00
hristoterezov
a3b1a80658 Merge pull request #791 from jitsi/remove-bottom-toolbar-button-separators
Remove bottom toolbar button separators
2016-08-16 16:53:03 -04:00
yanas
db20f145fb Remove unused css class 2016-08-16 15:20:32 -05:00
yanas
986bfd02b3 Remove bottom toolbar button separators 2016-08-16 15:14:30 -05:00
Paweł Domas
57506934f2 Merge pull request #790 from jitsi/show-toolbar-in-ring-overlay
Show the toolbar even if in a ring overlay
2016-08-16 15:07:51 -05:00
yanas
372a5e2a49 Update UI.js
Show the toolbar even if in a ring overlay.
2016-08-16 13:44:34 -05:00
Дамян Минков
726b972223 Merge pull request #789 from bgrozev/cleanup
refactor: removes unused code.
2016-08-15 22:59:35 -05:00
Boris Grozev
65300b34df refactor: removes unused code. 2016-08-15 22:57:36 -05:00
Paweł Domas
d7a7733d30 Merge pull request #787 from bgrozev/resolution-change
Brings back resolution changes logging to callstats.
2016-08-15 15:16:05 -05:00
Boris Grozev
7154fd4d39 Brings back resolution changes logging to callstats. 2016-08-15 14:40:55 -05:00
Дамян Минков
45830c1086 Merge pull request #785 from bgrozev/stream-switch-logs
Logs stream switch delays using analytics instead of callstats.
2016-08-12 16:51:39 -05:00
Дамян Минков
202ad0542f Merge pull request #784 from bgrozev/round-analytics-values
Rounds floats passed to analytics (instead of truncating them).
2016-08-12 16:51:29 -05:00
Boris Grozev
c59d9e7c8b Rounds floats passed to analytics (instead of truncating them). 2016-08-12 15:22:38 -05:00
Boris Grozev
f4f0a7d90e Logs stream switch delays using analytics instead of callstats. 2016-08-12 15:13:27 -05:00
damencho
ec98e6fdff Revert "Skips the analytics file from main folder when creating deb." Excluding file, excludes it and from subfolders.
This reverts commit 157bb1931d.
2016-08-11 12:53:37 -05:00
Дамян Минков
c68bcb8fbc Merge pull request #782 from jitsi/fix-unsupported-browser-images
Fix unsupported browser images
2016-08-11 11:08:59 -05:00
yanas
c37876a8b7 Fix unsupported browser images
Fixes the path of the unsupported browser images
2016-08-11 10:14:06 -05:00
bgrozev
23a805b79c Merge pull request #780 from jitsi/fix-duplicate-analytics-files
Skips the analytics file from main folder when creating deb.
2016-08-10 16:09:01 -05:00
damencho
157bb1931d Skips the analytics file from main folder when creating deb. 2016-08-10 15:47:54 -05:00
bgrozev
e59ad67055 Merge pull request #779 from jitsi/fix_ss_from_popup
Implements extension external installation for popup windows
2016-08-10 15:25:35 -05:00
hristoterezov
97b9b67768 Merge pull request #777 from jitsi/analytics-move2
Moves loading of analytics in the library.
2016-08-10 15:19:36 -05:00
hristoterezov
f899d16a79 Implements extension external installation for popup windows 2016-08-10 14:13:32 -05:00
damencho
53288fc997 Moves analytics js next to libraries so it can be dynamically loaded. 2016-08-10 13:28:15 -05:00
yanas
7c89f2b7d1 Merge pull request #778 from jitsi/fix-toolbar-hide
Fixes the check whether we are hovering over the toolbar.
2016-08-09 18:04:04 -05:00
damencho
2c39514359 Fixes the check whether we are hovering over the toolbar. 2016-08-09 17:39:24 -05:00
damencho
536ffb31e0 Moves loading of analytics in the library. 2016-08-09 15:04:40 -05:00
Дамян Минков
a50a980de4 Merge pull request #776 from jitsi/isolate-object-fix-browser-specific
Isolate object-fit fix for Windows Qt browser only
2016-08-08 17:19:56 -05:00
yanas
da0898a066 Isolate object-fit fix for Windows Qt browser only 2016-08-08 17:03:13 -05:00
Senya
8196233ed2 Update manual-install.md
There is no YOURSECRET4 mentions in the document
2016-05-29 15:04:33 +03:00
Bernhard Lichtinger
c9ebecbf10 Add Firefox in list of supportedBrowsers for invite emails 2016-04-22 12:32:46 +02:00
89 changed files with 4067 additions and 3538 deletions

2
.gitignore vendored
View File

@@ -6,4 +6,6 @@ node_modules
deploy-local.sh
libs/
all.css
*css.map
unsupported_browser.css
.remote-sync.json

View File

@@ -1,9 +1,13 @@
NPM = npm
BROWSERIFY = ./node_modules/.bin/browserify
NODE_SASS = ./node_modules/.bin/node-sass
UGLIFYJS = ./node_modules/.bin/uglifyjs
EXORCIST = ./node_modules/.bin/exorcist
CLEANCSS = ./node_modules/.bin/cleancss
CSS_FILES = font.css toastr.css main.css overlay.css videolayout_default.css font-awesome.css jquery-impromptu.css modaldialog.css notice.css popup_menu.css recording.css login_menu.css popover.css jitsi_popover.css contact_list.css chat.css welcome_page.css settingsmenu.css feedback.css jquery.contextMenu.css keyboard-shortcuts.css
STYLES_MAIN = css/main.scss
STYLES_UNSUPPORTED_BROWSER = css/unsupported_browser.scss
STYLES_BUNDLE = css/all.bundle.css
STYLES_DESTINATION = css/all.css
DEPLOY_DIR = libs
BROWSERIFY_FLAGS = -d
OUTPUT_DIR = .
@@ -12,8 +16,11 @@ IFRAME_API_DIR = ./modules/API/external
all: update-deps compile compile-iframe-api uglify uglify-iframe-api deploy clean
# FIXME: there is a problem with node-sass not correctly installed (compiled)
# a quick fix to make sure it is installed on every update
# the problem appears on linux and not on macosx
update-deps:
$(NPM) update
$(NPM) update && $(NPM) install node-sass
compile:
$(BROWSERIFY) $(BROWSERIFY_FLAGS) -e app.js -s APP | $(EXORCIST) $(OUTPUT_DIR)/app.bundle.js.map > $(OUTPUT_DIR)/app.bundle.js
@@ -34,6 +41,7 @@ deploy-appbundle:
$(OUTPUT_DIR)/app.bundle.js $(OUTPUT_DIR)/app.bundle.js.map \
$(OUTPUT_DIR)/external_api.js.map $(OUTPUT_DIR)/external_api.js \
$(OUTPUT_DIR)/external_api.min.map $(OUTPUT_DIR)/external_api.min.js \
$(OUTPUT_DIR)/analytics.js \
$(DEPLOY_DIR)
deploy-lib-jitsi-meet:
@@ -43,8 +51,12 @@ deploy-lib-jitsi-meet:
$(LIBJITSIMEET_DIR)/lib-jitsi-meet.js.map \
$(LIBJITSIMEET_DIR)/connection_optimization/external_connect.js \
$(DEPLOY_DIR)
deploy-css:
(cd css; cat $(CSS_FILES)) | $(CLEANCSS) > css/all.css
$(NODE_SASS) css/unsupported_browser.scss css/unsupported_browser.css ; \
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
$(CLEANCSS) $(STYLES_BUNDLE) > $(STYLES_DESTINATION) ; \
rm $(STYLES_BUNDLE)
deploy-local:
([ ! -x deploy-local.sh ] || ./deploy-local.sh)

View File

@@ -10,11 +10,13 @@
ga('send', 'pageview');
}
Analytics.prototype.sendEvent = function (action, data) {
// empty label and add the value, the value should be integer or null
var value = parseInt(data);
Analytics.prototype.sendEvent = function (action, data, label, browserName) {
// empty label if missing value for it and add the value,
// the value should be integer or null
var value = Math.round(parseFloat(data));
ga('send', 'event', 'jit.si', action, "", value ? value : null);
ga('send', 'event', 'jit.si',
action + '.' + browserName, label ? label : "", value ? value : null);
};
ctx.Analytics = Analytics;

4
app.js
View File

@@ -95,9 +95,9 @@ const APP = {
function setTokenData() {
let localUser = APP.tokenData.caller;
if(localUser) {
APP.settings.setEmail((localUser.getEmail() || "").trim());
APP.settings.setEmail((localUser.getEmail() || "").trim(), true);
APP.settings.setAvatarUrl((localUser.getAvatarUrl() || "").trim());
APP.settings.setDisplayName((localUser.getName() || "").trim());
APP.settings.setDisplayName((localUser.getName() || "").trim(), true);
}
}

View File

@@ -13,10 +13,11 @@ import CQEvents from './service/connectionquality/CQEvents';
import UIEvents from './service/UI/UIEvents';
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
import AnalyticsAdapter from './modules/statistics/AnalyticsAdapter';
import {reportError} from './modules/util/helpers';
import UIErrors from './modules/UI/UIErrors';
const ConnectionEvents = JitsiMeetJS.events.connection;
const ConnectionErrors = JitsiMeetJS.errors.connection;
@@ -33,6 +34,11 @@ let room, connection, localAudio, localVideo, roomLocker;
*/
let connectionIsInterrupted = false;
/**
* Indicates whether extension external installation is in progress or not.
*/
let DSExternalInstallationInProgress = false;
import {VIDEO_CONTAINER_TYPE} from "./modules/UI/videolayout/LargeVideo";
/**
@@ -42,6 +48,7 @@ const commands = {
CONNECTION_QUALITY: "stats",
EMAIL: "email",
AVATAR_URL: "avatar-url",
AVATAR_ID: "avatar-id",
ETHERPAD: "etherpad",
SHARED_VIDEO: "shared-video",
CUSTOM_ROLE: "custom-role"
@@ -229,15 +236,28 @@ function disconnectAndShowFeedback(requestFeedback) {
*/
function hangup (requestFeedback = false) {
const errCallback = (f, err) => {
console.error('Error occurred during hanging up: ', err);
return f();
// If we want to break out the chain in our error handler, it needs
// to return a rejected promise. In the case of feedback request
// in progress it's important to not redirect to the welcome page
// (see below maybeRedirectToWelcomePage call).
if (err === UIErrors.FEEDBACK_REQUEST_IN_PROGRESS) {
return Promise.reject('Feedback request in progress.');
}
else {
console.error('Error occurred during hanging up: ', err);
return Promise.resolve();
}
};
const disconnect = disconnectAndShowFeedback.bind(null, requestFeedback);
APP.conference._room.leave()
.then(disconnect)
.catch(errCallback.bind(null, disconnect))
.then(maybeRedirectToWelcomePage)
.catch(errCallback.bind(null, maybeRedirectToWelcomePage));
.catch(function(err){
console.log(err);
});
}
/**
@@ -270,7 +290,9 @@ function createLocalTracks (options, checkForPermissionPrompt) {
? APP.settings.getMicDeviceId()
: options.micDeviceId,
// adds any ff fake device settings if any
firefox_fake_device: config.firefox_fake_device
firefox_fake_device: config.firefox_fake_device,
desktopSharingExtensionExternalInstallation:
options.desktopSharingExtensionExternalInstallation
}, checkForPermissionPrompt)
.catch(function (err) {
console.error(
@@ -295,23 +317,6 @@ function changeLocalEmail(email = '') {
sendData(commands.EMAIL, email);
}
/**
* Changes the local avatar url for the local user
* @param avatarUrl {string} the new avatar url
*/
function changeLocalAvatarUrl(avatarUrl = '') {
avatarUrl = avatarUrl.trim();
if (avatarUrl === APP.settings.getAvatarUrl()) {
return;
}
APP.settings.setAvatarUrl(avatarUrl);
APP.UI.setUserAvatarUrl(room.myUserId(), avatarUrl);
sendData(commands.AVATAR_URL, avatarUrl);
}
/**
* Changes the display name for the local user
* @param nickname {string} the new display name
@@ -754,6 +759,8 @@ export default {
let avatarUrl = APP.settings.getAvatarUrl();
avatarUrl && sendData(this.commands.defaults.AVATAR_URL,
avatarUrl);
!email && sendData(
this.commands.defaults.AVATAR_ID, APP.settings.getAvatarId());
let nick = APP.settings.getDisplayName();
if (config.useNicks && !nick) {
@@ -850,6 +857,8 @@ export default {
return promise.then(function () {
if (stream) {
stream.on(TrackEvents.TRACK_AUDIO_NOT_WORKING,
APP.UI.showAudioNotWorkingDialog);
return room.addTrack(stream);
}
}).then(() => {
@@ -878,9 +887,38 @@ export default {
}
this.videoSwitchInProgress = true;
let externalInstallation = false;
if (shareScreen) {
createLocalTracks({ devices: ['desktop'] }).then(([stream]) => {
createLocalTracks({
devices: ['desktop'],
desktopSharingExtensionExternalInstallation: {
interval: 500,
checkAgain: () => {
return DSExternalInstallationInProgress;
},
listener: (status, url) => {
switch(status) {
case "waitingForExtension":
DSExternalInstallationInProgress = true;
externalInstallation = true;
APP.UI.showExtensionExternalInstallationDialog(
url);
break;
case "extensionFound":
if(externalInstallation) //close the dialog
$.prompt.close();
break;
default:
//Unknown status
}
}
}
}).then(([stream]) => {
DSExternalInstallationInProgress = false;
// close external installation dialog on success.
if(externalInstallation)
$.prompt.close();
stream.on(
TrackEvents.LOCAL_TRACK_STOPPED,
() => {
@@ -896,9 +934,13 @@ export default {
return this.useVideoStream(stream);
}).then(() => {
this.videoSwitchInProgress = false;
AnalyticsAdapter.sendEvent('conference.sharingDesktop.start');
JitsiMeetJS.analytics.sendEvent(
'conference.sharingDesktop.start');
console.log('sharing local desktop');
}).catch((err) => {
// close external installation dialog to show the error.
if(externalInstallation)
$.prompt.close();
this.videoSwitchInProgress = false;
this.toggleScreenSharing(false);
@@ -942,7 +984,8 @@ export default {
([stream]) => this.useVideoStream(stream)
).then(() => {
this.videoSwitchInProgress = false;
AnalyticsAdapter.sendEvent('conference.sharingDesktop.stop');
JitsiMeetJS.analytics.sendEvent(
'conference.sharingDesktop.stop');
console.log('sharing local video');
}).catch((err) => {
this.useVideoStream(null);
@@ -1120,6 +1163,12 @@ export default {
APP.UI.updateRecordingState(status);
});
room.on(ConferenceEvents.LOCK_STATE_CHANGED, (state, error) => {
console.log("Received channel password lock change: ", state,
error);
APP.UI.markRoomLocked(state);
});
room.on(ConferenceEvents.USER_STATUS_CHANGED, function (id, status) {
APP.UI.updateUserStatus(id, status);
});
@@ -1134,6 +1183,17 @@ export default {
APP.UI.updateDTMFSupport(isDTMFSupported);
});
APP.UI.addListener(UIEvents.EXTERNAL_INSTALLATION_CANCELED, () => {
// Wait a little bit more just to be sure that we won't miss the
// extension installation
setTimeout(() => DSExternalInstallationInProgress = false, 500);
});
APP.UI.addListener(UIEvents.OPEN_EXTENSION_STORE, (url) => {
window.open(
url, "extension_store_window",
"resizable,scrollbars=yes,status=1");
});
APP.UI.addListener(UIEvents.ROOM_LOCK_CLICKED, () => {
if (room.isModerator()) {
let promise = roomLocker.isLocked
@@ -1203,13 +1263,16 @@ export default {
APP.UI.setUserEmail(from, data.value);
});
APP.UI.addListener(UIEvents.AVATAR_URL_CHANGED, changeLocalAvatarUrl);
room.addCommandListener(this.commands.defaults.AVATAR_URL,
(data, from) => {
APP.UI.setUserAvatarUrl(from, data.value);
});
room.addCommandListener(this.commands.defaults.AVATAR_ID,
(data, from) => {
APP.UI.setUserAvatarID(from, data.value);
});
APP.UI.addListener(UIEvents.NICKNAME_CHANGED, changeLocalDisplayName);
APP.UI.addListener(UIEvents.START_MUTED_CHANGED,
@@ -1276,6 +1339,13 @@ export default {
delay: delay
};
room.sendApplicationLog(JSON.stringify(logObject));
// We only care about the delay between simulcast streams.
// Longer delays will be caused by something else and will just
// poison the data.
if (delay < 2000) {
JitsiMeetJS.analytics.sendEvent('stream.switch.delay', delay);
}
});
// Starts or stops the recording for the conference.
@@ -1304,9 +1374,15 @@ export default {
APP.UI.addListener(UIEvents.SELECTED_ENDPOINT, (id) => {
try {
// do not try to select participant if there is none (we are
// alone in the room), otherwise an error will be thrown cause
// reporting mechanism is not available (datachannels currently)
if (room.getParticipants().length === 0)
return;
room.selectParticipant(id);
} catch (e) {
AnalyticsAdapter.sendEvent('selectParticipant.failed');
JitsiMeetJS.analytics.sendEvent('selectParticipant.failed');
reportError(e);
}
});
@@ -1331,7 +1407,7 @@ export default {
APP.UI.addListener(
UIEvents.VIDEO_DEVICE_CHANGED,
(cameraDeviceId) => {
AnalyticsAdapter.sendEvent('settings.changeDevice.video');
JitsiMeetJS.analytics.sendEvent('settings.changeDevice.video');
createLocalTracks({
devices: ['video'],
cameraDeviceId: cameraDeviceId,
@@ -1352,7 +1428,8 @@ export default {
APP.UI.addListener(
UIEvents.AUDIO_DEVICE_CHANGED,
(micDeviceId) => {
AnalyticsAdapter.sendEvent('settings.changeDevice.audioIn');
JitsiMeetJS.analytics.sendEvent(
'settings.changeDevice.audioIn');
createLocalTracks({
devices: ['audio'],
cameraDeviceId: null,
@@ -1373,7 +1450,8 @@ export default {
APP.UI.addListener(
UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
(audioOutputDeviceId) => {
AnalyticsAdapter.sendEvent('settings.changeDevice.audioOut');
JitsiMeetJS.analytics.sendEvent(
'settings.changeDevice.audioOut');
APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
.then(() => console.log('changed audio output device'))
.catch((err) => {

View File

@@ -69,5 +69,9 @@ var config = {
'During that time service will not be available. ' +
'Apologise for inconvenience.',*/
disableThirdPartyRequests: false,
minHDHeight: 540
minHDHeight: 540,
// If true - all users without token will be considered guests and all users
// with token will be considered non-guests. Only guests will be allowed to
// edit their profile.
enableUserRolesBasedOnToken: false
};

BIN
css/.DS_Store vendored Normal file

Binary file not shown.

178
css/_base.scss Normal file
View File

@@ -0,0 +1,178 @@
* {
-webkit-user-select: none;
user-select: none;
}
html, body{
margin:0px;
height:100%;
color: $defaultColor;
font-size: 12px;
font-weight: 400;
background: #000000;
overflow: hidden;
}
html, body, input, textarea, keygen, select, button {
font-family: $baseFontFamily !important;
}
#nowebrtc {
display:none;
}
.no-fa-video-camera, .fa-microphone-slash {
color: #636363;
}
input[type='text'], input[type='password'], textarea {
-webkit-user-select: text;
user-select: text;
display: inline-block;
padding: 5px;
color: $defaultDarkColor;
border-radius: $borderRadius;
line-height: 32px;
height: 32px;
text-align: left;
border:1px solid $inputBorderColor;
outline: none; /* removes the default outline */
resize: none; /* prevents the user-resizing, adjust to taste */
}
textarea {
overflow: hidden;
word-wrap: break-word;
resize: horizontal;
}
button.no-icon {
padding: 0 1em;
}
button, input, select, textarea {
margin: 0;
vertical-align: baseline;
color: $defaultDarkColor;
background: $inputLightBackground;
font-size: 12px;
border: none;
box-shadow: none;
outline: none;
}
button, select, input[type="button"],
input[type="reset"], input[type="submit"] {
height: 32px;
line-height: 32px;
padding-left: 4px;
padding-right: 4px;
cursor: pointer;
}
button {
color: #FFF;
background-color: $buttonBackground !important;
border-radius: $borderRadius;
}
button,
form {
display: block;
}
#downloadlog {
display: none;
position: absolute;
bottom: 5;
left: 5;
overflow: visible;
color: rgba(255,255,255,.50);
}
.active {
background-color: #00ccff;
}
.glow
{
text-shadow: 0px 0px 30px #06a5df, 0px 0px 10px #06a5df, 0px 0px 5px #06a5df,0px 0px 3px #06a5df;
}
.watermark {
display: block;
position: absolute;
top: 15;
width: 186px;
height: 74px;
background-size: contain;
background-repeat: no-repeat;
z-index: 2;
}
.leftwatermark {
display: none;
left: $defaultToolbarSize;
margin-left: 10px;
background-image: url($defaultWatermarkLink);
background-position: center left;
}
.rightwatermark {
display: none;
right: 15;
background-position: center right;
}
.poweredby {
display: none;
position: absolute;
left: 25;
bottom: 7;
font-size: 11pt;
color: rgba(255,255,255,.50);
text-decoration: none;
z-index: 100;
}
.connected {
color: #21B9FC;
font-size: 12px;
}
.lastN, .disconnected {
color: #a3a3a3;
font-size: 12px;
}
/**
* Hides an element.
*/
.hide {
display: none !important;
}
/**
* Shows an element.
*/
.show {
display: block !important;
}
/**
* Shows an inline element.
*/
.show-inline {
display: inline-block !important;
}
/**
* Shows a flex element.
*/
.show-flex {
display: -webkit-box !important;
display: -moz-box !important;
display: -ms-flexbox !important;
display: -webkit-flex !important;
display: flex !important;
}

View File

@@ -1,10 +1,4 @@
#chatspace {
display: none;
background-color: black;
border-left: 1px solid #424242;
}
#chatspace * {
#chat_container * {
-webkit-user-select: text;
user-select: text;
}
@@ -22,8 +16,25 @@
overflow-y: scroll;
overflow-x: hidden;
word-wrap: break-word;
a:link {
color: rgb(184, 184, 184);
}
a:visited {
color: white;
}
a:hover {
color: rgb(213, 213, 213);
}
a:active {
color: black;
}
}
#chatspace.is-conversation-mode #chatconversation {
#chat_container.is-conversation-mode #chatconversation {
visibility: visible;
}
@@ -65,7 +76,7 @@
box-shadow: none;
}
#chatspace.is-conversation-mode #usermsg {
#chat_container.is-conversation-mode #usermsg {
visibility: visible;
}
@@ -80,7 +91,7 @@
width: 95%;
}
#chatspace.is-conversation-mode #nickname {
#chat_container.is-conversation-mode #nickname {
visibility: hidden;
}
@@ -98,26 +109,19 @@
position: absolute;
}
#bottomUnreadMessages {
top: 5px;
left: 10px;
position: absolute;
font-size: 8px;
}
#chatspace .username {
#chat_container .username {
float: left;
padding-left: 5px;
font-weight: bold;
}
#chatspace .timestamp {
#chat_container .timestamp {
float: right;
padding-right: 5px;
font-size: 11px;
}
#chatspace .usermessage {
#chat_container .usermessage {
padding-top: 20px;
padding-left: 5px;
}
@@ -178,7 +182,7 @@
visibility: hidden;
}
#chatspace.is-conversation-mode #smileysarea {
#chat_container.is-conversation-mode #smileysarea {
visibility: visible;
}
@@ -234,20 +238,4 @@
#usermsg::-webkit-scrollbar-track-piece {
background: #3a3a3a;
}
a:link {
color: rgb(184, 184, 184);
}
a:visited {
color: white;
}
a:hover {
color: rgb(213, 213, 213);
}
a:active {
color: black;
}

53
css/_contact_list.scss Normal file
View File

@@ -0,0 +1,53 @@
#contacts_container {
cursor: default;
> ul#contacts {
position: absolute;
top: 31px;
bottom: 0px;
width: 100%;
margin: 0px;
padding: 0px;
overflow-y: scroll;
overflow-x: hidden;
}
.clickable {
cursor: pointer;
}
}
#contacts {
>li {
list-style-type: none;
text-align: left;
white-space: nowrap;
color: #FFF;
font-size: 10pt;
padding: 7px 10px;
margin: 2px;
&:hover,
&:active {
background: $toolbarSelectBackground;
}
> p {
display: inline-block;
vertical-align: middle;
margin: 0px;
}
}
}
.avatar {
padding: 0px;
margin-right: 10px;
vertical-align: middle;
font-size: 22pt;
border-radius: 20px;
max-height: 30px;
max-width: 30px;
}

View File

@@ -0,0 +1,31 @@
.settingsContent {
display: flex;
display: -webkit-flex;
#localVideoPreview {
width: 50%;
align-self: baseline;
}
.deviceSelection {
display: flex;
display: -webkit-flex;
-webkit-flex: 1;
flex: 1;
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: left;
margin-left: 10px;
.device {
display: flex;
margin-bottom: 5px;
select {
flex: 1;
margin_right: 5px;
}
}
}
}

107
css/_feedback.scss Normal file
View File

@@ -0,0 +1,107 @@
@-webkit-keyframes shake-rotate {
0% {
-webkit-transform:scale(1) rotate(0deg);
transform:scale(1) rotate(0deg)
}
50% {
-webkit-transform:scale(.8) rotate(-5deg);
transform:scale(.8) rotate(-5deg)
}
to {
-webkit-transform:scale(1) rotate(3deg);
transform:scale(1) rotate(3deg)
}
}
@keyframes shake-rotate {
0% {
-webkit-transform:scale(1) rotate(0deg);
transform:scale(1) rotate(0deg)
}
50% {
-webkit-transform:scale(.8) rotate(-5deg);
transform:scale(.8) rotate(-5deg)
}
to {
-webkit-transform:scale(1) rotate(3deg);
transform:scale(1) rotate(3deg)
}
}
.shake-rotate {
-webkit-animation-duration: .4s;
animation-duration: .4s;
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
-webkit-animation-name: shake-rotate;
animation-name: shake-rotate;
-webkit-animation-timing-function: ease-in-out;
animation-timing-function: ease-in-out
}
.text-center {
text-align: center;
}
.feedbackDetails textarea {
resize: vertical;
min-height: 100px;
}
.feedback-rating {
line-height: 1.2;
padding: 20px 0;
h2 {
font-weight: 400;
font-size: 24px;
line-height: 1.2;
padding: auto;
margin: auto;
border: none;
}
p {
margin-top: 10px;
margin-left: 0px;
margin-bottom: 0px;
margin-right: 0px;
}
.star-label {
font-size: 16px;
color: $rateStarLabelColor;
}
.star-btn {
color: $rateStarDefault;
font-size: 36px;
position: relative;
cursor: pointer;
outline: none;
text-decoration: none;
@include transition(all .2s ease);
&.starHover,
&.active,
&:hover {
color: $rateStarActivity;
.fa {
top: -6px;
}
};
&.rated:hover .fa {
top: 0;
}
.fa {
position: relative;
}
}
}

View File

@@ -1,15 +1,3 @@
@font-face {
font-family: 'open_sanslight';
src: url('../fonts/OpenSans-Light-webfont.eot');
src: url('../fonts/OpenSans-Light-webfont.eot?#iefix') format('embedded-opentype'),
url('../fonts/OpenSans-Light-webfont.woff') format('woff'),
url('../fonts/OpenSans-Light-webfont.ttf') format('truetype'),
url('../fonts/OpenSans-Light-webfont.svg#open_sanslight') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'jitsi';
src:url('../fonts/jitsi.eot?94d075');
@@ -36,100 +24,105 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-contactList:before {
content: "\e615";
.icon-feedback:before {
content: "\e91d";
}
.icon-toggle-filmstrip:before {
content: "\e91c";
}
.icon-avatar:before {
content: "\e616";
content: "\e901";
}
.icon-callRetro:before {
content: "\e611";
.icon-autorenew:before {
content: "\e903";
}
.icon-callModern:before {
content: "\e612";
}
.icon-recDisable:before {
content: "\e613";
}
.icon-recEnable:before {
content: "\e614";
}
.icon-authenticate:before {
content: "\e1ae";
}
.icon-kick1:before {
content: "\e60f";
}
.icon-kick:before {
content: "\e610";
}
.icon-share-desktop:before {
content: "\e602";
}
.icon-chat-simple:before {
content: "\e606";
}
.icon-full-screen:before {
content: "\e60d";
}
.icon-exit-full-screen:before {
content: "\e60e";
}
.icon-link:before {
content: "\e600";
.icon-hangup:before {
content: "\e905";
}
.icon-chat:before {
content: "\e601";
content: "\e906";
}
.icon-presentation:before {
content: "\e603";
.icon-download:before {
content: "\e902";
}
.icon-security:before {
content: "\e604";
.icon-edit:before {
content: "\e907";
}
.icon-share-doc:before {
content: "\e605";
content: "\e908";
}
.icon-telephone:before {
content: "\e611";
content: "\e909";
}
.icon-kick:before {
content: "\e904";
}
.icon-full-screen:before {
content: "\e90b";
}
.icon-exit-full-screen:before {
content: "\e90c";
}
.icon-star-full:before {
content: "\e90a";
}
.icon-security:before {
content: "\e90d";
}
.icon-security-locked:before {
content: "\e607";
content: "\e90e";
}
.icon-camera:before {
content: "\e608";
}
.icon-camera-disabled:before {
content: "\e609";
}
.icon-mic-disabled:before {
content: "\e60a";
.icon-reload:before {
content: "\e90f";
}
.icon-microphone:before {
content: "\e60b";
content: "\e910";
}
.icon-hangup:before {
content: "\e617";
.icon-mic-empty:before {
content: "\e911";
}
.icon-reload:before {
content: "\e618";
.icon-mic-disabled:before {
content: "\e912";
}
.icon-filmstrip:before {
content: "\e619";
.icon-contactList:before {
content: "\e91b";
}
.icon-connection:before {
line-height: normal;
content: "\e61a";
.icon-link:before {
content: "\e913";
}
.icon-shared-video:before {
content: "\e914";
}
.icon-settings:before {
content: "\e61b";
content: "\e915";
}
.icon-dialPad:before {
content: "\e61c";
.icon-star:before {
content: "\e916";
}
.icon-share-desktop:before {
content: "\e917";
}
.icon-camera:before {
content: "\e918";
}
.icon-camera-disabled:before {
content: "\e919";
}
.icon-volume:before {
content: "\e91a";
}
.icon-connection-lost:before {
content: "\e900";
}
.icon-connection:before {
content: "\e61a";
}
.icon-recDisable:before {
content: "\e613";
}
.icon-recEnable:before {
content: "\e614";
}
.icon-presentation:before {
content: "\e603";
}

View File

@@ -55,7 +55,7 @@ div.jqi .jqibuttons{
div.jqi .jqibuttons button{
margin: 0;
padding: 5px 20px;
background-color: transparent;
background-color: transparent !important;
font-weight: normal;
border: none;
border-left: solid 1px #e4e4e4;

View File

@@ -2,11 +2,12 @@
display: none;
position: absolute;
bottom: 20px;
left: 20px;
left: $defaultToolbarSize;
overflow: hidden;
padding: 20px;
z-index: 1;
border-radius: 15px;
margin-left: 10px;
z-index: 10;
border-radius: $borderRadius;
background-attachment: scroll;
background-size: auto auto;
color: rgba(255, 255, 255, .8);
@@ -16,4 +17,9 @@
#keyboard-shortcuts .item-action {
color: #209EFF;
font-size: 14pt;
padding-right: 5px;
}
#keyboard-shortcuts-list {
list-style-type: none;
}

View File

@@ -1,6 +1,7 @@
/*Initialize*/
ul.loginmenu {
font-family: inherit;
font-family: $baseFontFamily;
line-height: normal;
display:none;
position: absolute;
margin: 0;
@@ -44,6 +45,10 @@ span.authentication:hover ul.loginmenu, ul.loginmenu:hover {
display:block !important;
}
span.authentication {
position: relative;
}
a.disabled {
color: gray !important;
pointer-events: none;
@@ -55,4 +60,17 @@ a.disabled {
position: absolute;
top: -30px;
left: 0px;
}
}
.loginmenu.extendedToolbarPopup {
left: 55px;
top: 0px;
}
ul.loginmenu.extendedToolbarPopup:after {
content: url('../images/leftDropdownPointer.png');
display: block;
position: absolute;
top: 18px;
left: -7px;
}

52
css/_mixins.scss Normal file
View File

@@ -0,0 +1,52 @@
/**
* Animation mixin.
*/
@mixin animation($animate...) {
$max: length($animate);
$animations: '';
@for $i from 1 through $max {
$animations: #{$animations + nth($animate, $i)};
@if $i < $max {
$animations: #{$animations + ", "};
}
}
-webkit-animation: $animations;
-moz-animation: $animations;
-o-animation: $animations;
animation: $animations;
}
/**
* Keyframes mixin.
*/
@mixin keyframes($animationName) {
@-webkit-keyframes #{$animationName} {
@content;
}
@-moz-keyframes #{$animationName} {
@content;
}
@-o-keyframes #{$animationName} {
@content;
}
@keyframes #{$animationName} {
@content;
}
}
@mixin transform($func) {
-moz-transform: $func;
-ms-transform: $func;
-webkit-transform: $func;
-o-transform: $func;
transform: $func;
}
@mixin transition($transition...) {
-moz-transition: $transition;
-o-transition: $transition;
-webkit-transition: $transition;
transition: $transition;
}

View File

@@ -0,0 +1,114 @@
/**
* Toolbar side panel main container element.
*/
#sideToolbarContainer {
display: inline-block;
position:absolute;
top: 0px;
left: $defaultToolbarSize;
width: 0%;
height: 100%;
max-width: 200px;
background-color: rgba(0,0,0,0.8);
z-index: 800;
overflow: hidden;
/**
* Labels inside the side panel.
*/
label {
color: $defaultSemiDarkColor;
}
/**
* Form elements and blocks.
*/
input, label, select, button, a, .sideToolbarBlock {
display: inline-block;
margin-top: 15px;
margin-left: 10%;
width: 80%;
}
/**
* Specify colors for edit elements.
*/
select, input[type="button"], input[type="text"],
input[type="reset"], input[type="submit"] {
color: $defaultColor;
background: $inputBackground;
border: none;
}
/**
* Specify styling of elements inside a block.
*/
.sideToolbarBlock {
input, label, button, a, select {
margin-top: 5px;
margin-left: 0;
width: 100%;
}
.startMutedLabel,
.followMeLabel {
display: inline;
margin-top: 0;
}
}
/**
* Inner container, for example contact list, settings or profile.
*/
.sideToolbarContainer__inner {
display: none;
width: 200px;
color: #FFF;
/**
* Titles and subtitles of inner containers.
*/
> div.title,
div.subTitle {
color: $defaultColor !important;
text-align: left;
margin: 10px 0px 10px 0px;
padding: 5px 10px 5px 10px;
}
/**
* Main title size.
*/
> div.title {
font-size: 16px;
}
/**
* Subtitle specific properties.
*/
> div.subTitle {
font-size: 12px;
background: $inputSemiBackground !important;
margin-top: 20px !important;
margin-bottom: 8px !important;
}
/**
* First element after a title.
*/
.first {
margin-top: 0px !important;
}
}
}
#device_settings {
width : auto !important;
text-align: center;
}
#startAudioMuted,
#startVideoMuted,
#followMeCheckBox {
width: 13px !important;
}

View File

@@ -19,32 +19,42 @@
.toast-message label {
color: #ffffff;
}
.toast-message .nickname {
font-weight: bold;
}
.toast-message a:hover {
color: #cccccc;
text-decoration: none;
}
.toast-close-button {
position: relative;
right: -0.3em;
top: -0.3em;
float: right;
font-size: 20px;
font-size: 15px;
height: 15px;
width: 15px;
font-weight: bold;
color: #ffffff;
background: transparent !important;
-webkit-text-shadow: 0 1px 0 #ffffff;
text-shadow: 0 1px 0 #ffffff;
opacity: 0.8;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80);
filter: alpha(opacity=80);
}
.toast-close-button:hover,
.toast-close-button:focus {
color: #000000;
color: #ffffff;
text-decoration: none;
cursor: pointer;
opacity: 0.4;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
filter: alpha(opacity=40);
opacity: 1;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
filter: alpha(opacity=100);
}
/*Additional properties for button version
iOS requires the button element instead of an anchor tag.
@@ -173,4 +183,30 @@ button.toast-close-button {
padding: 15px 15px 15px 15px;
width: 25em;
}
}
#toast-container.notification-bottom-right {
bottom: 140px;
right: 5px;
}
#toast-container.notification-bottom-right-center {
right: 205px;
}
#toast-container .toast-info {
-webkit-box-shadow: none;
box-shadow: none;
}
.toast-close-button {
right: -7px;
top: -19px;
}
#toast-container .toast-info {
background-color: black;
border: 1px solid #3a3a3a;
width: 220px;
padding: 10px 10px 10px 50px;
}

236
css/_toolbars.scss Normal file
View File

@@ -0,0 +1,236 @@
.toolbar {
background-color: rgba(0,0,0,0.5);
position: relative;
z-index: $toolbarZ;
height: 100%;
pointer-events: auto;
}
#mainToolbarContainer{
display: block;
position: absolute;
text-align: center;
top:0;
left:0;
right:0;
z-index: $toolbarZ;
pointer-events: none;
min-height: 100px;
transform: translateY(-100%);
-webkit-transform: translateY(-100%);
}
#subject {
position: relative;
z-index: 3;
width: auto;
padding: 5px;
margin-left: 40%;
margin-right: 40%;
text-align: center;
background: linear-gradient(to bottom, rgba(255,255,255,.85) , rgba(255,255,255,.35));
box-shadow: 0 0 2px #000000, 0 0 10px #000000;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
}
#mainToolbar {
height: $defaultToolbarSize;
display: inline-block;
position: relative;
top: 30px;
margin-left: auto;
margin-right: auto;
width: auto;
border-radius: 4px;
.first {
border-bottom-left-radius: 4px;
border-top-left-radius: 4px;
}
.last {
border-bottom-right-radius: 4px;
border-top-right-radius: 4px;
}
}
#extendedToolbar {
display: flex;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
width: $defaultToolbarSize;
height: 100%;
top: 0px;
left: 0px;
padding-top: 10px;
flex-direction: column;
flex-wrap: nowrap;
justify-content: flex-start;
align-items: center;
transform: translateX(-100%);
-webkit-transform: translateX(-100%);
}
#toolbar_button_hangup {
color: #BF2117;
font-size: $hangupFontSize !important;
}
#toolbar_button_etherpad {
display: none;
}
#numberOfParticipants {
position: absolute;
top: 5px;
line-height: 13px;
font-weight: bold;
font-size: 11px;
}
#mainToolbar a.button:last-child::after {
content: none;
}
.button {
display: inline-block;
position: relative;
color: #FFFFFF;
top:0px;
width: 50px;
height: 50px;
cursor: pointer;
text-align: center;
z-index: 1;
font-size: $toolbarFontSize !important;
line-height: 50px !important;
vertical-align: middle;
}
.button[disabled] {
opacity: 0.5;
}
.button.unclickable {
cursor: default;
}
a.button.unclickable:hover,
a.button.unclickable:active,
a.button.unclickable.selected{
cursor: default;
background: none;
}
a.button:hover,
a.button:active,
a.button.selected {
cursor: pointer;
// sum opacity with background layer should give us 0.8
background: $toolbarSelectBackground;
}
a.button>#avatar {
width: 30px;
border-radius: 50%;
padding-top: 10px;
padding-bottom: 10px;
}
#feedbackButton {
margin-top: auto;
}
/**
* START of slide in animation for extended toolbar.
*/
@include keyframes(slideInX) {
0% { transform: translateX(-100%); }
100% { transform: translateX(0%); }
}
.slideInX {
@include animation('slideInX .5s forwards');
}
@include keyframes(slideOutX) {
0% { transform: translateX(0%); }
100% { transform: translateX(-100%); }
}
.slideOutX {
@include animation('slideOutX .5s forwards');
}
@include keyframes(slideInExtX) {
0% { transform: translateX(-500%); }
100% { transform: translateX(0%); }
}
.slideInExtX {
@include animation('slideInExtX .5s forwards');
}
@include keyframes(slideOutExtX) {
0% { transform: translateX(0%); }
100% { transform: translateX(-500%); }
}
.slideOutExtX {
@include animation('slideOutExtX .5s forwards');
}
/**
* END of slide out animation for extended toolbar.
*/
/**
* START of slide in / out animation for main toolbar.
*/
@include keyframes(slideInY) {
100% { transform: translateY(0%); }
}
.slideInY {
@include animation('slideInY .5s forwards');
}
@include keyframes(slideOutY) {
0% { transform: translateY(0%); }
100% { transform: translateY(-100%); }
}
.slideOutY {
@include animation('slideOutY .5s forwards');
}
/**
* END of slide in / out animation for main toolbar.
*/
/**
* START of slide in animation for extended toolbar panel.
*/
@include keyframes(slideInExt) {
from { width: 0px; }
to { width: 200px; } // TO FIX: Make this value a percentage.
}
.slideInExt {
@include animation("slideInExt .5s forwards");
}
@include keyframes(slideOutExt) {
from { width: 200px; } // TO FIX: Make this value a percentage.
to { width: 0px; }
}
.slideOutExt {
@include animation("slideOutExt .5s forwards");
}
/**
* END of slide in animation for extended toolbar panel.
*/

42
css/_variables.scss Normal file
View File

@@ -0,0 +1,42 @@
/**
<<<<<<< HEAD
* Style variables
*/
$baseFontFamily: 'open_sanslight', 'Helvetica Neue', Helvetica, Arial, sans-serif;
$toolbarFontSize: 1.9em;
$hangupFontSize: 2em;
/**
* Size variables.
*/
$defaultToolbarSize: 50px;
/**
* Color variables.
*/
$defaultColor: #F1F1F1;
$defaultSemiDarkColor: #ACACAC;
$defaultDarkColor: #4F4F4F;
$defaultBackground: #474747;
$toolbarSelectBackground: rgba(0, 0, 0, .6);
$inputBackground: rgba(132, 132, 132, .5);
$inputSemiBackground: rgba(132, 132, 132, .8);
$inputLightBackground: #EBEBEB;
$inputBorderColor: #EBEBEB;
$buttonBackground: #44A5FF;
/**
* Misc.
*/
$borderRadius: 4px;
$defaultWatermarkLink: '../images/watermark.png';
/**
* Z-indexes. TODO: Replace this by a function.
*/
$toolbarZ: 900;
$overlayZ: 800;
$rateStarDefault: #ccc;
$rateStarActivity: #f6c342;
$rateStarLabelColor: #333;

View File

@@ -8,7 +8,15 @@
}
#remoteVideos {
display:block;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-end;
position:absolute;
text-align:right;
height:196px;
@@ -30,8 +38,6 @@
.videocontainer {
position: relative;
margin-left: auto;
margin-right: auto;
text-align: center;
}
@@ -41,13 +47,6 @@
background-size: contain;
border-radius:1px;
border: 1px solid #212425;
/**
* Some browsers don't have full support of the object-fit property for the
* video element and when we set video object-fit to "cover" the video
* actually overflows the boundaries of its container, so it's important
* to indicate that the "overflow" should be hidden.
*/
overflow: hidden;
}
#remoteVideos .videocontainer.videoContainerFocused {
@@ -142,10 +141,6 @@
z-index: 0;
}
#toolbar_button_etherpad {
display: none;
}
#remoteVideos .videocontainer>span.focusindicator,
#remoteVideos .videocontainer>div.remotevideomenu {
position: absolute;
@@ -358,33 +353,6 @@
z-index: 20; /*The reload button should appear on top of the header!*/
}
#header{
display:none;
position:absolute;
text-align:center;
top:0;
left:0;
right:0;
z-index:10;
pointer-events: none;
min-height: 100px;
}
#subject {
position: relative;
z-index: 3;
width: auto;
padding: 5px;
margin-left: 40%;
margin-right: 40%;
text-align: center;
background: linear-gradient(to bottom, rgba(255,255,255,.85) , rgba(255,255,255,.35));
box-shadow: 0 0 2px #000000, 0 0 10px #000000;
border-bottom-left-radius: 12px;
border-bottom-right-radius: 12px;
display: none;
}
.audiolevel {
display: inline-block;
position: absolute;

View File

@@ -1,4 +1,3 @@
#disable_welcome {
display:none;
}
@@ -59,6 +58,7 @@
font-size: 18px;
font-weight: 500;
padding-left: 20px;
color: $defaultDarkColor;
}
#enter_room_field {
@@ -188,9 +188,9 @@
#reload_roomname
{
width: 30px;
height: 19px;
color: #acacac;
margin-top: 22px;
font-size: 1.9em;
line-height: 55px;
z-index: 3;
float: left;
cursor: pointer;

View File

@@ -1,58 +0,0 @@
#contactlist {
display: none;
background-color: black;
cursor: default;
}
#contactlist>div.title {
text-align: left;
padding: 7px 10px;
margin: 2px;
color: #21B9FC;
font-size: 11pt;
border-bottom: 1px solid #676767;
}
#contactlist>div.title>span {
margin-left: 5px;
}
#contactlist>ul#contacts {
position: absolute;
top: 31px;
bottom: 0px;
width: 100%;
margin: 0px;
padding: 0px;
overflow-y: scroll;
overflow-x: hidden;
}
#contacts>li {
list-style-type: none;
text-align: left;
color: #FFF;
font-size: 10pt;
padding: 7px 10px;
margin: 2px;
}
#contacts>li>p {
display: inline-block;
vertical-align: middle;
margin: 0px;
}
.avatar {
padding: 0px;
margin-right: 10px;
vertical-align: middle;
font-size: 22pt;
border-radius: 20px;
max-height: 30px;
max-width: 30px;
}
#contactlist .clickable {
cursor: pointer;
}

View File

@@ -1,48 +0,0 @@
/**
* The feedback window inner div css.
*/
.feedback {
width: 450px;
display: block;
margin-left: auto;
margin-right: auto;
text-align: center;
font-size: 22px;
}
/**
* Style of the thank you text inside the feedback window.
*/
.feedbackTitle {
font-size: 22px;
color: #087dba;
}
/**
* Stars div css.
*/
#stars {
font-size: 30px;
}
/**
* Star css.
*/
#stars>a {
padding-right: 4px;
}
/**
* Mouse over a star.
*/
.starHover {
color: #087dba;
}
/**
* Detailed feedback section text area style.
*/
.feedbackDetails textarea {
resize: vertical;
min-height: 100px;
}

View File

@@ -1,394 +0,0 @@
* {
-webkit-user-select: none;
user-select: none;
}
html, body{
margin:0px;
height:100%;
color: #424242;
font-size: 14px;
font-weight: 400;
background: #000000;
overflow: hidden;
}
html, body, input, textarea, keygen, select, button {
font-family: 'open_sanslight',
'Helvetica Neue',
Helvetica,
sans-serif !important;
}
.right-panel {
display:none;
position:absolute;
float: right;
top: 0px;
bottom: 0px;
right: 0px;
width: 0px;
max-width: 200px;
overflow: hidden;
/* background-color:#dfebf1;*/
background-color:#FFFFFF;
border-left:1px solid #424242;
z-index: 5;
}
#nowebrtc {
display:none;
}
#header_container {
z-index: 1014;
}
.toolbar_span {
display: inline-block;
position: relative;
}
#toolbar a.button:last-child::after {
content: none;
}
.button {
display: inline-block;
position: relative;
color: #FFFFFF;
top:0px;
padding-top: 10px;
width: 38px;
height: 28px;
cursor: pointer;
text-align: center;
text-shadow: 0 1px 0 rgba(255,255,255,.3), 0 -1px 0 rgba(0,0,0,.6);
z-index: 1;
font-size: 1.44em !important;
vertical-align: middle;
}
.button[disabled] {
opacity: 0.5;
}
.toolbar_span>span {
display: inline-block;
position: absolute;
font-size: 7pt;
color: #ffffff;
text-align:center;
cursor: pointer;
}
#toolbar_button_chat, #chatBottomButton, #contactListButton, #numberOfParticipants {
-webkit-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;
}
/*#ffde00*/
#toolbar_button_chat.active, #contactListButton.glowing, #chatBottomButton.glowing {
-webkit-text-shadow: -1px 0 10px #21B9FC,
0 1px 10px #21B9FC,
1px 0 10px #21B9FC,
0 -1px 10px #21B9FC;
-moz-text-shadow: 1px 0 10px #21B9FC,
0 1px 10px #21B9FC,
1px 0 10px #21B9FC,
0 -1px 10px #21B9FC;
text-shadow: -1px 0 10px #21B9FC,
0 1px 10px #21B9FC,
1px 0 10px #21B9FC,
0 -1px 10px #21B9FC;
}
#toolbar_button_hangup {
color: #ff0000;
font-size: 1.4em;
}
#numberOfParticipants {
position: absolute;
top: 0px;
right: -1px;
color: white;
width: 13px;
height: 13px;
line-height: 13px;
font-weight: bold;
border-radius: 1px;
font-size: 11px;
text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;
}
#contactListButton.active #numberOfParticipants {
color: #21B9FC;
}
#toolbar {
display:inline-block;
position:relative;
top:5px;
margin-left:auto;
margin-right:auto;
height:38px;
width:auto;
background-color: rgba(0,0,0,0.5);
border-radius: 1px;
pointer-events: auto;
}
#toolbar_button_record {
-webkit-transition: all .5s ease-in-out;
-moz-transition: all .5s ease-in-out;
transition: all .5s ease-in-out;
}
a.button:hover,
a.bottomToolbarButton:hover {
cursor: pointer;
background: rgba(255, 255, 255, 0.1);
border-radius: 1px;
-webkit-border-radius: 1px;
}
.no-fa-video-camera, .fa-microphone-slash {
color: #636363;
}
.bottom_button_separator {
display: inline-block;
position: relative;
left: 5px;
width: 20px;
height: 1px;
background: #373737;
}
input[type='text'], input[type='password'], textarea {
-webkit-user-select: text;
user-select: text;
display: inline-block;
font-size: 14px;
padding: 5px;
background: #f3f3f3;
border-radius: 3px;
font-weight: 100;
line-height: 20px;
height: 40px;
color: #333;
text-align: left;
border:1px solid #ACD8F0;
outline: none; /* removes the default outline */
resize: none; /* prevents the user-resizing, adjust to taste */
}
input[type='text'], input[type='password'], textarea:focus {
box-shadow: inset 0 0 3px 2px #ACD8F0; /* provides a more style-able
replacement to the outline */
}
textarea {
overflow: hidden;
word-wrap: break-word;
resize: horizontal;
}
button.no-icon {
padding: 0 1em;
}
button {
border: none;
height: 35px;
padding: 0 1em 0 2em;
position: relative;
border-radius: 1px;
font-weight: bold;
color: #fff;
line-height: 35px;
background: #2c8ad2;
}
button, input, select, textarea {
margin: 0;
vertical-align: baseline;
}
button, input[type="button"], input[type="reset"], input[type="submit"] {
cursor: pointer;
-webkit-appearance: button;
}
form {
display: block;
}
#downloadlog {
position: absolute;
bottom: 5;
left: 5;
overflow: visible;
color: rgba(255,255,255,.50);
}
#feedbackButton {
position: absolute;
bottom: 60;
left: 60;
overflow: visible;
color: rgba(255,255,255,.50);
}
#feedbackButtonDiv {
display: none;
position: absolute;
background-color: rgba(0,0,0,.50);
border-radius: 50%;
width: 100px;
height: 100px;
bottom: -50px;
left: -50px;
z-index: 100;
overflow: hidden;
transition: all 2s ease-in-out;
}
#feedbackButtonDiv.hidden {
bottom: -246px;
}
div.feedbackButton:hover {
transform: scale(1.3);
}
#bottomToolbar {
display:block;
position: absolute;
right: 0;
margin-right: 5px;
bottom: 40px;
width: 29px;
border-radius: 1px;
color: #FFF;
background: rgba(0,0,0,0.5);
z-index: 6; /*+1 from #remoteVideos*/
}
.bottomToolbarButton {
display: inline-block;
position: relative;
color: #FFFFFF;
top: 0;
padding-top: 6px;
margin: 2px;
width: 25px;
height: 20px;
cursor: pointer;
font-size: 10pt;
text-align: center;
text-shadow: 0px 1px 0px rgba(255,255,255,.3), 0px -1px 0px rgba(0,0,0,.7);
z-index: 1;
}
.active {
background-color: #00ccff;
}
.bottomToolbar_span>span {
display: inline-block;
position: absolute;
font-size: 7pt;
color: #ffffff;
text-align: center;
cursor: pointer;
}
.glow
{
text-shadow: 0px 0px 30px #06a5df, 0px 0px 10px #06a5df, 0px 0px 5px #06a5df,0px 0px 3px #06a5df;
}
.watermark {
display: block;
position: absolute;
top: 15;
width: 186px;
height: 74px;
background-size: contain;
background-repeat: no-repeat;
z-index: 2;
}
.leftwatermark {
display: none;
left: 15;
background-image:url(../images/watermark.png);
background-position: center left;
}
.rightwatermark {
display: none;
right: 15;
background-position: center right;
}
.poweredby {
display: none;
position: absolute;
left: 25;
bottom: 7;
font-size: 11pt;
color: rgba(255,255,255,.50);
text-decoration: none;
z-index: 100;
}
#toast-container.notification-bottom-right {
bottom: 140px;
right: 5px;
}
#toast-container.notification-bottom-right-center {
right: 205px;
}
#toast-container .toast-info {
-webkit-box-shadow: none;
box-shadow: none;
}
.toast-close-button {
right: -7px;
top: -19px;
}
#toast-container .toast-info {
background-color: black;
border: 1px solid #3a3a3a;
width: 220px;
padding: 10px 10px 10px 50px;
}
.connected {
color: #21B9FC;
font-size: 12px;
}
.lastN, .disconnected {
color: #a3a3a3;
font-size: 12px;
}
.toast-close-button:hover,
.toast-close-button:focus {
color: #ffffff;
opacity: 1;
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100);
filter: alpha(opacity=100);
}
.toast-message .nickname {
font-weight: bold;
}

45
css/main.scss Normal file
View File

@@ -0,0 +1,45 @@
/* Variables BEGIN */
@import 'variables';
/* Variables END */
/* Mixins BEGIN */
@import "mixins";
/* Mixins END */
/* Fonts BEGIN */
@import 'font';
@import 'font-awesome';
/* Fonts END */
/* Modules BEGIN */
@import 'toastr';
@import 'base';
@import 'overlay/overlay';
@import 'videolayout_default';
@import 'jquery-impromptu';
@import 'modaldialog';
@import 'notice';
@import 'popup_menu';
@import 'recording';
@import 'login_menu';
@import 'popover';
@import 'jitsi_popover';
@import 'contact_list';
@import 'chat';
@import 'ringing/ringing';
@import 'welcome_page';
@import 'toolbars';
@import 'side_toolbar_container';
@import 'device_settings_dialog';
@import 'feedback';
@import 'jquery.contextMenu';
@import 'keyboard-shortcuts';
/* Modules END */

View File

@@ -1,11 +1,10 @@
.overlay {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1013;
z-index: $overlayZ;
background: #21B9FC; /* Old browsers */
opacity: 0.75;
display: block;
@@ -19,7 +18,7 @@
width: 100%;
height: 100%;
position: fixed;
z-index: 1013;
z-index: $overlayZ;
}
.overlay_content {
@@ -36,23 +35,6 @@
margin-left: -200px;
}
.overlay_avatar {
width: 200px;
height: 200px;
position: relative;
border-radius: 100px;
z-index: 1013;
float: left;
margin-left: 100px;
}
.overlay_text {
position: relative;
width: 400px;
z-index: 1013;
margin-top: 50px;
float: left;
}
.overlay_text_small {
font-size: 18px;

36
css/ringing/_ringing.scss Normal file
View File

@@ -0,0 +1,36 @@
.ringing {
display: block;
left: 0;
top: 0;
width: 100%;
height: 100%;
position: fixed;
z-index: $overlayZ;
background: linear-gradient(transparent, #000);
opacity: 0.8;
&__content {
position: absolute;
width: 400px;
height: 250px;
left: 50%;
top: 50%;
margin-left: -200px;
margin-top: -125px;
font-weight: 400;
font-size: 14px;
text-align: center;
}
&__avatar {
width: 100px;
height: 100px;
border-radius: 50%;
}
&__caller-info {
.mention {
color: #333;
}
}
}

View File

@@ -1,96 +0,0 @@
#settingsmenu {
display: none;
background: black;
color: #21B9FC;
overflow-y: auto;
}
#settingsmenu input, select {
margin-top: 10px;
margin-left: 10%;
width: 80%;
font-size: 14px;
background: #3a3a3a;
border: none;
box-shadow: none;
color: #a7a7a7;
}
#settingsmenu>div.title {
text-align: left;
padding: 7px 10px;
margin: 2px;
font-size: 11pt;
border-bottom: 1px solid #676767;
}
#settingsmenu>div.title>span {
margin-left: 5px;
}
#settingsmenu .arrow-up {
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid #3a3a3a;
position: relative;
top: 10px;
margin-left: auto;
margin-right: auto;
}
#settingsmenu #avatar {
width: 24%;
left: 38%;
border-radius: 25px;
position: relative;
}
#languages_selectbox {
height: 40px;
cursor: pointer;
}
#startMutedOptions,
#followMeOptions {
padding-left: 10%;
text-indent: -10%;
margin-top: 10px;
display: none; /* hide by default */
/* clearfix */
overflow: auto;
zoom: 1;
}
#startAudioMuted,
#startVideoMuted,
#followMeCheckBox {
width: 13px !important;
}
.startMutedLabel,
.followMeLabel {
width: 94%;
float: left;
cursor: pointer;
}
#devicesOptions {
display: none;
margin-top: 10px;
}
#devicesOptions label {
display: block;
margin-top: 15px;
}
#devicesOptions span {
padding-left: 10%;
}
#devicesOptions select {
height: 40px;
cursor: pointer;
}

View File

@@ -1,9 +1,11 @@
@import 'variables';
body {
width:100%;
height:100%;
background-color: white;
color: #424242;
font-family: 'open_sanslight', 'Helvetica Neue', Helvetica, sans-serif;
font-family: $baseFontFamily;
font-size: 28px;
margin:0;
padding:0;
@@ -98,39 +100,39 @@ a {
{
width: 78px;
height: 78px;
background-image: url('/images/chrome.png');
background-image: url('../images/chrome.png');
}
#chromium_logo
{
width: 77px;
height: 78px;
background-image: url('/images/chromium.png');
background-image: url('../images/chromium.png');
}
#firefox_logo
{
width: 86px;
height: 80px;
background-image: url('/images/firefox.png');
background-image: url('../images/firefox.png');
}
#opera_logo
{
width: 73px;
height: 78px;
background-image: url('/images/opera.png');
background-image: url('../images/opera.png');
}
#safari_logo
{
width: 78px;
height: 79px;
background-image: url('/images/safari.png');
background-image: url('../images/safari.png');
}
#ie_logo
{
width: 80px;
height: 78px;
background-image: url('/images/ie.png');
background-image: url('../images/ie.png');
}

View File

@@ -1,202 +0,0 @@
/*
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
* in FIPS PUB 180-1
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for details.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
/*
* Perform a simple self-test to see if the VM is working
*/
function sha1_vm_test()
{
return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}
/*
* Calculate the SHA-1 of an array of big-endian words, and a bit length
*/
function core_sha1(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << (24 - len % 32);
x[((len + 64 >> 9) << 4) + 15] = len;
var w = Array(80);
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
var e = -1009589776;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
var olde = e;
for(var j = 0; j < 80; j++)
{
if(j < 16) w[j] = x[i + j];
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
safe_add(safe_add(e, w[j]), sha1_kt(j)));
e = d;
d = c;
c = rol(b, 30);
b = a;
a = t;
}
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
e = safe_add(e, olde);
}
return Array(a, b, c, d, e);
}
/*
* Perform the appropriate triplet combination function for the current
* iteration
*/
function sha1_ft(t, b, c, d)
{
if(t < 20) return (b & c) | ((~b) & d);
if(t < 40) return b ^ c ^ d;
if(t < 60) return (b & c) | (b & d) | (c & d);
return b ^ c ^ d;
}
/*
* Determine the appropriate additive constant for the current iteration
*/
function sha1_kt(t)
{
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
(t < 60) ? -1894007588 : -899497514;
}
/*
* Calculate the HMAC-SHA1 of a key and some data
*/
function core_hmac_sha1(key, data)
{
var bkey = str2binb(key);
if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
return core_sha1(opad.concat(hash), 512 + 160);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}
/*
* Convert an 8-bit or 16-bit string to an array of big-endian words
* In 8-bit function, characters >255 have their hi-byte silently ignored.
*/
function str2binb(str)
{
var bin = Array();
var mask = (1 << chrsz) - 1;
for(var i = 0; i < str.length * chrsz; i += chrsz)
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
return bin;
}
/*
* Convert an array of big-endian words to a string
*/
function binb2str(bin)
{
var str = "";
var mask = (1 << chrsz) - 1;
for(var i = 0; i < bin.length * 32; i += chrsz)
str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
return str;
}
/*
* Convert an array of big-endian words to a hex string.
*/
function binb2hex(binarray)
{
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var str = "";
for(var i = 0; i < binarray.length * 4; i++)
{
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
}
return str;
}
/*
* Convert an array of big-endian words to a base-64 string
*/
function binb2b64(binarray)
{
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var str = "";
for(var i = 0; i < binarray.length * 4; i += 3)
{
var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
| (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
| ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
}
}
return str;
}

View File

@@ -1,240 +0,0 @@
/**
* Entity Capabilities (XEP-0115)
*
* Depends on disco plugin.
*
* See: http://xmpp.org/extensions/xep-0115.html
*
* Authors:
* - Michael Weibel <michael.weibel@gmail.com>
*
* Copyright:
* - Michael Weibel <michael.weibel@gmail.com>
*/
Strophe.addConnectionPlugin('caps', {
/** Constant: HASH
* Hash used
*
* Currently only sha-1 is supported.
*/
HASH: 'sha-1',
/** Variable: node
* Client which is being used.
*
* Can be overwritten as soon as Strophe has been initialized.
*/
node: 'http://strophe.im/strophejs/',
/** PrivateVariable: _ver
* Own generated version string
*/
_ver: '',
/** PrivateVariable: _connection
* Strophe connection
*/
_connection: null,
/** PrivateVariable: _knownCapabilities
* A hashtable containing version-strings and their capabilities, serialized
* as string.
*
* TODO: Maybe those caps shouldn't be serialized.
*/
_knownCapabilities: {},
/** PrivateVariable: _jidVerIndex
* A hashtable containing jids and their versions for better lookup of capabilities.
*/
_jidVerIndex: {},
/** Function: init
* Initialize plugin:
* - Add caps namespace
* - Add caps feature to disco plugin
* - Add handler for caps stanzas
*
* Parameters:
* (Strophe.Connection) conn - Strophe connection
*/
init: function(conn) {
this._connection = conn;
Strophe.addNamespace('CAPS', 'http://jabber.org/protocol/caps');
if (!this._connection.disco) {
throw "Caps plugin requires the disco plugin to be installed.";
}
this._connection.disco.addFeature(Strophe.NS.CAPS);
this._connection.addHandler(this._delegateCapabilities.bind(this), Strophe.NS.CAPS);
},
/** Function: generateCapsAttrs
* Returns the attributes for generating the "c"-stanza containing the own version
*
* Returns:
* (Object) - attributes
*/
generateCapsAttrs: function() {
return {
'xmlns': Strophe.NS.CAPS,
'hash': this.HASH,
'node': this.node,
'ver': this.generateVer()
};
},
/** Function: generateVer
* Returns the base64 encoded version string (encoded itself with sha1)
*
* Returns:
* (String) - version
*/
generateVer: function() {
if (this._ver !== "") {
return this._ver;
}
var ver = "",
identities = this._connection.disco._identities.sort(this._sortIdentities),
identitiesLen = identities.length,
features = this._connection.disco._features.sort(),
featuresLen = features.length;
for(var i = 0; i < identitiesLen; i++) {
var curIdent = identities[i];
ver += curIdent.category + "/" + curIdent.type + "/" + curIdent.lang + "/" + curIdent.name + "<";
}
for(var i = 0; i < featuresLen; i++) {
ver += features[i] + '<';
}
this._ver = b64_sha1(ver);
return this._ver;
},
/** Function: getCapabilitiesByJid
* Returns serialized capabilities of a jid (if available).
* Otherwise null.
*
* Parameters:
* (String) jid - Jabber id
*
* Returns:
* (String|null) - capabilities, serialized; or null when not available.
*/
getCapabilitiesByJid: function(jid) {
if (this._jidVerIndex[jid]) {
return this._knownCapabilities[this._jidVerIndex[jid]];
}
return null;
},
/** PrivateFunction: _delegateCapabilities
* Checks if the version has already been saved.
* If yes: do nothing.
* If no: Request capabilities
*
* Parameters:
* (Strophe.Builder) stanza - Stanza
*
* Returns:
* (Boolean)
*/
_delegateCapabilities: function(stanza) {
var from = stanza.getAttribute('from'),
c = stanza.querySelector('c'),
ver = c.getAttribute('ver'),
node = c.getAttribute('node');
if (!this._knownCapabilities[ver]) {
return this._requestCapabilities(from, node, ver);
} else {
this._jidVerIndex[from] = ver;
}
if (!this._jidVerIndex[from] || !this._jidVerIndex[from] !== ver) {
this._jidVerIndex[from] = ver;
}
return true;
},
/** PrivateFunction: _requestCapabilities
* Requests capabilities from the one which sent the caps-info stanza.
* This is done using disco info.
*
* Additionally, it registers a handler for handling the reply.
*
* Parameters:
* (String) to - Destination jid
* (String) node - Node attribute of the caps-stanza
* (String) ver - Version of the caps-stanza
*
* Returns:
* (Boolean) - true
*/
_requestCapabilities: function(to, node, ver) {
if (to !== this._connection.jid) {
var id = this._connection.disco.info(to, node + '#' + ver);
this._connection.addHandler(this._handleDiscoInfoReply.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'result', id, to);
}
return true;
},
/** PrivateFunction: _handleDiscoInfoReply
* Parses the disco info reply and adds the version & it's capabilities to the _knownCapabilities variable.
* Additionally, it adds the jid & the version to the _jidVerIndex variable for a better lookup.
*
* Parameters:
* (Strophe.Builder) stanza - Disco info stanza
*
* Returns:
* (Boolean) - false, to automatically remove the handler.
*/
_handleDiscoInfoReply: function(stanza) {
var query = stanza.querySelector('query'),
node = query.getAttribute('node').split('#'),
ver = node[1],
from = stanza.getAttribute('from');
if (!this._knownCapabilities[ver]) {
var childNodes = query.childNodes,
childNodesLen = childNodes.length;
this._knownCapabilities[ver] = [];
for(var i = 0; i < childNodesLen; i++) {
var node = childNodes[i];
this._knownCapabilities[ver].push({name: node.nodeName, attributes: node.attributes});
}
this._jidVerIndex[from] = ver;
} else if (!this._jidVerIndex[from] || !this._jidVerIndex[from] !== ver) {
this._jidVerIndex[from] = ver;
}
return false;
},
/** PrivateFunction: _sortIdentities
* Sorts two identities according the sorting requirements in XEP-0115.
*
* Parameters:
* (Object) a - Identity a
* (Object) b - Identity b
*
* Returns:
* (Integer) - 1, 0 or -1; according to which one's greater.
*/
_sortIdentities: function(a, b) {
if (a.category > b.category) {
return 1;
}
if (a.category < b.category) {
return -1;
}
if (a.type > b.type) {
return 1;
}
if (a.type < b.type) {
return -1;
}
if (a.lang > b.lang) {
return 1;
}
if (a.lang < b.lang) {
return -1;
}
return 0;
}
});

View File

@@ -1,232 +0,0 @@
/*
Copyright 2010, François de Metz <francois@2metz.fr>
*/
/**
* Disco Strophe Plugin
* Implement http://xmpp.org/extensions/xep-0030.html
* TODO: manage node hierarchies, and node on info request
*/
Strophe.addConnectionPlugin('disco',
{
_connection: null,
_identities : [],
_features : [],
_items : [],
/** Function: init
* Plugin init
*
* Parameters:
* (Strophe.Connection) conn - Strophe connection
*/
init: function(conn)
{
this._connection = conn;
this._identities = [];
this._features = [];
this._items = [];
// disco info
conn.addHandler(this._onDiscoInfo.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
// disco items
conn.addHandler(this._onDiscoItems.bind(this), Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);
},
/** Function: addIdentity
* See http://xmpp.org/registrar/disco-categories.html
* Parameters:
* (String) category - category of identity (like client, automation, etc ...)
* (String) type - type of identity (like pc, web, bot , etc ...)
* (String) name - name of identity in natural language
* (String) lang - lang of name parameter
*
* Returns:
* Boolean
*/
addIdentity: function(category, type, name, lang)
{
for (var i=0; i<this._identities.length; i++)
{
if (this._identities[i].category == category &&
this._identities[i].type == type &&
this._identities[i].name == name &&
this._identities[i].lang == lang)
{
return false;
}
}
this._identities.push({category: category, type: type, name: name, lang: lang});
return true;
},
/** Function: addFeature
*
* Parameters:
* (String) var_name - feature name (like jabber:iq:version)
*
* Returns:
* boolean
*/
addFeature: function(var_name)
{
for (var i=0; i<this._features.length; i++)
{
if (this._features[i] == var_name)
return false;
}
this._features.push(var_name);
return true;
},
/** Function: removeFeature
*
* Parameters:
* (String) var_name - feature name (like jabber:iq:version)
*
* Returns:
* boolean
*/
removeFeature: function(var_name)
{
for (var i=0; i<this._features.length; i++)
{
if (this._features[i] === var_name){
this._features.splice(i,1)
return true;
}
}
return false;
},
/** Function: addItem
*
* Parameters:
* (String) jid
* (String) name
* (String) node
* (Function) call_back
*
* Returns:
* boolean
*/
addItem: function(jid, name, node, call_back)
{
if (node && !call_back)
return false;
this._items.push({jid: jid, name: name, node: node, call_back: call_back});
return true;
},
/** Function: info
* Info query
*
* Parameters:
* (Function) call_back
* (String) jid
* (String) node
*/
info: function(jid, node, success, error, timeout)
{
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node)
attrs.node = node;
var info = $iq({from:this._connection.jid,
to:jid, type:'get'}).c('query', attrs);
this._connection.sendIQ(info, success, error, timeout);
},
/** Function: items
* Items query
*
* Parameters:
* (Function) call_back
* (String) jid
* (String) node
*/
items: function(jid, node, success, error, timeout)
{
var attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
if (node)
attrs.node = node;
var items = $iq({from:this._connection.jid,
to:jid, type:'get'}).c('query', attrs);
this._connection.sendIQ(items, success, error, timeout);
},
/** PrivateFunction: _buildIQResult
*/
_buildIQResult: function(stanza, query_attrs)
{
var id = stanza.getAttribute('id');
var from = stanza.getAttribute('from');
var iqresult = $iq({type: 'result', id: id});
if (from !== null) {
iqresult.attrs({to: from});
}
return iqresult.c('query', query_attrs);
},
/** PrivateFunction: _onDiscoInfo
* Called when receive info request
*/
_onDiscoInfo: function(stanza)
{
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node)
{
attrs.node = node;
}
var iqresult = this._buildIQResult(stanza, attrs);
for (var i=0; i<this._identities.length; i++)
{
var attrs = {category: this._identities[i].category,
type : this._identities[i].type};
if (this._identities[i].name)
attrs.name = this._identities[i].name;
if (this._identities[i].lang)
attrs['xml:lang'] = this._identities[i].lang;
iqresult.c('identity', attrs).up();
}
for (var i=0; i<this._features.length; i++)
{
iqresult.c('feature', {'var':this._features[i]}).up();
}
this._connection.send(iqresult.tree());
return true;
},
/** PrivateFunction: _onDiscoItems
* Called when receive items request
*/
_onDiscoItems: function(stanza)
{
var query_attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
if (node)
{
query_attrs.node = node;
var items = [];
for (var i = 0; i < this._items.length; i++)
{
if (this._items[i].node == node)
{
items = this._items[i].call_back(stanza);
break;
}
}
}
else
{
var items = this._items;
}
var iqresult = this._buildIQResult(stanza, query_attrs);
for (var i = 0; i < items.length; i++)
{
var attrs = {jid: items[i].jid};
if (items[i].name)
attrs.name = items[i].name;
if (items[i].node)
attrs.node = items[i].node;
iqresult.c('item', attrs).up();
}
this._connection.send(iqresult.tree());
return true;
}
});

View File

@@ -2,7 +2,7 @@
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`.
`YOURSECRET1`, `YOURSECRET2` and `YOURSECRET3`.
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.

Binary file not shown.

View File

@@ -4,36 +4,41 @@
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="jitsi" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<font-face units-per-em="1024" ascent="1024" descent="0" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" d="" horiz-adv-x="512" />
<glyph unicode="&#xe600;" d="M831.678 16.386h-144.885v258.653c-45.729 29.159-41.794 84.953-24.574 109.307 11.939 16.905 22.43 34.662 23.663 57.004 0.423 8.241 8.303 19.031 15.847 23.364 26.122 15.037 38.223 39.632 50.12 65.116 3.143 6.714 7.392 13.187 12.3 18.753 8.471 9.686 12.295 19.264 6.115 31.922-1.466 2.972 1.318 8.326 2.779 12.362 4.335 12.106 10.326 23.745 13.169 36.148 3.522 15.399 5.398 31.305 6.244 47.086 0.379 6.543-6.074 13.574-5.351 19.986 3.486 32.030-14.612 56.346-24.785 84.189-12.509 34.28-37.036 55.732-58.681 81.26-4.074 4.843-5.225 13.125-5.563 19.942-0.722 14.63-6.752 21.875-22.048 19.898-6.161-0.805-12.808-2.526-18.474-1.019-4.969 1.316-12.408 6.288-12.702 10.13-1.553 19.393-8.285 22.577-28.098 19.305-12.406-2.062-28.527 9.134-40.677 17.587-10.15 7.049-18.941 10.065-30.751 7.175-4.928-1.187-11.598-0.973-15.716 1.466-4.461 2.634-8.837 4.226-13.169 5.119v0.722c-0.975 0-1.976-0.17-2.934-0.276-0.975 0.106-1.951 0.276-2.908 0.276v-0.722c-4.355-0.893-8.726-2.485-13.169-5.119-4.167-2.441-10.811-2.652-15.718-1.466-11.851 2.89-20.598-0.126-30.751-7.175-12.212-8.453-28.287-19.648-40.671-17.587-19.816 3.272-26.591 0.085-28.119-19.305-0.299-3.844-7.73-8.816-12.7-10.13-5.692-1.509-12.32 0.212-18.497 1.019-15.27 1.976-21.302-5.269-22.024-19.898-0.338-6.819-1.486-15.102-5.565-19.942-21.622-25.528-46.154-46.98-58.684-81.26-10.171-27.843-28.271-52.161-24.765-84.189 0.699-6.412-5.736-13.443-5.395-19.986 0.87-15.78 2.74-31.687 6.267-47.086 2.843-12.403 8.835-24.042 13.187-36.148 1.466-4.033 4.229-9.387 2.784-12.362-6.203-12.658-2.379-22.236 6.115-31.922 4.887-5.565 9.134-12.039 12.277-18.753 11.874-25.484 24.001-50.079 50.125-65.116 7.516-4.332 15.417-15.122 15.863-23.364 1.21-22.342 11.701-40.099 23.64-57.004 18.33-25.954 21.194-86.95-34.216-114.687-76.673-38.336-154.083-75.357-232.624-109.632-49.189-21.498-73.891-57.6-82.238-108.192-2.549-15.331-5.862-30.539-7.88-45.961-3.014-22.956-7.839-69.874-7.839-69.874h831.678v80.386zM1188.556 80.89h-144.89v-144.89h-147.481v144.89h-144.885v147.481h144.885v144.888h147.481v-144.888h144.89v-147.481z" horiz-adv-x="1189" />
<glyph unicode="&#xe601;" d="M956.063 962.932h-819.824c-73.036 0-132.489-60.717-132.489-135.316v-540.205c0-74.537 59.453-135.246 132.489-135.246h35.826v-212.941l344.987 212.941h439.011c72.964 0 132.42 60.711 132.42 135.246v540.202c0 74.602-59.456 135.318-132.42 135.318zM496.5 293.887l-195.714-123.997v123.997h-158.261v527.257h807.189l0.064-527.255h-453.278zM239.062 715.554h605.126v-110.62h-605.126v110.62zM239.062 522.476h605.126v-110.615h-605.126v110.615z" horiz-adv-x="1088" />
<glyph unicode="&#xe602;" d="M955.816 960.317h-822.841c-73.303 0-132.975-60.931-132.975-135.844v-542.186c0-74.851 59.672-135.785 132.975-135.785h822.841c73.239 0 132.916 60.934 132.916 135.785v542.186c0 74.913-59.677 135.844-132.916 135.844zM949.51 288.784h-810.226v529.223h810.164l0.062-529.223zM945.219 0.34c0-35.738-28.261-64.66-63.207-64.66h-675.228c-34.949 0-63.209 28.921-63.209 64.66v29.618c0 35.7 28.261 64.657 63.209 64.657h675.228c34.946 0 63.207-28.957 63.207-64.657v-29.618zM776.792 342.434l-302.669 302.605 112.411 112.316 302.545-302.602v-112.318z" horiz-adv-x="1089" />
<glyph unicode="&#xe603;" d="M952.495 955.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 75.651h-806.14v737.822h806.015l0.126-737.822zM685.753 674.544h216.911v-566.758h-216.911v566.758zM428.672 546.002h216.911v-438.216h-216.911v438.216zM172.339 417.46h216.161v-309.677h-216.161v309.677z" horiz-adv-x="1088" />
<glyph unicode="&#xe604;" d="M878.259 965.513c-163.545 0-296.573-133.036-296.573-296.612v-43.752h-448.909c-73.14 0-132.777-60.909-132.777-135.751v-412.768c0-74.777 59.637-135.678 132.777-135.678h564.152c73.265 0 132.919 60.901 132.919 135.678v412.768c0 70.054-52.267 127.895-119.040 135.009v44.494c0 92.367 75.154 167.49 167.451 167.49 92.305 0 167.462-75.12 167.462-167.49v-77.422c0-35.681 28.883-64.564 64.556-64.564 35.69 0 64.569 28.883 64.569 64.564v77.422c-0.003 163.576-133.028 296.612-296.587 296.612z" horiz-adv-x="1179" />
<glyph unicode="&#xe605;" d="M1.518 641.614h277.533v319.798c0 0-78.033-8.102-176.18-111.633-98.139-103.529-101.353-208.165-101.353-208.165zM683.281 961.412h-339.684v-384.596l-342.080 0.251-1.515 3.468v-510.502c0-73.845 61.4-133.979 136.847-133.979h546.434c75.514 0 136.911 60.137 136.911 133.979v757.403c-0.003 73.843-61.397 133.976-136.914 133.976zM691.854 145.164h-572.848v92.788h572.845v-92.788zM691.854 338.802h-572.848v92.783h572.845v-92.783z" horiz-adv-x="820" />
<glyph unicode="&#xe606;" d="M953.901 962.387h-819.775c-72.965 0-132.418-60.712-132.418-135.344v-540.168c0-74.567 59.453-135.279 132.418-135.279h35.823v-212.891l344.966 212.891h438.986c72.963 0 132.415 60.709 132.415 135.279v540.168c0.003 74.632-59.45 135.344-132.415 135.344zM494.429 293.354l-195.769-124.001v124.001h-158.184v527.252h807.078l0.124-527.252h-453.249z" horiz-adv-x="1089" />
<glyph unicode="&#xe607;" d="M709.515 620.094v44.455c0 163.090-132.662 295.749-295.749 295.749-163.093 0-295.752-132.659-295.752-295.749v-44.455c-66.226-7.393-118.013-64.915-118.013-134.607v-411.623c0-74.629 59.481-135.365 132.472-135.365h562.583c73.059 0 132.534 60.736 132.534 135.365v411.623c0 69.697-51.792 127.219-118.074 134.607zM413.765 831.537c92.043 0 166.987-74.944 166.987-166.987v-43.632h-333.978v43.632c0 92.043 74.883 166.987 166.99 166.987z" horiz-adv-x="828" />
<glyph unicode="&#xe608;" d="M1223.129 717.217l-180.128-175.796v217.716c0 74.673-59.512 135.496-132.599 135.496h-634.716c-73.084 0-132.596-60.823-132.596-135.496v-609.237c0-74.673 59.512-135.496 132.596-135.496h634.716c73.084 0 132.599 60.82 132.599 135.496v172.679l193.45-153.712c48.784-35.558 96.695 5.178 96.695 40.424v483.533c-0.003 35.248-55.897 71.306-110.017 24.393zM601.169 199.935c-141.111 0-255.524 114.411-255.524 255.521s114.411 255.521 255.524 255.521c141.108 0 255.519-114.411 255.519-255.521 0-141.113-114.408-255.521-255.519-255.521zM599.045 600.249c-80.474 0-145.727-65.253-145.727-145.729 0-80.471 65.25-145.727 145.727-145.727s145.729 65.256 145.729 145.727c0 80.474-65.253 145.729-145.729 145.729z" horiz-adv-x="1334" />
<glyph unicode="&#xe609;" d="M1223.934 717.147l-180.299-175.956v217.848c0 7.661-0.666 15.148-1.902 22.432l73.695 65.346c26.349 23.41 28.841 63.8 5.369 90.24-23.475 26.406-63.803 28.872-90.273 5.4l-1009.019-894.712c-26.408-23.41-28.841-63.806-5.398-90.209 12.607-14.237 30.183-21.539 47.85-21.539 15.076 0 30.214 5.305 42.39 16.1l95.841 84.979c20.995-14.627 46.26-23.232 73.592-23.232h635.191c73.099 0 132.66 60.807 132.66 135.537v172.868l193.659-153.955c48.815-35.46 96.765 5.248 96.765 40.584v483.829c0.003 35.305-55.933 71.386-110.123 24.44zM601.515 199.448c-58.81 0-112.566 20.216-155.526 53.797l82.93 73.533c20.863-11.665 44.849-18.386 70.47-18.386 80.533 0 145.832 65.299 145.832 145.835 0 19.421-3.896 37.857-10.857 54.713l86.847 77.001c22.848-38.259 36.012-82.969 36.012-130.782 0-141.214-114.493-255.71-255.707-255.71zM345.797 455.16c0 141.216 114.496 255.715 255.717 255.715 21.501 0 42.075-3.434 61.986-8.429l216.757 192.191h-604.474c-73.138 0-132.697-60.838-132.697-135.597v-518.074l205.894 182.543c-1.308 10.486-3.184 20.853-3.184 31.651z" horiz-adv-x="1334" />
<glyph unicode="&#xe60a;" d="M1121.124 938.866c-23.48 26.413-63.883 28.849-90.296 5.372l-1009.306-894.905c-26.413-23.418-28.852-63.816-5.434-90.232 12.612-14.243 30.224-21.547 47.893-21.547 15.050 0 30.224 5.307 42.403 16.108l257.072 227.934c33.225-22.863 69.988-39.678 108.611-49.713-70.191-35.653-118.771-107.715-118.771-191.894h431.872c0 85.737-50.329 159.115-122.765 194.079 54.95 14.49 105.842 39.965 147.497 77.496 51.888 46.712 113.712 131.515 113.712 270.329v130.323c0 18.812-7.924 35.767-20.585 47.798l212.664 188.558c26.419 23.477 28.849 63.816 5.434 90.294zM791.535 481.891c0-157.985-117.649-229.923-226.99-229.923-35.214 0-68.659 7.217-98.285 20.786l55.735 49.416c14.733-4.59 30.356-7.132 46.609-7.132 89.357 0 161.984 72.687 161.984 161.979v30.1l60.947 54.042v-79.269zM730.589 793.867c0 89.298-72.625 161.984-161.984 161.984-89.298 0-161.984-72.687-161.984-161.984v-316.85c0-0.25 0-0.498 0-0.748l323.969 287.25v30.348zM350.795 426.69c-3.246 17.483-5.119 35.782-5.119 55.201v130.323c0 36.406-29.6 66.004-66.006 66.004-36.466 0-66.004-29.597-66.004-66.004v-130.323c0-57.198 11.115-107.026 29.099-150.931l108.030 95.73z" horiz-adv-x="1137" />
<glyph unicode="&#xe60b;" d="M858.414 679.944c-36.595 0-66.246-29.652-66.246-66.182v-130.725c0-158.421-117.982-230.597-227.635-230.597-58.674 0-112.618 19.87-151.86 55.959-44.23 40.819-67.696 101.203-67.696 174.64v130.725c0 36.53-29.654 66.182-66.182 66.182-36.53 0-66.182-29.652-66.182-66.182v-130.725c0-195.834 119.494-314.763 259.177-351.040-70.408-35.71-119.176-108.014-119.176-192.431h433.118c0 85.993-50.409 159.621-123.029 194.572 55.079 14.64 106.121 40.127 147.886 77.79 52.050 46.877 114.008 131.925 114.008 271.106v130.725c0 36.53-29.59 66.182-66.184 66.182zM568.571 315.719c-89.589 0-162.459 72.932-162.459 162.521v317.665c0 89.589 72.87 162.459 162.459 162.459 89.592 0 162.524-72.87 162.524-162.459v-317.665c0.003-89.592-72.929-162.521-162.524-162.521z" horiz-adv-x="1137" />
<glyph unicode="&#xe60c;" d="M512.356 960c-282.456 0-512.356-229.838-512.356-512.478 0-282.389 229.9-512.227 512.356-512.227 282.515 0 512.414 229.838 512.414 512.227 0 282.64-229.9 512.478-512.414 512.478zM512.356 14.856c-238.545 0-432.671 194.126-432.671 432.666 0 238.796 194.126 432.858 432.671 432.858 238.601 0 432.856-194.062 432.856-432.858 0-238.54-194.255-432.666-432.856-432.666zM512.545 854.962c-224.755 0-407.508-182.75-407.508-407.315 0-224.563 182.75-407.315 407.508-407.315 224.437 0 407.187 182.755 407.187 407.315-0.003 224.566-182.75 407.315-407.187 407.315zM512.545 127.323c-176.715 0-320.453 143.804-320.453 320.324 0 176.523 143.737 320.324 320.453 320.324 176.523 0 320.196-143.802 320.196-320.324 0-176.52-143.673-320.324-320.196-320.324zM283.851 562.789l-0.954-1.398v-234.413l0.954-1.398c15.757-23.060 36.473-44.542 61.699-63.797l8.961-6.801v378.341l-8.961-6.735c-25.1-19.191-45.814-40.544-61.699-63.799zM415.637 665.729l-3.621-1.334v-440.36l3.621-1.398c18.683-7.055 38.887-11.94 61.766-14.931l6.224-0.762v474.415l-6.163-0.762c-22.237-2.795-43.016-7.753-61.827-14.869zM547.367 680.599l-6.165 0.762v-474.415l6.165 0.762c22.301 2.793 43.077 7.811 61.763 14.864l3.685 1.4v440.488l-3.685 1.334c-18.811 7.053-39.525 12.010-61.763 14.805zM740.98 562.789c-15.692 23.002-36.473 44.48-61.699 63.797l-8.894 6.86v-378.469l8.894 6.801c25.351 19.381 46.132 40.862 61.699 63.861l0.89 1.398v234.352l-0.89 1.4z" horiz-adv-x="1025" />
<glyph unicode="&#xe60d;" d="M952.366 960.134h-820.477c-73.027 0-132.531-60.761-132.531-135.455v-752.358c0-74.66 59.504-135.424 132.531-135.424h820.48c73.089 0 132.596 60.766 132.596 135.424v752.358c-0.003 74.694-59.507 135.455-132.599 135.455zM946.135 78.801h-807.894v739.462h807.834l0.059-739.462zM569.742 511.875l91.772 96.865-77.305 77.308 316.393 85.040-84.981-316.391-75.357 75.293-91.834-96.865zM514.763 384.563l-91.767-96.865 77.3-77.305-316.388-85.043 84.979 316.388 75.357-75.29 91.834 96.871z" horiz-adv-x="1089" />
<glyph unicode="&#xe60e;" d="M953.225 959.18h-820.663c-73.045 0-132.562-60.776-132.562-135.488v-752.525c0-74.647 59.517-135.421 132.562-135.421h820.66c73.107 0 132.624 60.776 132.624 135.421v752.525c0.003 74.712-59.515 135.488-132.622 135.488zM946.994 77.647h-808.079v739.596h808.017l0.062-739.596zM915.539 706.7l-91.795-96.889 77.326-77.323-316.463-85.030 84.999 316.463 75.373-75.339 91.852 96.889zM170.625 188.221l91.793 96.884-77.323 77.326 316.463 85.028-84.997-316.46-75.373 75.342-91.857-96.891z" horiz-adv-x="1089" />
<glyph unicode="&#xe60f;" d="M513.036 960c283.57-0.188 512.414-229.474 512.037-512.664-0.377-283.756-228.965-512.228-512.541-512.288-283.191-0.067-512.912 229.474-512.533 512.099 0.374 284.638 228.965 513.103 513.036 512.853zM512.285 810.729c-200.79-0.126-362.957-162.291-362.831-363.014 0-201.105 161.788-363.081 362.831-363.334 201.164-0.312 363.581 162.294 363.455 363.772-0.25 200.852-162.417 362.702-363.455 362.575zM597.392 447.588h-0.503l-0.126 0.126h0.63l115.615 115.866c0 0-78.247 78.505-82.153 82.153l-117.754-116.183-119.014 118.196-82.024-82.279 88.815-88.818 26.674-29.061h0.503l0.253-0.253h-0.756l-115.489-115.806c0 0 78.249-78.564 82.024-82.212l117.76 116.245 119.008-118.26 82.153 82.406-88.815 88.82-26.8 29.061z" horiz-adv-x="1025" />
<glyph unicode="&#xe610;" d="M66.491-63.997h1027.94zM1198.596 487.986c-135.702 135.893-271.415 271.66-407.367 407.241-6.244 6.089-13.868 11.714-21.867 14.653-31.236 11.399-63.48-12.808-63.728-47.674-0.253-67.663-0.126-135.331-0.126-202.965 0-4.281 0-8.62 0-13.964-6.123 0-10.87 0-15.62 0-106.247 0-212.334 0.062-318.485 0-35.178-0.031-54.86-19.71-54.86-54.922-0.059-92.778-0.059-185.5 0-278.345 0-35.8 19.682-55.479 55.611-55.479 105.775-0.062 211.423 0 317.11 0 4.877 0 9.622 0 16.245 0 0-5.375 0-9.374 0-13.309 0-66.793 0.25-133.703 0-200.496 0-23.057 9.247-40.241 30.242-49.547 21.116-9.371 39.361-2.81 55.231 12.937 135.955 136.079 272.031 272.034 407.989 408.175 23.49 23.431 23.24 50.112-0.374 73.695zM532.596 44.729c-2.627 19.62-22.055 32.116-27.928 35.426-8.811 5.186-18.371 5.811-25.683 5.811l-6.37-0.126-227.926 0.188c-56.042 0.124-98.468 42.173-98.591 97.717-0.188 177.127-0.188 354.321 0.065 531.51 0.059 53.983 42.671 96.53 96.968 96.811l235.922 0.062c33.426 0.031 51.294 16.121 54.481 49.235 1.001 12.965 0.81 26.052 0.439 39.172-1.128 39.737-19.369 57.481-59.107 57.512l-217.866-0.121c-15.494 0-30.926-0.562-46.361-2.343-115.52-13.154-207.555-113.649-209.681-228.798-1.313-63.888-0.996-127.804-0.684-191.718l0.186-60.201h-0.377c0 0-0.121-227.954 0.065-289.811 0.248-135.702 101.528-240.796 235.545-244.48 33.176-0.875 66.419-1.189 99.654-1.189h0.065l148.012 0.753c29.368 0 47.483 17.37 49.73 47.545 1.755 22.058 1.628 40.173-0.557 57.045z" horiz-adv-x="1216" />
<glyph unicode="&#xe611;" d="M839.334 386.487c0-79.199-64.257-143.461-143.486-143.461-79.174 0-143.431 64.262-143.431 143.461 0 79.227 64.257 143.431 143.431 143.431 79.23-0.003 143.486-64.204 143.486-143.431zM1372.769 706.485c-6.595 39.459-29.496 64.168-70.606 69.276-23.788 2.918-38.256 15.637-44.726 39.040-9.706 35.519-33.678 58.993-67.811 70.76-24.807 8.595-50.3 16.462-76.186 20.491-69.655 10.911-140.51 15.924-209.526 29.943-69.53 14.178-139.053 23.342-208.893 24.073-69.845-0.731-139.371-9.895-208.893-24.073-69.022-14.016-139.876-19.029-209.526-29.94-25.884-4.028-51.385-11.896-76.189-20.491-34.13-11.767-58.105-35.24-67.814-70.76-6.469-23.403-20.934-36.122-44.723-39.040-41.105-5.108-64.006-29.82-70.601-69.278-6.788-40.41-11.737-81.202-16.811-121.885-2.728-22.109 6.405-32.576 30.386-32.448 120.839 0.697 241.692 0.697 362.595 0.095 24.045-0.128 33.115 9.388 33.433 33.338 0.762 57.369-4.631 111.895-47.136 156.618-7.041 7.39-10.849 25.281-6.726 33.846 4.062 8.5 40.856 16.716 45.992 16.716 43.328-0.19 43.138-0.223 49.418-43.423 1.205-8.28 2.539-18.46 7.803-23.853 30.319-30.863 21.252-66.706 7.234-97.634-30.389-67.139-61.537-134.827-100.867-196.869-73.458-115.831-104.41-160.744-198.679-260.844-58.675-62.293-68.573-101.687-68.573-137.466 0-70.989 41.038-96.744 137.148-96.744 181.614 0 260.908 0.315 442.528 0.315 181.614 0 260.905-0.315 442.528-0.315 96.102 0 137.142 25.752 137.142 96.744 0 35.779-9.898 75.173-68.573 137.466-94.264 100.1-125.222 145.012-198.679 260.844-39.333 62.042-70.475 129.73-100.861 196.869-14.019 30.927-23.091 66.77 7.234 97.634 5.264 5.393 6.595 15.575 7.803 23.853 6.28 43.202 6.090 43.233 49.412 43.423 5.139 0 41.933-8.216 45.992-16.716 4.126-8.565 0.318-26.453-6.723-33.846-42.502-44.723-47.898-99.249-47.136-156.618 0.315-23.947 9.388-33.466 33.43-33.338 120.909 0.603 241.753 0.603 362.601-0.095 23.975-0.126 33.109 10.342 30.383 32.448-5.075 40.686-10.018 81.475-16.806 121.885zM959.991 383.505c0-146.315-118.624-264.936-264.97-264.936s-264.973 118.621-264.973 264.936c0 146.318 118.624 264.936 264.973 264.936 146.343 0 264.97-118.621 264.97-264.936z" horiz-adv-x="1390" />
<glyph unicode="&#xe612;" d="M310.262 30.43c0-52.13-42.207-94.43-94.399-94.43h-121.405c-52.195 0-94.458 42.3-94.458 94.43v835.67c0 52.158 42.266 94.458 94.458 94.458h121.403c52.195 0 94.399-42.3 94.399-94.458v-835.67zM1077.118 960.56h-561.987c-72.919 0-132.33-60.673-132.33-135.253v-754.115c0-74.518 59.411-135.191 132.33-135.191h561.987c72.98 0 132.394 60.673 132.394 135.191v754.115c0 74.58-59.414 135.253-132.394 135.253zM529.83 826.906h532.653l0.062-143.298h-532.715v143.298zM643.255 51.628h-113.551v113.551h113.551v-113.551zM643.255 256.748h-113.551v113.554h113.551v-113.554zM643.382 463.756h-113.551v113.554h113.551v-113.554zM852.9 51.628h-113.551v113.551h113.551v-113.551zM852.9 256.748h-113.551v113.554h113.551v-113.554zM853.027 463.756h-113.556v113.554h113.556v-113.554zM1062.548 51.628h-113.556v113.551h113.556v-113.551zM1062.548 256.748h-113.556v113.554h113.556v-113.554zM1062.669 463.756h-113.554v113.554h113.554v-113.554z" horiz-adv-x="1210" />
<glyph unicode="&#xe613;" d="M1123.444 939.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 449.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 331.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 626.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 561.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" horiz-adv-x="1140" />
<glyph unicode="&#xe614;" d="M581.278 961.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 811.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 624.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 494.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 624.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 535.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" horiz-adv-x="1142" />
<glyph unicode="&#xe615;" d="M1016.824 5.766c-2.051 15.373-5.331 30.537-7.859 45.847-8.334 50.458-33.006 86.503-82.063 107.922-78.373 34.198-155.103 72.121-232.111 109.395-55.686 27.025-52.409 88.519-34.097 114.413 11.89 16.88 22.344 34.572 23.575 56.852 0.444 8.226 8.303 18.99 15.817 23.294 26.070 15.035 38.161 39.565 50.020 64.982 3.109 6.696 7.379 13.187 12.266 18.722 8.471 9.668 12.264 19.235 6.079 31.842-1.435 2.97 1.331 8.334 2.8 12.367 4.304 12.026 10.285 23.676 13.12 36.043 3.556 15.339 5.398 31.225 6.252 46.975 0.374 6.523-6.045 13.528-5.362 19.95 3.483 31.912-14.557 56.202-24.739 83.977-12.465 34.198-36.928 55.619-58.519 81.106-4.066 4.784-5.227 13.051-5.571 19.886-0.72 14.588-6.732 21.797-22.004 19.813-6.11-0.787-12.772-2.495-18.41-0.991-4.957 1.334-12.406 6.288-12.676 10.112-1.538 19.336-8.264 22.517-28.016 19.235-12.364-2.049-28.457 9.155-40.584 17.561-10.145 7.041-18.89 10.045-30.681 7.176-4.915-1.195-11.544-0.991-15.716 1.435-4.441 2.663-8.775 4.237-13.118 5.124v0.748c-0.957-0.031-1.982-0.204-2.939-0.307-0.955 0.103-1.912 0.274-2.867 0.307v-0.751c-4.371-0.888-8.749-2.462-13.156-5.124-4.133-2.425-10.762-2.632-15.68-1.435-11.822 2.867-20.569-0.137-30.684-7.176-12.158-8.404-28.217-19.609-40.584-17.561-19.746 3.282-26.509 0.103-28.047-19.235-0.307-3.824-7.686-8.778-12.676-10.112-5.669-1.504-12.297 0.204-18.446 0.991-15.236 1.984-21.25-5.225-21.97-19.813-0.338-6.832-1.502-15.102-5.568-19.886-21.588-25.485-46.051-46.908-58.555-81.106-10.117-27.773-28.189-52.063-24.669-83.975 0.686-6.422-5.744-13.427-5.4-19.95 0.89-15.749 2.735-31.636 6.254-46.975 2.836-12.367 8.814-24.019 13.151-36.043 1.437-4.033 4.242-9.397 2.769-12.367-6.182-12.607-2.358-22.174 6.115-31.842 4.853-5.535 9.121-12.026 12.23-18.722 11.859-25.417 23.947-49.947 50.014-64.982 7.519-4.304 15.378-15.068 15.858-23.294 1.192-22.28 11.65-39.972 23.57-56.852 18.281-25.895 21.147-86.738-34.162-114.413-76.456-38.264-153.741-75.2-232.042-109.395-49.129-21.418-73.726-57.463-82.063-107.922-2.526-15.311-5.878-30.475-7.859-45.847-3.009-22.928-7.823-69.766-7.823-69.766h1024.611c-0.003 0-4.781 46.838-7.787 69.766z" horiz-adv-x="1025" />
<glyph unicode="&#xe616;" d="M1026.175 449.067c0-283.345-229.709-513.051-513.108-513.051-283.348 0-513.069 229.703-513.069 513.051 0 283.384 229.721 513.106 513.069 513.106 283.4 0 513.108-229.721 513.108-513.106zM513.069-63.985c135.725 0 259.112 52.711 350.874 138.739-9.717 11.65-22.551 21.114-39.113 28.343-58.934 25.745-116.627 54.256-174.534 82.256-41.871 20.322-39.405 66.587-25.639 86.057 8.943 12.692 16.805 25.998 17.728 42.746 0.333 6.169 6.241 14.284 11.889 17.522 19.604 11.304 28.697 29.747 37.604 48.861 2.34 5.031 5.555 9.89 9.229 14.077 6.37 7.243 9.224 14.436 4.572 23.942-1.086 2.209 1.004 6.241 2.105 9.273 3.238 9.067 7.733 17.801 9.861 27.115 2.67 11.549 4.059 23.48 4.701 35.323 0.281 4.902-4.544 10.184-4.033 14.986 2.621 24.022-10.943 42.271-18.598 63.158-9.376 25.714-27.771 41.82-44.005 60.97-3.058 3.623-3.927 9.838-4.188 14.952-0.539 10.981-5.060 16.415-16.544 14.924-4.598-0.601-9.611-1.886-13.843-0.756-3.728 0.988-9.327 4.724-9.534 7.604-1.153 14.539-6.213 16.929-21.065 14.475-9.296-1.543-21.395 6.858-30.513 13.203-7.632 5.292-14.209 7.555-23.069 5.38-3.697-0.9-8.682-0.746-11.815 1.094-3.341 1.976-6.605 3.184-9.867 3.839v0.552c-0.72-0.013-1.489-0.129-2.209-0.217-0.722 0.088-1.44 0.206-2.157 0.217v-0.552c-3.29-0.653-6.574-1.863-9.89-3.839-3.112-1.837-8.091-1.992-11.797-1.094-8.886 2.173-15.448-0.088-23.064-5.38-9.157-6.345-21.222-14.746-30.518-13.203-14.862 2.451-19.94 0.062-21.093-14.475-0.219-2.879-5.793-6.616-9.516-7.604-4.278-1.13-9.247 0.155-13.887 0.756-11.456 1.491-15.987-3.943-16.516-14.924-0.261-5.111-1.13-11.33-4.19-14.952-16.222-19.15-34.616-35.258-44.018-60.97-7.627-20.887-21.204-39.136-18.57-63.158 0.511-4.802-4.304-10.083-4.046-14.986 0.655-11.846 2.041-23.776 4.701-35.323 2.131-9.312 6.626-18.048 9.89-27.115 1.094-3.032 3.174-7.062 2.082-9.273-4.649-9.508-1.788-16.702 4.582-23.942 3.674-4.188 6.874-9.046 9.224-14.077 8.899-19.111 17.992-37.557 37.596-48.861 5.653-3.238 11.559-11.353 11.905-17.522 0.913-16.748 8.773-30.054 17.739-42.746 13.745-19.47 15.902-65.245-25.673-86.057-57.52-28.772-115.6-56.511-174.503-82.256-16.57-7.23-29.409-16.694-39.123-28.338 91.759-86.031 215.138-138.744 350.843-138.744z" horiz-adv-x="1026" />
<glyph unicode="&#xe617;" d="M65.774 516.748c10.052 9.358 25.988 21.772 43.285 32.699 51.335 32.619 108.115 50.897 166.829 64.133 48.763 10.95 98.246 16.887 148.066 20.203 55.755 3.754 111.559 3.78 167.392 0.797 39.944-2.185 79.686-6.502 119.119-13.211 43.956-7.506 87.037-17.994 128.785-33.751 24.419-9.175 48.172-20.105 70.534-33.573 29.716-17.891 56.552-39.224 77.22-67.348 20.819-28.302 31.721-60.407 33.829-95.392 1.747-27.402 0.717-54.697-5.656-81.588-2.877-12.083-8.226-23.134-16.554-32.386-11.417-12.648-26.424-17.021-42.772-17.636-20.463-0.668-40.411 4.113-60.361 8.226-38.912 7.97-77.836 16.245-116.699 24.728-14.137 3.034-28.689 5.093-41.649 12.596-22.308 12.955-34.445 31.775-34.033 57.783 0.258 16.869 2.216 33.782 2.469 50.695 0.312 25.010-9.923 45.161-30.898 59.042-14.395 9.539-30.694 14.911-47.452 18.665-40.411 9.046-81.387 10.179-122.561 10.050-28.821-0.126-57.582-1.669-86.093-6.143-19.947-3.161-39.562-7.813-57.732-17.014-24.318-12.364-39.353-31.465-40.073-59.352-0.441-16.351 0.973-32.802 1.927-49.15 0.642-11.822-0.080-23.446-5.295-34.288-9.26-19.385-24.935-31.672-45.37-36.964-22.646-5.865-45.605-10.54-68.504-15.223-38.102-7.764-76.216-15.368-114.416-22.517-14.526-2.774-29.254-1.951-43.133 3.602-18.742 7.457-28.924 22.775-34.138 41.337-7.096 25.397-7.841 51.513-6.966 77.731 0.9 27.040 5.883 53.237 18.404 77.607 9.794 19.065 22.491 35.955 42.493 55.642z" horiz-adv-x="1026" />
<glyph unicode="&#xe618;" d="M797.086 112.301c-0.059 0.163-0.119 0.328-0.16 0.485-71.399-45.638-151.782-69.931-234.023-69.931-0.013 0-0.021 0-0.028 0-122.52 0-237.501 52.772-315.469 144.741-99.778 117.698-134.252 329.954-73.022 427.789 4.004-1.662 7.875-3.233 11.68-4.773 13.585-5.511 26.413-10.716 42.305-19.096 6.063-3.202 12.338-4.812 18.673-4.812 11.714 0 22.6 5.648 29.848 15.486 7.815 10.617 10.313 24.778 6.538 36.951l-3.525 11.41c-10.687 34.59-21.723 70.354-34.211 105.078-9.983 27.765-22.399 62.327-59.226 62.327-12.057 0-26.037-3.656-46.73-12.204-44.294-18.319-71.058-29.961-114.534-49.81-15.102-6.887-25.234-22.698-25.203-39.343 0.028-15.842 8.992-29.337 23.975-36.115 18.208-8.257 30.536-13.716 43.468-19.447l10.687-4.753c-101.938-259.102 24.803-526.458 211.314-639.212 83.497-50.474 178.5-77.14 274.769-77.14h0.041c102.72 0 205.561 31.099 284.501 85.198-31.729 28.803-45.566 69.167-51.671 87.171zM1098.203 210.090c-18.113 8.577-30.356 14.258-43.221 20.244l-10.496 4.892c106.448 257.268-15.569 526.801-200.067 642.788-85.36 53.663-183.123 82.032-282.716 82.032-104.848 0-206.41-30.593-285.967-86.165l-5.385-3.764c31.597-27.564 45.86-66.788 52.917-86.41 72.926 47.94 155.675 73.409 239.895 73.409 125.407 0 242.142-54.785 320.294-150.316 97.683-119.447 128.439-332.255 65.498-429.015-3.989 1.736-7.815 3.385-11.624 4.998-13.471 5.759-26.204 11.18-41.954 19.821-6.203 3.424-12.645 5.155-19.212 5.155-11.585 0-22.399-5.558-29.69-15.267-7.813-10.434-10.478-24.432-6.966-36.515l3.279-11.301c10.096-34.845 20.531-70.857 32.412-105.842 9.588-28.238 21.514-63.382 59.179-63.382 11.843 0 25.577 3.424 45.881 11.399 44.351 17.439 71.319 28.601 115.409 47.777 15.19 6.623 25.601 22.252 25.859 38.894 0.281 15.822-8.445 29.499-23.325 36.569z" horiz-adv-x="1122" />
<glyph unicode="&#xe619;" d="M46.993 961.7c461.234 0 553.793 0 1015.024 0 35.919 0 53.356-25.959 53.356-57.959-0.581-303.259-0.325-606.488-0.449-909.809 0-43.984-13.203-57.058-57.703-57.058-443.072-0.126-556.453-0.126-999.553 0-44.534 0-57.799 13.009-57.799 57.058-0.098 303.257 0.485 608.072-0.093 911.329-0.034 26.21 11.301 53.761 47.217 56.439zM311.405 450.298c0-119.045-0.072-172.168 0.057-291.249 0.036-50.043 11.208-61.050 62.12-61.050 233.352 0 137.075 0 370.522 0 47.075 0 59.249 11.982 59.249 58.095 0.126 239.111 0.126 346.338 0 585.389 0 48.138-10.687 58.991-57.768 58.991-235.323 0.101-140.844 0.101-376.157 0-47.044 0-57.93-11.043-57.966-58.89-0.129-119.109-0.057-172.209-0.057-291.287zM153.944 838.566c-74.929-0.062-66.687 5.958-66.845-66.685-0.201-63.95-7.054-63.534 62.528-63.372 72.999 0.194 67.201-3.764 67.302 67.554 0 67.722 4.087 62.595-62.985 62.502zM963.644 838.566c-71.159-0.034-65.56 6.185-65.751-65.364-0.129-67.302-4.508-64.693 64.528-64.693 73.089 0 65.299-2.031 65.299 66.238-0.003 68.646 6.956 63.911-64.076 63.818zM216.828 122.408c0.359 73.094 4.639 66.914-67.358 67.17-68.104 0.191-62.569 2.763-62.407-63.31 0.129-73.476-6.954-66.52 67.074-66.649 66.042-0.065 63.142-6.056 62.691 62.789zM1027.718 124.4c0.134 68.334 6.443 65.304-63.297 65.178-70.132-0.132-66.656 5.793-66.527-65.304 0.129-70.645-4.384-64.721 63.756-64.657 71.995 0.132 66.202-6.698 66.068 64.783zM1027.718 342.077c0 70.55 7.219 66.842-67.485 66.522-0.898 0-1.873 0-2.838 0-59.375 0-59.375 0-59.375-58.023 0-77.922-6.443-69.936 69.293-70.196 66.076-0.387 60.539-3.091 60.405 61.697zM151.307 489.873c68.295-0.163 65.815-5.568 65.624 62.982-0.194 71.128 4.895 64.917-66.014 65.010-69.905 0.101-63.813 4.704-63.885-63.978-0.062-67.431-5.7-64.463 64.275-64.014zM961.263 489.873c72.511-0.258 66.589-4.603 66.455 64.494 0 68.558 6.185 63.537-64.267 63.498-70.196-0.028-65.686 6.053-65.498-65.493 0.132-62.5 0.067-62.5 63.31-62.5zM150.399 280.38c71.004 0 66.659-6.567 66.466 64.528-0.163 63.694-0.036 63.501-65.013 63.756-70.805 0.258-64.822 2.673-64.822-63.756 0.036-69.167-5.919-64.788 63.369-64.528z" horiz-adv-x="1115" />
<glyph unicode="&#xe61a;" d="M3.881 146.835h220.26v-210.835h-220.26v210.835zM308.817 350.143h220.27v-414.143h-220.27v414.143zM613.764 553.412h220.268v-617.412h-220.268v617.412zM918.685 756.715h220.265v-820.715h-220.265v820.715zM1223.629 960h220.263v-1024h-220.263v1024z" horiz-adv-x="1444" />
<glyph unicode="&#xe61b;" d="M526.071 234.749c-28.637-30.869-56.465-60.861-84.282-90.859-51.578-55.636-103.047-111.376-154.842-166.832-7.606-8.135-15.958-16.1-25.317-22.012-28.075-17.708-58.31-18.090-88.472-6.492-59.84 23.028-80.004 90.727-59.734 139.234 5.413 12.95 13.721 23.601 23.709 33.173 70.256 67.351 140.506 134.717 210.76 202.077 15.638 14.993 31.264 29.995 47.364 45.45-9.302 9.529-18.386 18.833-27.451 28.137-12.122 12.442-13.234 20.28-5.067 35.498 4.735 8.816 4.789 8.878-2.627 16.198-20.012 19.72-40.168 39.198-63.498 55.188-27.167 18.624-57.161 24.233-89.083 19.849-53.402-7.328-91.609-38.372-121.413-81.046-12.774-18.299-15.365-40.313-17.517-61.875-3.23-32.245-2.415-64.479 2.209-96.597 1.654-11.515-3.863-16.539-13.835-11.175-8.306 4.448-16.095 11.048-22.115 18.353-15.574 18.89-22.223 42.042-27.474 65.395-12.955 57.652-8.86 114.49 12.191 169.495 32.345 84.537 79.743 159.571 145.953 221.932 13.659 12.857 176.841 180.564 202.944 207.021 7.493 7.599 14.895 7.635 22.393 0.028 43.009-43.641 85.985-87.316 128.927-131.029 8.117-8.267 8.019-15.097-0.222-23.49-26.339-26.834-52.726-53.627-79.106-80.419-6.244-6.334-97.34-82.437-73.027-128.816 22.693-25.090 46.196-49.449 69.575-73.904 1.189-1.238 4.686-1.386 6.523-0.632 3.63 1.499 6.848 3.997 10.248 6.066 9.745 5.94 19.545 4.918 27.812-3.083 11.755-11.381 23.405-22.858 35.392-34.59 4.807 4.575 9.939 9.41 15.027 14.294 27.128 26.039 54.272 52.071 81.351 78.146 16.413 15.778 18.652 28.418 11.038 49.658-10.473 29.221-14.356 59.677-13.85 90.624 1.017 61.045 20.438 115.334 61.003 161.416 32.825 37.286 72.054 64.311 121.643 74.325 35.227 7.101 69.139 4.513 100.663-14.026 6.365-3.752 11.908-9.007 17.455-14.005 3.491-3.125 3.153-6.236-0.565-9.98-42.503-42.885-84.772-86.013-127.154-129.035-12.442-12.638-12.356-23.167 0.196-35.914 40.344-40.978 80.597-82.050 120.936-123.052 10.076-10.233 19.537-10.021 29.504 0.134 43.195 44.077 86.449 88.090 129.706 132.118 1.21 1.233 2.572 2.322 5.135 4.624 5.491-5.893 11.895-10.924 15.961-17.406 19.452-30.944 22.608-64.83 17.073-100.25-14.253-91.080-97.188-175.638-197.712-190.123-39.977-5.764-79.372-2.562-118.067 9.031-5.898 1.775-11.541 4.629-17.538 5.829-12.47 2.474-23.872-0.366-32.74-9.877-30.921-33.168-61.674-66.484-92.474-99.758-0.73-0.805-1.349-1.718-0.181-1.099 8.992-10.006 17.354-20.662 27.061-29.94 81.064-77.54 164.91-151.986 250.882-224.063 9.936-8.347 10.274-15.695 1.040-25.1-42.338-43.068-84.689-86.111-127.059-129.154-9.413-9.575-16.846-9.152-25.291 1.295-76.686 94.78-156.8 186.609-239.707 276.002-1.334 1.453-2.562 3.029-4.257 5.042z" horiz-adv-x="1105" />
<glyph unicode="&#xe61c;" d="M74.418 881.299h239.304v-228.491h-239.304v228.491zM393.455 881.299h239.304v-228.491h-239.304v228.491zM712.494 881.299h239.263v-228.491h-239.263v228.491zM74.418 562.265h239.304v-228.555h-239.304v228.555zM393.455 562.265h239.304v-228.555h-239.304v228.555zM712.494 562.265h239.263v-228.555h-239.263v228.555zM74.418 243.166h239.304v-228.465h-239.304v228.465zM393.455 243.166h239.304v-228.465h-239.304v228.465zM712.494 243.166h239.263v-228.465h-239.263v228.465z" horiz-adv-x="1026" />
<glyph unicode="&#x20;" d="" />
<glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
<glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
<glyph unicode="&#xe614;" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
<glyph unicode="&#xe61a;" glyph-name="connection" horiz-adv-x="1444" d="M3.881 210.835h220.26v-210.835h-220.26v210.835zM308.817 414.143h220.27v-414.143h-220.27v414.143zM613.764 617.412h220.268v-617.412h-220.268v617.412zM918.685 820.715h220.265v-820.715h-220.265v820.715zM1223.629 1024h220.263v-1024h-220.263v1024z" />
<glyph unicode="&#xe900;" glyph-name="connection-lost" horiz-adv-x="1414" d="M0 299.153h196.337v-187.951h-196.337v187.951zM271.842 480.372h196.337v-369.169h-196.337v369.169zM543.656 661.562h196.337v-550.36h-196.337v550.36zM815.47 842.766v-731.564h119.56c-14.589 33.025-23.125 71.503-23.232 111.943 0.132 86.42 38.697 163.851 99.656 216.468l0.348 403.153h-196.332zM1087.292 1024v-533.672c28.874 10.572 62.222 16.73 97.009 16.825 35.717-0.129 69.823-6.614 101.322-18.371l-1.999 535.218h-196.332zM1192.868 439.852c-0.009 0-0.020 0-0.031 0-122.247 0-221.351-98.447-221.372-219.896 0-0.007 0-0.014 0-0.021 0-121.467 99.111-219.935 221.372-219.935 0.011 0 0.021 0 0.032 0 122.248 0.014 221.345 98.477 221.345 219.935 0 0.007 0 0.013 0 0.020-0.021 121.441-99.11 219.883-221.345 219.897zM1194.706 372.607c87.601-0.006 158.614-69.787 158.614-155.866 0-0.006 0-0.012 0-0.019-0.022-86.062-71.026-155.822-158.614-155.828-87.588 0.006-158.593 69.766-158.615 155.826 0 0.007 0 0.014 0 0.020 0 86.079 71.013 155.86 158.613 155.866zM1286.795 355.682l48.348-52.528-236.375-217.567-48.348 52.528 236.375 217.567z" />
<glyph unicode="&#xe901;" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
<glyph unicode="&#xe902;" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-8 198-100 198-212 0-118-96-214-214-214h-554c-142 0-256 114-256 256 0 132 100 240 228 254 54 102 160 174 284 174 156 0 284-110 314-258z" />
<glyph unicode="&#xe903;" glyph-name="autorenew" d="M800 694c34-52 54-116 54-182 0-188-154-342-342-342v-128l-170 172 170 170v-128c142 0 256 114 256 256 0 44-12 84-30 120zM512 768c-142 0-256-114-256-256 0-44 10-84 30-120l-62-62c-34 52-54 116-54 182 0 188 154 342 342 342v128l170-172-170-170v128z" />
<glyph unicode="&#xe904;" glyph-name="kick" d="M512 810l284-426h-568zM214 298h596v-84h-596v84z" />
<glyph unicode="&#xe905;" glyph-name="hangup" d="M512 640c-68 0-134-10-196-30v-132c0-16-10-34-24-40-42-20-80-46-114-78-8-8-18-12-30-12s-22 4-30 12l-106 106c-8 8-12 18-12 30s4 22 12 30c130 124 306 200 500 200s370-76 500-200c8-8 12-18 12-30s-4-22-12-30l-106-106c-8-8-18-12-30-12s-22 4-30 12c-34 32-72 58-114 78-14 6-24 20-24 38v132c-62 20-128 32-196 32z" />
<glyph unicode="&#xe906;" glyph-name="chat" d="M854 342v512h-684v-598l86 86h598zM854 938c46 0 84-38 84-84v-512c0-46-38-86-84-86h-598l-170-170v768c0 46 38 84 84 84h684z" />
<glyph unicode="&#xe907;" glyph-name="edit" d="M884 724l-78-78-160 160 78 78c16 16 44 16 60 0l100-100c16-16 16-44 0-60zM128 288l472 472 160-160-472-472h-160v160z" />
<glyph unicode="&#xe908;" glyph-name="share-doc" d="M554 640h236l-236 234v-234zM682 426v86h-340v-86h340zM682 256v86h-340v-86h340zM598 938l256-256v-512c0-46-40-84-86-84h-512c-46 0-86 38-86 84l2 684c0 46 38 84 84 84h342z" />
<glyph unicode="&#xe909;" glyph-name="telephone" d="M854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24zM854 810v44h-44v-44h44zM768 896h128v-128h-86v-86h-42v214zM640 810v-128h-128v44h86v42h-86v128h128v-42h-86v-44h86zM726 896v-214h-44v214h44z" />
<glyph unicode="&#xe90a;" glyph-name="star-full" d="M512 288l-264-160 70 300-232 202 306 26 120 282 120-282 306-26-232-202 70-300z" />
<glyph unicode="&#xe90b;" glyph-name="full-screen" d="M598 810h212v-212h-84v128h-128v84zM726 298v128h84v-212h-212v84h128zM214 598v212h212v-84h-128v-128h-84zM298 426v-128h128v-84h-212v212h84z" />
<glyph unicode="&#xe90c;" glyph-name="exit-full-screen" d="M682 682h128v-84h-212v212h84v-128zM598 214v212h212v-84h-128v-128h-84zM342 682v128h84v-212h-212v84h128zM214 342v84h212v-212h-84v128h-128z" />
<glyph unicode="&#xe90d;" glyph-name="security" d="M768 170v428h-512v-428h512zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h388v86c0 72-60 132-132 132s-132-60-132-132h-82c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe90e;" glyph-name="security-locked" d="M768 170v428h-512v-428h512zM380 768v-86h264v86c0 72-60 132-132 132s-132-60-132-132zM768 682c46 0 86-38 86-84v-428c0-46-40-84-86-84h-512c-46 0-86 38-86 84v428c0 46 40 84 86 84h42v86c0 118 96 214 214 214s214-96 214-214v-86h42zM512 298c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
<glyph unicode="&#xe90f;" glyph-name="reload" d="M512 256v128l170-170-170-172v128c-188 0-342 154-342 342 0 66 20 130 54 182l62-62c-20-36-30-76-30-120 0-142 114-256 256-256zM512 854c188 0 342-154 342-342 0-66-20-130-54-182l-62 62c20 36 30 76 30 120 0 142-114 256-256 256v-128l-170 170 170 172v-128z" />
<glyph unicode="&#xe910;" glyph-name="microphone" d="M738 554h72c0-146-116-266-256-286v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216s226 88 226 216zM512 426c-70 0-128 58-128 128v256c0 70 58 128 128 128s128-58 128-128v-256c0-70-58-128-128-128z" />
<glyph unicode="&#xe911;" glyph-name="mic-empty" d="M738 554h72c0-146-116-266-256-286v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216s226 88 226 216zM460 814v-264c0-28 24-50 52-50s50 22 50 50l2 264c0 28-24 52-52 52s-52-24-52-52zM512 426c-70 0-128 58-128 128v256c0 70 58 128 128 128s128-58 128-128v-256c0-70-58-128-128-128z" />
<glyph unicode="&#xe912;" glyph-name="mic-disabled" d="M182 896l714-714-54-54-178 178c-32-20-72-32-110-38v-140h-84v140c-140 20-256 140-256 286h72c0-128 108-216 226-216 34 0 68 8 98 22l-70 70c-8-2-18-4-28-4-70 0-128 58-128 128v32l-256 256zM640 548l-256 254v8c0 70 58 128 128 128s128-58 128-128v-262zM810 554c0-50-14-98-38-140l-52 54c12 26 18 54 18 86h72z" />
<glyph unicode="&#xe913;" glyph-name="link" d="M640 426c114 0 342-56 342-170v-86h-684v86c0 114 228 170 342 170zM256 598h128v-86h-128v-128h-86v128h-128v86h128v128h86v-128zM640 512c-94 0-170 76-170 170s76 172 170 172 170-78 170-172-76-170-170-170z" />
<glyph unicode="&#xe914;" glyph-name="shared-video" d="M512 170c188 0 342 154 342 342s-154 342-342 342-342-154-342-342 154-342 342-342zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426zM426 320v384l256-192z" />
<glyph unicode="&#xe915;" glyph-name="settings" d="M512 362c82 0 150 68 150 150s-68 150-150 150-150-68-150-150 68-150 150-150zM830 470l90-70c8-6 10-18 4-28l-86-148c-6-10-16-12-26-8l-106 42c-22-16-46-32-72-42l-16-112c-2-10-10-18-20-18h-172c-10 0-18 8-20 18l-16 112c-26 10-50 24-72 42l-106-42c-10-4-20-2-26 8l-86 148c-6 10-4 22 4 28l90 70c-2 14-2 28-2 42s0 28 2 42l-90 70c-8 6-10 18-4 28l86 148c6 10 16 12 26 8l106-42c22 16 46 32 72 42l16 112c2 10 10 18 20 18h172c10 0 18-8 20-18l16-112c26-10 50-24 72-42l106 42c10 4 20 2 26-8l86-148c6-10 4-22-4-28l-90-70c2-14 2-28 2-42s0-28-2-42z" />
<glyph unicode="&#xe916;" glyph-name="star" d="M512 366l160-96-42 182 142 124-188 16-72 172-72-172-188-16 142-124-42-182zM938 630l-232-202 70-300-264 160-264-160 70 300-232 202 306 26 120 282 120-282z" />
<glyph unicode="&#xe917;" glyph-name="share-desktop" d="M896 298v512h-768v-512h768zM896 896c46 0 86-40 86-86l-2-512c0-46-38-84-84-84h-214v-86h-340v86h-214c-46 0-86 38-86 84v512c0 46 40 86 86 86h768z" />
<glyph unicode="&#xe918;" glyph-name="camera" d="M726 576l170 170v-468l-170 170v-150c0-24-20-42-44-42h-512c-24 0-42 18-42 42v428c0 24 18 42 42 42h512c24 0 44-18 44-42v-150z" />
<glyph unicode="&#xe919;" glyph-name="camera-disabled" d="M140 938l756-756-54-54-136 136c-6-4-16-8-24-8h-512c-24 0-42 18-42 42v428c0 24 18 42 42 42h32l-116 116zM896 746v-456l-478 478h264c24 0 44-18 44-42v-150z" />
<glyph unicode="&#xe91a;" glyph-name="volume" d="M598 886c172-38 298-192 298-374s-126-336-298-374v88c124 36 212 150 212 286s-88 250-212 286v88zM704 512c0-76-42-140-106-172v344c64-32 106-96 106-172zM128 640h170l214 214v-684l-214 214h-170v256z" />
<glyph unicode="&#xe91b;" glyph-name="contactList" d="M704 746c-46 0-86-38-86-84s40-86 86-86 86 40 86 86-40 84-86 84zM704 512c-82 0-150 68-150 150s68 148 150 148 150-66 150-148-68-150-150-150zM320 746c-46 0-86-38-86-84s40-86 86-86 86 40 86 86-40 84-86 84zM320 512c-82 0-150 68-150 150s68 148 150 148 150-66 150-148-68-150-150-150zM918 278v52c0 24-110 76-214 76-46 0-90-12-128-24 14-16 22-32 22-52v-52h320zM534 278v52c0 24-110 76-214 76s-214-52-214-76v-52h428zM704 470c92 0 278-48 278-140v-116h-940v116c0 92 186 140 278 140 52 0 130-16 192-44 62 28 140 44 192 44z" />
<glyph unicode="&#xe91c;" glyph-name="toggle-filmstrip" d="M896 896h-768c-46.933 0-85.333-38.4-85.333-85.333v-597.333c0-46.933 38.4-85.333 85.333-85.333h768c46.933 0 85.333 38.4 85.333 85.333v597.333c0 46.933-38.4 85.333-85.333 85.333zM896 213.333h-768v128h768v-128z" />
<glyph unicode="&#xe91d;" glyph-name="feedback" d="M42.667 128h170.667v512h-170.667v-512zM981.333 597.333c0 46.933-38.4 85.333-85.333 85.333h-269.227l40.533 194.987 1.28 13.653c0 17.493-7.253 33.707-18.773 45.227l-45.227 44.8-280.747-281.173c-15.787-15.36-25.173-36.693-25.173-60.16v-426.667c0-46.933 38.4-85.333 85.333-85.333h384c35.413 0 65.707 21.333 78.507 52.053l128.853 300.8c3.84 9.813 5.973 20.053 5.973 31.147v81.493l-0.427 0.427 0.427 3.413z" />
</font></defs></svg>

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

View File

@@ -96,58 +96,121 @@
<!--#include virtual="plugin.welcomepage.footer.html" -->
</div>
<div id="videoconference_page">
<div style="position: relative;" id="header_container">
<div id="header">
<div id="mainToolbarContainer">
<div id="notice" class="notice" style="display: none">
<span id="noticeText" class="noticeText"></span>
</div>
<span id="toolbar">
<span id="authentication" class="authentication" style="display: none">
<a class="button icon-avatar" id="toolbar_button_authentication" data-i18n="[content]toolbar.authenticate"></a>
<ul class="loginmenu">
<span class="loginmenuPadding"></span>
<li id="toolbar_auth_identity"></li>
<li id="toolbar_button_login">
<a class="authButton" data-i18n="toolbar.login"></a>
</li>
<li id="toolbar_button_logout">
<a class="authButton" data-i18n="toolbar.logout"></a>
</li>
</ul>
</span>
<a class="button icon-microphone" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute">
<ul id="micMutedPopup" class="loginmenu">
<li data-i18n="[html]toolbar.micMutedPopup"></li>
</ul>
<ul id="unableToUnmutePopup" class="loginmenu">
<li data-i18n="[html]toolbar.unableToUnmutePopup"></li>
</ul>
</a>
<a class="button icon-camera" id="toolbar_button_camera" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleVideoPopover" data-i18n="[content]toolbar.videomute" content="Start / stop camera"></a>
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="bottom" style="display: none"></a>
<a class="button icon-security" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.lock" content="Lock / unlock room"></a>
<a class="button icon-link" id="toolbar_button_link" data-container="body" data-toggle="popover" data-placement="bottom" data-i18n="[content]toolbar.invite" content="Invite others"></a>
<a class="button icon-chat" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="bottom" data-i18n="[content]toolbar.chat" content="Open / close chat">
<span id="unreadMessages"></span>
</a>
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
<a class="button fa fa-share-alt-square" id="toolbar_button_sharedvideo" data-container="body" data-toggle="popover" data-placement="bottom" content="Share a YouTube video" data-i18n="[content]toolbar.sharedvideo" style="display: none">
<ul id="sharedVideoMutedPopup" class="loginmenu">
<li data-i18n="[html]toolbar.sharedVideoMutedPopup"></li>
</ul>
</a>
<a class="button icon-share-desktop" id="toolbar_button_desktopsharing" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="toggleDesktopSharingPopover" content="Share screen" data-i18n="[content]toolbar.sharescreen" style="display: none"></a>
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="bottom" content="Enter / Exit Full Screen" data-i18n="[content]toolbar.fullscreen"></a>
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="bottom" content="Call SIP number" data-i18n="[content]toolbar.sip" style="display: none"></a>
<a class="button icon-dialpad" id="toolbar_button_dialpad" data-container="body" data-toggle="popover" data-placement="bottom" content="Open dialpad" data-i18n="[content]toolbar.dialpad" style="display: none"></a>
<a class="button icon-settings" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="bottom" content="Settings" data-i18n="[content]toolbar.Settings"></a>
<a class="button icon-hangup" id="toolbar_button_hangup" data-container="body" data-toggle="popover" data-placement="bottom" content="Hang Up" data-i18n="[content]toolbar.hangup"></a>
</span>
<span id="mainToolbar" class="toolbar"></span>
</div>
<div id="subject" class="hide"></div>
<div id="extendedToolbar" class="toolbar">
<a class="button" id="toolbar_button_profile" data-container="body" data-placement="right" data-i18n="[content]toolbar.profile" content="Edit your profile">
<img id="avatar" src="images/avatar2.png"/>
</a>
<span id="authentication" class="authentication" style="display: none">
<a class="button icon-avatar" id="toolbar_button_authentication" data-i18n="[content]toolbar.authenticate"></a>
<ul class="loginmenu extendedToolbarPopup">
<span class="loginmenuPadding"></span>
<li id="toolbar_auth_identity"></li>
<li id="toolbar_button_login">
<a class="authButton" data-i18n="toolbar.login"></a>
</li>
<li id="toolbar_button_logout">
<a class="authButton" data-i18n="toolbar.logout"></a>
</li>
</ul>
</span>
<a class="button icon-contactList" id="toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="right" shortcut="contactlistpopover" data-i18n="[content]bottomtoolbar.contactlist" content="Open / close contact list">
<span id="numberOfParticipants"></span>
</a>
<a class="button icon-chat" id="toolbar_button_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="right" data-i18n="[content]toolbar.chat" content="Open / close chat">
<span id="unreadMessages"></span>
</a>
<a class="button" id="toolbar_button_record" data-container="body" data-toggle="popover" data-placement="right" style="display: none"></a>
<a class="button icon-security" id="toolbar_button_security" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[content]toolbar.lock" content="Lock / unlock room"></a>
<a class="button icon-share-doc" id="toolbar_button_etherpad" data-container="body" data-toggle="popover" data-placement="right" content="Shared document" data-i18n="[content]toolbar.etherpad"></a>
<a class="button icon-shared-video" id="toolbar_button_sharedvideo" data-container="body" data-toggle="popover" data-placement="right" content="Share a YouTube video" data-i18n="[content]toolbar.sharedvideo" style="display: none">
<ul id="sharedVideoMutedPopup" class="loginmenu extendedToolbarPopup">
<li data-i18n="[html]toolbar.sharedVideoMutedPopup"></li>
</ul>
</a>
<a class="button icon-telephone" id="toolbar_button_sip" data-container="body" data-toggle="popover" data-placement="right" content="Call SIP number" data-i18n="[content]toolbar.sip" style="display: none"></a>
<a class="button icon-dialpad" id="toolbar_button_dialpad" data-container="body" data-toggle="popover" data-placement="right" content="Open dialpad" data-i18n="[content]toolbar.dialpad" style="display: none"></a>
<a class="button icon-settings" id="toolbar_button_settings" data-container="body" data-toggle="popover" data-placement="right" content="Settings" data-i18n="[content]toolbar.Settings"></a>
<a class="button icon-full-screen" id="toolbar_button_fullScreen" data-container="body" data-toggle="popover" data-placement="right" shortcut="toggleFullscreenPopover" data-i18n="[content]toolbar.fullscreen" content="Enter / Exit Full Screen"></a>
<a class="button icon-toggle-filmstrip" id="toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="right" data-i18n="[content]toolbar.filmstrip" content="Show / hide videos"></a>
<a class="button icon-feedback" id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[content]feedback"></a>
<div id="sideToolbarContainer">
<div id="profile_container" class="sideToolbarContainer__inner">
<div class="title" data-i18n="profile.title"></div>
<div class="sideToolbarBlock first">
<label class="first" data-i18n="profile.setDisplayNameLabel"></label>
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
</div>
<div class="sideToolbarBlock">
<label data-i18n="profile.setEmailLabel"></label>
<input type="text" id="setEmail" placeholder="Enter e-mail">
</div>
</div>
<div id="chat_container" class="sideToolbarContainer__inner">
<div id="nickname">
<span data-i18n="chat.nickname.title"></span>
<form>
<input type='text' id="nickinput" data-i18n="[placeholder]chat.nickname.popover" autofocus>
</form>
</div>
<div id="chatconversation"></div>
<audio id="chatNotification" src="sounds/incomingMessage.wav" preload="auto"></audio>
<textarea id="usermsg" data-i18n="[placeholder]chat.messagebox" autofocus></textarea>
<div id="smileysarea">
<div id="smileys" id="toggle_smileys">
<img src="images/smile.svg"/>
</div>
</div>
</div>
<div id="contacts_container" class="sideToolbarContainer__inner">
<div class="title" data-i18n="contactlist"></div>
<ul id="contacts"></ul>
</div>
<div id="settings_container" class="sideToolbarContainer__inner">
<div class="title" data-i18n="settings.title"></div>
<select id="languages_selectbox" class="first hide"></select>
<div id="deviceOptionsTitle" class="subTitle hide" data-i18n="settings.audioVideo"></div>
<div id="devicesOptions" class="hide">
<div class="sideToolbarBlock first">
<label class="first" data-i18n="settings.selectCamera"></label>
<select id="selectCamera"></select>
</div>
<div class="sideToolbarBlock">
<label data-i18n="settings.selectMic"></label>
<select id="selectMic"></select>
</div>
<div class="sideToolbarBlock">
<label data-i18n="settings.selectAudioOutput"></label>
<select id="selectAudioOutput"></select>
</div>
</div>
<div id="moderatorOptionsTitle" class="subTitle hide" data-i18n="settings.moderator"></div>
<div id = "startMutedOptions" class="hide">
<div class="sideToolbarBlock first">
<input type="checkbox" id="startAudioMuted">
<label class="startMutedLabel" for="startAudioMuted" data-i18n="settings.startAudioMuted"></label>
</div>
<div class="sideToolbarBlock">
<input type="checkbox" id="startVideoMuted">
<label class="startMutedLabel" for="startVideoMuted" data-i18n="settings.startVideoMuted"></label>
</div>
</div>
<div id="followMeOptions" class="sideToolbarBlock hide">
<input type="checkbox" id="followMeCheckBox">
<label class="followMeLabel" for="followMeCheckBox" data-i18n="settings.followMe"></label>
</div>
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="icon-download"></i></a>
</div>
</div>
<div id="subject"></div>
</div>
<div id="videospace">
<div id="largeVideoContainer" class="videocontainer">
<div id="presentation"></div>
<div id="sharedVideo"><div id="sharedVideoIFrame"></div></div>
@@ -183,165 +246,12 @@
<audio id="userJoined" src="sounds/joined.wav" preload="auto"></audio>
<audio id="userLeft" src="sounds/left.wav" preload="auto"></audio>
</div>
<span id="bottomToolbar">
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_chat" data-container="body" data-toggle="popover" shortcut="toggleChatPopover" data-placement="top" data-i18n="[content]bottomtoolbar.chat" content="Open / close chat">
<i id="chatBottomButton" class="icon-chat-simple">
<span id="bottomUnreadMessages"></span>
</i>
</a>
</span>
<div class="bottom_button_separator"></div>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_contact_list" data-container="body" data-toggle="popover" data-placement="top" id="contactlistpopover" data-i18n="[content]bottomtoolbar.contactlist" content="Open / close contact list">
<i id="contactListButton" class="icon-contactList">
<span id="numberOfParticipants"></span>
</i>
</a>
</span>
<div class="bottom_button_separator"></div>
<span class="bottomToolbar_span">
<a class="bottomToolbarButton" id="bottom_toolbar_film_strip" data-container="body" data-toggle="popover" shortcut="filmstripPopover" data-placement="top" data-i18n="[content]bottomtoolbar.filmstrip" content="Show / hide film strip">
<i id="filmStripButton" class="icon-filmstrip"></i>
</a>
</span>
</span>
</div>
<div id="chatspace" class="right-panel" style="display:none;">
<div id="nickname">
<span data-i18n="chat.nickname.title"></span>
<form>
<input type='text' id="nickinput" data-i18n="[placeholder]chat.nickname.popover" autofocus>
</form>
</div>
<div id="chatconversation"></div>
<audio id="chatNotification" src="sounds/incomingMessage.wav" preload="auto"></audio>
<textarea id="usermsg" data-i18n="[placeholder]chat.messagebox" autofocus></textarea>
<div id="smileysarea">
<div id="smileys" id="toggle_smileys">
<img src="images/smile.svg"/>
</div>
</div>
</div>
<div id="contactlist" class="right-panel" style="display:none;">
<div class="title">
<i class="icon-contactList"></i>
<span data-i18n="contactlist"></span>
</div>
<ul id="contacts"></ul>
</div>
<div id="settingsmenu" class="right-panel" style="display:none;">
<div class="title">
<i class="icon-settings"></i>
<span data-i18n="settings.title"></span>
</div>
<img id="avatar" src="images/avatar2.png"/>
<div class="arrow-up"></div>
<input type="text" id="setDisplayName" data-i18n="[placeholder]settings.name" placeholder="Name">
<input type="text" id="setEmail" placeholder="E-Mail">
<input type="text" id="setAvatarUrl" placeholder="Avatar URL" data-i18n="[placeholder]settings.avatarUrl">
<select id="languages_selectbox"></select>
<div id = "startMutedOptions">
<label class = "startMutedLabel">
<input type="checkbox" id="startAudioMuted">
<span data-i18n="settings.startAudioMuted"></span>
</label>
<label class = "startMutedLabel">
<input type="checkbox" id="startVideoMuted">
<span data-i18n="settings.startVideoMuted"></span>
</label>
</div>
<div id="devicesOptions">
<label className="devicesOptionsLabel">
<span data-i18n="settings.selectCamera"></span>
<select id="selectCamera"></select>
</label>
<label className="devicesOptionsLabel">
<span data-i18n="settings.selectMic"></span>
<select id="selectMic"></select>
</label>
<label className="devicesOptionsLabel">
<span data-i18n="settings.selectAudioOutput"></span>
<select id="selectAudioOutput"></select>
</label>
</div>
<div id="followMeOptions">
<label class = "followMeLabel">
<input type="checkbox" id="followMeCheckBox">
<span data-i18n="settings.followMe"></span>
</label>
</div>
<a id="downloadlog" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]downloadlogs" ><i class="fa fa-cloud-download"></i></a>
</div>
<div id="feedbackButtonDiv">
<a id="feedbackButton" data-container="body" data-toggle="popover" data-placement="right" data-i18n="[data-content]feedback"><i class="fa fa-heart"></i></a>
</div>
</div>
<div id="keyboard-shortcuts" class="keyboard-shortcuts" style="display:none;">
<div class="header"><h3 data-i18n="keyboardShortcuts.keyboardShortcuts"></h3></div>
<div class="content">
<ul class="item">
<li>
<span class="item-action">
<kbd class="regular-key">M</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.mute"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">V</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.videoMute"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">C</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleChat"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">R</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.raiseHand"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">T</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.pushToTalk"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">D</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleScreensharing"></span>
</li>
<li class="item-details">
<span class="item-action">
<kbd class="regular-key">F</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleFilmstrip"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">?</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.toggleShortcuts"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">0</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.focusLocal"></span>
</li>
<li>
<span class="item-action">
<kbd class="regular-key">1-9</kbd>
</span>
<span class="item-description" data-i18n="keyboardShortcuts.focusRemote"></span>
</li>
<ul id="keyboard-shortcuts-list" class="item">
</ul>
</div>
</div>

View File

@@ -2,6 +2,9 @@ var interfaceConfig = {
CANVAS_EXTRA: 104,
CANVAS_RADIUS: 0,
SHADOW_COLOR: '#ffffff',
// TO FIX: this needs to be handled from SASS variables. There are some
// methods allowing to use variables both in css and js.
DEFAULT_BACKGROUND: '#474747',
INITIAL_TOOLBAR_TIMEOUT: 20000,
TOOLBAR_TIMEOUT: 4000,
DEFAULT_REMOTE_DISPLAY_NAME: "Fellow Jitster",
@@ -16,7 +19,9 @@ var interfaceConfig = {
INVITATION_POWERED_BY: true,
// the toolbar buttons line is intentionally left in one line, to be able
// to easily override values or remove them using regex
TOOLBAR_BUTTONS: ['authentication', 'microphone', 'camera', 'desktop', 'recording', 'security', 'invite', 'chat', 'etherpad', 'sharedvideo', 'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip', 'contacts'], // jshint ignore:line
MAIN_TOOLBAR_BUTTONS: ['microphone', 'camera', 'desktop', 'invite', 'hangup'], // jshint ignore:line
TOOLBAR_BUTTONS: ['profile', 'authentication', 'microphone', 'camera', 'desktop', 'recording', 'security', 'invite', 'chat', 'etherpad', 'sharedvideo', 'fullscreen', 'sip', 'dialpad', 'settings', 'hangup', 'filmstrip', 'contacts'], // jshint ignore:line
SETTINGS_SECTIONS: ['language', 'devices', 'moderator'],
// Determines how the video would fit the screen. 'both' would fit the whole
// screen, 'height' would fit the original video height to the height of the
// screen, 'width' would fit the original video width to the width of the
@@ -29,4 +34,4 @@ var interfaceConfig = {
RANDOM_AVATAR_URL_PREFIX: false,
RANDOM_AVATAR_URL_SUFFIX: false,
FILM_STRIP_MAX_HEIGHT: 120
};
};

15
lang/languages-ru.json Normal file
View File

@@ -0,0 +1,15 @@
{
"en": "",
"bg": "",
"de": "",
"es": "",
"fr": "",
"hy": "",
"it": "",
"oc": "",
"ptBR": "",
"sk": "",
"sl": "",
"sv": "",
"tr": ""
}

301
lang/main-ru.json Normal file
View File

@@ -0,0 +1,301 @@
{
"contactlist": "",
"connectionsettings": "",
"poweredby": "",
"downloadlogs": "",
"feedback": "",
"roomUrlDefaultMsg": "",
"participant": "",
"me": "",
"speaker": "",
"raisedHand": "",
"defaultNickname": "",
"defaultLink": "",
"calling": "",
"userMedia": {
"react-nativeGrantPermissions": "",
"chromeGrantPermissions": "",
"androidGrantPermissions": "",
"firefoxGrantPermissions": "",
"operaGrantPermissions": "",
"iexplorerGrantPermissions": "",
"safariGrantPermissions": "",
"nwjsGrantPermissions": ""
},
"keyboardShortcuts": {
"keyboardShortcuts": "",
"raiseHand": "",
"pushToTalk": "",
"toggleScreensharing": "",
"toggleFilmstrip": "",
"toggleShortcuts": "",
"focusLocal": "",
"focusRemote": "",
"toggleChat": "",
"mute": "",
"videoMute": ""
},
"welcomepage": {
"go": "",
"roomname": "",
"disable": "",
"feature1": {
"title": "",
"content": ""
},
"feature2": {
"title": "",
"content": ""
},
"feature3": {
"title": "",
"content": ""
},
"feature4": {
"title": "",
"content": ""
},
"feature5": {
"title": "",
"content": ""
},
"feature6": {
"title": "",
"content": ""
},
"feature7": {
"title": "",
"content": ""
},
"feature8": {
"title": "",
"content": ""
}
},
"toolbar": {
"mute": "",
"videomute": "",
"authenticate": "",
"lock": "",
"invite": "",
"chat": "",
"etherpad": "",
"sharedvideo": "",
"sharescreen": "",
"fullscreen": "",
"sip": "",
"Settings": "",
"hangup": "",
"login": "",
"logout": "",
"dialpad": "",
"sharedVideoMutedPopup": "",
"micMutedPopup": "",
"unableToUnmutePopup": "",
"cameraDisabled": "",
"micDisabled": ""
},
"bottomtoolbar": {
"chat": "",
"filmstrip": "",
"contactlist": ""
},
"chat": {
"nickname": {
"title": "",
"popover": ""
},
"messagebox": ""
},
"settings": {
"title": "",
"update": "",
"name": "",
"startAudioMuted": "",
"startVideoMuted": "",
"selectCamera": "",
"selectMic": "",
"selectAudioOutput": "",
"followMe": "",
"noDevice": "",
"noPermission": "",
"avatarUrl": ""
},
"videothumbnail": {
"editnickname": "",
"moderator": "",
"videomute": "",
"mute": "",
"kick": "",
"muted": "",
"domute": "",
"flip": ""
},
"connectionindicator": {
"bitrate": "",
"packetloss": "",
"resolution": "",
"less": "",
"more": "",
"address": "",
"remoteport": "",
"remoteport_plural_2": "",
"localport": "",
"localport_plural_2": "",
"localaddress": "",
"localaddress_plural_2": "",
"remoteaddress": "",
"remoteaddress_plural_2": "",
"transport": "",
"bandwidth": "",
"na": ""
},
"notify": {
"disconnected": "",
"moderator": "",
"connected": "",
"somebody": "",
"me": "",
"focus": "",
"focusFail": "",
"grantedTo": "",
"grantedToUnknown": "",
"muted": "",
"mutedTitle": "",
"raisedHand": ""
},
"dialog": {
"kickMessage": "",
"popupError": "",
"passwordError": "",
"passwordError2": "",
"connectError": "",
"connectErrorWithMsg": "",
"connecting": "",
"error": "",
"detectext": "",
"failtoinstall": "",
"failedpermissions": "",
"bridgeUnavailable": "",
"jicofoUnavailable": "",
"maxUsersLimitReached": "",
"lockTitle": "",
"lockMessage": "",
"warning": "",
"passwordNotSupported": "",
"sorry": "",
"internalError": "",
"unableToSwitch": "",
"SLDFailure": "",
"SRDFailure": "",
"oops": "",
"defaultError": "",
"passwordRequired": "",
"Ok": "",
"Remove": "",
"shareVideoTitle": "",
"shareVideoLinkError": "",
"removeSharedVideoTitle": "",
"removeSharedVideoMsg": "",
"alreadySharedVideoMsg": "",
"WaitingForHost": "",
"WaitForHostMsg": "",
"IamHost": "",
"Cancel": "",
"retry": "",
"logoutTitle": "",
"logoutQuestion": "",
"sessTerminated": "",
"hungUp": "",
"joinAgain": "",
"Share": "",
"Save": "",
"recording": "",
"recordingToken": "",
"Dial": "",
"sipMsg": "",
"passwordCheck": "",
"passwordMsg": "",
"Invite": "",
"shareLink": "",
"settings1": "",
"settings2": "",
"settings3": "",
"yourPassword": "",
"Back": "",
"serviceUnavailable": "",
"gracefulShutdown": "",
"Yes": "",
"reservationError": "",
"reservationErrorMsg": "",
"password": "",
"userPassword": "",
"token": "",
"tokenAuthFailed": "",
"displayNameRequired": "",
"extensionRequired": "",
"firefoxExtensionPrompt": "",
"feedbackQuestion": "",
"thankYou": "",
"sorryFeedback": "",
"liveStreaming": "",
"streamKey": "",
"startLiveStreaming": "",
"stopStreamingWarning": "",
"stopRecordingWarning": "",
"stopLiveStreaming": "",
"stopRecording": "",
"doNotShowWarningAgain": "",
"permissionDenied": "",
"screenSharingPermissionDeniedError": "",
"micErrorPresent": "",
"cameraErrorPresent": "",
"cameraUnsupportedResolutionError": "",
"cameraUnknownError": "",
"cameraPermissionDeniedError": "",
"cameraNotFoundError": "",
"cameraConstraintFailedError": "",
"micUnknownError": "",
"micPermissionDeniedError": "",
"micNotFoundError": "",
"micConstraintFailedError": ""
},
"email": {
"sharedKey": "",
"subject": "",
"body": "",
"and": ""
},
"connection": {
"ERROR": "",
"CONNECTING": "",
"RECONNECTING": "",
"CONNFAIL": "",
"AUTHENTICATING": "",
"AUTHFAIL": "",
"CONNECTED": "",
"DISCONNECTED": "",
"DISCONNECTING": "",
"ATTACHED": ""
},
"recording": {
"pending": "",
"on": "",
"off": "",
"failedToStart": "",
"buttonTooltip": "",
"error": "",
"unavailable": ""
},
"liveStreaming": {
"pending": "",
"on": "",
"off": "",
"unavailable": "",
"failedToStart": "",
"buttonTooltip": "",
"streamIdRequired": "",
"error": "",
"busy": ""
}
}

View File

@@ -1,5 +1,5 @@
{
"contactlist": "CONTACT LIST",
"contactlist": "ON CALL (__participants__)",
"connectionsettings": "Connection Settings",
"poweredby": "powered by",
"downloadlogs": "Download logs",
@@ -11,7 +11,7 @@
"raisedHand": "Would like to speak",
"defaultNickname": "ex. Jane Pink",
"defaultLink": "e.g. __url__",
"calling": "Calling __name__ ...",
"callingName": "__name__",
"userMedia": {
"react-nativeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
"chromeGrantPermissions": "Please grant permissions to use your camera and microphone by pressing <b><i>Allow</i></b> button",
@@ -27,7 +27,7 @@
"raiseHand": "Raise your hand.",
"pushToTalk": "Push to talk.",
"toggleScreensharing": "Switch between camera and screensharing.",
"toggleFilmstrip": "Show or hide the filmstrip.",
"toggleFilmstrip": "Show or hide the videos.",
"toggleShortcuts": "Show or hide this help menu.",
"focusLocal": "Focus on the local video.",
"focusRemote": "Focus on one of the remote videos.",
@@ -93,11 +93,12 @@
"micMutedPopup": "Your microphone has been muted so that you<br/>would fully enjoy your shared video.",
"unableToUnmutePopup": "You cannot un-mute while the shared video is on.",
"cameraDisabled": "Camera is not available",
"micDisabled": "Microphone is not available"
"micDisabled": "Microphone is not available",
"filmstrip": "Show / hide videos"
},
"bottomtoolbar": {
"chat": "Open / close chat",
"filmstrip": "Show / hide film strip",
"filmstrip": "Show / hide videos",
"contactlist": "Open / close contact list"
},
"chat":{
@@ -112,15 +113,24 @@
"title": "SETTINGS",
"update": "Update",
"name": "Name",
"startAudioMuted": "Start without audio",
"startVideoMuted": "Start without video",
"startAudioMuted": "Everyone starts muted",
"startVideoMuted": "Everyone starts hidden",
"selectCamera": "Select camera",
"selectMic": "Select microphone",
"selectAudioOutput": "Select audio output",
"followMe": "Enable follow me",
"followMe": "Everyone follows me",
"noDevice": "None",
"noPermission": "Permission to use device is not granted",
"avatarUrl": "Avatar URL"
"cameraAndMic": "Camera and microphone",
"moderator": "MODERATOR",
"password": "SET PASSWORD",
"audioVideo": "AUDIO / VIDEO",
"setPasswordLabel": "Lock your room with a password."
},
"profile": {
"title": "PROFILE",
"setDisplayNameLabel": "Set your display name",
"setEmailLabel": "Set your gravatar email"
},
"videothumbnail":
{
@@ -222,24 +232,26 @@
"Remove": "Remove",
"passwordMsg": "Set a password to lock your room",
"Invite": "Invite",
"shareLink": "Share this link with everyone you want to invite",
"shareLink": "Copy and share this link",
"settings1": "Configure your conference",
"settings2": "Participants join muted",
"settings3": "Require nicknames<br/><br/>Set a password to lock your room:",
"yourPassword": "your password",
"yourPassword": "Enter new 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",
"password": "Enter password",
"userPassword": "user password",
"token": "token",
"tokenAuthFailed": "Failed to authenticate with XMPP server: invalid token",
"displayNameRequired": "Please enter your display name",
"extensionRequired": "Extension required:",
"firefoxExtensionPrompt": "You need to install a Firefox extension in order to use screen sharing. Please try again after you <a href='__url__'>get it from here</a>!",
"rateExperience": "Please rate your meeting experience.",
"feedbackHelp": "Your feedback will help us to improve our video experience.",
"feedbackQuestion": "How was your call?",
"thankYou": "Thank you for using __appName__!",
"sorryFeedback": "We're sorry to hear that. Would you like to tell us more?",
@@ -258,12 +270,16 @@
"cameraUnsupportedResolutionError": "Your camera does not support required video resolution.",
"cameraUnknownError": "Cannot use camera for a unknown reason.",
"cameraPermissionDeniedError": "You have not granted permission to use your camera. You can still join the conference but others won't see you. Use the camera button in the address bar to fix this.",
"cameraNotFoundError": "Requested camera was not found.",
"cameraNotFoundError": "Camera was not found.",
"cameraConstraintFailedError": "Yor camera does not satisfy some of required constraints.",
"micUnknownError": "Cannot use microphone for a unknown reason.",
"micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
"micNotFoundError": "Requested microphone was not found.",
"micConstraintFailedError": "Yor microphone does not satisfy some of required constraints."
"micNotFoundError": "Microphone was not found.",
"micConstraintFailedError": "Yor microphone does not satisfy some of required constraints.",
"micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to restart the application.",
"goToStore": "Go to the webstore",
"externalInstallationTitle": "Extension required",
"externalInstallationMsg": "You need to install our desktop sharing extension."
},
"email":
{

View File

@@ -65,9 +65,12 @@ class TokenData{
* @param {string} the JWT token
*/
constructor(jwt) {
this.isGuest = true;
if(!jwt)
return;
this.isGuest = config.enableUserRolesBasedOnToken !== true;
this.jwt = jwt;
//External API settings

View File

@@ -1,6 +1,5 @@
/* global $, APP, config, interfaceConfig */
/* global $, APP, config, interfaceConfig, JitsiMeetJS */
import UIEvents from "../../service/UI/UIEvents";
import AnalyticsAdapter from '../statistics/AnalyticsAdapter';
/**
* Constructs the html for the overall feedback window.
@@ -21,11 +20,11 @@ var constructOverallFeedbackHtml = function() {
feedbackQuestion +
'</div><br/><br/>' +
'<div id="stars">' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="fa fa-star-o fa fa-star"></i></a>' +
'<a><i class="icon-star icon-star-full"></i></a>' +
'<a><i class="icon-star icon-star-full"></i></a>' +
'<a><i class="icon-star icon-star-full"></i></a>' +
'<a><i class="icon-star icon-star-full"></i></a>' +
'<a><i class="icon-star icon-star-full"></i></a>' +
'</div></div>';
return message;
@@ -50,6 +49,37 @@ var constructDetailedFeedbackHtml = function() {
return message;
};
var createRateFeedbackHTML = function () {
var rate = APP.translation.translateString('dialog.rateExperience'),
help = APP.translation.translateString('dialog.feedbackHelp');
return `
<div class="feedback-rating text-center">
<h2>${ rate }</h2>
<p class="star-label">&nbsp;</p>
<div id="stars" class="feedback-stars">
<a class="star-btn">
<i class="fa fa-star shake-rotate"></i>
</a>
<a class="star-btn">
<i class="fa fa-star shake-rotate"></i>
</a>
<a class="star-btn">
<i class="fa fa-star shake-rotate"></i>
</a>
<a class="star-btn">
<i class="fa fa-star shake-rotate"></i>
</a>
<a class="star-btn">
<i class="fa fa-star shake-rotate"></i>
</a>
</div>
<p>&nbsp;</p>
<p>${ help }</p>
</div>
`;
};
/**
* The callback function corresponding to the openFeedbackWindow parameter.
*
@@ -91,6 +121,7 @@ var Feedback = {
* The feedback score. -1 indicates no score has been given for now.
*/
feedbackScore: -1,
/**
* Initialise the Feedback functionality.
* @param emitter the EventEmitter to associate with the Feedback.
@@ -135,6 +166,17 @@ var Feedback = {
isEnabled: function() {
return this.enabled && APP.conference.isCallstatsEnabled();
},
/**
* Returns true if the feedback window is currently visible and false
* otherwise.
* @return {boolean} true if the feedback window is visible, false
* otherwise
*/
isVisible: function() {
return $(".feedback").is(":visible");
},
/**
* Opens the feedback window.
*/
@@ -181,7 +223,7 @@ var Feedback = {
// Defines the different states of the feedback window.
var states = {
overall_feedback: {
html: constructOverallFeedbackHtml(),
html: createRateFeedbackHTML(),
persistent: false,
buttons: {},
closeText: '',
@@ -201,8 +243,6 @@ var Feedback = {
= document.getElementById("feedbackTextArea").value;
if (feedbackDetails && feedbackDetails.length > 0) {
AnalyticsAdapter.sendEvent(
'feedback.rating', Feedback.feedbackScore);
APP.conference.sendFeedback( Feedback.feedbackScore,
feedbackDetails);
}
@@ -231,7 +271,7 @@ var Feedback = {
closeText: '',
loaded: onLoadFunction,
position: {width: 500}}, null);
AnalyticsAdapter.sendEvent('feedback.open');
JitsiMeetJS.analytics.sendEvent('feedback.open');
},
/**
* Toggles the appropriate css class for the given number of stars, to
@@ -243,10 +283,10 @@ var Feedback = {
{
$('#stars >a >i').each(function(index) {
if (index <= starCount) {
$(this).removeClass("fa-star-o");
$(this).removeClass("icon-star");
}
else
$(this).addClass("fa-star-o");
$(this).addClass("icon-star");
});
},
/**
@@ -271,7 +311,7 @@ var Feedback = {
unhoverStars: function (starCount)
{
$('#stars >a >i').each(function(index) {
if (index <= starCount && $(this).hasClass("fa-star-o"))
if (index <= starCount && $(this).hasClass("icon-star"))
$(this).removeClass("starHover");
});
}

View File

@@ -5,10 +5,9 @@ var UI = {};
import Chat from "./side_pannels/chat/Chat";
import Toolbar from "./toolbars/Toolbar";
import ToolbarToggler from "./toolbars/ToolbarToggler";
import BottomToolbar from "./toolbars/BottomToolbar";
import ContactList from "./side_pannels/contactlist/ContactList";
import Avatar from "./avatar/Avatar";
import PanelToggler from "./side_pannels/SidePanelToggler";
import SideContainerToggler from "./side_pannels/SideContainerToggler";
import UIUtil from "./util/UIUtil";
import UIEvents from "../../service/UI/UIEvents";
import CQEvents from '../../service/connectionquality/CQEvents';
@@ -20,9 +19,11 @@ import GumPermissionsOverlay from './gum_overlay/UserMediaPermissionsGuidanceOve
import VideoLayout from "./videolayout/VideoLayout";
import FilmStrip from "./videolayout/FilmStrip";
import SettingsMenu from "./side_pannels/settings/SettingsMenu";
import Profile from "./side_pannels/profile/Profile";
import Settings from "./../settings/Settings";
import { reload } from '../util/helpers';
import RingOverlay from "./ring_overlay/RingOverlay";
import UIErrors from './UIErrors';
var EventEmitter = require("events");
UI.messageHandler = require("./util/MessageHandler");
@@ -133,7 +134,6 @@ function setupChat() {
*/
function setupToolbars() {
Toolbar.init(eventEmitter);
BottomToolbar.setupListeners(eventEmitter);
}
/**
@@ -141,7 +141,7 @@ function setupToolbars() {
* (a.k.a. presentation mode in Chrome).
* @see https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API
*/
function toggleFullScreen () {
UI.toggleFullScreen = function() {
// alternative standard method
let isNotFullScreen = !document.fullscreenElement &&
!document.mozFullScreenElement && // current working methods
@@ -170,7 +170,7 @@ function toggleFullScreen () {
document.webkitExitFullscreen();
}
}
}
};
/**
* Notify user that server has shut down.
@@ -252,7 +252,7 @@ UI.changeDisplayName = function (id, displayName) {
VideoLayout.onDisplayNameChanged(id, displayName);
if (APP.conference.isLocalId(id) || id === 'localVideoContainer') {
SettingsMenu.changeDisplayName(displayName);
Profile.changeDisplayName(displayName);
Chat.setChatConversationMode(!!displayName);
}
};
@@ -298,10 +298,7 @@ UI.initConference = function () {
//if local role changes buttons state will be again updated
UI.updateLocalRole(false);
// Once we've joined the muc show the toolbar
if (!RingOverlay.isDisplayed()) {
ToolbarToggler.showToolbar();
}
UI.showToolbar();
let displayName = config.displayJids ? id : Settings.getDisplayName();
@@ -310,7 +307,12 @@ UI.initConference = function () {
}
// Make sure we configure our avatar id, before creating avatar for us
UI.setUserEmail(id, Settings.getEmail());
let email = Settings.getEmail();
if (email) {
UI.setUserEmail(id, email);
} else {
UI.setUserAvatarID(id, Settings.getAvatarId());
}
Toolbar.checkAutoEnableDesktopSharing();
@@ -329,6 +331,14 @@ UI.mucJoined = function () {
VideoLayout.mucJoined();
};
/***
* Handler for toggling filmstrip
*/
UI.handleToggleFilmStrip = () => {
UI.toggleFilmStrip();
VideoLayout.resizeVideoArea(true, false);
};
/**
* Setup some UI event listeners.
*/
@@ -346,21 +356,23 @@ function registerListeners() {
}
});
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, toggleFullScreen);
UI.addListener(UIEvents.FULLSCREEN_TOGGLE, UI.toggleFullScreen);
UI.addListener(UIEvents.TOGGLE_CHAT, UI.toggleChat);
UI.addListener(UIEvents.TOGGLE_SETTINGS, function () {
PanelToggler.toggleSettingsMenu();
UI.toggleSidePanel("settings_container");
});
UI.addListener(UIEvents.TOGGLE_CONTACT_LIST, UI.toggleContactList);
UI.addListener(UIEvents.TOGGLE_FILM_STRIP, function () {
UI.toggleFilmStrip();
VideoLayout.resizeVideoArea(PanelToggler.isVisible(), true, false);
UI.addListener( UIEvents.TOGGLE_PROFILE, function() {
if(APP.tokenData.isGuest)
UI.toggleSidePanel("profile_container");
});
UI.addListener(UIEvents.TOGGLE_FILM_STRIP, UI.handleToggleFilmStrip);
UI.addListener(UIEvents.FOLLOW_ME_ENABLED, function (isEnabled) {
if (followMeHandler)
followMeHandler.enableFollowMe(isEnabled);
@@ -372,8 +384,8 @@ function registerListeners() {
*/
function bindEvents() {
function onResize() {
PanelToggler.resizeChat();
VideoLayout.resizeVideoArea(PanelToggler.isVisible());
SideContainerToggler.resize();
VideoLayout.resizeVideoArea();
}
// Resize and reposition videos in full screen mode.
@@ -422,22 +434,22 @@ UI.start = function () {
registerListeners();
ToolbarToggler.init();
BottomToolbar.init();
SideContainerToggler.init(eventEmitter);
FilmStrip.init(eventEmitter);
VideoLayout.init(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
VideoLayout.initLargeVideo(PanelToggler.isVisible());
VideoLayout.initLargeVideo();
}
VideoLayout.resizeVideoArea(PanelToggler.isVisible(), true, true);
VideoLayout.resizeVideoArea(true, true);
ContactList.init(eventEmitter);
bindEvents();
sharedVideoManager = new SharedVideoManager(eventEmitter);
if (!interfaceConfig.filmStripOnly) {
$("#videospace").mousemove(function () {
return ToolbarToggler.showToolbar();
$("#videoconference_page").mousemove(function () {
return UI.showToolbar();
});
setupToolbars();
setupChat();
@@ -460,9 +472,8 @@ UI.start = function () {
elem.href = 'data:application/json;charset=utf-8,\n' + data;
});
} else {
$("#header").css("display", "none");
$("#mainToolbarContainer").css("display", "none");
$("#downloadlog").css("display", "none");
BottomToolbar.hide();
FilmStrip.setupFilmStripOnly();
messageHandler.enableNotifications(false);
$('body').popover("disable");
@@ -491,17 +502,11 @@ UI.start = function () {
"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(eventEmitter);
Profile.init(eventEmitter);
}
if(APP.tokenData.callee) {
@@ -513,7 +518,6 @@ UI.start = function () {
return true;
};
/**
* Show local stream on UI.
* @param {JitsiTrack} track stream to show
@@ -720,16 +724,26 @@ UI.isFilmStripVisible = function () {
* Toggles chat panel.
*/
UI.toggleChat = function () {
PanelToggler.toggleChat();
UI.toggleSidePanel("chat_container");
};
/**
* Toggles contact list panel.
*/
UI.toggleContactList = function () {
PanelToggler.toggleContactList();
UI.toggleSidePanel("contacts_container");
};
/**
* Toggles the given side panel.
*
* @param {String} sidePanelId the identifier of the side panel to toggle
*/
UI.toggleSidePanel = function (sidePanelId) {
SideContainerToggler.toggle(sidePanelId);
};
/**
* Handle new user display name.
*/
@@ -821,6 +835,16 @@ UI.removeListener = function (type, listener) {
eventEmitter.removeListener(type, listener);
};
/**
* Emits the event of given type by specifying the parameters in options.
*
* @param type the type of the event we're emitting
* @param options the parameters for the event
*/
UI.emitEvent = function (type, options) {
eventEmitter.emit(type, options);
};
UI.clickOnVideo = function (videoNumber) {
var remoteVideos = $(".videocontainer:not(#mixedstream)");
if (remoteVideos.length > videoNumber) {
@@ -841,20 +865,20 @@ UI.dockToolbar = function (isDock) {
/**
* Updates the avatar for participant.
* @param {string} id user id
* @param {stirng} avatarUrl the URL for the avatar
* @param {string} avatarUrl the URL for the avatar
*/
function changeAvatar(id, avatarUrl) {
VideoLayout.changeUserAvatar(id, avatarUrl);
ContactList.changeUserAvatar(id, avatarUrl);
if (APP.conference.isLocalId(id)) {
SettingsMenu.changeAvatar(avatarUrl);
Profile.changeAvatar(avatarUrl);
}
}
/**
* Update user email.
* @param {string} id user id
* @param {stirng} email user email
* @param {string} email user email
*/
UI.setUserEmail = function (id, email) {
// update avatar
@@ -863,11 +887,22 @@ UI.setUserEmail = function (id, email) {
changeAvatar(id, Avatar.getAvatarUrl(id));
};
/**
* Update user avtar id.
* @param {string} id user id
* @param {string} avatarId user's avatar id
*/
UI.setUserAvatarID = function (id, avatarId) {
// update avatar
Avatar.setUserAvatarID(id, avatarId);
changeAvatar(id, Avatar.getAvatarUrl(id));
};
/**
* Update user avatar URL.
* @param {string} id user id
* @param {stirng} url user avatar url
* @param {string} url user avatar url
*/
UI.setUserAvatarUrl = function (id, url) {
// update avatar
@@ -1033,7 +1068,7 @@ UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
}
let and = APP.translation.translateString("email.and");
let supportedBrowsers = `Chromium, Google Chrome ${and} Opera`;
let supportedBrowsers = `Chromium, Google Chrome, Firefox ${and} Opera`;
let subject = APP.translation.translateString(
"email.subject", {appName:interfaceConfig.APP_NAME, conferenceName}
@@ -1066,29 +1101,32 @@ UI.inviteParticipants = function (roomUrl, conferenceName, key, nick) {
* @returns {Promise} when dialog is closed.
*/
UI.requestFeedback = function () {
return new Promise(function (resolve, reject) {
if (Feedback.isEnabled()) {
// If the user has already entered feedback, we'll show the window and
// immidiately start the conference dispose timeout.
if (Feedback.feedbackScore > 0) {
Feedback.openFeedbackWindow();
resolve();
if (Feedback.isVisible())
return Promise.reject(UIErrors.FEEDBACK_REQUEST_IN_PROGRESS);
else
return new Promise(function (resolve, reject) {
if (Feedback.isEnabled()) {
// If the user has already entered feedback, we'll show the
// window and immidiately start the conference dispose timeout.
if (Feedback.feedbackScore > 0) {
Feedback.openFeedbackWindow();
resolve();
} else { // Otherwise we'll wait for user's feedback.
Feedback.openFeedbackWindow(resolve);
} else { // Otherwise we'll wait for user's feedback.
Feedback.openFeedbackWindow(resolve);
}
} else {
// If the feedback functionality isn't enabled we show a thank
// you dialog.
messageHandler.openMessageDialog(
null, null, null,
APP.translation.translateString(
"dialog.thankYou", {appName:interfaceConfig.APP_NAME}
)
);
resolve();
}
} else {
// If the feedback functionality isn't enabled we show a thank you
// dialog.
messageHandler.openMessageDialog(
null, null, null,
APP.translation.translateString(
"dialog.thankYou", {appName:interfaceConfig.APP_NAME}
)
);
resolve();
}
});
});
};
UI.updateRecordingState = function (state) {
@@ -1213,6 +1251,33 @@ UI.showExtensionRequiredDialog = function (url) {
"dialog.firefoxExtensionPrompt", {url: url}));
};
/**
* Shows "Please go to chrome webstore to install the desktop sharing extension"
* 2 button dialog with buttons - cancel and go to web store.
* @param url {string} the url of the extension.
*/
UI.showExtensionExternalInstallationDialog = function (url) {
messageHandler.openTwoButtonDialog(
"dialog.externalInstallationTitle",
null,
"dialog.externalInstallationMsg",
null,
true,
"dialog.goToStore",
function(e,v,m,f){
if (v) {
e.preventDefault();
eventEmitter.emit(UIEvents.OPEN_EXTENSION_STORE, url);
}
},
function () {},
function () {
eventEmitter.emit(UIEvents.EXTERNAL_INSTALLATION_CANCELED);
}
);
};
/**
* Shows dialog with combined information about camera and microphone errors.
* @param {JitsiTrackError} micError
@@ -1348,6 +1413,18 @@ UI.showDeviceErrorDialog = function (micError, cameraError) {
}
};
/**
* Shows error dialog that informs the user that no data is received from the
* microphone.
*/
UI.showAudioNotWorkingDialog = function () {
messageHandler.openMessageDialog(
"dialog.error",
"dialog.micNotSendingData",
null,
null);
};
UI.updateDevicesAvailability = function (id, devices) {
VideoLayout.setDeviceAvailabilityIcons(id, devices);
};
@@ -1412,8 +1489,6 @@ UI.enableMicrophoneButton = function () {
Toolbar.markAudioIconAsDisabled(false);
};
let bottomToolbarEnabled = null;
UI.showRingOverLay = function () {
RingOverlay.show(APP.tokenData.callee);
FilmStrip.toggleFilmStrip(false);
@@ -1425,6 +1500,15 @@ UI.hideRingOverLay = function () {
FilmStrip.toggleFilmStrip(true);
};
/**
* Indicates if the ring overlay is currently visible.
*
* @returns {*|boolean} {true} if the ring overlay is visible, {false} otherwise
*/
UI.isRingOverlayVisible = function () {
return RingOverlay.isVisible();
};
/**
* Shows browser-specific overlay with guidance how to proceed with gUM prompt.
* @param {string} browser - name of browser for which to show the guidance

10
modules/UI/UIErrors.js Normal file
View File

@@ -0,0 +1,10 @@
/**
* A list of all UI errors.
*/
module.exports = {
/**
* Indicates that a Feedback request is currently in progress.
* @type {{FEEDBACK_REQUEST_IN_PROGRESS: string}}
*/
FEEDBACK_REQUEST_IN_PROGRESS: "FeedbackRequestInProgress"
};

View File

@@ -1,7 +1,5 @@
/* global APP, JitsiMeetJS */
import UIUtil from '../util/UIUtil';
//FIXME:
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
/**
* Show dialog which asks user for new password for the conference.
@@ -152,7 +150,7 @@ export default function createRoomLocker (room) {
return askToUnlock().then(
() => { return lock(); }
).then(function () {
AnalyticsAdapter.sendEvent('toolbar.lock.disabled');
JitsiMeetJS.analytics.sendEvent('toolbar.lock.disabled');
}).catch(
reason => {
if (reason !== APP.UI.messageHandler.CANCEL)
@@ -170,7 +168,7 @@ export default function createRoomLocker (room) {
return askForNewPassword().then(
newPass => { return lock(newPass);}
).then(function () {
AnalyticsAdapter.sendEvent('toolbar.lock.enabled');
JitsiMeetJS.analytics.sendEvent('toolbar.lock.enabled');
}).catch(
reason => {
if (reason !== APP.UI.messageHandler.CANCEL)

View File

@@ -37,6 +37,15 @@ export default {
this._setUserProp(id, "url", url);
},
/**
* Sets the user's avatar id.
* @param id id of the user
* @param avatarId an id to be used for the avatar
*/
setUserAvatarID: function (id, avatarId) {
this._setUserProp(id, "avatarId", avatarId);
},
/**
* Returns the URL of the image for the avatar of a particular user,
* identified by its id.
@@ -55,11 +64,16 @@ export default {
let avatarId = null;
const user = users[userId];
// The priority is url, email and lowest is avatarId
if(user) {
if(user.url)
return users[userId].url;
return user.url;
avatarId = users[userId].email;
if (user.email)
avatarId = user.email;
else {
avatarId = user.avatarId;
}
}
// If the ID looks like an email, we'll use gravatar.

View File

@@ -4,7 +4,6 @@ import VideoLayout from "../videolayout/VideoLayout";
import LargeContainer from '../videolayout/LargeContainer';
import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
import SidePanelToggler from "../side_pannels/SidePanelToggler";
import FilmStrip from '../videolayout/FilmStrip';
/**

View File

@@ -1,4 +1,4 @@
/* global APP, $, config, interfaceConfig */
/* global APP, $, config, interfaceConfig, JitsiMeetJS */
/*
* Copyright @ 2015 Atlassian Pty Ltd
*
@@ -19,8 +19,6 @@ import UIUtil from '../util/UIUtil';
import VideoLayout from '../videolayout/VideoLayout';
import Feedback from '../Feedback.js';
import Toolbar from '../toolbars/Toolbar';
import BottomToolbar from '../toolbars/BottomToolbar';
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
/**
* The dialog for user input.
@@ -261,7 +259,6 @@ var Recording = {
VideoLayout.setLocalVideoVisible(false);
Feedback.enableFeedback(false);
Toolbar.enable(false);
BottomToolbar.enable(false);
APP.UI.messageHandler.enableNotifications(false);
APP.UI.messageHandler.enablePopups(false);
}
@@ -307,7 +304,7 @@ var Recording = {
selector.click(function () {
if (dialog)
return;
AnalyticsAdapter.sendEvent('recording.clicked');
JitsiMeetJS.analytics.sendEvent('recording.clicked');
switch (self.currentState) {
case Status.ON:
case Status.RETRYING:
@@ -315,7 +312,8 @@ var Recording = {
_showStopRecordingPrompt(recordingType).then(
() => {
self.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
AnalyticsAdapter.sendEvent('recording.stopped');
JitsiMeetJS.analytics.sendEvent(
'recording.stopped');
},
() => {});
break;
@@ -326,13 +324,14 @@ var Recording = {
_requestLiveStreamId().then((streamId) => {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{streamId: streamId});
AnalyticsAdapter.sendEvent('recording.started');
JitsiMeetJS.analytics.sendEvent(
'recording.started');
}).catch(
reason => {
if (reason !== APP.UI.messageHandler.CANCEL)
console.error(reason);
else
AnalyticsAdapter.sendEvent(
JitsiMeetJS.analytics.sendEvent(
'recording.canceled');
}
);
@@ -340,20 +339,22 @@ var Recording = {
if (self.predefinedToken) {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{token: self.predefinedToken});
AnalyticsAdapter.sendEvent('recording.started');
JitsiMeetJS.analytics.sendEvent(
'recording.started');
return;
}
_requestRecordingToken().then((token) => {
self.eventEmitter.emit( UIEvents.RECORDING_TOGGLED,
{token: token});
AnalyticsAdapter.sendEvent('recording.started');
JitsiMeetJS.analytics.sendEvent(
'recording.started');
}).catch(
reason => {
if (reason !== APP.UI.messageHandler.CANCEL)
console.error(reason);
else
AnalyticsAdapter.sendEvent(
JitsiMeetJS.analytics.sendEvent(
'recording.canceled');
}
);

View File

@@ -1,4 +1,5 @@
/* global $ */
/* jshint -W101 */
/**
* Shows ring overlay
@@ -8,35 +9,39 @@ class RingOverlay {
* @param callee instance of User class from TokenData.js
*/
constructor(callee) {
this._containerId = 'ringOverlay';
this._audioContainerId = 'ringOverlayRinging';
this.callee = callee;
this._buildHtml();
this.audio = $("#ring_overlay_ringing");
this.audio[0].play();
this.render();
this.audio = document.getElementById(this._audioContainerId);
this.audio.play();
this._setAudioTimeout();
}
/**
* Builds and appends the ring overlay to the html document
*/
_buildHtml() {
$("body").append("<div class='overlay_container' >" +
"<div class='overlay' /><div class='overlay_content'>" +
"<img class='overlay_avatar' src='" +
this.callee.getAvatarUrl() + "' />" +
"<span data-i18n='calling' data-i18n-options='" +
JSON.stringify({name: this.callee.getName()}) +
"' class='overlay_text'>Calling " +
this.callee.getName() + "...</span></div>" +
"<audio id='ring_overlay_ringing' src='/sounds/ring.ogg' /></div>");
_getHtmlStr(callee) {
return `
<div id="${this._containerId}" class='ringing' >
<div class='ringing__content'>
<p>Calling...</p>
<img class='ringing__avatar' src="${callee.getAvatarUrl()}" />
<div class="ringing__caller-info">
<p>${callee.getName()}</p>
</div>
</div>
<audio id="${this._audioContainerId}" src="/sounds/ring.ogg" />
</div>`;
}
/**
* Sets the interval that is going to play the ringing sound.
*
*/
_setAudioTimeout() {
this.interval = setInterval( () => {
this.audio[0].play();
}, 5000);
render() {
this.htmlStr = this._getHtmlStr(this.callee);
this._attach();
}
/**
@@ -44,9 +49,28 @@ class RingOverlay {
* related to the ring overlay.
*/
destroy() {
if(this.interval)
if (this.interval) {
clearInterval(this.interval);
$(".overlay_container").remove();
}
this._detach();
}
_attach() {
$("body").append(this.htmlStr);
}
_detach() {
$(`#${this._containerId}`).remove();
}
/**
* Sets the interval that is going to play the ringing sound.
*/
_setAudioTimeout() {
this.interval = setInterval( () => {
this.audio.play();
}, 5000);
}
}
@@ -66,26 +90,30 @@ export default {
if(overlay) {
this.hide();
}
overlay = new RingOverlay(callee);
},
/**
* Hides the ring overlay. Destroys all the elements related to the ring
* overlay.
*/
hide() {
if(!overlay)
if(!overlay) {
return false;
}
overlay.destroy();
overlay = null;
return true;
},
/**
* Checks whether or not the ring overlay is currently displayed.
*
* @returns {boolean} true if the ring overlay is currently displayed or
* false otherwise.
*/
isDisplayed () {
isVisible () {
return overlay !== null;
}
};

View File

@@ -1,4 +1,5 @@
/* global $, APP, YT, onPlayerReady, onPlayerStateChange, onPlayerError */
/* global $, APP, YT, onPlayerReady, onPlayerStateChange, onPlayerError,
JitsiMeetJS */
import UIUtil from '../util/UIUtil';
import UIEvents from '../../../service/UI/UIEvents';
@@ -8,7 +9,6 @@ import LargeContainer from '../videolayout/LargeContainer';
import SmallVideo from '../videolayout/SmallVideo';
import FilmStrip from '../videolayout/FilmStrip';
import ToolbarToggler from "../toolbars/ToolbarToggler";
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
export const SHARED_VIDEO_CONTAINER_TYPE = "sharedvideo";
@@ -72,11 +72,11 @@ export default class SharedVideoManager {
url => {
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
AnalyticsAdapter.sendEvent('sharedvideo.started');
JitsiMeetJS.analytics.sendEvent('sharedvideo.started');
},
err => {
console.log('SHARED VIDEO CANCELED', err);
AnalyticsAdapter.sendEvent('sharedvideo.canceled');
JitsiMeetJS.analytics.sendEvent('sharedvideo.canceled');
}
);
return;
@@ -86,7 +86,7 @@ export default class SharedVideoManager {
showStopVideoPropmpt().then(() => {
this.emitter.emit(
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
AnalyticsAdapter.sendEvent('sharedvideo.stoped');
JitsiMeetJS.analytics.sendEvent('sharedvideo.stoped');
},
() => {});
} else {
@@ -98,7 +98,7 @@ export default class SharedVideoManager {
dialog = null;
}
);
AnalyticsAdapter.sendEvent('sharedvideo.alreadyshared');
JitsiMeetJS.analytics.sendEvent('sharedvideo.alreadyshared');
}
}
@@ -202,7 +202,7 @@ export default class SharedVideoManager {
self.smartAudioMute();
} else if (event.data == YT.PlayerState.PAUSED) {
self.smartAudioUnmute();
AnalyticsAdapter.sendEvent('sharedvideo.paused');
JitsiMeetJS.analytics.sendEvent('sharedvideo.paused');
}
self.fireSharedVideoEvent(event.data == YT.PlayerState.PAUSED);
};
@@ -232,7 +232,7 @@ export default class SharedVideoManager {
else if (event.data.volume <=0 || event.data.muted) {
self.smartAudioUnmute();
}
AnalyticsAdapter.sendEvent('sharedvideo.volumechanged');
JitsiMeetJS.analytics.sendEvent('sharedvideo.volumechanged');
};
window.onPlayerReady = function(event) {

View File

@@ -0,0 +1,127 @@
/* global $ */
import UIEvents from "../../../service/UI/UIEvents";
/**
* Handles open and close of the extended toolbar side panel
* (chat, settings, etc.).
*
* @type {{init, toggle, isVisible, hide, show, resize}}
*/
const SideContainerToggler = {
/**
* Initialises this toggler by registering the listeners.
*
* @param eventEmitter
*/
init(eventEmitter) {
this.eventEmitter = eventEmitter;
// Adds a listener for the animation end event that would take care
// of hiding all internal containers when the extendedToolbarPanel is
// closed.
document.getElementById("sideToolbarContainer")
.addEventListener("animationend", function(e) {
if(e.animationName === "slideOutExt")
$("#sideToolbarContainer").children().each(function() {
if ($(this).hasClass("show"))
SideContainerToggler.hideInnerContainer($(this));
});
}, false);
},
/**
* Toggles the container with the given element id.
*
* @param {String} elementId the identifier of the container element to
* toggle
*/
toggle(elementId) {
let elementSelector = $(`#${elementId}`);
let isSelectorVisible = elementSelector.hasClass("show");
if (isSelectorVisible) {
this.hide();
}
else {
if (this.isVisible())
$("#sideToolbarContainer").children().each(function() {
if ($(this).id !== elementId && $(this).hasClass("show"))
SideContainerToggler.hideInnerContainer($(this));
});
if (!this.isVisible())
this.show();
this.showInnerContainer(elementSelector);
}
},
/**
* Returns {true} if the side toolbar panel is currently visible,
* otherwise returns {false}.
*/
isVisible() {
return $("#sideToolbarContainer").hasClass("slideInExt");
},
/**
* Returns {true} if the side toolbar panel is currently hovered and
* {false} otherwise.
*/
isHovered() {
return $("#sideToolbarContainer:hover").length > 0;
},
/**
* Hides the side toolbar panel with a slide out animation.
*/
hide() {
$("#sideToolbarContainer")
.removeClass("slideInExt").addClass("slideOutExt");
},
/**
* Shows the side toolbar panel with a slide in animation.
*/
show() {
if (!this.isVisible())
$("#sideToolbarContainer")
.removeClass("slideOutExt").addClass("slideInExt");
},
/**
* Hides the inner container given by the selector.
*
* @param {Object} containerSelector the jquery selector for the
* element to hide
*/
hideInnerContainer(containerSelector) {
containerSelector.removeClass("show").addClass("hide");
this.eventEmitter.emit(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
containerSelector.attr('id'), false);
},
/**
* Shows the inner container given by the selector.
*
* @param {Object} containerSelector the jquery selector for the
* element to show
*/
showInnerContainer(containerSelector) {
containerSelector.removeClass("hide").addClass("show");
this.eventEmitter.emit(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
containerSelector.attr('id'), true);
},
/**
* TO FIX: do we need to resize the chat?
*/
resize () {
//let [width, height] = UIUtil.getSidePanelSize();
//Chat.resizeChat(width, height);
}
};
export default SideContainerToggler;

View File

@@ -1,186 +0,0 @@
/* global require, $ */
import Chat from "./chat/Chat";
import ContactList from "./contactlist/ContactList";
import Settings from "./../../settings/Settings";
import SettingsMenu from "./settings/SettingsMenu";
import VideoLayout from "../videolayout/VideoLayout";
import ToolbarToggler from "../toolbars/ToolbarToggler";
import UIUtil from "../util/UIUtil";
const buttons = {
'#chatspace': '#chatBottomButton',
'#contactlist': '#contactListButton',
'#settingsmenu': '#toolbar_button_settings'
};
var currentlyOpen = null;
/**
* 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
* @param onVideoResizeComplete function to be called after the video area
* is resized
*/
function toggle (object, selector, onOpenComplete,
onOpen, onClose, onVideoResizeComplete) {
let isSideBarVisible = object.isVisible();
UIUtil.buttonClick(buttons[selector], "active");
if (isSideBarVisible) {
$("#toast-container").animate({
right: 5
}, {
queue: false,
duration: 500
});
$(selector).hide("slide", {
direction: "right",
queue: false,
duration: 500,
// Set the size to 0 at the end of the animation to ensure that
// the is(":visible") function on this selector will return {false}
// when the element is hidden.
complete: function() {$(selector).css("width", "0");}
});
resizeVideoArea(false, onVideoResizeComplete);
if(typeof onClose === "function") {
onClose();
}
currentlyOpen = null;
} else {
resizeVideoArea(true, onVideoResizeComplete);
// Undock the toolbar when the chat is shown and if we're in a
// video mode.
if (VideoLayout.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: (UIUtil.getSidePanelSize()[0] + 5)
}, {
queue: false,
duration: 500
});
// Set the size dynamically (not in the css), so that we're sure that
// when is(":visible") is called on this selector the result is {false}
// before it's actually visible.
// (Chrome seems to return {true} for an element which is in the DOM and
// has non-null size set).
$(selector).css("width", "20%");
$(selector).show("slide", {
direction: "right",
queue: false,
duration: 500,
complete: onOpenComplete
});
if(typeof onOpen === "function") {
onOpen();
}
currentlyOpen = selector;
}
}
function resizeVideoArea(isSidePanelVisible, completeFunction) {
VideoLayout.resizeVideoArea(isSidePanelVisible,
false,//don't force thumbnail count update
true, //animate
completeFunction);
}
/**
* Toggler for the chat, contact list, settings menu, etc..
*/
var PanelToggler = {
/**
* Opens / closes the chat area.
*/
toggleChat () {
var chatCompleteFunction = Chat.isVisible()
? function () {}
: function () {
Chat.scrollChatToBottom();
$('#chatspace').trigger('shown');
};
toggle(Chat, //Object
'#chatspace', // Selector
function () { //onOpenComplete
// Request the focus in the nickname field or the chat input
// field.
if ($('#nickname').css('visibility') === 'visible') {
$('#nickinput').focus();
} else {
$('#usermsg').focus();
}
},
() => this.resizeChat(), //OnOpen
null,
chatCompleteFunction); //OnClose
},
resizeChat () {
let [width, height] = UIUtil.getSidePanelSize();
Chat.resizeChat(width, height);
},
/**
* Opens / closes the contact list area.
*/
toggleContactList () {
var completeFunction = ContactList.isVisible()
? function () {}
: function () {
$('#contactlist').trigger('shown');
};
toggle(ContactList,
'#contactlist',
null,
function() {
ContactList.setVisualNotification(false);
},
null,
completeFunction);
},
/**
* Opens / closes the settings menu
*/
toggleSettingsMenu () {
toggle(SettingsMenu,
'#settingsmenu',
null,
function() {},
null);
},
isVisible () {
return (Chat.isVisible() ||
ContactList.isVisible() ||
SettingsMenu.isVisible());
}
};
export default PanelToggler;

View File

@@ -18,15 +18,11 @@ var unreadMessages = 0;
*/
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);
@@ -42,19 +38,6 @@ function setVisualNotification(show) {
'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');
@@ -62,7 +45,6 @@ function setVisualNotification(show) {
}
else {
unreadMsgElement.innerHTML = '';
unreadMsgBottomElement.innerHTML = '';
glower.removeClass('icon-chat-simple');
glower.addClass('icon-chat');
}
@@ -70,15 +52,12 @@ function setVisualNotification(show) {
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');
}
}
@@ -144,7 +123,7 @@ function addSmileys() {
smileysContainer.appendChild(smileyContainer);
}
$("#chatspace").append(smileysContainer);
$("#chat_container").append(smileysContainer);
}
/**
@@ -152,7 +131,7 @@ function addSmileys() {
*/
function resizeChatConversation() {
var msgareaHeight = $('#usermsg').outerHeight();
var chatspace = $('#chatspace');
var chatspace = $('#chat_container');
var width = chatspace.width();
var chat = $('#chatconversation');
var smileys = $('#smileysarea');
@@ -208,7 +187,7 @@ var Chat = {
};
usermsg.autosize({callback: onTextAreaResize});
$("#chatspace").bind("shown",
$("#chat_container").bind("shown",
function () {
unreadMessages = 0;
setVisualNotification(false);
@@ -296,7 +275,8 @@ var Chat = {
* conversation mode or not.
*/
setChatConversationMode (isConversationMode) {
$('#chatspace').toggleClass('is-conversation-mode', isConversationMode);
$('#chat_container')
.toggleClass('is-conversation-mode', isConversationMode);
if (isConversationMode) {
$('#usermsg').focus();
}
@@ -306,7 +286,7 @@ var Chat = {
* Resizes the chat area.
*/
resizeChat (width, height) {
$('#chatspace').width(width).height(height);
$('#chat_container').width(width).height(height);
resizeChatConversation();
},
@@ -315,7 +295,8 @@ var Chat = {
* Indicates if the chat is currently visible.
*/
isVisible () {
return UIUtil.isVisible(document.getElementById("chatspace"));
return UIUtil.isVisible(
document.getElementById("chat_container"));
},
/**
* Shows and hides the window with the smileys

View File

@@ -14,16 +14,19 @@ let notificationInterval;
*/
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 {
if (numberOfContacts <= 0) {
console.error("Invalid number of participants: " + numberOfContacts);
return;
}
let buttonIndicatorText = (numberOfContacts === 1) ? '' : numberOfContacts;
$("#numberOfParticipants").text(buttonIndicatorText);
$("#contacts_container>div.title").text(
APP.translation.translateString(
"contactlist", {participants: numberOfContacts}
));
}
/**
@@ -131,23 +134,6 @@ var ContactList = {
}
},
setVisualNotification (show, stopGlowingIn) {
let 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 (id, isClickable) {
getContactEl(id).toggleClass('clickable', isClickable);
},

View File

@@ -0,0 +1,61 @@
/* global APP, $, JitsiMeetJS */
import UIUtil from "../../util/UIUtil";
import UIEvents from "../../../../service/UI/UIEvents";
import languages from "../../../../service/translation/languages";
import Settings from '../../../settings/Settings';
export default {
init (emitter) {
// DISPLAY NAME
function updateDisplayName () {
emitter.emit(UIEvents.NICKNAME_CHANGED, $('#setDisplayName').val());
}
$('#setDisplayName')
.val(Settings.getDisplayName())
.keyup(function (event) {
if (event.keyCode === 13) { // enter
updateDisplayName();
}
})
.focusout(updateDisplayName);
// EMAIL
function updateEmail () {
emitter.emit(UIEvents.EMAIL_CHANGED, $('#setEmail').val());
}
$('#setEmail')
.val(Settings.getEmail())
.keyup(function (event) {
if (event.keyCode === 13) { // enter
updateEmail();
}
}).focusout(updateEmail);
},
/**
* Check if settings menu is visible or not.
* @returns {boolean}
*/
isVisible () {
return UIUtil.isVisible(document.getElementById("profile_container"));
},
/**
* Change user display name in the settings menu.
* @param {string} newDisplayName
*/
changeDisplayName (newDisplayName) {
$('#setDisplayName').val(newDisplayName);
},
/**
* Change user avatar in the settings menu.
* @param {string} avatarUrl url of the new avatar
*/
changeAvatar (avatarUrl) {
$('#avatar').attr('src', avatarUrl);
}
};

View File

@@ -1,4 +1,4 @@
/* global APP, $, JitsiMeetJS */
/* global APP, $, JitsiMeetJS, interfaceConfig */
import UIUtil from "../../util/UIUtil";
import UIEvents from "../../../../service/UI/UIEvents";
import languages from "../../../../service/translation/languages";
@@ -6,6 +6,7 @@ import Settings from '../../../settings/Settings';
/**
* Generate html select options for available languages.
*
* @param {string[]} items available languages
* @param {string} [currentLang] current language
* @returns {string}
@@ -28,6 +29,7 @@ function generateLanguagesOptions(items, currentLang) {
/**
* Generate html select options for available physical devices.
*
* @param {{ deviceId, label }[]} items available devices
* @param {string} [selectedId] id of selected device
* @param {boolean} permissionGranted if permission to use selected device type
@@ -62,88 +64,59 @@ function generateDevicesOptions(items, selectedId, permissionGranted) {
export default {
init (emitter) {
if (UIUtil.isSettingEnabled('devices')) {
// DEVICES LIST
JitsiMeetJS.mediaDevices.isDeviceListAvailable()
.then((isDeviceListAvailable) => {
if (isDeviceListAvailable &&
JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
this._initializeDeviceSelectionSettings(emitter);
}
});
// DISPLAY NAME
function updateDisplayName () {
emitter.emit(UIEvents.NICKNAME_CHANGED, $('#setDisplayName').val());
}
$('#setDisplayName')
.val(Settings.getDisplayName())
.keyup(function (event) {
if (event.keyCode === 13) { // enter
updateDisplayName();
}
})
.focusout(updateDisplayName);
// Only show the subtitle if this isn't the only setting section.
if (interfaceConfig.SETTINGS_SECTIONS.length > 1)
UIUtil.showElement("deviceOptionsTitle");
// EMAIL
function updateEmail () {
emitter.emit(UIEvents.EMAIL_CHANGED, $('#setEmail').val());
UIUtil.showElement("devicesOptions");
}
// AVATAR URL CHANGED
function updateAvatarUrl () {
emitter.emit(UIEvents.AVATAR_URL_CHANGED, $('#setAvatarUrl').val());
}
$('#setEmail')
.val(Settings.getEmail())
.keyup(function (event) {
if (event.keyCode === 13) { // enter
updateEmail();
}
}).focusout(updateEmail);
$('#setAvatarUrl')
.val(Settings.getAvatarUrl())
.keyup(function (event) {
if (event.keyCode === 13) { // enter
updateAvatarUrl();
}
}).focusout(updateAvatarUrl);
// START MUTED
$("#startMutedOptions").change(function () {
let startAudioMuted = $("#startAudioMuted").is(":checked");
let startVideoMuted = $("#startVideoMuted").is(":checked");
emitter.emit(
UIEvents.START_MUTED_CHANGED,
startAudioMuted,
startVideoMuted
);
});
// FOLLOW ME
$("#followMeOptions").change(function () {
let isFollowMeEnabled = $("#followMeCheckBox").is(":checked");
emitter.emit(
UIEvents.FOLLOW_ME_ENABLED,
isFollowMeEnabled
);
});
// LANGUAGES BOX
let languagesBox = $("#languages_selectbox");
languagesBox.html(generateLanguagesOptions(
languages.getLanguages(),
APP.translation.getCurrentLanguage()
));
APP.translation.translateElement(languagesBox);
languagesBox.change(function () {
emitter.emit(UIEvents.LANG_CHANGED, languagesBox.val());
});
// DEVICES LIST
JitsiMeetJS.mediaDevices.isDeviceListAvailable()
.then((isDeviceListAvailable) => {
if (isDeviceListAvailable &&
JitsiMeetJS.mediaDevices.isDeviceChangeAvailable()) {
this._initializeDeviceSelectionSettings(emitter);
}
if (UIUtil.isSettingEnabled('language')) {
//LANGUAGES BOX
let languagesBox = $("#languages_selectbox");
languagesBox.html(generateLanguagesOptions(
languages.getLanguages(),
APP.translation.getCurrentLanguage()
));
APP.translation.translateElement(languagesBox);
languagesBox.change(function () {
emitter.emit(UIEvents.LANG_CHANGED, languagesBox.val());
});
UIUtil.showElement("languages_selectbox");
}
if (UIUtil.isSettingEnabled('moderator')) {
// START MUTED
$("#startMutedOptions").change(function () {
let startAudioMuted = $("#startAudioMuted").is(":checked");
let startVideoMuted = $("#startVideoMuted").is(":checked");
emitter.emit(
UIEvents.START_MUTED_CHANGED,
startAudioMuted,
startVideoMuted
);
});
// FOLLOW ME
$("#followMeOptions").change(function () {
let isFollowMeEnabled = $("#followMeCheckBox").is(":checked");
emitter.emit(
UIEvents.FOLLOW_ME_ENABLED,
isFollowMeEnabled
);
});
}
},
_initializeDeviceSelectionSettings(emitter) {
@@ -175,10 +148,19 @@ export default {
* @param {boolean} show
*/
showStartMutedOptions (show) {
if (show) {
$("#startMutedOptions").css("display", "block");
if (show && UIUtil.isSettingEnabled('moderator')) {
// Only show the subtitle if this isn't the only setting section.
if (!$("#moderatorOptionsTitle").is(":visible")
&& interfaceConfig.SETTINGS_SECTIONS.length > 1)
UIUtil.showElement("moderatorOptionsTitle");
UIUtil.showElement("startMutedOptions");
} else {
$("#startMutedOptions").css("display", "none");
// Only show the subtitle if this isn't the only setting section.
if ($("#moderatorOptionsTitle").is(":visible"))
UIUtil.hideElement("moderatorOptionsTitle");
UIUtil.hideElement("startMutedOptions");
}
},
@@ -193,10 +175,10 @@ export default {
* @param {boolean} show {true} to show those options, {false} to hide them
*/
showFollowMeOptions (show) {
if (show) {
$("#followMeOptions").css("display", "block");
if (show && UIUtil.isSettingEnabled('moderator')) {
UIUtil.showElement("followMeOptions");
} else {
$("#followMeOptions").css("display", "none");
UIUtil.hideElement("followMeOptions");
}
},
@@ -205,23 +187,7 @@ export default {
* @returns {boolean}
*/
isVisible () {
return UIUtil.isVisible(document.getElementById("settingsmenu"));
},
/**
* Change user display name in the settings menu.
* @param {string} newDisplayName
*/
changeDisplayName (newDisplayName) {
$('#setDisplayName').val(newDisplayName);
},
/**
* Change user avatar in the settings menu.
* @param {string} avatarUrl url of the new avatar
*/
changeAvatar (avatarUrl) {
$('#avatar').attr('src', avatarUrl);
return UIUtil.isVisible(document.getElementById("settings_container"));
},
/**
@@ -304,6 +270,6 @@ export default {
$('#devicesOptions').show();
APP.translation.translateElement($('#settingsmenu option'));
APP.translation.translateElement($('#settings_container option'));
}
};
};

View File

@@ -1,99 +0,0 @@
/* global $, APP, interfaceConfig*/
import UIUtil from '../util/UIUtil';
import UIEvents from '../../../service/UI/UIEvents';
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
const defaultBottomToolbarButtons = {
'chat': '#bottom_toolbar_chat',
'contacts': '#bottom_toolbar_contact_list',
'filmstrip': '#bottom_toolbar_film_strip'
};
const BottomToolbar = {
init () {
this.toolbar = $('#bottomToolbar');
// The bottom toolbar is enabled by default.
this.enabled = true;
},
/**
* Enables / disables the bottom toolbar.
* @param {e} set to {true} to enable the bottom toolbar or {false}
* to disable it
*/
enable (e) {
this.enabled = e;
if (!e && this.isVisible())
this.hide(false);
},
/**
* Indicates if the bottom toolbar is currently enabled.
* @return {this.enabled}
*/
isEnabled() {
return this.enabled;
},
setupListeners (emitter) {
UIUtil.hideDisabledButtons(defaultBottomToolbarButtons);
const buttonHandlers = {
"bottom_toolbar_contact_list": function () {
AnalyticsAdapter.sendEvent('bottomtoolbar.contacts.toggled');
emitter.emit(UIEvents.TOGGLE_CONTACT_LIST);
},
"bottom_toolbar_film_strip": function () {
AnalyticsAdapter.sendEvent('bottomtoolbar.filmstrip.toggled');
emitter.emit(UIEvents.TOGGLE_FILM_STRIP);
},
"bottom_toolbar_chat": function () {
AnalyticsAdapter.sendEvent('bottomtoolbar.chat.toggled');
emitter.emit(UIEvents.TOGGLE_CHAT);
}
};
Object.keys(buttonHandlers).forEach(
buttonId => $(`#${buttonId}`).click(buttonHandlers[buttonId])
);
},
resizeToolbar (thumbWidth, thumbHeight) {
let bottom = (thumbHeight - this.toolbar.outerHeight())/2 + 18;
this.toolbar.css({bottom});
},
/**
* Returns true if this toolbar is currently visible, or false otherwise.
* @return <tt>true</tt> if currently visible, <tt>false</tt> - otherwise
*/
isVisible() {
return this.toolbar.is(":visible");
},
/**
* Hides the bottom toolbar with animation or not depending on the animate
* parameter.
* @param animate <tt>true</tt> to hide the bottom toolbar with animation,
* <tt>false</tt> or nothing to hide it without animation.
*/
hide(animate) {
if (animate)
this.toolbar.hide("slide", {direction: "right", duration: 300});
else
this.toolbar.css("display", "none");
},
/**
* Shows the bottom toolbar with animation or not depending on the animate
* parameter.
* @param animate <tt>true</tt> to show the bottom toolbar with animation,
* <tt>false</tt> or nothing to show it without animation.
*/
show(animate) {
if (animate)
this.toolbar.show("slide", {direction: "right", duration: 300});
else
this.toolbar.css("display", "block");
}
};
export default BottomToolbar;

View File

@@ -1,8 +1,8 @@
/* global APP, $, config, interfaceConfig */
/* global APP, $, config, interfaceConfig, JitsiMeetJS */
/* jshint -W101 */
import UIUtil from '../util/UIUtil';
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
import UIEvents from '../../../service/UI/UIEvents';
import SideContainerToggler from "../side_pannels/SideContainerToggler";
let roomUrl = null;
let emitter = null;
@@ -20,17 +20,21 @@ function openLinkDialog () {
} else {
inviteAttributes = "value=\"" + encodeURI(roomUrl) + "\"";
}
let title = APP.translation.generateTranslationHTML("dialog.shareLink");
APP.UI.messageHandler.openTwoButtonDialog(
"dialog.shareLink", null, null,
`<input id="inviteLinkRef" type="text" ${inviteAttributes} onclick="this.select();" readonly>`,
null, null, null,
'<h2>' + title + '</h2>'
+ '<input id="inviteLinkRef" type="text" '
+ inviteAttributes + ' onclick="this.select();" readonly>',
false, "dialog.Invite",
function (e, v) {
if (v && roomUrl) {
AnalyticsAdapter.sendEvent('toolbar.invite.button');
JitsiMeetJS.analytics.sendEvent('toolbar.invite.button');
emitter.emit(UIEvents.USER_INVITED, roomUrl);
}
else {
AnalyticsAdapter.sendEvent('toolbar.invite.cancel');
JitsiMeetJS.analytics.sendEvent('toolbar.invite.cancel');
}
},
function (event) {
@@ -38,18 +42,23 @@ function openLinkDialog () {
document.getElementById('inviteLinkRef').select();
} else {
if (event && event.target) {
$(event.target).find('button[value=true]').prop('disabled', true);
$(event.target).find('button[value=true]')
.prop('disabled', true);
}
}
},
function (e, v, m, f) {
if(!v && !m && !f)
AnalyticsAdapter.sendEvent('toolbar.invite.close');
JitsiMeetJS.analytics.sendEvent('toolbar.invite.close');
}
);
}
const buttonHandlers = {
"toolbar_button_profile": function () {
JitsiMeetJS.analytics.sendEvent('toolbar.profile.toggled');
emitter.emit(UIEvents.TOGGLE_PROFILE);
},
"toolbar_button_mute": function () {
let sharedVideoManager = APP.UI.getSharedVideoManager();
@@ -64,78 +73,84 @@ const buttonHandlers = {
$("#unableToUnmutePopup"), true, 5000);
}
else {
AnalyticsAdapter.sendEvent('toolbar.audio.unmuted');
JitsiMeetJS.analytics.sendEvent('toolbar.audio.unmuted');
emitter.emit(UIEvents.AUDIO_MUTED, false, true);
}
} else {
AnalyticsAdapter.sendEvent('toolbar.audio.muted');
JitsiMeetJS.analytics.sendEvent('toolbar.audio.muted');
emitter.emit(UIEvents.AUDIO_MUTED, true, true);
}
},
"toolbar_button_camera": function () {
if (APP.conference.videoMuted) {
AnalyticsAdapter.sendEvent('toolbar.video.enabled');
JitsiMeetJS.analytics.sendEvent('toolbar.video.enabled');
emitter.emit(UIEvents.VIDEO_MUTED, false);
} else {
AnalyticsAdapter.sendEvent('toolbar.video.disabled');
JitsiMeetJS.analytics.sendEvent('toolbar.video.disabled');
emitter.emit(UIEvents.VIDEO_MUTED, true);
}
},
"toolbar_button_security": function () {
AnalyticsAdapter.sendEvent('toolbar.lock.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.lock.clicked');
emitter.emit(UIEvents.ROOM_LOCK_CLICKED);
},
"toolbar_button_link": function () {
AnalyticsAdapter.sendEvent('toolbar.invite.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.invite.clicked');
openLinkDialog();
},
"toolbar_button_chat": function () {
AnalyticsAdapter.sendEvent('toolbar.chat.toggled');
JitsiMeetJS.analytics.sendEvent('toolbar.chat.toggled');
emitter.emit(UIEvents.TOGGLE_CHAT);
},
"toolbar_contact_list": function () {
JitsiMeetJS.analytics.sendEvent(
'toolbar.contacts.toggled');
emitter.emit(UIEvents.TOGGLE_CONTACT_LIST);
},
"toolbar_button_etherpad": function () {
AnalyticsAdapter.sendEvent('toolbar.etherpad.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.etherpad.clicked');
emitter.emit(UIEvents.ETHERPAD_CLICKED);
},
"toolbar_button_sharedvideo": function () {
AnalyticsAdapter.sendEvent('toolbar.sharedvideo.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.sharedvideo.clicked');
emitter.emit(UIEvents.SHARED_VIDEO_CLICKED);
},
"toolbar_button_desktopsharing": function () {
if (APP.conference.isSharingScreen) {
AnalyticsAdapter.sendEvent('toolbar.screen.disabled');
JitsiMeetJS.analytics.sendEvent('toolbar.screen.disabled');
} else {
AnalyticsAdapter.sendEvent('toolbar.screen.enabled');
JitsiMeetJS.analytics.sendEvent('toolbar.screen.enabled');
}
emitter.emit(UIEvents.TOGGLE_SCREENSHARING);
},
"toolbar_button_fullScreen": function() {
AnalyticsAdapter.sendEvent('toolbar.fullscreen.enabled');
UIUtil.buttonClick("#toolbar_button_fullScreen", "icon-full-screen icon-exit-full-screen");
JitsiMeetJS.analytics.sendEvent('toolbar.fullscreen.enabled');
UIUtil.buttonClick("toolbar_button_fullScreen",
"icon-full-screen icon-exit-full-screen");
emitter.emit(UIEvents.FULLSCREEN_TOGGLE);
},
"toolbar_button_sip": function () {
AnalyticsAdapter.sendEvent('toolbar.sip.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.sip.clicked');
showSipNumberInput();
},
"toolbar_button_dialpad": function () {
AnalyticsAdapter.sendEvent('toolbar.sip.dialpad.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.sip.dialpad.clicked');
dialpadButtonClicked();
},
"toolbar_button_settings": function () {
AnalyticsAdapter.sendEvent('toolbar.settings.toggled');
JitsiMeetJS.analytics.sendEvent('toolbar.settings.toggled');
emitter.emit(UIEvents.TOGGLE_SETTINGS);
},
"toolbar_button_hangup": function () {
AnalyticsAdapter.sendEvent('toolbar.hangup');
JitsiMeetJS.analytics.sendEvent('toolbar.hangup');
emitter.emit(UIEvents.HANGUP);
},
"toolbar_button_login": function () {
AnalyticsAdapter.sendEvent('toolbar.authenticate.login.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.login.clicked');
emitter.emit(UIEvents.AUTH_CLICKED);
},
"toolbar_button_logout": function () {
AnalyticsAdapter.sendEvent('toolbar.authenticate.logout.clicked');
JitsiMeetJS.analytics.sendEvent('toolbar.authenticate.logout.clicked');
// Ask for confirmation
APP.UI.messageHandler.openTwoButtonDialog(
"dialog.logoutTitle",
@@ -150,19 +165,130 @@ const buttonHandlers = {
}
}
);
},
"toolbar_film_strip": function () {
JitsiMeetJS.analytics.sendEvent(
'bottomtoolbar.filmstrip.toggled');
emitter.emit(UIEvents.TOGGLE_FILM_STRIP);
}
};
const defaultToolbarButtons = {
'microphone': '#toolbar_button_mute',
'camera': '#toolbar_button_camera',
'desktop': '#toolbar_button_desktopsharing',
'security': '#toolbar_button_security',
'invite': '#toolbar_button_link',
'chat': '#toolbar_button_chat',
'etherpad': '#toolbar_button_etherpad',
'fullscreen': '#toolbar_button_fullScreen',
'settings': '#toolbar_button_settings',
'hangup': '#toolbar_button_hangup'
'microphone': {
id: 'toolbar_button_mute',
className: "button icon-microphone",
shortcut: 'M',
shortcutAttr: 'mutePopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.audiomute.toggled');
APP.conference.toggleAudioMuted();
},
shortcutDescription: "keyboardShortcuts.mute",
popups: [
{
id: "micMutedPopup",
className: "loginmenu",
dataAttr: "[html]toolbar.micMutedPopup"
},
{
id: "unableToUnmutePopup",
className: "loginmenu",
dataAttr: "[html]toolbar.unableToUnmutePopup"
}
],
content: "Mute / Unmute",
i18n: "[content]toolbar.mute"
},
'camera': {
id: 'toolbar_button_camera',
className: "button icon-camera",
shortcut: 'V',
shortcutAttr: 'toggleVideoPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.videomute.toggled');
APP.conference.toggleVideoMuted();
},
shortcutDescription: "keyboardShortcuts.videoMute",
content: "Start / stop camera",
i18n: "[content]toolbar.videomute"
},
'desktop': {
id: 'toolbar_button_desktopsharing',
className: 'button icon-share-desktop',
shortcut: 'D',
shortcutAttr: 'toggleDesktopSharingPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.screen.toggled');
APP.conference.toggleScreenSharing();
},
shortcutDescription: 'keyboardShortcuts.toggleScreensharing',
content: 'Share screen',
i18n: '[content]toolbar.sharescreen'
},
'security': {
id: 'toolbar_button_security'
},
'invite': {
id: 'toolbar_button_link',
className: 'button icon-link',
content: 'Invite others',
i18n: '[content]toolbar.invite'
},
'chat': {
id: 'toolbar_button_chat',
shortcut: 'C',
shortcutAttr: 'toggleChatPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.chat.toggled');
APP.UI.toggleChat();
},
shortcutDescription: 'keyboardShortcuts.toggleChat',
sideContainerId: 'chat_container'
},
'contacts': {
id: 'toolbar_contact_list',
sideContainerId: 'contacts_container'
},
'profile': {
id: 'toolbar_button_profile',
sideContainerId: 'profile_container'
},
'etherpad': {
id: 'toolbar_button_etherpad'
},
'fullscreen': {
id: 'toolbar_button_fullScreen',
className: "button icon-full-screen",
shortcut: 'F',
shortcutAttr: 'toggleFullscreenPopover',
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent('shortcut.fullscreen.toggled');
APP.UI.toggleFullScreen();
},
shortcutDescription: "keyboardShortcuts.toggleChat",
content: "Enter / Exit Full Screen",
i18n: "[content]toolbar.fullscreen"
},
'settings': {
id: 'toolbar_button_settings',
sideContainerId: "settings_container"
},
'hangup': {
id: 'toolbar_button_hangup',
className: "button icon-hangup",
content: "Hang Up",
i18n: "[content]toolbar.hangup"
},
'filmstrip': {
id: 'toolbar_film_strip',
shortcut: "F",
shortcutAttr: "filmstripPopover",
shortcutFunc: function() {
JitsiMeetJS.analytics.sendEvent("shortcut.film.toggled");
APP.UI.toggleFilmStrip();
},
shortcutDescription: "keyboardShortcuts.toggleFilmstrip"
}
};
function dialpadButtonClicked() {
@@ -194,15 +320,44 @@ const Toolbar = {
emitter = eventEmitter;
// The toolbar is enabled by default.
this.enabled = true;
this.toolbarSelector = $("#header");
this.toolbarSelector = $("#mainToolbarContainer");
this.extendedToolbarSelector = $("#extendedToolbar");
this._initMainToolbarButtons();
UIUtil.hideDisabledButtons(defaultToolbarButtons);
Object.keys(defaultToolbarButtons).forEach(
id => {
if (UIUtil.isButtonEnabled(id)) {
var button = defaultToolbarButtons[id];
if (button.shortcut)
APP.keyboardshortcut.registerShortcut(
button.shortcut,
button.shortcutAttr,
button.shortcutFunc,
button.shortcutDescription
);
}
}
);
Object.keys(buttonHandlers).forEach(
buttonId => $(`#${buttonId}`).click(function(event) {
!$(this).prop('disabled') && buttonHandlers[buttonId](event);
})
);
APP.UI.addListener(UIEvents.SIDE_TOOLBAR_CONTAINER_TOGGLED,
function(containerId, isVisible) {
Toolbar._handleSideToolbarContainerToggled( containerId,
isVisible);
});
if(!APP.tokenData.isGuest) {
$("#toolbar_button_profile").addClass("unclickable");
}
},
/**
* Enables / disables the toolbar.
@@ -243,7 +398,7 @@ const Toolbar = {
*/
unlockLockButton () {
if ($("#toolbar_button_security").hasClass("icon-security-locked"))
UIUtil.buttonClick("#toolbar_button_security",
UIUtil.buttonClick("toolbar_button_security",
"icon-security icon-security-locked");
},
@@ -252,7 +407,7 @@ const Toolbar = {
*/
lockLockButton () {
if ($("#toolbar_button_security").hasClass("icon-security"))
UIUtil.buttonClick("#toolbar_button_security",
UIUtil.buttonClick("toolbar_button_security",
"icon-security icon-security-locked");
},
@@ -428,16 +583,24 @@ const Toolbar = {
/**
* Indicates if the toolbar is currently hovered.
* @return {true} if the toolbar is currently hovered, {false} otherwise
* @return {boolean} true if the toolbar is currently hovered,
* false otherwise
*/
isHovered() {
var hovered = false;
this.toolbarSelector.find('*').each(function () {
let id = $(this).attr('id');
if ($(`#${id}:hover`).length > 0) {
return true;
hovered = true;
// break each
return false;
}
});
if ($("#bottomToolbar:hover").length > 0) {
if (hovered)
return true;
if ($("#bottomToolbar:hover").length > 0
|| $("#extendedToolbar:hover").length > 0
|| SideContainerToggler.isHovered()) {
return true;
}
return false;
@@ -448,7 +611,7 @@ const Toolbar = {
* @return <tt>true</tt> if currently visible, <tt>false</tt> - otherwise
*/
isVisible() {
return this.toolbarSelector.is(":visible");
return this.toolbarSelector.hasClass("slideInY");
},
/**
@@ -456,8 +619,17 @@ const Toolbar = {
* parameter.
*/
hide() {
this.toolbarSelector.hide(
"slide", { direction: "up", duration: 300});
this.toolbarSelector.toggleClass("slideInY").toggleClass("slideOutY");
let slideInAnimation = (SideContainerToggler.isVisible)
? "slideInExtX"
: "slideInX";
let slideOutAnimation = (SideContainerToggler.isVisible)
? "slideOutExtX"
: "slideOutX";
this.extendedToolbarSelector.toggleClass(slideInAnimation)
.toggleClass(slideOutAnimation);
},
/**
@@ -465,8 +637,117 @@ const Toolbar = {
* parameter.
*/
show() {
this.toolbarSelector.show(
"slide", { direction: "up", duration: 300});
if (this.toolbarSelector.hasClass("slideOutY"))
this.toolbarSelector.toggleClass("slideOutY");
let slideInAnimation = (SideContainerToggler.isVisible)
? "slideInExtX"
: "slideInX";
let slideOutAnimation = (SideContainerToggler.isVisible)
? "slideOutExtX"
: "slideOutX";
if (this.extendedToolbarSelector.hasClass(slideOutAnimation))
this.extendedToolbarSelector.toggleClass(slideOutAnimation);
this.toolbarSelector.toggleClass("slideInY");
this.extendedToolbarSelector.toggleClass(slideInAnimation);
},
registerClickListeners(listener) {
$('#mainToolbarContainer').click(listener);
$("#extendedToolbar").click(listener);
},
/**
* Handles the side toolbar toggle.
*/
_handleSideToolbarContainerToggled(containerId, isVisible) {
Object.keys(defaultToolbarButtons).forEach(
id => {
if (!UIUtil.isButtonEnabled(id))
return;
var button = defaultToolbarButtons[id];
if (button.sideContainerId
&& button.sideContainerId === containerId) {
UIUtil.buttonClick(button.id, "selected");
return;
}
}
);
},
/**
* TODO: Fix mic popups
* <a class="button icon-microphone" id="toolbar_button_mute" data-container="body" data-toggle="popover" data-placement="bottom" shortcut="mutePopover" data-i18n="[content]toolbar.mute" content="Mute / Unmute">
* <ul id="micMutedPopup" class="loginmenu">
* <li data-i18n="[html]toolbar.micMutedPopup"></li>
* </ul>
* <ul id="unableToUnmutePopup" class="loginmenu">
* <li data-i18n="[html]toolbar.unableToUnmutePopup"></li>
* </ul>
* </a>
*/
_initMainToolbarButtons() {
interfaceConfig.MAIN_TOOLBAR_BUTTONS.forEach((value, index) => {
if (value && value in defaultToolbarButtons) {
let button = defaultToolbarButtons[value];
this._addMainToolbarButton(
button,
(index === 0),
(index === interfaceConfig.MAIN_TOOLBAR_BUTTONS.length -1));
}
});
},
/**
* Adds the given button to the main (top) toolbar.
*
* @param {Object} the button to add.
* @param {boolean} isFirst indicates if this is the first button in the
* toolbar
* @param {boolean} isLast indicates if this is the last button in the
* toolbar
*/
_addMainToolbarButton(button, isFirst, isLast) {
let buttonElement = document.createElement("a");
if (button.className)
buttonElement.className = button.className
+ ((isFirst) ? " first" : "")
+ ((isLast) ? " last" : "");
buttonElement.id = button.id;
if (button.shortcutAttr)
buttonElement.setAttribute("shortcut", button.shortcutAttr);
if (button.content)
buttonElement.setAttribute("content", button.content);
if (button.i18n)
buttonElement.setAttribute("data-i18n", button.i18n);
buttonElement.setAttribute("data-container", "body");
buttonElement.setAttribute("data-toggle", "popover");
buttonElement.setAttribute("data-placement", "bottom");
this._addPopups(buttonElement, button.popups);
document.getElementById("mainToolbar")
.appendChild(buttonElement);
},
_addPopups(buttonElement, popups = []) {
popups.forEach((popup) => {
let popupElement = document.createElement("ul");
popupElement.id = popup.id;
popupElement.className = popup.className;
let liElement = document.createElement("li");
liElement.setAttribute("data-i18n", popup.dataAttr);
popupElement.appendChild(liElement);
buttonElement.appendChild(popupElement);
});
}
};

View File

@@ -1,8 +1,8 @@
/* global APP, config, $, interfaceConfig */
import UIUtil from '../util/UIUtil';
import BottomToolbar from './BottomToolbar';
import Toolbar from './Toolbar';
import SideContainerToggler from "../side_pannels/SideContainerToggler";
import FilmStrip from '../videolayout/FilmStrip.js';
let toolbarTimeoutObject;
@@ -23,8 +23,11 @@ function showDesktopSharingButton() {
/**
* Hides the toolbar.
*
* @param force {true} to force the hiding of the toolbar without caring about
* the extended toolbar side panels.
*/
function hideToolbar() {
function hideToolbar(force) {
if (alwaysVisibleToolbar) {
return;
}
@@ -32,14 +35,11 @@ function hideToolbar() {
clearTimeout(toolbarTimeoutObject);
toolbarTimeoutObject = null;
if (Toolbar.isHovered()) {
if (Toolbar.isHovered() || APP.UI.isRingOverlayVisible()) {
toolbarTimeoutObject = setTimeout(hideToolbar, toolbarTimeout);
} else {
} else if (!SideContainerToggler.isVisible() || force) {
Toolbar.hide();
$('#subject').animate({top: "-=40"}, 300);
if (!FilmStrip.isFilmStripVisible()) {
BottomToolbar.hide(true);
}
}
}
@@ -49,7 +49,26 @@ const ToolbarToggler = {
*/
init() {
alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
this._registerWindowClickListeners();
},
/**
* Registers click listeners handling the show and hode of toolbars when
* user clicks outside of toolbar area.
*/
_registerWindowClickListeners() {
$(window).click(function() {
(Toolbar.isEnabled() && Toolbar.isVisible())
? hideToolbar(true)
: this.showToolbar();
}.bind(this));
Toolbar.registerClickListeners(function(event){
event.stopPropagation();
});
},
/**
* Sets the value of alwaysVisibleToolbar variable.
* @param value {boolean} the new value of alwaysVisibleToolbar variable
@@ -57,12 +76,14 @@ const ToolbarToggler = {
setAlwaysVisibleToolbar(value) {
alwaysVisibleToolbar = value;
},
/**
* Resets the value of alwaysVisibleToolbar variable to the default one.
*/
resetAlwaysVisibleToolbar() {
alwaysVisibleToolbar = (config.alwaysVisibleToolbar === true);
},
/**
* Shows the main toolbar.
*/
@@ -78,11 +99,6 @@ const ToolbarToggler = {
updateTimeout = true;
}
if (BottomToolbar.isEnabled() && !BottomToolbar.isVisible()) {
BottomToolbar.show(true);
updateTimeout = true;
}
if (updateTimeout) {
if (toolbarTimeoutObject) {
clearTimeout(toolbarTimeoutObject);

View File

@@ -258,7 +258,8 @@ var messageHandler = {
notify: function(displayName, displayNameKey, cls, messageKey,
messageArguments, options) {
if(!notificationsEnabled)
// If we're in ringing state we skip all toaster notifications.
if(!notificationsEnabled || APP.UI.isRingOverlayVisible())
return;
var displayNameSpan = '<span class="nickname" ';

View File

@@ -5,31 +5,12 @@
*/
var UIUtil = {
/**
* Returns the size of the side panel.
*/
getSidePanelSize () {
var availableHeight = window.innerHeight;
var availableWidth = window.innerWidth;
var panelWidth = 200;
if (availableWidth * 0.2 < 200) {
panelWidth = availableWidth * 0.2;
}
return [panelWidth, availableHeight];
},
/**
* Returns the available video width.
*/
getAvailableVideoWidth: function (isSidePanelVisible) {
getAvailableVideoWidth: function () {
let rightPanelWidth = 0;
if (isSidePanelVisible) {
rightPanelWidth = UIUtil.getSidePanelSize()[0];
}
return window.innerWidth - rightPanelWidth;
},
@@ -37,7 +18,8 @@
* Changes the style class of the element given by id.
*/
buttonClick: function(id, classname) {
$(id).toggleClass(classname); // add the class to the clicked element
// add the class to the clicked element
$("#" + id).toggleClass(classname);
},
/**
* Returns the text width for the given element.
@@ -122,15 +104,57 @@
}
},
/**
* Indicates if a toolbar button is enabled.
* @param name the name of the setting section as defined in
* interface_config.js and Toolbar.js
* @returns {boolean} {true} to indicate that the given toolbar button
* is enabled, {false} - otherwise
*/
isButtonEnabled: function (name) {
return interfaceConfig.TOOLBAR_BUTTONS.indexOf(name) !== -1;
},
/**
* Indicates if the setting section is enabled.
*
* @param name the name of the setting section as defined in
* interface_config.js and SettingsMenu.js
* @returns {boolean} {true} to indicate that the given setting section
* is enabled, {false} - otherwise
*/
isSettingEnabled: function (name) {
return interfaceConfig.SETTINGS_SECTIONS.indexOf(name) !== -1;
},
/**
* Shows the element given by id.
*
* @param {String} the identifier of the element to show
*/
showElement(id) {
if ($("#"+id).hasClass("hide"))
$("#"+id).removeClass("hide");
$("#"+id).addClass("show");
},
/**
* Hides the element given by id.
*
* @param {String} the identifier of the element to hide
*/
hideElement(id) {
if ($("#"+id).hasClass("show"))
$("#"+id).removeClass("show");
$("#"+id).addClass("hide");
},
hideDisabledButtons: function (mappings) {
var selector = Object.keys(mappings)
.map(function (buttonName) {
return UIUtil.isButtonEnabled(buttonName)
? null : mappings[buttonName]; })
? null : "#" + mappings[buttonName].id; })
.filter(function (item) { return item; })
.join(',');
$(selector).hide();
@@ -138,7 +162,7 @@
redirect (url) {
window.location.href = url;
},
},
isFullScreen () {
return document.fullScreen

View File

@@ -68,10 +68,8 @@ const FilmStrip = {
/**
* Calculates the thumbnail size.
* @param videoAreaAvailableWidth the currently available video area width
* that we want to take into account when calculating the film strip width.
*/
calculateThumbnailSize (isSideBarVisible) {
calculateThumbnailSize () {
let availableHeight = interfaceConfig.FILM_STRIP_MAX_HEIGHT;
let numvids = this.getThumbs(true).length;
@@ -84,7 +82,7 @@ const FilmStrip = {
* film strip size hasn't been updated yet, but it will be.
*/
let videoAreaAvailableWidth
= UIUtil.getAvailableVideoWidth(isSideBarVisible)
= UIUtil.getAvailableVideoWidth()
- UIUtil.parseCssInt(this.filmStrip.css('right'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('paddingLeft'), 10)
- UIUtil.parseCssInt(this.filmStrip.css('paddingRight'), 10)

View File

@@ -317,6 +317,15 @@ class VideoContainer extends LargeContainer {
* @param {boolean} show
*/
showAvatar (show) {
// TO FIX: Video background need to be black, so that we don't have a
// flickering effect when scrolling between videos and have the screen
// move to grey before going back to video. Avatars though can have the
// default background set.
// In order to fix this code we need to introduce video background or
// find a workaround for the video flickering.
$("#largeVideoContainer").css("background",
(show) ? interfaceConfig.DEFAULT_BACKGROUND : "#000");
this.$avatar.css("visibility", show ? "visible" : "hidden");
}
@@ -536,11 +545,10 @@ export default class LargeVideoManager {
}
/**
* Update container size optionally taking side bar size into account.
* @param {boolean} isSideBarVisible if side bar is visible.
* Update container size.
*/
updateContainerSize (isSideBarVisible) {
this.width = UIUtil.getAvailableVideoWidth(isSideBarVisible);
updateContainerSize () {
this.width = UIUtil.getAvailableVideoWidth();
this.height = window.innerHeight;
}

View File

@@ -21,7 +21,14 @@ function LocalVideo(VideoLayout, emitter) {
return APP.conference.getMyUserId();
}
});
this.initBrowserSpecificProperties();
SmallVideo.call(this, VideoLayout);
// Set default display name.
this.setDisplayName();
this.createConnectionIndicator();
}
LocalVideo.prototype = Object.create(SmallVideo.prototype);
@@ -38,7 +45,7 @@ function createEditDisplayNameButton() {
UIUtil.setTooltip(editButton,
"videothumbnail.editnickname",
"top");
editButton.innerHTML = '<i class="fa fa-pencil"></i>';
editButton.innerHTML = '<i class="icon-edit"></i>';
return editButton;
}

View File

@@ -13,6 +13,7 @@ function RemoteVideo(id, VideoLayout, emitter) {
this.emitter = emitter;
this.videoSpanId = `participant_${id}`;
SmallVideo.call(this, VideoLayout);
this.hasRemoteVideoMenu = false;
this.addRemoteVideoContainer();
this.connectionIndicator = new ConnectionIndicator(this, id);
this.setDisplayName();
@@ -27,6 +28,9 @@ RemoteVideo.prototype.constructor = RemoteVideo;
RemoteVideo.prototype.addRemoteVideoContainer = function() {
this.container = RemoteVideo.createContainer(this.videoSpanId);
this.initBrowserSpecificProperties();
if (APP.conference.isModerator) {
this.addRemoteVideoMenu();
}
@@ -90,7 +94,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
APP.translation.translateString("videothumbnail.muted") +
"</div>";
muteLinkItem.id = "muteLinkItem";
muteLinkItem.id = "mutelink_" + this.id;
if (this.isMuted) {
muteLinkItem.innerHTML = mutedHTML;
@@ -102,7 +106,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
}
// Delegate event to the document.
$(document).on("click", ".mutelink", function(){
$(document).on("click", "#mutelink_" + this.id, function(){
if (this.isMuted)
return;
@@ -115,7 +119,7 @@ RemoteVideo.prototype._generatePopupContent = function () {
muteMenuItem.appendChild(muteLinkItem);
popupmenuElement.appendChild(muteMenuItem);
var ejectIndicator = "<i style='float:left;' class='fa fa-eject'></i>";
var ejectIndicator = "<i style='float:left;' class='icon-kick'></i>";
var ejectMenuItem = document.createElement('li');
var ejectLinkItem = document.createElement('a');
@@ -127,8 +131,9 @@ RemoteVideo.prototype._generatePopupContent = function () {
ejectLinkItem.className = 'ejectlink';
ejectLinkItem.innerHTML = ejectIndicator + ' ' + ejectText;
ejectLinkItem.id = "ejectlink_" + this.id;
$(document).on("click", ".ejectlink", function(){
$(document).on("click", "#ejectlink_" + this.id, function(){
this.emitter.emit(UIEvents.USER_KICKED, this.id);
this.popover.forceHide();
}.bind(this));
@@ -175,6 +180,7 @@ if (!interfaceConfig.filmStripOnly) {
spanElement.appendChild(menuElement);
this._initPopupMenu(this._generatePopupContent());
this.hasRemoteVideoMenu = true;
};
} else {
@@ -416,6 +422,7 @@ RemoteVideo.prototype.removeRemoteVideoMenu = function() {
if (menuSpan.length) {
this.popover.forceHide();
menuSpan.remove();
this.hasRemoteVideoMenu = false;
}
};

View File

@@ -3,7 +3,6 @@
import Avatar from "../avatar/Avatar";
import UIUtil from "../util/UIUtil";
import UIEvents from "../../../service/UI/UIEvents";
import AnalyticsAdapter from '../../statistics/AnalyticsAdapter';
const RTCUIHelper = JitsiMeetJS.util.RTCUIHelper;
@@ -310,7 +309,7 @@ SmallVideo.prototype.createModeratorIndicatorElement = function () {
if (indicatorSpan.children().length !== 0)
return;
var moderatorIndicator = document.createElement('i');
moderatorIndicator.className = 'fa fa-star';
moderatorIndicator.className = 'icon-star';
indicatorSpan[0].appendChild(moderatorIndicator);
UIUtil.setTooltip(indicatorSpan[0],
@@ -528,4 +527,26 @@ SmallVideo.prototype.waitForResolutionChange = function() {
};
};
/**
* Initalizes any browser specific properties. Currently sets the overflow
* property for Qt browsers on Windows to hidden, thus fixing the following
* problem:
* Some browsers don't have full support of the object-fit property for the
* video element and when we set video object-fit to "cover" the video
* actually overflows the boundaries of its container, so it's important
* to indicate that the "overflow" should be hidden.
*
* Setting this property for all browsers will result in broken audio levels,
* which makes this a temporary solution, before reworking audio levels.
*/
SmallVideo.prototype.initBrowserSpecificProperties = function() {
var userAgent = window.navigator.userAgent;
if (userAgent.indexOf("QtWebEngine") > -1
&& (userAgent.indexOf("Windows") > -1
|| userAgent.indexOf("Linux") > -1)) {
$('#' + this.videoSpanId).css("overflow", "hidden");
}
};
export default SmallVideo;

View File

@@ -3,7 +3,6 @@
import AudioLevels from "../audio_levels/AudioLevels";
import Avatar from "../avatar/Avatar";
import BottomToolbar from "../toolbars/BottomToolbar";
import FilmStrip from "./FilmStrip";
import UIEvents from "../../../service/UI/UIEvents";
import UIUtil from "../util/UIUtil";
@@ -12,7 +11,6 @@ import RemoteVideo from "./RemoteVideo";
import LargeVideoManager, {VIDEO_CONTAINER_TYPE} from "./LargeVideo";
import {SHARED_VIDEO_CONTAINER_TYPE} from '../shared_video/SharedVideo';
import LocalVideo from "./LocalVideo";
import PanelToggler from "../side_pannels/SidePanelToggler";
const RTCUIUtil = JitsiMeetJS.util.RTCUIHelper;
@@ -107,18 +105,20 @@ var VideoLayout = {
localVideoThumbnail.setVideoType(VIDEO_CONTAINER_TYPE);
// if we do not resize the thumbs here, if there is no video device
// the local video thumb maybe one pixel
this.resizeThumbnails(false, true, false);
let {thumbWidth, thumbHeight}
= this.resizeThumbnails(false, true, false);
AudioLevels.updateAudioLevelCanvas(null, thumbWidth, thumbHeight);
emitter.addListener(UIEvents.CONTACT_CLICKED, onContactClicked);
this.lastNCount = config.channelLastN;
},
initLargeVideo (isSideBarVisible) {
initLargeVideo () {
largeVideo = new LargeVideoManager();
if(localFlipX) {
largeVideo.onLocalFlipXChange(localFlipX);
}
largeVideo.updateContainerSize(isSideBarVisible);
largeVideo.updateContainerSize();
AudioLevels.init();
},
@@ -158,16 +158,9 @@ var VideoLayout = {
},
changeLocalVideo (stream) {
// Set default display name.
localVideoThumbnail.setDisplayName();
localVideoThumbnail.createConnectionIndicator();
let localId = APP.conference.getMyUserId();
this.onVideoTypeChanged(localId, stream.videoType);
let {thumbWidth, thumbHeight} = this.resizeThumbnails(false, true);
AudioLevels.updateAudioLevelCanvas(null, thumbWidth, thumbHeight);
if (!stream.isMuted()) {
localVideoThumbnail.changeVideo(stream);
}
@@ -472,7 +465,7 @@ var VideoLayout = {
remoteVideo.createModeratorIndicatorElement();
} else if (isModerator) {
// We are moderator, but user is not - add menu
if ($(`#remote_popupmenu_${id}`).length <= 0) {
if(!remoteVideo.hasRemoteVideoMenu) {
remoteVideo.addRemoteVideoMenu();
}
}
@@ -492,21 +485,16 @@ var VideoLayout = {
*/
resizeThumbnails ( animate = false,
forceUpdate = false,
isSideBarVisible = null,
onComplete = null) {
isSideBarVisible
= (isSideBarVisible !== null)
? isSideBarVisible : PanelToggler.isVisible();
let {thumbWidth, thumbHeight}
= FilmStrip.calculateThumbnailSize(isSideBarVisible);
= FilmStrip.calculateThumbnailSize();
$('.userAvatar').css('left', (thumbWidth - thumbHeight) / 2);
FilmStrip.resizeThumbnails(thumbWidth, thumbHeight,
animate, forceUpdate)
.then(function () {
BottomToolbar.resizeToolbar(thumbWidth, thumbHeight);
AudioLevels.updateCanvasSize(thumbWidth, thumbHeight);
if (onComplete && typeof onComplete === "function")
onComplete();
@@ -896,31 +884,29 @@ var VideoLayout = {
/**
* Resizes the video area.
*
* @param isSideBarVisible indicates if the side bar is currently visible
* @param forceUpdate indicates that hidden thumbnails will be shown
* @param completeFunction a function to be called when the video area is
* resized.
*/
resizeVideoArea (isSideBarVisible,
forceUpdate = false,
resizeVideoArea (forceUpdate = false,
animate = false,
completeFunction = null) {
if (largeVideo) {
largeVideo.updateContainerSize(isSideBarVisible);
largeVideo.updateContainerSize();
largeVideo.resize(animate);
}
// Calculate available width and height.
let availableHeight = window.innerHeight;
let availableWidth = UIUtil.getAvailableVideoWidth(isSideBarVisible);
let availableWidth = UIUtil.getAvailableVideoWidth();
if (availableWidth < 0 || availableHeight < 0) {
return;
}
// Resize the thumbnails first.
this.resizeThumbnails(false, forceUpdate, isSideBarVisible);
this.resizeThumbnails(false, forceUpdate);
// Resize the video area element.
$('#videospace').animate({

View File

@@ -36,6 +36,12 @@ var URLProcessor = {
if (key.indexOf("config.") === 0) {
confObj = configJSON.config;
confKey = key.substr("config.".length);
// prevent passing some parameters which can inject scripts
if (confKey === 'analyticsScriptUrl'
|| confKey === 'callStatsCustomScriptUrl')
continue;
} else if (key.indexOf("interfaceConfig.") === 0) {
confObj = configJSON.interfaceConfig;
confKey = key.substr("interfaceConfig.".length);

View File

@@ -1,92 +1,62 @@
/* global APP, $ */
import AnalyticsAdapter from '../statistics/AnalyticsAdapter';
//maps keycode to character, id of popover for given function and function
var shortcuts = {};
function initShortcutHandlers() {
shortcuts = {
"ESCAPE": {
character: "Esc",
function: function() {
APP.UI.showKeyboardShortcutsPanel(false);
}
},
"C": {
character: "C",
id: "toggleChatPopover",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.chat.toggled');
APP.UI.toggleChat();
}
},
"D": {
character: "D",
id: "toggleDesktopSharingPopover",
function: function () {
AnalyticsAdapter.sendEvent('shortcut.screen.toggled');
APP.conference.toggleScreenSharing();
}
},
"F": {
character: "F",
id: "filmstripPopover",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.film.toggled');
APP.UI.toggleFilmStrip();
}
},
"M": {
character: "M",
id: "mutePopover",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.audiomute.toggled');
APP.conference.toggleAudioMuted();
}
},
"R": {
character: "R",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.raisedhand.toggled');
APP.conference.maybeToggleRaisedHand();
}
/* global APP, $, JitsiMeetJS */
},
"T": {
character: "T",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.talk.clicked');
APP.conference.muteAudio(true);
}
},
"V": {
character: "V",
id: "toggleVideoPopover",
function: function() {
AnalyticsAdapter.sendEvent('shortcut.videomute.toggled');
APP.conference.toggleVideoMuted();
}
},
"?": {
character: "?",
function: function(e) {
AnalyticsAdapter.sendEvent('shortcut.shortcut.help');
APP.UI.toggleKeyboardShortcutsPanel();
}
}
};
/**
* Initialise global shortcuts.
* Global shortcuts are shortcuts for features that don't have a button or
* link associated with the action. In other words they represent actions
* triggered _only_ with a shortcut.
*/
function initGlobalShortcuts() {
KeyboardShortcut.registerShortcut("ESCAPE", null, function() {
APP.UI.showKeyboardShortcutsPanel(false);
});
KeyboardShortcut.registerShortcut("?", null, function() {
JitsiMeetJS.analytics.sendEvent("shortcut.shortcut.help");
APP.UI.toggleKeyboardShortcutsPanel();
}, "keyboardShortcuts.toggleShortcuts");
KeyboardShortcut.registerShortcut("R", null, function() {
JitsiMeetJS.analytics.sendEvent("shortcut.raisedhand.toggled");
APP.conference.maybeToggleRaisedHand();
}, "keyboardShortcuts.raiseHand");
KeyboardShortcut.registerShortcut("T", null, function() {
JitsiMeetJS.analytics.sendEvent("shortcut.talk.clicked");
APP.conference.muteAudio(true);
}, "keyboardShortcuts.pushToTalk");
/**
* FIXME: Currently focus keys are directly implemented below in onkeyup.
* They should be moved to the SmallVideo instead.
*/
KeyboardShortcut._addShortcutToHelp("0", "keyboardShortcuts.focusLocal");
KeyboardShortcut._addShortcutToHelp("1-9", "keyboardShortcuts.focusRemote");
}
/**
* Map of shortcuts. When a shortcut is registered it enters the mapping.
* @type {{}}
*/
let _shortcuts = {};
/**
* Maps keycode to character, id of popover for given function and function.
*/
var KeyboardShortcut = {
init: function () {
initShortcutHandlers();
initGlobalShortcuts();
var self = this;
window.onkeyup = function(e) {
var key = self.getKeyboardKey(e).toUpperCase();
var key = self._getKeyboardKey(e).toUpperCase();
var num = parseInt(key, 10);
if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) {
if (shortcuts.hasOwnProperty(key)) {
shortcuts[key].function(e);
if (_shortcuts.hasOwnProperty(key)) {
_shortcuts[key].function(e);
}
else if (!isNaN(num) && num >= 0 && num <= 9) {
APP.UI.clickOnVideo(num + 1);
@@ -102,7 +72,7 @@ var KeyboardShortcut = {
if(!($(":focus").is("input[type=text]") ||
$(":focus").is("input[type=password]") ||
$(":focus").is("textarea"))) {
var key = self.getKeyboardKey(e).toUpperCase();
var key = self._getKeyboardKey(e).toUpperCase();
if(key === "T") {
if(APP.conference.isLocalAudioMuted())
APP.conference.muteAudio(false);
@@ -112,31 +82,75 @@ var KeyboardShortcut = {
$('body').popover({ selector: '[data-toggle=popover]',
trigger: 'click hover',
content: function() {
return this.getAttribute("content") +
self.getShortcut(this.getAttribute("shortcut"));
return this.getAttribute("content")
+ self._getShortcutTooltip(this.getAttribute("shortcut"));
}
});
},
/**
* Registers a new shortcut.
*
* @param id indicates the popover associated with the shortcut
* @returns {string} the keyboard shortcut used for the id given
* @param shortcutChar the shortcut character triggering the action
* @param shortcutAttr the "shortcut" html element attribute mappring an
* element to this shortcut and used to show the shortcut character on the
* element tooltip
* @param exec the function to be executed when the shortcut is pressed
* @param helpDescription the description of the shortcut that would appear
* in the help menu
*/
getShortcut: function (id) {
for (var key in shortcuts) {
if (shortcuts.hasOwnProperty(key)) {
if (shortcuts[key].id === id) {
return " (" + shortcuts[key].character + ")";
registerShortcut: function( shortcutChar,
shortcutAttr,
exec,
helpDescription) {
_shortcuts[shortcutChar] = {
character: shortcutChar,
shortcutAttr: shortcutAttr,
function: exec
};
if (helpDescription)
this._addShortcutToHelp(shortcutChar, helpDescription);
},
/**
* Unregisters a shortcut.
*
* @param shortcutChar unregisters the given shortcut, which means it will
* no longer be usable
*/
unregisterShortcut: function(shortcutChar) {
_shortcuts.remove(shortcutChar);
this._removeShortcutFromHelp(shortcutChar);
},
/**
* Returns the tooltip string for the given shortcut attribute.
*
* @param shortcutAttr indicates the popover associated with the shortcut
* @returns {string} the tooltip string to add to the given shortcut popover
* or an empty string if the shortcutAttr is null, an empty string or not
* found in the shortcut mapping
*/
_getShortcutTooltip: function (shortcutAttr) {
if (typeof shortcutAttr === "string" && shortcutAttr.length > 0) {
for (var key in _shortcuts) {
if (_shortcuts.hasOwnProperty(key)
&& _shortcuts[key].shortcutAttr
&& _shortcuts[key].shortcutAttr === shortcutAttr) {
return " (" + _shortcuts[key].character + ")";
}
}
}
return "";
},
/**
* @param e a KeyboardEvent
* @returns {string} e.key or something close if not supported
*/
getKeyboardKey: function (e) {
_getKeyboardKey: function (e) {
if (typeof e.key === "string") {
return e.key;
}
@@ -157,6 +171,57 @@ var KeyboardShortcut = {
} else {
return String.fromCharCode(e.which).toLowerCase();
}
},
/**
* Adds the given shortcut to the help dialog.
*
* @param shortcutChar the shortcut character
* @param shortcutDescriptionKey the description of the shortcut
* @private
*/
_addShortcutToHelp: function (shortcutChar, shortcutDescriptionKey) {
var listElement = document.createElement("li");
listElement.id = shortcutChar;
var spanElement = document.createElement("span");
spanElement.className = "item-action";
var kbdElement = document.createElement("kbd");
kbdElement.className = "regular-key";
kbdElement.innerHTML = shortcutChar;
spanElement.appendChild(kbdElement);
var descriptionElement = document.createElement("span");
descriptionElement.className = "item-description";
descriptionElement.setAttribute("data-i18n", shortcutDescriptionKey);
descriptionElement.innerHTML
= APP.translation.translateString(shortcutDescriptionKey);
listElement.appendChild(spanElement);
listElement.appendChild(descriptionElement);
var parentListElement
= document.getElementById("keyboard-shortcuts-list");
if (parentListElement)
parentListElement.appendChild(listElement);
},
/**
* Removes the list element corresponding to the given shortcut from the
* help dialog
* @private
*/
_removeShortcutFromHelp: function (shortcutChar) {
var parentListElement
= document.getElementById("keyboard-shortcuts-list");
var shortcutElement = document.getElementById(shortcutChar);
if (shortcutElement)
parentListElement.removeChild(shortcutElement);
}
};

View File

@@ -3,6 +3,7 @@
import UIUtil from '../UI/util/UIUtil';
let email = '';
let avatarId = '';
let displayName = '';
let language = null;
let cameraDeviceId = '';
@@ -35,7 +36,13 @@ if (supportsLocalStorage()) {
}
email = UIUtil.unescapeHtml(window.localStorage.email || '');
avatarUrl = UIUtil.unescapeHtml(window.localStorage.avatarUrl || '');
avatarId = UIUtil.unescapeHtml(window.localStorage.avatarId || '');
if (!avatarId) {
// if there is no avatar id, we generate a unique one and use it forever
avatarId = generateUniqueId();
window.localStorage.avatarId = avatarId;
}
localFlipX = JSON.parse(window.localStorage.localFlipX || true);
displayName = UIUtil.unescapeHtml(window.localStorage.displayname || '');
language = window.localStorage.language;
@@ -69,10 +76,13 @@ export default {
* Sets the local user display name and saves it to local storage
*
* @param {string} newDisplayName unescaped display name for the local user
* @param {boolean} disableLocalStore disables local store the display name
*/
setDisplayName (newDisplayName) {
setDisplayName (newDisplayName, disableLocalStore) {
displayName = newDisplayName;
window.localStorage.displayname = UIUtil.escapeHtml(displayName);
if (!disableLocalStore)
window.localStorage.displayname = UIUtil.escapeHtml(displayName);
},
/**
@@ -86,10 +96,13 @@ export default {
/**
* Sets new email for local user and saves it to the local storage.
* @param {string} newEmail new email for the local user
* @param {boolean} disableLocalStore disables local store the email
*/
setEmail: function (newEmail) {
setEmail: function (newEmail, disableLocalStore) {
email = newEmail;
window.localStorage.email = UIUtil.escapeHtml(newEmail);
if (!disableLocalStore)
window.localStorage.email = UIUtil.escapeHtml(newEmail);
},
/**
@@ -100,13 +113,20 @@ export default {
return email;
},
/**
* Returns avatar id of the local user.
* @returns {string} avatar id
*/
getAvatarId: function () {
return avatarId;
},
/**
* Sets new avatarUrl for local user and saves it to the local storage.
* @param {string} newAvatarUrl new avatarUrl for the local user
*/
setAvatarUrl: function (newAvatarUrl) {
avatarUrl = newAvatarUrl;
window.localStorage.avatarUrl = UIUtil.escapeHtml(newAvatarUrl);
},
/**
@@ -117,7 +137,6 @@ export default {
return avatarUrl;
},
getLanguage () {
return language;
},

View File

@@ -1,18 +0,0 @@
/* global config, JitsiMeetJS */
// Load the integration of a third-party analytics API such as Google Analytics.
// Since we cannot guarantee the quality of the third-party service (e.g. their
// server may take noticeably long time to respond), it is in our best interest
// (in the sense that the intergration of the analytics API is important to us
// but not enough to allow it to prevent people from joining a conference) to
// download the API asynchronously. Additionally, Google Analytics will download
// its implementation asynchronously anyway so it makes sense to append the
// loading on our side rather than prepend it.
if (config.disableThirdPartyRequests !== true) {
JitsiMeetJS.util.ScriptUtil.loadScript(
'analytics.js?v=1',
/* async */ true,
/* prepend */ false);
}
export default JitsiMeetJS.analytics;

View File

@@ -35,16 +35,18 @@
"jws": "*"
},
"devDependencies": {
"babel-polyfill": "*",
"babel-preset-es2015": "*",
"babelify": "*",
"browserify": "11.1.x",
"browserify-css": "^0.9.2",
"browserify-shim": "^3.8.10",
"clean-css": "*",
"exorcist": "*",
"jshint": "2.8.0",
"node-sass": "^3.8.0",
"precommit-hook": "3.0.0",
"uglify-js": "2.4.24",
"clean-css": "*",
"babelify": "*",
"babel-preset-es2015": "*",
"babel-polyfill": "*"
"uglify-js": "2.4.24"
},
"license": "Apache-2.0",
"scripts": {
@@ -57,13 +59,19 @@
"browserify": {
"transform": [
"browserify-shim",
["babelify", {
"ignore": "node_modules"
}]
"browserify-css",
[
"babelify",
{
"ignore": "node_modules"
}
]
]
},
"babel": {
"presets": ["es2015"]
"presets": [
"es2015"
]
},
"browser": {
"jquery": "./node_modules/jquery/dist/jquery.js",
@@ -102,10 +110,13 @@
"depends": "jquery:jQuery"
},
"jquery-contextmenu": {
"depends": "jquery:jQuery"
"depends": "jquery:jQuery"
},
"autosize": {
"depends": "jquery:jQuery"
},
"browserify-css": {
"autoInject": true
}
}
}

View File

@@ -1,10 +1,18 @@
-- Token authentication
-- Copyright (C) 2015 Atlassian
local generate_uuid = require "util.uuid".generate;
local new_sasl = require "util.sasl".new;
local sasl = require "util.sasl";
local basexx = require "basexx";
local have_async, async = pcall(require, "util.async");
local hex = require "util.hex";
local formdecode = require "util.http".formdecode;
local generate_uuid = require "util.uuid".generate;
local http = require "net.http";
local json = require "cjson";
local new_sasl = require "util.sasl".new;
local path = require "util.paths";
local sasl = require "util.sasl";
local sha256 = require "util.hashes".sha256;
local timer = require "util.timer";
local token_util = module:require "token/util";
-- define auth provider
@@ -14,9 +22,14 @@ local host = module.host;
local appId = module:get_option_string("app_id");
local appSecret = module:get_option_string("app_secret");
local asapKeyServer = module:get_option_string("asap_key_server");
local allowEmptyToken = module:get_option_boolean("allow_empty_token");
local disableRoomNameConstraints = module:get_option_boolean("disable_room_name_constraints");
-- TODO: Figure out a less arbitrary default cache size.
local cacheSize = module:get_option_number("jwt_pubkey_cache_size", 128);
local cache = require"util.cache".new(cacheSize);
if allowEmptyToken == true then
module:log("warn", "WARNING - empty tokens allowed");
end
@@ -26,8 +39,13 @@ if appId == nil then
return;
end
if appSecret == nil then
module:log("error", "'app_secret' must not be empty");
if appSecret == nil and asapKeyServer == nil then
module:log("error", "'app_secret' or 'asap_key_server' must be specified");
return;
end
if asapKeyServer and not have_async then
module:log("error", "requires a version of Prosody with util.async");
return;
end
@@ -38,7 +56,7 @@ module:hook("bosh-session", function(event)
if query ~= nil then
session.auth_token = query and formdecode(query).token or nil;
end
end)
end);
function provider.test_password(username, password)
return nil, "Password based auth not supported";
@@ -64,6 +82,61 @@ function provider.delete_user(username)
return nil;
end
local http_timeout = 30;
local http_headers = {
["User-Agent"] = "Prosody ("..prosody.version.."; "..prosody.platform..")"
};
function get_public_key(keyId)
local content = cache:get(keyId);
if content == nil then
-- If the key is not found in the cache.
module:log("debug", "Cache miss for key: "..keyId);
local code;
local wait, done = async.waiter();
local function cb(content_, code_, response_, request_)
content, code = content_, code_;
if code == 200 or code == 204 then
cache:set(keyId, content);
end
done();
end
local keyurl = path.join(asapKeyServer, hex.to(sha256(keyId))..'.pem');
module:log("debug", "Fetching public key from: "..keyurl);
-- We hash the key ID to work around some legacy behavior and make
-- deployment easier. It also helps prevent directory
-- traversal attacks (although path cleaning could have done this too).
local request = http.request(keyurl, {
headers = http_headers or {},
method = "GET"
}, cb);
-- TODO: Is the done() call racey? Can we cancel this if the request
-- succeedes?
local function cancel()
-- TODO: This check is racey. Not likely to be a problem, but we should
-- still stick a mutex on content / code at some point.
if code == nil then
http.destroy_request(request);
done();
end
end
timer.add_task(http_timeout, cancel);
wait();
if code == 200 or code == 204 then
return content;
end
else
-- If the key is in the cache, use it.
module:log("debug", "Cache hit for key: "..keyId);
return content;
end
return nil;
end
function provider.get_sasl_handler(session)
-- JWT token extracted from BOSH URL
local token = session.auth_token;
@@ -71,31 +144,41 @@ function provider.get_sasl_handler(session)
local function get_username_from_token(self, message)
if token == nil then
if allowEmptyToken == true then
if allowEmptyToken then
return true;
else
return false, "not-allowed", "token required";
end
end
-- here we check if 'room' claim exists
local room, roomErr = token_util.get_room_name(token, appSecret);
if room == nil and disableRoomNameConstraints ~= true then
if roomErr == nil then
roomErr = "'room' claim is missing";
end
return false, "not-allowed", roomErr;
local pubKey;
if asapKeyServer and session.auth_token ~= nil then
local dotFirst = session.auth_token:find("%.");
if not dotFirst then return nil, "Invalid token" end
local header = json.decode(basexx.from_url64(session.auth_token:sub(1,dotFirst-1)));
local kid = header["kid"];
if kid == nil then
return false, "not-allowed", "'kid' claim is missing";
end
pubKey = get_public_key(kid);
if pubKey == nil then
return false, "not-allowed", "could not obtain public key";
end
end
-- now verify the whole token
local result, msg
= token_util.verify_token(token, appId, appSecret, room, disableRoomNameConstraints);
if result == true then
-- Binds room name to the session which is later checked on MUC join
session.jitsi_meet_room = room;
return true
local claims, msg;
if asapKeyServer then
claims, msg = token_util.verify_token(token, appId, pubKey, disableRoomNameConstraints);
else
return false, "not-allowed", msg
claims, msg = token_util.verify_token(token, appId, appSecret, disableRoomNameConstraints);
end
if claims ~= nil then
-- Binds room name to the session which is later checked on MUC join
session.jitsi_meet_room = claims["room"];
return true;
else
return false, "not-allowed", msg;
end
end
@@ -114,10 +197,10 @@ local function anonymous(self, message)
self.username = username;
if result == true then
return "success"
return "success";
else
return "failure", err, msg
return "failure", err, msg;
end
end

View File

@@ -5,16 +5,7 @@ local jwt = require "luajwtjitsi";
local _M = {};
local function _get_room_name(token, appSecret)
local claims, err = jwt.decode(token, appSecret);
if claims ~= nil then
return claims["room"];
else
return nil, err;
end
end
local function _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
local function _verify_token(token, appId, appSecret, disableRoomNameConstraints)
local claims, err = jwt.decode(token, appSecret, true);
if claims == nil then
@@ -38,19 +29,12 @@ local function _verify_token(token, appId, appSecret, roomName, disableRoomNameC
if roomClaim == nil and disableRoomNameConstraints ~= true then
return nil, "'room' claim is missing";
end
if roomName ~= nil and roomName ~= roomClaim and disableRoomNameConstraints ~= true then
return nil, "Invalid room name('room' claim)";
end
return true;
return claims;
end
function _M.verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints)
return _verify_token(token, appId, appSecret, roomName, disableRoomNameConstraints);
end
function _M.get_room_name(token, appSecret)
return _get_room_name(token, appSecret);
function _M.verify_token(token, appId, appSecret, disableRoomNameConstraints)
return _verify_token(token, appId, appSecret, disableRoomNameConstraints);
end
return _M;

View File

@@ -14,10 +14,6 @@ export default {
* Notifies that local user changed email.
*/
EMAIL_CHANGED: "UI.email_changed",
/**
* Notifies that local user changed avatar url.
*/
AVATAR_URL_CHANGED: "UI.avatar_url_changed",
/**
* Notifies that "start muted" settings changed.
*/
@@ -41,6 +37,10 @@ export default {
TOGGLE_CHAT: "UI.toggle_chat",
TOGGLE_SETTINGS: "UI.toggle_settings",
TOGGLE_CONTACT_LIST: "UI.toggle_contact_list",
/**
* Notifies that the profile toolbar button has been clicked.
*/
TOGGLE_PROFILE: "UI.toggle_profile",
/**
* Notifies that a command to toggle the film strip has been issued. The
* event may optionally specify a {Boolean} (primitive) value to assign to
@@ -72,16 +72,38 @@ export default {
VIDEO_DEVICE_CHANGED: "UI.video_device_changed",
AUDIO_DEVICE_CHANGED: "UI.audio_device_changed",
AUDIO_OUTPUT_DEVICE_CHANGED: "UI.audio_output_device_changed",
/**
* Notifies interested listeners that the follow-me feature is enabled or
* disabled.
*/
FOLLOW_ME_ENABLED: "UI.follow_me_enabled",
/**
* Notifies that flipX property of the local video is changed.
*/
LOCAL_FLIPX_CHANGED: "UI.local_flipx_changed",
// An event which indicates that the resolution of a remote video has
// changed.
RESOLUTION_CHANGED: "UI.resolution_changed"
RESOLUTION_CHANGED: "UI.resolution_changed",
/**
* Notifies that the button "Go to webstore" is pressed on the dialog for
* external extension installation.
*/
OPEN_EXTENSION_STORE: "UI.open_extension_store",
/**
* Notifies that the button "Cancel" is pressed on the dialog for
* external extension installation.
*/
EXTERNAL_INSTALLATION_CANCELED: "UI.external_installation_canceled",
/**
* Notifies that the side toolbar container has been toggled. The actual
* event must contain the identifier of the container that has been toggled
* and information about toggle on or off.
*/
SIDE_TOOLBAR_CONTAINER_TOGGLED: "UI.side_container_toggled"
};