Browse code

added ChangeLog.txt Contributors.txt lang/en.json js/phone.js js/phone.min.js

DoubleBastionAdmin authored on11/05/2022 18:52:28
Showing5 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,19 @@
1
+
2
+======= CHANGELOG =======
3
+
4
+Versions:
5
+
6
+== 1.0.0 - 2022-1-27 ==
7
+* Initial Release.
8
+
9
+== 1.0.1 - 2022-3-26 ==
10
+* Fixed error message on logout.
11
+
12
+== 1.0.2 - 2022-3-28 ==
13
+* Small text changes in README.md.
14
+
15
+== 1.0.3 - 2022-4-9 ==
16
+* Fixed sign up bug and changed the app presentation text.
17
+
18
+== 1.0.4 - 2022-5-11 ==
19
+* Removed sign out warning related to Roundpin logout.
0 20
new file mode 100644
... ...
@@ -0,0 +1,18 @@
1
+
2
+========================== CONTRIBUTORS ==========================
3
+
4
+"Roundpin" includes large portions from "Browser Phone", developed
5
+by Conrad de Wet, and large portions from "Cyber Mega Phone 2k",
6
+developed by Digium, Inc. We once again thank them for their
7
+amazing work and for licensing their software in a way that
8
+promotes user freedom and collaboration. We also thank all
9
+the developers who created the other pieces of software that
10
+we included in "Roundpin". They are all mentioned, along with
11
+the appropriate copyright notice, in the LICENSE.txt file
12
+located in the root directory.
13
+
14
+
15
+Double Bastion LLC is the leader of the "Roundpin" project 
16
+and the main contributor. We'll list below all the other 
17
+developers who will contribute to "Roundpin".
18
+
0 19
new file mode 100644
... ...
@@ -0,0 +1,12550 @@
1
+/**
2
+ *  Copyright (C) 2021  Double Bastion LLC
3
+ *
4
+ *  This file is part of Roundpin, which is licensed under the
5
+ *  GNU Affero General Public License Version 3.0. The license terms
6
+ *  are detailed in the "LICENSE.txt" file located in the root directory.
7
+ *
8
+ *  This is a modified version of the original file
9
+ *  "phone.js", first modified in 2020.
10
+ *
11
+ *  The original "phone.js" file was written by Conrad de Wet.
12
+ *  We thank Conrad de Wet for his work and we list below
13
+ *  the copyright notice of the original "phone.js" file:
14
+
15
+/*
16
+-------------------------------------------------------------
17
+ Copyright (c) 2020  - Conrad de Wet - All Rights Reserved.
18
+=============================================================
19
+File: phone.js
20
+License: GNU Affero General Public License v3.0
21
+Version: 0.1.0
22
+Owner: Conrad de Wet
23
+Date: April 2020
24
+Git: https://github.com/InnovateAsterisk/Browser-Phone
25
+*/
26
+
27
+// Global Settings
28
+// ===============
29
+var enabledExtendedServices = false;
30
+var enabledGroupServices = false;
31
+
32
+// Lanaguage Packs (lang/xx.json)
33
+// ===============
34
+// Note: The following should correspond to files on your server.
35
+// eg: If you list "fr" then you need to add the file "fr.json".
36
+// Use the "en.json" as a template.
37
+// More specific lanagauge must be first. ie: "zh-hans" should be before "zh".
38
+// "en.json" is always loaded by default
39
+const availableLang = ["ja", "zh-hans", "zh", "ru", "tr", "nl"];
40
+
41
+// User Settings & Defaults
42
+// ========================
43
+var wssServer = getDbItem("wssServer", null);
44
+var profileUserID = getDbItem("profileUserID", null);
45
+var profileUser = getDbItem("profileUser", null);
46
+var profileName = getDbItem("profileName", null);
47
+var WebSocketPort = getDbItem("WebSocketPort", null);
48
+var ServerPath = getDbItem("ServerPath", null);
49
+var SipUsername = getDbItem("SipUsername", null);
50
+var SipPassword = getDbItem("SipPassword", null);
51
+var StunServer = getDbItem("StunServer", "");
52
+
53
+var TransportConnectionTimeout = parseInt(getDbItem("TransportConnectionTimeout", 15));        // The timeout in seconds for the initial connection to make on the web socket port
54
+var TransportReconnectionAttempts = parseInt(getDbItem("TransportReconnectionAttempts", 99));  // The number of times to attempt to reconnect to a WebSocket when the connection drops.
55
+var TransportReconnectionTimeout = parseInt(getDbItem("TransportReconnectionTimeout", 15));    // The time in seconds to wait between WebSocket reconnection attempts.
56
+
57
+var userAgentStr = getDbItem("UserAgentStr", "Roundpin (SipJS - 0.11.6)");          // Set this to whatever you want.
58
+var hostingPrefex = getDbItem("HostingPrefex", "");                                 // Use if hosting off root directiory. eg: "/phone/" or "/static/"
59
+var RegisterExpires = parseInt(getDbItem("RegisterExpires", 300));                  // Registration expiry time (in seconds)
60
+var WssInTransport = (getDbItem("WssInTransport", "1") == "1");                     // Set the transport parameter to wss when used in SIP URIs. (Required for Asterisk as it doesnt support Path)
61
+var IpInContact = (getDbItem("IpInContact", "1") == "1");                           // Set a random IP address as the host value in the Contact header field and Via sent-by parameter. (Suggested for Asterisk)
62
+var IceStunCheckTimeout = parseInt(getDbItem("IceStunCheckTimeout", 500));  // Set amount of time in milliseconds to wait for the ICE/STUN server
63
+var AutoAnswerEnabled = (getDbItem("AutoAnswerEnabled", "0") == "1");       // Automatically answers the phone when the call comes in, if you are not on a call already
64
+var DoNotDisturbEnabled = (getDbItem("DoNotDisturbEnabled", "0") == "1");   // Rejects any inbound call, while allowing outbound calls
65
+var CallWaitingEnabled = (getDbItem("CallWaitingEnabled", "1") == "1");     // Rejects any inbound call if you are on a call already.
66
+var RecordAllCalls = (getDbItem("RecordAllCalls", "0") == "1");             // Starts Call Recording when a call is established.
67
+var StartVideoFullScreen = (getDbItem("StartVideoFullScreen", "1") == "0"); // Starts a vdeo call in the full screen (browser screen, not dektop)
68
+var AutoGainControl = (getDbItem("AutoGainControl", "1") == "1");           // Attempts to adjust the microphone volume to a good audio level. (OS may be better at this)
69
+var EchoCancellation = (getDbItem("EchoCancellation", "1") == "1");         // Attemots to remove echo over the line.
70
+var NoiseSuppression = (getDbItem("NoiseSuppression", "1") == "1");         // Attempts to clear the call qulity of noise.
71
+var MirrorVideo = getDbItem("VideoOrientation", "rotateY(180deg)");         // Displays the self-preview in normal or mirror view, to better present the preview. 
72
+var maxFrameRate = getDbItem("FrameRate", "");                              // Suggests a frame rate to your webcam if possible.
73
+var videoHeight = getDbItem("VideoHeight", "");                             // Suggests a video height (and therefore picture quality) to your webcam.
74
+var videoAspectRatio = getDbItem("AspectRatio", "");                        // Suggests an aspect ratio (1:1 | 4:3 | 16:9) to your webcam.
75
+var NotificationsActive = (getDbItem("Notifications", "0") == "1");
76
+var StreamBuffer = parseInt(getDbItem("StreamBuffer", 50));                 // The amount of rows to buffer in the Buddy Stream
77
+var PosterJpegQuality = parseFloat(getDbItem("PosterJpegQuality", 0.6));    // The image quality of the Video Poster images
78
+var VideoResampleSize = getDbItem("VideoResampleSize", "HD");               // The resample size (height) to re-render video that gets presented (sent). (SD = ???x360 | HD = ???x720 | FHD = ???x1080)
79
+var RecordingVideoSize = getDbItem("RecordingVideoSize", "HD");             // The size/quality of the video track in the recodings (SD = 640x360 | HD = 1280x720 | FHD = 1920x1080)
80
+var RecordingVideoFps = parseInt(getDbItem("RecordingVideoFps", 12));       // The Frame Per Second of the Video Track recording
81
+var RecordingLayout = getDbItem("RecordingLayout", "them-pnp");             // The Layout of the Recording Video Track (side-by-side | us-pnp | them-pnp | us-only | them-only)
82
+var DidLength = parseInt(getDbItem("DidLength", 6));                        // DID length from which to decide if an incoming caller is a "contact" or an "extension".
83
+var MaxDidLength = parseInt(getDbItem("maximumNumberLength", 16));          // Maximum langth of any DID number including international dialled numbers.
84
+var DisplayDateFormat = getDbItem("DateFormat", "YYYY-MM-DD");              // The display format for all dates. https://momentjs.com/docs/#/displaying/
85
+var DisplayTimeFormat = getDbItem("TimeFormat", "h:mm:ss A");               // The display format for all times. https://momentjs.com/docs/#/displaying/
86
+var Language = getDbItem("Language", "auto");                               // Overrides the langauage selector or "automatic". Must be one of availableLang[]. If not defaults to en. Testing: zh-Hans-CN, zh-cmn-Hans-CN, zh-Hant, de, de-DE, en-US, fr, fr-FR, es-ES, sl-IT-nedis, hy-Latn-IT-arevela
87
+
88
+// Permission Settings
89
+var EnableTextMessaging = (getDbItem("EnableTextMessaging", "1") == "1");               // Enables the Text Messaging
90
+var DisableFreeDial = (getDbItem("DisableFreeDial", "0") == "1");                       // Removes the Dial icon in the profile area, users will need to add buddies in order to dial.
91
+var DisableBuddies = (getDbItem("DisableBuddies", "0") == "1");                         // Removes the Add Someone menu item and icon from the profile area. Buddies will still be created automatically. 
92
+var EnableTransfer = (getDbItem("EnableTransfer", "1") == "1");                         // Controls Transfering during a call
93
+var EnableConference = (getDbItem("EnableConference", "1") == "1");                     // Controls Conference during a call
94
+var AutoAnswerPolicy = getDbItem("AutoAnswerPolicy", "allow");                          // allow = user can choose | disabled = feature is disabled | enabled = feature is always on
95
+var DoNotDisturbPolicy = getDbItem("DoNotDisturbPolicy", "allow");                      // allow = user can choose | disabled = feature is disabled | enabled = feature is always on
96
+var CallWaitingPolicy = getDbItem("CallWaitingPolicy", "allow");                        // allow = user can choose | disabled = feature is disabled | enabled = feature is always on
97
+var CallRecordingPolicy = getDbItem("CallRecordingPolicy", "allow");                    // allow = user can choose | disabled = feature is disabled | enabled = feature is always on
98
+var EnableAccountSettings = (getDbItem("EnableAccountSettings", "1") == "1");           // Controls the Account tab in Settings
99
+var EnableAudioVideoSettings = (getDbItem("EnableAudioVideoSettings", "1") == "1");     // Controls the Audio & Video tab in Settings
100
+var EnableAppearanceSettings = (getDbItem("EnableAppearanceSettings", "1") == "1");     // Controls the Appearance tab in Settings
101
+var EnableChangeUserPasswordSettings = (getDbItem("EnableChangeUserPasswordSettings", "1") == "1"); // Controls the 'Change Password' tab in Settings
102
+var EnableChangeUserEmailSettings = (getDbItem("EnableChangeUserEmailSettings", "1") == "1");       // Controls the 'Change Email' tab in Settings
103
+var EnableCloseUserAccount = (getDbItem("EnableCloseUserAccount", "1") == "1");                     // Controls the 'Close Account' tab in Settings
104
+var EnableAlphanumericDial = (getDbItem("EnableAlphanumericDial", "1") == "1");                     // Allows calling /[^\da-zA-Z\*\#\+]/g default is /[^\d\*\#\+]/g
105
+var EnableVideoCalling = (getDbItem("EnableVideoCalling", "1") == "1");                             // Enables Video during a call
106
+var winVideoConf = null;
107
+var winVideoConfCheck = 0;
108
+
109
+// System variables
110
+// ================
111
+var localDB = window.localStorage;
112
+var userAgent = null;
113
+var voicemailSubs = null;
114
+var BlfSubs = [];
115
+var CanvasCollection = [];
116
+var Buddies = [];
117
+var isReRegister = false;
118
+var dhtmlxPopup = null;
119
+var selectedBuddy = null;
120
+var selectedLine = null;
121
+var alertObj = null;
122
+var confirmObj = null;
123
+var promptObj = null;
124
+var windowsCollection = null;
125
+var messagingCollection = null;
126
+var HasVideoDevice = false;
127
+var HasAudioDevice = false;
128
+var HasSpeakerDevice = false;
129
+var AudioinputDevices = [];
130
+var VideoinputDevices = [];
131
+var SpeakerDevices = [];
132
+var Lines = [];
133
+var lang = {};
134
+var audioBlobs = {};
135
+var newLineNumber = 0;
136
+var videoAudioCheck = 0;
137
+var RCLoginCheck = 0;
138
+var decSipPass = '';
139
+var currentChatPrivKey = '';
140
+var sendFileCheck = 0;
141
+var upFileName = '';
142
+var sendFileChatErr = '';
143
+var pubKeyCheck = 0;
144
+var splitMessage = {};
145
+
146
+// Utilities
147
+// =========
148
+function uID(){
149
+    return Date.now()+Math.floor(Math.random()*10000).toString(16).toUpperCase();
150
+}
151
+function utcDateNow(){
152
+    return moment().utc().format("YYYY-MM-DD HH:mm:ss UTC");
153
+}
154
+function getDbItem(itemIndex, defaultValue){
155
+    var localDB = window.localStorage;
156
+    if(localDB.getItem(itemIndex) != null) return localDB.getItem(itemIndex);
157
+    return defaultValue;
158
+}
159
+function getAudioSrcID(){
160
+    var id = localDB.getItem("AudioSrcId");
161
+    return (id != null)? id : "default";
162
+}
163
+function getAudioOutputID(){
164
+    var id = localDB.getItem("AudioOutputId");
165
+    return (id != null)? id : "default";
166
+}
167
+function getVideoSrcID(){
168
+    var id = localDB.getItem("VideoSrcId");
169
+    return (id != null)? id : "default";
170
+}
171
+function getRingerOutputID(){
172
+    var id = localDB.getItem("RingOutputId");
173
+    return (id != null)? id : "default";
174
+}
175
+function formatDuration(seconds){
176
+    var sec = Math.floor(parseFloat(seconds));
177
+    if(sec < 0){
178
+        return sec;
179
+    }
180
+    else if(sec >= 0 && sec < 60){
181
+         return sec + " " + ((sec != 1) ? lang.seconds_plural : lang.second_single);
182
+    } 
183
+    else if(sec >= 60 && sec < 60 * 60){ // greater then a minute and less then an hour
184
+        var duration = moment.duration(sec, 'seconds');
185
+        return duration.minutes() + " "+ ((duration.minutes() != 1) ? lang.minutes_plural: lang.minute_single) +" " + duration.seconds() +" "+ ((duration.seconds() != 1) ? lang.seconds_plural : lang.second_single);
186
+    } 
187
+    else if(sec >= 60 * 60 && sec < 24 * 60 * 60){ // greater than an hour and less then a day
188
+        var duration = moment.duration(sec, 'seconds');
189
+        return duration.hours() + " "+ ((duration.hours() != 1) ? lang.hours_plural : lang.hour_single) +" " + duration.minutes() + " "+ ((duration.minutes() != 1) ? lang.minutes_plural: lang.minute_single) +" " + duration.seconds() +" "+ ((duration.seconds() != 1) ? lang.seconds_plural : lang.second_single);
190
+    } 
191
+    //  Otherwise.. this is just too long
192
+}
193
+function formatShortDuration(seconds) {
194
+    var sec = Math.floor(parseFloat(seconds));
195
+    if(sec < 0){
196
+        return sec;
197
+    } 
198
+    else if(sec >= 0 && sec < 60){
199
+        return "00:"+ ((sec > 9)? sec : "0"+sec );
200
+    } 
201
+    else if(sec >= 60 && sec < 60 * 60){ // greater then a minute and less then an hour
202
+        var duration = moment.duration(sec, 'seconds');
203
+        return ((duration.minutes() > 9)? duration.minutes() : "0"+duration.minutes()) + ":" + ((duration.seconds() > 9)? duration.seconds() : "0"+duration.seconds());
204
+    } 
205
+    else if(sec >= 60 * 60 && sec < 24 * 60 * 60){ // greater than an hour and less then a day
206
+        var duration = moment.duration(sec, 'seconds');
207
+        return ((duration.hours() > 9)? duration.hours() : "0"+duration.hours())  + ":" + ((duration.minutes() > 9)? duration.minutes() : "0"+duration.minutes())  + ":" + ((duration.seconds() > 9)? duration.seconds() : "0"+duration.seconds());
208
+    } 
209
+    //  Otherwise.. this is just too long
210
+}
211
+function formatBytes(bytes, decimals) {
212
+    if (bytes === 0) return "0 "+ lang.bytes;
213
+    var k = 1024;
214
+    var dm = (decimals && decimals >= 0)? decimals : 2;
215
+    var sizes = [lang.bytes, lang.kb, lang.mb, lang.gb, lang.tb, lang.pb, lang.eb, lang.zb, lang.yb];
216
+    var i = Math.floor(Math.log(bytes) / Math.log(k));
217
+    return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
218
+}
219
+function UserLocale(){
220
+    var language = window.navigator.userLanguage || window.navigator.language; // "en", "en-US", "fr", "fr-FR", "es-ES", etc.
221
+
222
+    langtag = language.split('-');
223
+    if(langtag.length == 1){
224
+        return ""; 
225
+    } 
226
+    else if(langtag.length == 2) {
227
+        return langtag[1].toLowerCase();  // en-US => us
228
+    }
229
+    else if(langtag.length >= 3) {
230
+        return langtag[1].toLowerCase();  // en-US => us
231
+    }
232
+}
233
+function GetAlternateLanguage() {
234
+    var userLanguage = window.navigator.userLanguage || window.navigator.language; // "en", "en-US", "fr", "fr-FR", "es-ES", etc.
235
+
236
+    if(Language != "auto") userLanguage = Language;
237
+    userLanguage = userLanguage.toLowerCase();
238
+    if(userLanguage == "en" || userLanguage.indexOf("en-") == 0) return "";  // English is already loaded
239
+
240
+    for(l = 0; l < availableLang.length; l++) {
241
+        if(userLanguage.indexOf(availableLang[l].toLowerCase()) == 0) {
242
+            console.log("Alternate Language detected: ", userLanguage);
243
+            // Set up Moment with the same langugae settings
244
+            moment.locale(userLanguage);
245
+            return availableLang[l].toLowerCase();
246
+        }
247
+    }
248
+    return "";
249
+}
250
+function getFilter(filter, keyword) {
251
+    if(filter.indexOf(",", filter.indexOf(keyword +": ") + keyword.length + 2) != -1) {
252
+        return filter.substring(filter.indexOf(keyword +": ") + keyword.length + 2, filter.indexOf(",", filter.indexOf(keyword +": ") + keyword.length + 2));
253
+    }
254
+    else {
255
+        return filter.substring(filter.indexOf(keyword +": ") + keyword.length + 2);
256
+    }
257
+}
258
+function base64toBlob(base64Data, contentType) {
259
+    if(base64Data.indexOf("," != -1)) base64Data = base64Data.split(",")[1]; // [data:image/png;base64] , [xxx...]
260
+    var byteCharacters = atob(base64Data);
261
+    var slicesCount = Math.ceil(byteCharacters.length / 1024);
262
+    var byteArrays = new Array(slicesCount);
263
+    for (var s = 0; s < slicesCount; ++s) {
264
+        var begin = s * 1024;
265
+        var end = Math.min(begin + 1024, byteCharacters.length);
266
+        var bytes = new Array(end - begin);
267
+        for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
268
+            bytes[i] = byteCharacters[offset].charCodeAt(0);
269
+        }
270
+        byteArrays[s] = new Uint8Array(bytes);
271
+    }
272
+    return new Blob(byteArrays, { type: contentType });
273
+}
274
+function MakeDataArray(defaultValue, count) {
275
+    var rtnArray = new Array(count);
276
+    for(var i=0; i< rtnArray.length; i++) {
277
+        rtnArray[i] = defaultValue;
278
+    }
279
+    return rtnArray;
280
+}
281
+// Save account configuration data to SQL database
282
+function saveConfToSqldb() {
283
+
284
+    wssServer = $("#Configure_Account_wssServer").val();
285
+    WebSocketPort = parseInt($("#Configure_Account_WebSocketPort").val());
286
+    ServerPath = $("#Configure_Account_ServerPath").val();
287
+    profileName = $("#Configure_Account_profileName").val();
288
+    SipUsername = $("#Configure_Account_SipUsername").val();
289
+    SipPassword = $("#Configure_Account_SipPassword").val();
290
+
291
+    var STUNServer = $("#Configure_Account_StunServer").val();
292
+    var AUDIOOutputId = $("#playbackSrc").val();
293
+    var VIDEOSrcId = $("#previewVideoSrc").val();
294
+    var VIDEOHeight = $("input[name=Settings_Quality]:checked").val(); 
295
+    var FRAMERate = parseInt($("input[name=Settings_FrameRate]:checked").val());
296
+    var ASPECTRatio = $("input[name=Settings_AspectRatio]:checked").val();
297
+    var VIDEOOrientation = $("input[name=Settings_Oriteation]:checked").val();
298
+    var AUDIOSrcId = $("#microphoneSrc").val();
299
+    var AUTOGainControl = ($("#Settings_AutoGainControl").is(':checked'))? "1" : "0";
300
+    var ECHOCancellation = ($("#Settings_EchoCancellation").is(':checked'))? "1" : "0";
301
+    var NOISESuppression = ($("#Settings_NoiseSuppression").is(':checked'))? "1" : "0";
302
+    var RINGOutputId = $("#ringDevice").val();
303
+    var VideoConfExtension = $("#Video_Conf_Extension").val();
304
+    var VideoConfWindowWidth = $("#Video_Conf_Window_Width").val();
305
+    var PROFILEpicture = (typeof getDbItem("profilePicture", "") === 'undefined') ? "" : getDbItem("profilePicture", "");
306
+    var NOTIFYCheck = ($("#Settings_Notifications").is(":checked"))? 1 : 0;
307
+    var EMAILIntegration = ($("#emailIntegration").is(":checked"))? 1 : 0;
308
+    var RCDomain = $("#RoundcubeDomain").val();
309
+    var RCbasicauthuser = $("#rcBasicAuthUser").val();
310
+    var RCbasicauthpass = $("#rcBasicAuthPass").val();
311
+    var RCuser = $("#RoundcubeUser").val();
312
+    var RCpassword = $("#RoundcubePass").val();
313
+
314
+    if (userName != '') {
315
+
316
+       if (wssServer != '' && WebSocketPort != '' && ServerPath != '' && SipUsername != '' && SipPassword != '') {
317
+
318
+          $.ajax({
319
+             type: "POST",
320
+             url: "save-update-settings.php",
321
+             dataType: "JSON",
322
+             data: {
323
+                     username: userName,
324
+                     wss_server: wssServer,
325
+                     web_socket_port: WebSocketPort,
326
+                     server_path: ServerPath,
327
+                     profile_name: profileName,
328
+                     sip_username: SipUsername,
329
+                     sip_password: SipPassword,
330
+                     stun_server: STUNServer,
331
+                     audio_output_id: AUDIOOutputId,
332
+                     video_src_id: VIDEOSrcId,
333
+                     video_height: VIDEOHeight,
334
+                     frame_rate: FRAMERate,
335
+                     aspect_ratio: ASPECTRatio,
336
+                     video_orientation: VIDEOOrientation,
337
+                     audio_src_id: AUDIOSrcId,
338
+                     auto_gain_control: AUTOGainControl,
339
+                     echo_cancellation: ECHOCancellation,
340
+                     noise_suppression: NOISESuppression,
341
+                     ring_output_id: RINGOutputId,
342
+                     video_conf_extension: VideoConfExtension,
343
+                     video_conf_window_width: VideoConfWindowWidth,
344
+                     profile_picture: PROFILEpicture,
345
+                     notifications: NOTIFYCheck,
346
+                     use_roundcube: EMAILIntegration,
347
+                     rcdomain: RCDomain,
348
+                     rcbasicauthuser: RCbasicauthuser,
349
+                     rcbasicauthpass: RCbasicauthpass,
350
+                     rcuser: RCuser,
351
+                     rcpassword: RCpassword,
352
+                     s_ajax_call: validateSToken
353
+             },
354
+             success: function(response) {
355
+                             window.location.reload();
356
+                      },
357
+             error: function(response) {
358
+                             alert("An error occurred while attempting to save the account configuration data!");
359
+             }
360
+          });
361
+
362
+       } else { alert("Fields: 'WebSocket Domain', 'WebSocket Port', 'WebSocket Path', 'SIP Username' and 'SIP Password' are required ! Please fill in all these fields !"); }
363
+
364
+    } else { alert("An error occurred while attempting to save the data!"); }
365
+}
366
+// Get account configuration data from SQL database
367
+function getConfFromSqldb() {
368
+
369
+          $.ajax({
370
+             type: "POST",
371
+             url: "get-settings.php",
372
+             dataType: "JSON",
373
+             data: {
374
+                     username: userName,
375
+                     s_ajax_call: validateSToken
376
+             },
377
+             success: function(datafromdb) {
378
+
379
+                        // Get configuration data from SQL database
380
+                        if (datafromdb.wss_server == null || datafromdb.web_socket_port == null || datafromdb.server_path == null ||
381
+                            datafromdb.sip_username == null || datafromdb.sip_password == null) {
382
+
383
+                            ConfigureExtensionWindow();
384
+
385
+                        } else {
386
+
387
+				if (!localStorage.getItem('firstReLoad')) {
388
+				     localStorage['firstReLoad'] = true;
389
+				     window.setTimeout(function() { window.location.reload(); }, 200);
390
+				}
391
+
392
+                                if (datafromdb.stun_server == null || typeof datafromdb.stun_server == 'undefined') {
393
+                                    var stunData = '';
394
+                                } else { var stunData = datafromdb.stun_server; }
395
+
396
+		                localDB.setItem("profileUserID", uID());
397
+				localDB.setItem("userrole", datafromdb.userrole);
398
+				localDB.setItem("wssServer", datafromdb.wss_server);
399
+				localDB.setItem("WebSocketPort", datafromdb.web_socket_port);
400
+				localDB.setItem("ServerPath", datafromdb.server_path);
401
+				localDB.setItem("profileUser", datafromdb.sip_username);
402
+				localDB.setItem("profileName", datafromdb.profile_name);
403
+				localDB.setItem("SipUsername", datafromdb.sip_username);
404
+				localDB.setItem("SipPassword", datafromdb.sip_password);
405
+				localDB.setItem("StunServer", stunData);
406
+				localDB.setItem("AudioOutputId", datafromdb.audio_output_id);
407
+				localDB.setItem("VideoSrcId", datafromdb.video_src_id);
408
+				localDB.setItem("VideoHeight", datafromdb.video_height);
409
+				localDB.setItem("FrameRate", datafromdb.frame_rate);
410
+				localDB.setItem("AspectRatio", datafromdb.aspect_ratio);
411
+				localDB.setItem("VideoOrientation", datafromdb.video_orientation);
412
+				localDB.setItem("AudioSrcId", datafromdb.audio_src_id);
413
+				localDB.setItem("AutoGainControl", datafromdb.auto_gain_control);
414
+				localDB.setItem("EchoCancellation", datafromdb.echo_cancellation);
415
+				localDB.setItem("NoiseSuppression", datafromdb.noise_suppression);
416
+				localDB.setItem("RingOutputId", datafromdb.ring_output_id);
417
+                                localDB.setItem("VidConfExtension", datafromdb.video_conf_extension);
418
+                                localDB.setItem("VidConfWindowWidth", datafromdb.video_conf_window_width);
419
+				localDB.setItem("profilePicture", datafromdb.profile_picture);
420
+				localDB.setItem("Notifications", datafromdb.notifications);
421
+                                localDB.setItem("useRoundcube", datafromdb.use_roundcube);
422
+                                localDB.setItem("rcDomain", (datafromdb.rcdomain != '' && datafromdb.rcdomain != null && typeof datafromdb.rcdomain != 'undefined')? datafromdb.rcdomain : "");
423
+                                localDB.setItem("rcBasicAuthUser", datafromdb.rcbasicauthuser);
424
+                                localDB.setItem("rcBasicAuthPass", datafromdb.rcbasicauthpass);
425
+                                localDB.setItem("RoundcubeUser", datafromdb.rcuser);
426
+                                localDB.setItem("RoundcubePass", datafromdb.rcpassword);
427
+
428
+		                Register();
429
+                        }
430
+                      },
431
+             error: function(datafromdb) {
432
+                           alert("An error occurred while attempting to retrieve account configuration data from the database!");
433
+             }
434
+          });
435
+}
436
+
437
+// Save contact data to SQL database
438
+function saveContactToSQLDB(newPerson) {
439
+
440
+    if (newPerson.length != 0) {
441
+             $.ajax({
442
+                type: "POST",
443
+                url: "save-contact.php",
444
+                dataType: "JSON",
445
+                data: {
446
+                     username: userName,
447
+                     contact_name: newPerson[0],
448
+                     contact_desc: newPerson[1],
449
+                     extension_number: newPerson[2],
450
+                     contact_mobile: newPerson[3],
451
+                     contact_num1: newPerson[4],
452
+                     contact_num2: newPerson[5],
453
+                     contact_email: newPerson[6],
454
+                     s_ajax_call: validateSToken
455
+                },
456
+                success: function(response) {
457
+                         if (response.result != 'success') { alert(response.result); }
458
+                },
459
+                error: function(response) {
460
+                         alert("An error occurred while attempting to save the contact to the database!");
461
+                }
462
+          });
463
+    } else { alert("An error occurred while attempting to save the data!"); }
464
+}
465
+
466
+// Update contact data in SQL database
467
+function updateContactToSQLDB(newPerson) {
468
+
469
+    if (newPerson.length != 0) {
470
+        if (newPerson[7] != '' && newPerson[7] != null && typeof newPerson[7] != 'undefined') {
471
+            $.ajax({
472
+                type: "POST",
473
+                url: "update-contact.php",
474
+                dataType: "JSON",
475
+                data: {
476
+                     contact_name: newPerson[0],
477
+                     contact_desc: newPerson[1],
478
+                     extension_number: newPerson[2],
479
+                     contact_mobile: newPerson[3],
480
+                     contact_num1: newPerson[4],
481
+                     contact_num2: newPerson[5],
482
+                     contact_email: newPerson[6],
483
+                     contactDBID: newPerson[7],
484
+                     s_ajax_call: validateSToken
485
+                },
486
+                success: function(response) {
487
+                         if (response.result != 'success') { alert(response.result); }
488
+                },
489
+                error: function(response) {
490
+                         alert("An error occurred while attempting to save the data!");
491
+                }
492
+            });
493
+        } else { alert("Error while attempting to retrieve contact data from the database!"); }
494
+    } else { alert("An error occurred while attempting to save the data!"); }
495
+}
496
+// Save contact picture data to SQL database
497
+function saveContactPicToSQLDB(newPic) {
498
+
499
+    if (newPic.length != 0) {
500
+        $.ajax({
501
+             type: "POST",
502
+             url: "save-update-contact-picture.php",
503
+             dataType: "JSON",
504
+             data: {
505
+                     username: userName,
506
+                     contact_name: newPic[0],
507
+                     profile_picture_c: newPic[1],
508
+                     s_ajax_call: validateSToken
509
+             },
510
+             success: function(response) {
511
+                      },
512
+             error: function(response) {
513
+                          alert("An error occurred while attempting to save contact picture!");
514
+             }
515
+        });
516
+    } else { alert("An error occurred while attempting to save contact picture!"); }
517
+}
518
+// Get contact data from SQL database
519
+function getContactsFromSQLDB() {
520
+
521
+          $.ajax({
522
+             type: "POST",
523
+             url: "get-contacts.php",
524
+             dataType: "JSON",
525
+             data: {
526
+                     username: userName,
527
+                     s_ajax_call: validateSToken
528
+             },
529
+             success: function(contactsfromdb) {
530
+
531
+                        // Get contacts from SQL database and save them to localDB
532
+                        var jsonCon = InitUserBuddies();
533
+
534
+                        $.each(contactsfromdb.contactsinfo, function(ind, contactrow) {
535
+			       var id = uID();
536
+			       var dateNow = utcDateNow();
537
+
538
+                               if (contactsfromdb.contactsinfo[ind].extension_number == '' || contactsfromdb.contactsinfo[ind].extension_number == null) {
539
+ 
540
+				    jsonCon.DataCollection.push(
541
+					{
542
+					    Type: "contact",
543
+					    LastActivity: dateNow,
544
+					    ExtensionNumber: "",
545
+					    MobileNumber: contactsfromdb.contactsinfo[ind].contact_mobile,
546
+					    ContactNumber1: contactsfromdb.contactsinfo[ind].contact_num1,
547
+					    ContactNumber2: contactsfromdb.contactsinfo[ind].contact_num2,
548
+					    uID: null,
549
+					    cID: id,
550
+					    gID: null,
551
+					    DisplayName: contactsfromdb.contactsinfo[ind].contact_name,
552
+					    Position: "",
553
+					    Description: contactsfromdb.contactsinfo[ind].contact_desc, 
554
+					    Email: contactsfromdb.contactsinfo[ind].contact_email,
555
+					    MemberCount: 0
556
+					}
557
+				    );
558
+
559
+                                    if (contactsfromdb.contactsinfo[ind].profile_picture_c != '' && contactsfromdb.contactsinfo[ind].profile_picture_c != null) {
560
+                                        localDB.setItem("img-"+id+"-contact", contactsfromdb.contactsinfo[ind].profile_picture_c);
561
+                                    }
562
+
563
+			            var buddyObj = new Buddy("contact", id, contactsfromdb.contactsinfo[ind].contact_name, "", contactsfromdb.contactsinfo[ind].contact_mobile, contactsfromdb.contactsinfo[ind].contact_num1, contactsfromdb.contactsinfo[ind].contact_num2, dateNow, contactsfromdb.contactsinfo[ind].contact_desc, contactsfromdb.contactsinfo[ind].contact_email);
564
+			            AddBuddy(buddyObj, true, false, false);
565
+
566
+                               } else {
567
+
568
+				    jsonCon.DataCollection.push(
569
+					{
570
+					    Type: "extension",
571
+					    LastActivity: dateNow,
572
+					    ExtensionNumber: contactsfromdb.contactsinfo[ind].extension_number,
573
+					    MobileNumber: contactsfromdb.contactsinfo[ind].contact_mobile,
574
+					    ContactNumber1: contactsfromdb.contactsinfo[ind].contact_num1,
575
+					    ContactNumber2: contactsfromdb.contactsinfo[ind].contact_num2,
576
+					    uID: id,
577
+					    cID: null,
578
+					    gID: null,
579
+					    DisplayName: contactsfromdb.contactsinfo[ind].contact_name,
580
+					    Position: contactsfromdb.contactsinfo[ind].contact_desc,
581
+					    Description: "", 
582
+					    Email: contactsfromdb.contactsinfo[ind].contact_email,
583
+					    MemberCount: 0
584
+					}
585
+				    );
586
+
587
+                                    if (contactsfromdb.contactsinfo[ind].profile_picture_c != '' && contactsfromdb.contactsinfo[ind].profile_picture_c != null) {
588
+                                        localDB.setItem("img-"+id+"-extension", contactsfromdb.contactsinfo[ind].profile_picture_c);
589
+                                    }
590
+
591
+			            var buddyObj = new Buddy("extension", id, contactsfromdb.contactsinfo[ind].contact_name, contactsfromdb.contactsinfo[ind].extension_number, contactsfromdb.contactsinfo[ind].contact_mobile, contactsfromdb.contactsinfo[ind].contact_num1, contactsfromdb.contactsinfo[ind].contact_num2, dateNow, contactsfromdb.contactsinfo[ind].contact_desc, contactsfromdb.contactsinfo[ind].contact_email);
592
+			            AddBuddy(buddyObj, true, false, true);
593
+                               } 
594
+                        });
595
+
596
+		        jsonCon.TotalRows = jsonCon.DataCollection.length;
597
+		        localDB.setItem(profileUserID + "-Buddies", JSON.stringify(jsonCon));
598
+
599
+                        UpdateUI();
600
+                        PopulateBuddyList();
601
+
602
+                      },
603
+             error: function(contactsfromdb) {
604
+                           alert("An error occurred while attempting to retrieve contacts data from the database!");
605
+             }
606
+          });
607
+}
608
+
609
+// Get external users data from SQL database
610
+function getExternalUserConfFromSqldb() {
611
+
612
+         $.ajax({
613
+             type: "POST",
614
+             url: "get-external-users-conf.php",
615
+             dataType: "JSON",
616
+             data: {
617
+                     username: userName, 
618
+                     s_ajax_call: validateSToken
619
+             },
620
+             success: function(extdatafromdb) {
621
+
622
+                        var extdatafromdbstr = JSON.stringify(extdatafromdb);
623
+
624
+                        if (extdatafromdbstr.length > 0) {
625
+
626
+                            localDB.setItem("externalUserConfElem", extdatafromdb.length);
627
+
628
+                            for (var t = 0; t < extdatafromdb.length; t++) {
629
+				 localDB.setItem("extUserExtension-"+t, extdatafromdb[t]['exten_for_external']);
630
+                                 localDB.setItem("extUserExtensionPass-"+t, extdatafromdb[t]['exten_for_ext_pass']);
631
+                                 localDB.setItem("confAccessLink-"+t, extdatafromdb[t]['conf_access_link']);
632
+                            }
633
+                        }
634
+                      },
635
+             error: function(extdatafromdb) {
636
+                           alert("An error occurred while attempting to retrieve external users configuration data from the database!");
637
+             }
638
+         });
639
+}
640
+
641
+// Check if there are any links for external access associated with the current username
642
+function checkExternalLinks() {
643
+
644
+        if (confirm("Links that can allow external users to access the video conferences are stored in the database. If you don\'t need them anymore, it\'s recommended to remove them in order to prevent unwanted access to your conferences. Press OK to remove all the links for external access associated with your username from the database and then exit Roundpin, or press Cancel to leave the links in the database and just exit.")) {
645
+
646
+             $.ajax({
647
+                 type: "POST",
648
+                 url: "remove-links-for-external-access.php",
649
+                 dataType: "JSON",
650
+                 data: {
651
+                        username: userName,
652
+                        s_ajax_call: validateSToken
653
+                       },
654
+                 success: function(response) {
655
+                            alert("All the links for external access to conferences associated with your username have been successfully removed from the database!");
656
+
657
+		            Unregister();
658
+			    console.log("Signing Out ...");
659
+			    localStorage.clear();
660
+			    if (winVideoConf != null) {
661
+			        winVideoConf.close();
662
+			    }
663
+			    window.open('https://' + window.location.host + '/logout.php', '_self');
664
+                 },
665
+                 error: function(response) {
666
+                            alert("An error occurred while trying to remove the data from the database!");
667
+
668
+		            Unregister();
669
+			    console.log("Signing Out ...");
670
+			    localStorage.clear();
671
+			    if (winVideoConf != null) {
672
+			        winVideoConf.close();
673
+			    }
674
+			    window.open('https://' + window.location.host + '/logout.php', '_self');
675
+                 }
676
+             });
677
+
678
+        } else {
679
+                 Unregister();
680
+		 console.log("Signing Out ...");
681
+		 localStorage.clear();
682
+		 if (winVideoConf != null) {
683
+		     winVideoConf.close();
684
+		 }
685
+		 window.open('https://' + window.location.host + '/logout.php', '_self');
686
+        }
687
+}
688
+
689
+// Remove contact from SQL database
690
+function deleteBuddyFromSqldb(buddyDisplayName) {
691
+
692
+          $.ajax({
693
+             type: "POST",
694
+             url: "remove-contact.php",
695
+             dataType: "JSON",
696
+             data: {
697
+                     username: userName,
698
+                     contact_name: buddyDisplayName,
699
+                     s_ajax_call: validateSToken
700
+             },
701
+             success: function(delresult) {
702
+             },
703
+             error: function(delresult) {
704
+                        alert("An error occurred while attempting to remove the contact from the database!");
705
+             }
706
+          });
707
+}
708
+
709
+// Save new Roundpin user password
710
+function saveNewUserPassword() {
711
+
712
+      var currentPassword = $("#Current_User_Password").val();
713
+      var newPassword = $("#New_User_Password").val();
714
+      var repeatPassword = $("#Repeat_New_User_Password").val();
715
+
716
+      if (currentPassword == '' || newPassword == '' || repeatPassword == '') { alert("Please fill in all the fields!"); return; }
717
+
718
+      // Verify if the new password meets constraints
719
+      if (/^((?=.*\d)(?=.*[a-z])(?=.*\W).{10,})$/.test(newPassword)) {
720
+
721
+          if (repeatPassword == newPassword) {
722
+		  $.ajax({
723
+		     type: "POST",
724
+		     url: "save-new-user-password.php",
725
+		     dataType: "JSON",
726
+		     data: {
727
+		             username: userName,
728
+		             current_password: currentPassword,
729
+                             new_password: newPassword,
730
+		             s_ajax_call: validateSToken
731
+		     },
732
+		     success: function(passchangemessage) {
733
+                                       alert(passchangemessage);
734
+		     },
735
+		     error: function(passchangemessage) {
736
+		                alert("An error occurred while attempting to change the user password!");
737
+		     }
738
+		  });
739
+          } else { alert("The passwords entered in the new password fields don't match!"); }
740
+
741
+      } else {
742
+          alert("The new password does not meet the requirements (to be at least 10 characters long, to contain at least one letter, at least one digit and at least one special character). Please choose a different password ! ");
743
+        }
744
+}
745
+
746
+// Save new Roundpin user email
747
+function saveNewUserEmail() {
748
+
749
+      var currentEmail = $("#Current_User_Email").val();
750
+      var newEmail = $("#New_User_Email").val();
751
+      var repeatEmail = $("#Repeat_New_User_Email").val();
752
+
753
+      if (currentEmail == '' || newEmail == '' || repeatEmail == '') { alert("Please fill in all the fields!"); return; }
754
+
755
+      // Verify if the new email is a valid email address
756
+      if (/^[A-Za-z0-9\_\.\-\~\%\+\!\?\&\*\^\=\#\$\{\}\|\/]+@[A-Za-z0-9\.\-]+\.[A-Za-z0-9\-]{2,63}$/.test(newEmail)) {
757
+
758
+          if (repeatEmail == newEmail) {
759
+		  $.ajax({
760
+		     type: "POST",
761
+		     url: "save-new-user-email.php",
762
+		     dataType: "JSON",
763
+		     data: {
764
+		             username: userName,
765
+		             current_email: currentEmail,
766
+                             new_email: newEmail,
767
+		             s_ajax_call: validateSToken
768
+		     },
769
+		     success: function(emailchangemessage) {
770
+                                       alert(emailchangemessage);
771
+		     },
772
+		     error: function(emailchangemessage) {
773
+		                alert("An error occurred while attempting to change the user email address!");
774
+		     }
775
+		  });
776
+          } else { alert("The email addresses entered in the new email fields don't match!"); }
777
+
778
+      } else {
779
+            alert("The new email address is not a valid email address. Please enter a valid email address!");
780
+        }
781
+}
782
+
783
+// Close Roundpin user account
784
+function closeUserAccount() {
785
+
786
+      closeVideoAudio();
787
+
788
+      ConfirmConfigExtWindow(lang.confirm_close_account, lang.close_roundpin_user_account, function() {
789
+
790
+		  $.ajax({
791
+		     type: "POST",
792
+		     url: "close-user-account.php",
793
+		     dataType: "JSON",
794
+		     data: {
795
+		             username: userName,
796
+		             s_ajax_call: validateSToken
797
+		     },
798
+		     success: function(closeaccountmessage) {
799
+                                       alert(closeaccountmessage);
800
+                                       SignOut();
801
+		     },
802
+		     error: function(closeaccountmessage) {
803
+		                alert("An error occurred while attempting to close your user account!");
804
+		     }
805
+		  });
806
+      });
807
+}
808
+
809
+// Launch video conference
810
+function LaunchVideoConference() {
811
+     if (winVideoConfCheck == 0) {
812
+	    winVideoConf = window.open('https://' + window.location.host + '/videoconference/index.php');
813
+	    winVideoConfCheck = 1;
814
+     } else { alert("The video conference has been launched. If you want to launch it again refresh the page, then click \"Launch Video Conference\"."); }
815
+}
816
+// Generate a new text chat key
817
+function generateChatRSAKeys(currentSIPusername) {
818
+
819
+          var crypto = new JSEncrypt({default_key_size: 1024});
820
+          crypto.getKey();
821
+          var currentChatPubKey = crypto.getPublicKey();
822
+          currentChatPrivKey = crypto.getPrivateKey();
823
+
824
+	  $.ajax({
825
+	     type: "POST",
826
+	     url: "save-text-chat-pub-key.php",
827
+	     dataType: "JSON",
828
+	     data: {
829
+	             currentextension: currentSIPusername,
830
+                     currentchatpubkey: currentChatPubKey,
831
+	             s_ajax_call: validateSToken
832
+	     },
833
+	     success: function() {
834
+	     },
835
+	     error: function() {
836
+	                alert("An error occurred while trying to save the new text chat public key!");
837
+	     }
838
+	  });
839
+}
840
+
841
+// Remove the 'uploads' directory used to temporarily store files received during text chat
842
+function removeTextChatUploads(currentSIPUser) {
843
+	$.ajax({
844
+             'async': false,
845
+             'global': false,
846
+	     type: "POST",
847
+	     url: "text-chat-remove-uploaded-files.php",
848
+	     dataType: "JSON",
849
+	     data: {
850
+		     sipusername: currentSIPUser,
851
+		     s_ajax_call: validateSToken
852
+	     },
853
+	     success: function(resresult) {
854
+                          if (resresult.note != 'success') {
855
+                              alert("An error occurred while trying to remove the text chat 'uploads' directory!");
856
+                          }
857
+             },
858
+             error: function(resresult) {
859
+                              alert("An error occurred while attempting to remove the text chat 'uploads' directory!");
860
+             }
861
+        });
862
+}
863
+
864
+// On page reload, close the video conference tab if it is opened
865
+function closeVideoConfTab() {
866
+         if (winVideoConf) { winVideoConf.close(); }
867
+}
868
+
869
+// Show Email window
870
+function ShowEmailWindow() {
871
+
872
+   if (getDbItem("useRoundcube", "") == 1) {
873
+
874
+      $("#roundcubeFrame").remove();
875
+      $("#rightContent").show();
876
+      $(".streamSelected").each(function() { $(this).css("display", "none"); });
877
+      $("#rightContent").append('<iframe id="roundcubeFrame" name="displayFrame"></iframe>');
878
+
879
+      var rcDomain = '';
880
+      var rcBasicAuthUser = '';
881
+      var rcBasicAuthPass = '';
882
+      var rcUsername = '';
883
+      var rcPasswd = '';
884
+
885
+      $.ajax({
886
+             'async': false,
887
+             'global': false,
888
+             type: "POST",
889
+             url: "get-email-info.php",
890
+             dataType: "JSON",
891
+             data: {
892
+                     username: userName,
893
+                     s_ajax_call: validateSToken
894
+             },
895
+             success: function(datafromdb) {
896
+                               rcDomain = datafromdb.rcdomain;
897
+                               rcBasicAuthUser = encodeURIComponent(datafromdb.rcbasicauthuser);
898
+                               rcBasicAuthPass = encodeURIComponent(datafromdb.rcbasicauthpass);
899
+                               rcUsername = datafromdb.rcuser;
900
+                               rcPasswd = datafromdb.rcpassword;
901
+             },
902
+             error: function(datafromdb) {
903
+                             alert("An error occurred while trying to retrieve data from the database!");
904
+             }
905
+      });
906
+
907
+      if (rcBasicAuthUser != '' && rcBasicAuthPass != '') { 
908
+          var actionURL = "https://"+ rcBasicAuthUser +":"+ rcBasicAuthPass +"@"+ rcDomain +"/"; 
909
+      } else { var actionURL = "https://"+ rcDomain +"/"; }
910
+
911
+      var form = '<form id="rcForm" method="POST" action="'+ actionURL +'" target="displayFrame">'; 
912
+      form += '<input type="hidden" name="_action" value="login" />';
913
+      form += '<input type="hidden" name="_task" value="login" />';
914
+      form += '<input type="hidden" name="_autologin" value="1" />';
915
+      form += '<input name="_user" value="'+ rcUsername +'" type="text" />';
916
+      form += '<input name="_pass" value="'+ rcPasswd +'" type="password" />';
917
+      form += '<input id="submitButton" type="submit" value="Login" />';
918
+      form += '</form>';
919
+
920
+      $("#roundcubeFrame").append(form);
921
+
922
+      if (RCLoginCheck == 0) {
923
+          $("#submitButton").click();
924
+          RCLoginCheck = 1;
925
+      } else { $("#roundcubeFrame").attr("src", actionURL); }
926
+
927
+   } else { alert("Email Integration is not enabled ! You can enable Roundcube email integration by clicking on the 'Settings' wheel in the user profile section from below > 'Settings' > 'Email Integration' > 'Enable Roundcube email integration'"); }
928
+}
929
+
930
+// Shrink the left panel
931
+function CollapseLeftPanel() {
932
+    if ($(window).width() >= 920) {
933
+        if ($("#leftContent").hasClass("shrinkLeftContent")) {
934
+            $("#leftContent").removeClass("shrinkLeftContent");
935
+            $("#rightContent").removeClass("widenRightContent");
936
+            $('#aboutImg').css("margin-right", "-3px");
937
+        } else {
938
+            $("#leftContent").addClass("shrinkLeftContent");
939
+            $("#rightContent").addClass("widenRightContent");
940
+            $('#aboutImg').css("margin-right", "3px");
941
+        }
942
+    }
943
+}
944
+
945
+// Show the 'About' window
946
+function ShowAboutWindow() {
947
+
948
+   $.jeegoopopup.close();
949
+   var aboutHtml = '<div>';
950
+   aboutHtml += '<div id="windowCtrls"><img id="minimizeImg" src="images/1_minimize.svg" title="Restore" /><img id="maximizeImg" src="images/2_maximize.svg" title="Maximize" /><img id="closeImg" src="images/3_close.svg" title="Close" /></div>';
951
+   aboutHtml += '<div class="UiWindowField scroller">';
952
+   aboutHtml += '<div><img id="AboutLogoImg" src="images/login-logo.svg"/></div>';
953
+   aboutHtml += '<div id="aboutPopup">'+lang.about_text+'</div>';
954
+   aboutHtml += '</div></div>';
955
+
956
+   $.jeegoopopup.open({
957
+                title: 'About Roundpin',
958
+                html: aboutHtml,
959
+                width: '640',
960
+                height: '500',
961
+                center: true,
962
+                scrolling: 'no',
963
+                skinClass: 'jg_popup_basic',
964
+                overlay: true,
965
+                opacity: 50,
966
+                draggable: true,
967
+                resizable: false,
968
+                fadeIn: 0
969
+   });
970
+
971
+   $("#jg_popup_b").append('<button id="ok_button">'+ lang.ok +'</button>');
972
+
973
+   var maxWidth = $(window).width() - 12;
974
+   var maxHeight = $(window).height() - 88;
975
+
976
+   if (maxWidth < 656 || maxHeight < 500) { 
977
+       $.jeegoopopup.width(maxWidth).height(maxHeight);
978
+       $.jeegoopopup.center();
979
+       $("#maximizeImg").hide();
980
+       $("#minimizeImg").hide();
981
+   } else { 
982
+       $.jeegoopopup.width(640).height(500);
983
+       $.jeegoopopup.center();
984
+       $("#minimizeImg").hide();
985
+       $("#maximizeImg").show();
986
+   }
987
+
988
+   $(window).resize(function() {
989
+       maxWidth = $(window).width() - 12;
990
+       maxHeight = $(window).height() - 88;
991
+
992
+       $.jeegoopopup.center();
993
+       if (maxWidth < 656 || maxHeight < 500) { 
994
+           $.jeegoopopup.width(maxWidth).height(maxHeight);
995
+           $.jeegoopopup.center();
996
+           $("#maximizeImg").hide();
997
+           $("#minimizeImg").hide();
998
+       } else { 
999
+           $.jeegoopopup.width(640).height(500);
1000
+           $.jeegoopopup.center();
1001
+           $("#minimizeImg").hide();
1002
+           $("#maximizeImg").show();
1003
+       }
1004
+   });
1005
+
1006
+   
1007
+   $("#minimizeImg").click(function() { $.jeegoopopup.width(640).height(500); $.jeegoopopup.center(); $("#maximizeImg").show(); $("#minimizeImg").hide(); });
1008
+   $("#maximizeImg").click(function() { $.jeegoopopup.width(maxWidth).height(maxHeight); $.jeegoopopup.center(); $("#minimizeImg").show(); $("#maximizeImg").hide(); });
1009
+
1010
+   $("#closeImg").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1011
+   $("#ok_button").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1012
+   $("#jg_popup_overlay").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1013
+   $(window).on('keydown', function(event) { if (event.key == "Escape") { $.jeegoopopup.close(); $("#jg_popup_b").empty(); } });
1014
+}
1015
+
1016
+// Show system notifications on incoming calls
1017
+function incomingCallNote() {
1018
+   var incomingCallNotify = new Notification(lang.incomming_call, { icon: "../images/notification-logo.svg", body: "New incoming call !!!" });
1019
+   incomingCallNotify.onclick = function (event) {
1020
+       return;
1021
+   };
1022
+
1023
+   if (document.hasFocus()) {
1024
+       return;
1025
+   } else { setTimeout(incomingCallNote, 8000); }
1026
+}
1027
+
1028
+// Change page title on incoming calls
1029
+function changePageTitle() {
1030
+    if ($(document).attr("title") == "Roundpin") { $(document).prop("title", "New call !!!"); } else { $(document).prop("title", "Roundpin"); }
1031
+    if (document.hasFocus()) {
1032
+        $(document).prop("title", "Roundpin");
1033
+        return;
1034
+    } else { setTimeout(changePageTitle, 460); }
1035
+}
1036
+
1037
+// Window and Document Events
1038
+// ==========================
1039
+$(window).on("beforeunload", function() {
1040
+    Unregister();
1041
+});
1042
+$(window).on("resize", function() {
1043
+    UpdateUI();
1044
+});
1045
+
1046
+// User Interface
1047
+// ==============
1048
+function UpdateUI(){
1049
+    if($(window).outerWidth() < 920){
1050
+        // Narrow Layout
1051
+        if(selectedBuddy == null & selectedLine == null) {
1052
+            // Nobody Selected
1053
+            $("#rightContent").hide();
1054
+
1055
+            $("#leftContent").css("width", "100%");
1056
+            $("#leftContent").show();
1057
+        }
1058
+        else {
1059
+            $("#rightContent").css("margin-left", "0px");
1060
+            $("#rightContent").show();
1061
+
1062
+            $("#leftContent").hide();
1063
+
1064
+            if(selectedBuddy != null) updateScroll(selectedBuddy.identity);
1065
+        }
1066
+    }
1067
+    else {
1068
+        // Wide Screen Layout
1069
+        if(selectedBuddy == null & selectedLine == null) {
1070
+            $("#leftContent").css("width", "320px");
1071
+            $("#rightContent").css("margin-left", "0px");
1072
+            $("#leftContent").show();
1073
+            $("#rightContent").hide();
1074
+        }
1075
+        else{
1076
+            $("#leftContent").css("width", "320px");
1077
+            $("#rightContent").css("margin-left", "320px");
1078
+            $("#leftContent").show();
1079
+            $("#rightContent").show();
1080
+
1081
+            if(selectedBuddy != null) updateScroll(selectedBuddy.identity);
1082
+        }
1083
+    }
1084
+    for(var l=0; l<Lines.length; l++){
1085
+        updateLineScroll(Lines[l].LineNumber);
1086
+    }
1087
+}
1088
+
1089
+// UI Windows
1090
+// ==========
1091
+function AddSomeoneWindow(numberStr){
1092
+
1093
+    $("#userMenu").hide();
1094
+    $.jeegoopopup.close();
1095
+
1096
+    var html = "<div id='AddNewContact'>";
1097
+
1098
+    html += "<div id='windowCtrls'><img id='minimizeImg' src='images/1_minimize.svg' title='Restore' /><img id='maximizeImg' src='images/2_maximize.svg' title='Maximize' /><img id='closeImg' src='images/3_close.svg' title='Close' /></div>";
1099
+
1100
+    html += "<div class='UiWindowField scroller'>";
1101
+
1102
+    html += "<div class=UiText>"+ lang.display_name +":</div>";
1103
+    html += "<div><input id=AddSomeone_Name class=UiInputText type=text placeholder='"+ lang.eg_display_name +"'></div>";
1104
+
1105
+    html += "<div class=UiText>"+ lang.title_description +":</div>";
1106
+    html += "<div><input id=AddSomeone_Desc class=UiInputText type=text placeholder='"+ lang.eg_general_manager +"'></div>";
1107
+
1108
+    html += "<div class=UiText>"+ lang.internal_subscribe_extension +":</div>";
1109
+    if(numberStr && numberStr.length > 1 && numberStr.length < DidLength && numberStr.substring(0,1) != "*"){
1110
+        html += "<div><input id=AddSomeone_Exten class=UiInputText type=text value="+ numberStr +" placeholder='"+ lang.eg_internal_subscribe_extension +"'></div>";
1111
+    }
1112
+    else{
1113
+        html += "<div><input id=AddSomeone_Exten class=UiInputText type=text placeholder='"+ lang.eg_internal_subscribe_extension +"'></div>";
1114
+    }
1115
+
1116
+    html += "<div class=UiText>"+ lang.mobile_number +":</div>";
1117
+    html += "<div><input id=AddSomeone_Mobile class=UiInputText type=text placeholder='"+ lang.eg_mobile_number +"'></div>";
1118
+
1119
+    html += "<div class=UiText>"+ lang.contact_number_1 +":</div>";
1120
+    if(numberStr && numberStr.length > 1){
1121
+        html += "<div><input id=AddSomeone_Num1 class=UiInputText type=text value="+ numberStr +" placeholder='"+ lang.eg_contact_number_1 +"'></div>";
1122
+    }
1123
+    else {
1124
+        html += "<div><input id=AddSomeone_Num1 class=UiInputText type=text placeholder='"+ lang.eg_contact_number_1 +"'></div>";
1125
+    }
1126
+
1127
+    html += "<div class=UiText>"+ lang.contact_number_2 +":</div>";
1128
+    html += "<div><input id=AddSomeone_Num2 class=UiInputText type=text placeholder='"+ lang.eg_contact_number_2 +"'></div>";
1129
+
1130
+    html += "<div class=UiText>"+ lang.email +":</div>";
1131
+    html += "<div><input id=AddSomeone_Email class=UiInputText type=text placeholder='"+ lang.eg_email +"'></div>";
1132
+
1133
+    html += "</div></div>"
1134
+
1135
+    $.jeegoopopup.open({
1136
+                title: 'Add Contact',
1137
+                html: html,
1138
+                width: '640',
1139
+                height: '500',
1140
+                center: true,
1141
+                scrolling: 'no',
1142
+                skinClass: 'jg_popup_basic',
1143
+                contentClass: 'addContactPopup',
1144
+                overlay: true,
1145
+                opacity: 50,
1146
+                draggable: true,
1147
+                resizable: false,
1148
+                fadeIn: 0
1149
+    });
1150
+
1151
+    $("#jg_popup_b").append("<div id=bottomButtons><button id=save_button>Save</button><button id=cancel_button>Cancel</button></div>");
1152
+
1153
+    $("#save_button").click(function() {
1154
+
1155
+      var currentASName = $("#AddSomeone_Name").val();
1156
+
1157
+      if (currentASName != null && currentASName.trim() !== '') {
1158
+
1159
+        if (/^[A-Za-z0-9\s\-\'\[\]\(\)]+$/.test(currentASName)) {
1160
+
1161
+	   var currentDesc = $("#AddSomeone_Desc").val();
1162
+	   if (currentDesc != null && currentDesc.trim() !== '') {
1163
+	       if (/^[A-Za-z0-9\s\-\.\'\"\[\]\(\)\{\}\_\!\?\~\@\%\^\&\*\+\>\<\;\:\=]+$/.test(currentDesc)) { var finCurrentDesc = currentDesc; } else { 
1164
+		   var finCurrentDesc = ''; alert('The title/description that you entered is not valid!'); }
1165
+	   } else { var finCurrentDesc = ''; }
1166
+
1167
+	   var currentExtension = $("#AddSomeone_Exten").val();
1168
+	   if (currentExtension != null && currentExtension.trim() !== '') {
1169
+	       if (/^[a-zA-Z0-9\*\#]+$/.test(currentExtension)) { var finCurrentExtension = currentExtension; } else { 
1170
+		   var finCurrentExtension = ''; alert("The extension that you entered in the 'Extension (Internal)' field is not a valid extension!"); }
1171
+	   } else { var finCurrentExtension = ''; }
1172
+
1173
+	   var currentMobile = $("#AddSomeone_Mobile").val();
1174
+	   if (currentMobile != null && currentMobile.trim() !== '') {
1175
+	       if (/^[0-9\s\+\#]+$/.test(currentMobile)) { var finCurrentMobile = currentMobile; } else {
1176
+		   var finCurrentMobile = ''; alert("The phone number that you entered in the 'Mobile Number' field is not valid! The only allowed characters are: digits, spaces, plus signs and pound signs."); }
1177
+	   } else { var finCurrentMobile = ''; }
1178
+
1179
+	   var currentNum1 = $("#AddSomeone_Num1").val();
1180
+	   if (currentNum1 != null && currentNum1.trim() !== '') {
1181
+	       if (/^[0-9\s\+\#]+$/.test(currentNum1)) { var finCurrentNum1 = currentNum1; } else {
1182
+		   var finCurrentNum1 = ''; alert("The phone number that you entered in the 'Contact Number 1' field is not valid! The only allowed characters are: digits, spaces, plus signs and pound signs."); }
1183
+	   } else { var finCurrentNum1 = ''; }
1184
+
1185
+	   var currentNum2 = $("#AddSomeone_Num2").val();
1186
+	   if (currentNum2 != null && currentNum2.trim() !== '') {
1187
+	       if (/^[0-9\s\+\#]+$/.test(currentNum2)) { var finCurrentNum2 = currentNum2; } else {
1188
+		   var finCurrentNum2 = ''; alert("The phone number that you entered in the 'Contact Number 2' field is not valid! The only allowed characters are: digits, spaces, plus signs and pound signs."); }
1189
+           } else { var finCurrentNum2 = ''; }
1190
+
1191
+	   var currentEmail = $("#AddSomeone_Email").val();
1192
+	   if (currentEmail != null && currentEmail.trim() !== '') {
1193
+	       if (/^[A-Za-z0-9\_\.\-\~\%\+\!\?\&\*\^\=\#\$\{\}\|\/]+@[A-Za-z0-9\.\-]+\.[A-Za-z0-9\-]{2,63}$/.test(currentEmail)) { var finCurrentEmail = currentEmail; } else {
1194
+		   var finCurrentEmail = ''; alert("The email that you entered is not a valid email address!"); }
1195
+	   } else { var finCurrentEmail = ''; }
1196
+
1197
+
1198
+           // Add Contact / Extension
1199
+           var json = JSON.parse(localDB.getItem(profileUserID + "-Buddies"));
1200
+           if(json == null) json = InitUserBuddies();
1201
+
1202
+           if(finCurrentExtension == ''){
1203
+               // Add Regular Contact
1204
+               var id = uID();
1205
+               var dateNow = utcDateNow();
1206
+               json.DataCollection.push(
1207
+                   {
1208
+                    Type: "contact",
1209
+                    LastActivity: dateNow,
1210
+                    ExtensionNumber: "",
1211
+                    MobileNumber: finCurrentMobile,
1212
+                    ContactNumber1: finCurrentNum1,
1213
+                    ContactNumber2: finCurrentNum2,
1214
+                    uID: null,
1215
+                    cID: id,
1216
+                    gID: null,
1217
+                    DisplayName: currentASName,
1218
+                    Position: "",
1219
+                    Description: finCurrentDesc,
1220
+                    Email: finCurrentEmail,
1221
+                    MemberCount: 0
1222
+                   }
1223
+               );
1224
+               var newPerson = [];
1225
+               newPerson = [currentASName, finCurrentDesc, "", finCurrentMobile, finCurrentNum1, finCurrentNum2, finCurrentEmail];
1226
+               saveContactToSQLDB(newPerson);
1227
+
1228
+               var buddyObj = new Buddy("contact", id, currentASName, "", finCurrentMobile, finCurrentNum1, finCurrentNum2, dateNow, finCurrentDesc, finCurrentEmail);
1229
+               AddBuddy(buddyObj, false, false, false);
1230
+
1231
+           } else {
1232
+               // Add Extension
1233
+               var id = uID();
1234
+               var dateNow = utcDateNow();
1235
+               json.DataCollection.push(
1236
+                   {
1237
+                    Type: "extension",
1238
+                    LastActivity: dateNow,
1239
+                    ExtensionNumber: finCurrentExtension,
1240
+                    MobileNumber: finCurrentMobile,
1241
+                    ContactNumber1: finCurrentNum1,
1242
+                    ContactNumber2: finCurrentNum2,
1243
+                    uID: id,
1244
+                    cID: null,
1245
+                    gID: null,
1246
+                    DisplayName: currentASName,
1247
+                    Position: finCurrentDesc,
1248
+                    Description: "",
1249
+                    Email: finCurrentEmail,
1250
+                    MemberCount: 0
1251
+                   }
1252
+               );
1253
+
1254
+               var newPerson = [];
1255
+
1256
+               newPerson = [currentASName, finCurrentDesc, finCurrentExtension, finCurrentMobile, finCurrentNum1, finCurrentNum2, finCurrentEmail];
1257
+               saveContactToSQLDB(newPerson);
1258
+
1259
+               var buddyObj = new Buddy("extension", id, currentASName, finCurrentExtension, finCurrentMobile, finCurrentNum1, finCurrentNum2, dateNow, finCurrentDesc, finCurrentEmail);
1260
+               AddBuddy(buddyObj, false, false, true);
1261
+
1262
+           }
1263
+           // Update Size:
1264
+           json.TotalRows = json.DataCollection.length;
1265
+
1266
+           // Save To Local DB
1267
+           localDB.setItem(profileUserID + "-Buddies", JSON.stringify(json));
1268
+           UpdateBuddyList();
1269
+
1270
+           $.jeegoopopup.close();
1271
+           $("#jg_popup_b").empty();
1272
+
1273
+        } else { alert('The display name that you entered is not a valid display name!'); }
1274
+
1275
+      } else { alert("'Display Name' cannot be empty!"); }
1276
+       
1277
+   });
1278
+
1279
+   var maxWidth = $(window).width() - 12;
1280
+   var maxHeight = $(window).height() - 110;
1281
+
1282
+   if (maxWidth < 656 || maxHeight < 500) { 
1283
+       $.jeegoopopup.width(maxWidth).height(maxHeight);
1284
+       $.jeegoopopup.center();
1285
+       $("#maximizeImg").hide();
1286
+       $("#minimizeImg").hide();
1287
+   } else { 
1288
+       $.jeegoopopup.width(640).height(500);
1289
+       $.jeegoopopup.center();
1290
+       $("#minimizeImg").hide();
1291
+       $("#maximizeImg").show();
1292
+   }
1293
+
1294
+   $(window).resize(function() {
1295
+       maxWidth = $(window).width() - 16;
1296
+       maxHeight = $(window).height() - 110;
1297
+       $.jeegoopopup.center();
1298
+       if (maxWidth < 656 || maxHeight < 500) { 
1299
+           $.jeegoopopup.width(maxWidth).height(maxHeight);
1300
+           $.jeegoopopup.center();
1301
+           $("#maximizeImg").hide();
1302
+           $("#minimizeImg").hide();
1303
+       } else { 
1304
+           $.jeegoopopup.width(640).height(500);
1305
+           $.jeegoopopup.center();
1306
+           $("#minimizeImg").hide();
1307
+           $("#maximizeImg").show();
1308
+       }
1309
+   });
1310
+
1311
+   $("#minimizeImg").click(function() { $.jeegoopopup.width(640).height(500); $.jeegoopopup.center(); $("#maximizeImg").show(); $("#minimizeImg").hide(); });
1312
+   $("#maximizeImg").click(function() { $.jeegoopopup.width(maxWidth).height(maxHeight); $.jeegoopopup.center(); $("#minimizeImg").show(); $("#maximizeImg").hide(); });
1313
+
1314
+   $("#closeImg").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1315
+   $("#cancel_button").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1316
+   $("#jg_popup_overlay").click(function() { $.jeegoopopup.close(); $("#jg_popup_b").empty(); });
1317
+   $(window).on('keydown', function(event) { if (event.key == "Escape") { $.jeegoopopup.close(); $("#jg_popup_b").empty(); } });
1318
+}
1319
+
1320
+function CreateGroupWindow(){
1321
+}
1322
+
1323
+function closeVideoAudio() {
1324
+
1325
+        var localVideo = $("#local-video-preview").get(0);
1326
+        try{
1327
+            var tracks = localVideo.srcObject.getTracks();
1328
+            tracks.forEach(function(track) {
1329
+                track.stop();
1330
+            });
1331
+            localVideo.srcObject = null;
1332
+        }
1333
+        catch(e){}
1334
+
1335
+        try{
1336
+            var tracks = window.SettingsMicrophoneStream.getTracks();
1337
+            tracks.forEach(function(track) {
1338
+                track.stop();
1339
+            });
1340
+        }
1341
+        catch(e){}
1342
+        window.SettingsMicrophoneStream = null;
1343
+
1344
+        try{
1345
+            var soundMeter = window.SettingsMicrophoneSoundMeter;
1346
+            soundMeter.stop();
1347
+        }
1348
+        catch(e){}
1349
+        window.SettingsMicrophoneSoundMeter = null;
1350
+
1351
+        try{
1352
+            window.SettingsOutputAudio.pause();
1353
+        }
1354
+        catch(e){}
1355
+        window.SettingsOutputAudio = null;
1356
+
1357
+        try{
1358
+            var tracks = window.SettingsOutputStream.getTracks();
1359
+            tracks.forEach(function(track) {
1360
+                track.stop();
1361
+            });
1362
+        }
1363
+        catch(e){}
1364
+        window.SettingsOutputStream = null;
1365
+
1366
+        try{
1367
+            var soundMeter = window.SettingsOutputStreamMeter;
1368
+            soundMeter.stop();
1369
+        }
1370
+        catch(e){}
1371
+        window.SettingsOutputStreamMeter = null;
1372
+
1373
+        return true;
1374
+}
1375
+function ConfigureExtensionWindow() {
1376
+
1377
+    $("#settingsCMenu").hide();
1378
+    $.jeegoopopup.close();
1379
+
1380
+    var configWindow = "<div id=\"mainConfWindow\">";
1381
+    configWindow += "<div id='windowCtrls'><img id='minimizeImg' src='images/1_minimize.svg' title='Restore' /><img id='maximizeImg' src='images/2_maximize.svg' title='Maximize' /><img id='closeImg' src='images/3_close.svg' title='Close' /></div>"; 
1382
+    configWindow += "<div id=\"mainRightConf\">";
1383
+    configWindow += "<div id=\"rightMainConfWindow\">";
1384
+
1385
+    configWindow +=  "<div id=\"AccountHtml\" class=\"settingsSubSection\" style=\"display:block;\">";
1386
+    configWindow += "<div class=UiText>"+ lang.asterisk_server_address +": *</div>";
1387
+    configWindow += "<div><input id=Configure_Account_wssServer class=UiInputText type=text placeholder='"+ lang.eg_asterisk_server_address +"' value='"+ getDbItem("wssServer", "") +"'></div>";
1388
+    configWindow += "<div class=UiText>"+ lang.websocket_port +": *</div>";
1389
+    configWindow += "<div><input id=Configure_Account_WebSocketPort class=UiInputText type=text placeholder='"+ lang.eg_websocket_port +"' value='"+ getDbItem("WebSocketPort", "") +"'></div>";
1390
+    configWindow += "<div class=UiText>"+ lang.websocket_path +": *</div>";
1391
+    configWindow += "<div><input id=Configure_Account_ServerPath class=UiInputText type=text placeholder='"+ lang.eg_websocket_path +"' value='"+ getDbItem("ServerPath", "") +"'></div>";
1392
+    configWindow += "<div class=UiText>"+ lang.display_name +": *</div>";
1393
+
1394
+    var escapedDisplayName = 'value="' + getDbItem("profileName", "").replace("'","\'") + '"';
1395
+    configWindow += "<div><input id=Configure_Account_profileName class=UiInputText type=text placeholder='"+ lang.eg_display_name +"' "+ escapedDisplayName +"></div>";
1396
+    configWindow += "<div class=UiText>"+ lang.sip_username +": *</div>";
1397
+    configWindow += "<div><input id=Configure_Account_SipUsername class=UiInputText type=text placeholder='"+ lang.eg_sip_username +"' value='"+ getDbItem("SipUsername", "") +"'></div>";
1398
+    configWindow += "<div class=UiText>"+ lang.sip_password +": *</div>";
1399
+    configWindow += "<div><input id=Configure_Account_SipPassword class=UiInputText type=password placeholder='"+ lang.eg_sip_password +"' value='"+ getDbItem("SipPassword", "") +"'></div>";
1400
+    configWindow += "<div class=UiText>"+ lang.stun_server +":</div>";
1401
+    configWindow += "<div><input id=Configure_Account_StunServer class=UiInputText type=text placeholder='Eg: 123.123.123.123:8443' value='"+ getDbItem("StunServer", "") +"'></div>";
1402
+    configWindow += "<p style=\"color:#363636;\">* Required field.</p><br><br></div>";
1403
+
1404
+    configWindow += "<div id=\"AudioVideoHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1405
+    configWindow += "<div class=UiText>"+ lang.speaker +":</div>";
1406
+    configWindow += "<div style=\"text-align:center\"><select id=playbackSrc style=\"width:100%\"></select></div>";
1407
+    configWindow += "<div class=Settings_VolumeOutput_Container><div id=Settings_SpeakerOutput class=Settings_VolumeOutput></div></div>";
1408
+    configWindow += "<div><button class=on_white id=preview_output_play><i class=\"fa fa-play\"></i></button><button class=on_white id=preview_output_pause><i class=\"fa fa-pause\"></i></button></div>";
1409
+
1410
+    configWindow += "<br><div class=UiText>"+ lang.ring_device +":</div>";
1411
+    configWindow += "<div style=\"text-align:center\"><select id=ringDevice style=\"width:100%\"></select></div>";
1412
+    configWindow += "<div class=Settings_VolumeOutput_Container><div id=Settings_RingerOutput class=Settings_VolumeOutput></div></div>";
1413
+    configWindow += "<div><button class=on_white id=preview_ringer_play><i class=\"fa fa-play\"></i></button></div>";
1414
+
1415
+    configWindow += "<br><div class=UiText>"+ lang.microphone +":</div>";
1416
+    configWindow += "<div style=\"text-align:center\"><select id=microphoneSrc style=\"width:100%\"></select></div>";
1417
+    configWindow += "<div class=Settings_VolumeOutput_Container><div id=Settings_MicrophoneOutput class=Settings_VolumeOutput></div></div>";
1418
+    configWindow += "<br><br><div><input type=checkbox id=Settings_AutoGainControl><label for=Settings_AutoGainControl> "+ lang.auto_gain_control +"<label></div>";
1419
+    configWindow += "<div><input type=checkbox id=Settings_EchoCancellation><label for=Settings_EchoCancellation> "+ lang.echo_cancellation +"<label></div>";
1420
+    configWindow += "<div><input type=checkbox id=Settings_NoiseSuppression><label for=Settings_NoiseSuppression> "+ lang.noise_suppression +"<label></div>";
1421
+    configWindow += "<br><div class=UiText>"+ lang.camera +":</div>";
1422
+    configWindow += "<div style=\"text-align:center\"><select id=previewVideoSrc style=\"width:100%\"></select></div>";
1423
+    configWindow += "<br><div class=UiText>"+ lang.frame_rate +":</div>"
1424
+    configWindow += "<div class=pill-nav>";
1425
+    configWindow += "<input name=Settings_FrameRate id=r40 type=radio value=\"2\"><label class=radio_pill for=r40>2</label>";
1426
+    configWindow += "<input name=Settings_FrameRate id=r41 type=radio value=\"5\"><label class=radio_pill for=r41>5</label>";
1427
+    configWindow += "<input name=Settings_FrameRate id=r42 type=radio value=\"10\"><label class=radio_pill for=r42>10</label>";
1428
+    configWindow += "<input name=Settings_FrameRate id=r43 type=radio value=\"15\"><label class=radio_pill for=r43>15</label>";
1429
+    configWindow += "<input name=Settings_FrameRate id=r44 type=radio value=\"20\"><label class=radio_pill for=r44>20</label>";
1430
+    configWindow += "<input name=Settings_FrameRate id=r45 type=radio value=\"25\"><label class=radio_pill for=r45>25</label>";
1431
+    configWindow += "<input name=Settings_FrameRate id=r46 type=radio value=\"30\"><label class=radio_pill for=r46>30</label>";
1432
+    configWindow += "<input name=Settings_FrameRate id=r47 type=radio value=\"\"><label class=radio_pill for=r47><i class=\"fa fa-trash\"></i></label>";
1433
+    configWindow += "</div>";
1434
+    configWindow += "<br><br><div class=UiText>"+ lang.quality +":</div>";
1435
+    configWindow += "<div class=pill-nav>";
1436
+    configWindow += "<input name=Settings_Quality id=r30 type=radio value=\"160\"><label class=radio_pill for=r30><i class=\"fa fa-video-camera\" style=\"transform: scale(0.4)\"></i> HQVGA</label>";
1437
+    configWindow += "<input name=Settings_Quality id=r31 type=radio value=\"240\"><label class=radio_pill for=r31><i class=\"fa fa-video-camera\" style=\"transform: scale(0.6)\"></i> QVGA</label>";
1438
+    configWindow += "<input name=Settings_Quality id=r32 type=radio value=\"480\"><label class=radio_pill for=r32><i class=\"fa fa-video-camera\" style=\"transform: scale(0.8)\"></i> VGA</label>";
1439
+    configWindow += "<input name=Settings_Quality id=r33 type=radio value=\"720\"><label class=radio_pill for=r33><i class=\"fa fa-video-camera\" style=\"transform: scale(1)\"></i> HD</label>";
1440
+    configWindow += "<input name=Settings_Quality id=r34 type=radio value=\"\"><label class=radio_pill for=r34><i class=\"fa fa-trash\"></i></label>";
1441
+    configWindow += "</div>";
1442
+    configWindow += "<br><br><div class=UiText>"+ lang.image_orientation +":</div>";
1443
+    configWindow += "<div class=pill-nav>";
1444
+    configWindow += "<input name=Settings_Oriteation id=r20 type=radio value=\"rotateY(0deg)\"><label class=radio_pill for=r20><i class=\"fa fa-address-card\" style=\"transform: rotateY(0deg)\"></i> Normal</label>";
1445
+    configWindow += "<input name=Settings_Oriteation id=r21 type=radio value=\"rotateY(180deg)\"><label class=radio_pill for=r21><i class=\"fa fa-address-card\" style=\"transform: rotateY(180deg)\"></i> Mirror</label>";
1446
+    configWindow += "</div>";
1447
+    configWindow += "<br><br><div class=UiText>"+ lang.aspect_ratio +":</div>";
1448
+    configWindow += "<div class=pill-nav>";
1449
+    configWindow += "<input name=Settings_AspectRatio id=r10 type=radio value=\"1\"><label class=radio_pill for=r10><i class=\"fa fa-square-o\" style=\"transform: scaleX(1); margin-left: 7px; margin-right: 7px\"></i> 1:1</label>";
1450
+    configWindow += "<input name=Settings_AspectRatio id=r11 type=radio value=\"1.33\"><label class=radio_pill for=r11><i class=\"fa fa-square-o\" style=\"transform: scaleX(1.33); margin-left: 5px; margin-right: 5px;\"></i> 4:3</label>";
1451
+    configWindow += "<input name=Settings_AspectRatio id=r12 type=radio value=\"1.77\"><label class=radio_pill for=r12><i class=\"fa fa-square-o\" style=\"transform: scaleX(1.77); margin-right: 3px;\"></i> 16:9</label>";
1452
+    configWindow += "<input name=Settings_AspectRatio id=r13 type=radio value=\"\"><label class=radio_pill for=r13><i class=\"fa fa-trash\"></i></label>";
1453
+    configWindow += "</div>";
1454
+    configWindow += "<br><br><div class=UiText>"+ lang.preview +":</div>";
1455
+    configWindow += "<div style=\"text-align:center; margin-top:10px\"><video id=\"local-video-preview\" class=\"previewVideo\"></video></div>";
1456
+    configWindow += "<br><div class=UiText>"+ lang.video_conference_extension +":</div>";
1457
+    configWindow += "<div><input id=Video_Conf_Extension class=UiInputText type=text placeholder='"+ lang.video_conference_extension_example +"' value='"+ getDbItem("VidConfExtension", "") +"'></div>";
1458
+    configWindow += "<br><div class=UiText>"+ lang.video_conference_window_width +":</div>";
1459
+    configWindow += "<div><input id=Video_Conf_Window_Width class=UiInputText type=text placeholder='"+ lang.video_conf_window_width_explanation +"' value='"+ getDbItem("VidConfWindowWidth", "") +"'></div>";
1460
+    if (getDbItem("userrole", "") == "superadmin") {
1461
+
1462
+	configWindow += "<div id=confTableSection>"+ lang.external_conf_users +"</div>";
1463
+        configWindow += "<div class=confTable><table id=vidConfExternalTable>";
1464
+        configWindow += "<tr class=btnTableRow><td><label for=extConfExtension id=extensionThLabel class=confExtLabels>Extension</label></td><td><label for=extConfExtensionPass id=extPassThLabel class=confExtLabels>SIP Password</label></td><td><label for=extConfExtensionLink id=extLinkThLabel class=confExtLabels>Link</label></td></tr>";
1465
+
1466
+        for (var cqt = 0; cqt < getDbItem("externalUserConfElem", ""); cqt++) {
1467
+	     configWindow += "<tr class=btnTableRow><td><input type=text class=extConfExtension name=extConfExtension value='"+ getDbItem('extUserExtension-'+cqt, '') +"' disabled=\"disabled\" /></td><td><input type=password class=extConfExtensionPass name=extConfExtensionPass value='"+ getDbItem('extUserExtensionPass-'+cqt, '') +"' disabled=\"disabled\"/></td><td><input type=text class=extConfExtensionLink name=extConfExtensionLink value='"+ getDbItem('confAccessLink-'+cqt, '') +"' /></td><td><span class=\"copyToClipboard\"><i class=\"fa fa-clipboard\" aria-hidden=\"true\" title=\"Copy link to clipboard.\"></i></span></td><td><span class=\"deleteExtRow\" title=\"Delete extension data from database.\">X</span></td><td><input type=submit class=saveExtConfExtension value=\"Edit\" title=\"Edit this row.\" /></td></tr>";
1468
+        }
1469
+
1470
+	configWindow += "<tr id=emptyExtRow class=btnTableRow><td><input type=text class=extConfExtension name=extConfExtension placeholder=\"Eg: 711\" /></td><td><input type=password class=extConfExtensionPass name=extConfExtensionPass placeholder=\"Eg: d5?W?9q?8rg*R9!eFrVth?9\" /></td><td><input type=text class=extConfExtensionLink name=extConfExtensionLink placeholder=\"Generated on 'Save'\"  disabled=\"disabled\" /></td><td><span class=\"copyToClipboard\"><i class=\"fa fa-clipboard\" aria-hidden=\"true\" title=\"Copy link to clipboard.\"></i></span></td><td><span class=\"deleteExtRow deleteExtRowDisabled\" title=\"Delete extension data from database.\">X</span></td><td><input type=submit class=saveExtConfExtension value=Save title=\"Save this row.\" /></td></tr>";
1471
+        configWindow += "</table></div>";
1472
+	configWindow += "<button id=add_New_External_User>Add External User</button>";
1473
+    }
1474
+    configWindow += "<br><br></div>";
1475
+
1476
+    configWindow += "<div id=\"AppearanceHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1477
+    configWindow += "<div id=ImageCanvas style=\"width:150px; height:150px;\"></div>";
1478
+    configWindow += "<label for=fileUploader class=customBrowseButton style=\"margin-left: 200px; margin-top: -2px;\">Select File</label>";
1479
+    configWindow += "<div><input id=fileUploader type=file></div>";
1480
+    configWindow += "<div style=\"margin-top: 50px\"></div>";
1481
+    configWindow += "</div>";
1482
+
1483
+    configWindow += "<div id=\"NotificationsHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1484
+    configWindow += "<div class=UiText>"+ lang.notifications +":</div>";
1485
+    configWindow += "<div id=\"notificationsCheck\"><input type=checkbox id=Settings_Notifications><label for=Settings_Notifications> "+ lang.enable_onscreen_notifications +"<label></div>";
1486
+    configWindow += "</div>";
1487
+
1488
+    configWindow += "<div id=\"RoundcubeEmailHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1489
+    configWindow += "<div class=UiText>"+ lang.email_integration +":</div>";
1490
+    configWindow += "<div id=\"enableRCcheck\"><input id=emailIntegration type=checkbox ><label for=emailIntegration> "+ lang.enable_roundcube_integration +"<label></div>";
1491
+    configWindow += "<div class=UiText>"+ lang.roundcube_domain +":</div>";
1492
+    configWindow += "<div><input id=RoundcubeDomain class=UiInputText type=text placeholder='Roundcube domain (Eg: mail.example.com).' value='"+ getDbItem("rcDomain", "") +"'></div>";
1493
+    configWindow += "<div class=UiText>"+ lang.roundcube_user +":</div>";
1494
+    configWindow += "<div><input id=RoundcubeUser class=UiInputText type=text placeholder='Roundcube login user (Eg: john.doe@example.com or john_doe).' value='"+ getDbItem("RoundcubeUser", "") +"'></div>";
1495
+    configWindow += "<div class=UiText>"+ lang.roundcube_password +":</div>";
1496
+    configWindow += "<div><input id=RoundcubePass class=UiInputText type=password placeholder='Roundcube login password.' value='"+ getDbItem("RoundcubePass", "") +"'></div>";
1497
+    configWindow += "<div class=UiText>"+ lang.rc_basic_auth_user +":</div>";
1498
+    configWindow += "<div><input id=rcBasicAuthUser class=UiInputText type=text placeholder='If you have a Roundcube basic auth user, enter it here.' value='"+ getDbItem("rcBasicAuthUser", "") +"'></div>";
1499
+    configWindow += "<div class=UiText>"+ lang.rc_basic_auth_password +":</div>";
1500
+    configWindow += "<div><input id=rcBasicAuthPass class=UiInputText type=password placeholder='If you have a Roundcube basic auth password, enter it here.' value='"+ getDbItem("rcBasicAuthPass", "") +"'></div>";
1501
+
1502
+    configWindow += "<br><br></div>";
1503
+
1504
+    configWindow += "<div id=\"ChangePasswordHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1505
+    configWindow += "<div class=UiText>"+ lang.current_user_password +":</div>";
1506
+    configWindow += "<div><input id=Current_User_Password class=UiInputText type=password placeholder='Enter your current Roundpin user password.' value=''></div>";
1507
+    configWindow += "<div class=UiText>"+ lang.new_user_password +":</div>";
1508
+    configWindow += "<div><input id=New_User_Password class=UiInputText type=password placeholder='Enter your new Roundpin user password.' value=''></div>";
1509
+    configWindow += "<div class=UiText>"+ lang.repeat_new_user_password +":</div>";
1510
+    configWindow += "<div><input id=Repeat_New_User_Password class=UiInputText type=password placeholder='Enter your new Roundpin user password again.' value=''></div><br>";
1511
+    configWindow += "<div><input id=Save_New_User_Password type=button value='Save New Password' onclick='saveNewUserPassword()' ></div>";
1512
+    configWindow += "<br><br></div>";
1513
+
1514
+    configWindow += "<div id=\"ChangeEmailHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1515
+    configWindow += "<div class=UiText>"+ lang.current_user_email +":</div>";
1516
+    configWindow += "<div><input id=Current_User_Email class=UiInputText type=text placeholder='Enter your current Roundpin email address.' value=''></div>";
1517
+    configWindow += "<div class=UiText>"+ lang.new_user_email +":</div>";
1518
+    configWindow += "<div><input id=New_User_Email class=UiInputText type=text placeholder='Enter your new email address.' value=''></div>";
1519
+    configWindow += "<div class=UiText>"+ lang.repeat_new_user_email +":</div>";
1520
+    configWindow += "<div><input id=Repeat_New_User_Email class=UiInputText type=text placeholder='Enter your new email address again.' value=''></div><br>";
1521
+    configWindow += "<div><input id=Save_New_User_Email type=button value='Save New Email' onclick='saveNewUserEmail()' ></div>";
1522
+    configWindow += "<br><br></div>";
1523
+
1524
+    configWindow += "<div id=\"CloseAccountHtml\" class=\"settingsSubSection\" style=\"display:none;\">";
1525
+    configWindow += "<div class=UiText>"+ lang.if_you_want_to_close_account +":</div><br><br>";
1526
+    configWindow += "<div><input id=Close_User_Account type=button value='Close User Account' onclick='closeUserAccount()' ></div>";
1527
+    configWindow += "<br><br></div>";
1528
+
1529
+    configWindow += "</div></div></div>";
1530
+
1531
+    var settingsSections = "<table id=leftPanelSettings cellspacing=14 cellpadding=0 style=\"width:184px;margin-left:8px;margin-top:14px;font-size:15px;\">";
1532
+    settingsSections += "<tr id=ConnectionSettingsRow><td class=SettingsSection>Connection Settings</td></tr>";
1533
+    settingsSections += "<tr id=AudioAndVideoRow><td class=SettingsSection>Audio & Video</td></tr>";
1534
+    settingsSections += "<tr id=ProfilePictureRow><td class=SettingsSection>Profile Picture</td></tr>";
1535
+    settingsSections += "<tr id=NotificationsRow><td class=SettingsSection>Notifications</td></tr>";
1536
+    settingsSections += "<tr id=RoundcubeEmailRow><td class=SettingsSection>Email Integration</td></tr>";
1537
+    settingsSections += "<tr id=ChangePasswordRow><td class=SettingsSection>Change Password</td></tr>";
1538
+    settingsSections += "<tr id=ChangeEmailRow><td class=SettingsSection>Change Email</td></tr>";
1539
+    settingsSections += "<tr id=CloseAccountRow><td class=SettingsSection>Close Account</td></tr></table>";
1540
+
1541
+    $.jeegoopopup.open({
1542
+                title: '<span id=settingsTitle>Settings</span>',
1543
+                html: configWindow,
1544
+                width: '520',
1545
+                height: '500',
1546
+                center: true,
1547
+                scrolling: 'no',
1548
+                skinClass: 'jg_popup_basic',
1549
+                contentClass: 'configPopup',
1550
+                overlay: true,
1551
+                opacity: 50,
1552
+                draggable: true,
1553
+                resizable: false,
1554
+                fadeIn: 0
1555
+    });
1556
+
1557
+    $("#jg_popup_b").append('<div id="bottomButtonsConf"><button id="save_button_conf">Save</button><button id="cancel_button_conf">Cancel</button></div>');
1558
+    $("#jg_popup_l").append(settingsSections);
1559
+
1560
+    if (getDbItem("useRoundcube", "") == 1) { $("#emailIntegration").prop("checked", true); } else { $("#emailIntegration").prop("checked", false); }
1561
+
1562
+    $("#ConnectionSettingsRow td").addClass("selectedSettingsSection");
1563
+
1564
+    $("#ConnectionSettingsRow").click(function() {
1565
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1566
+        $("#AccountHtml").css("display", "block");
1567
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1568
+        $("#ConnectionSettingsRow td").addClass("selectedSettingsSection");
1569
+    });
1570
+
1571
+    $("#ProfilePictureRow").click(function() {
1572
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1573
+        $("#AppearanceHtml").css("display", "block");
1574
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1575
+        $("#ProfilePictureRow td").addClass("selectedSettingsSection");
1576
+    });
1577
+
1578
+    $("#NotificationsRow").click(function() {
1579
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1580
+        $("#NotificationsHtml").css("display", "block");
1581
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1582
+        $("#NotificationsRow td").addClass("selectedSettingsSection");
1583
+    });
1584
+
1585
+    $("#RoundcubeEmailRow").click(function() {
1586
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1587
+        $("#RoundcubeEmailHtml").css("display", "block");
1588
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1589
+        $("#RoundcubeEmailRow td").addClass("selectedSettingsSection");
1590
+    });
1591
+
1592
+    $("#ChangePasswordRow").click(function() {
1593
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1594
+        $("#ChangePasswordHtml").css("display", "block");
1595
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1596
+        $("#ChangePasswordRow td").addClass("selectedSettingsSection");
1597
+    });
1598
+
1599
+    $("#ChangeEmailRow").click(function() {
1600
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1601
+        $("#ChangeEmailHtml").css("display", "block");
1602
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1603
+        $("#ChangeEmailRow td").addClass("selectedSettingsSection");
1604
+    });
1605
+
1606
+    $("#CloseAccountRow").click(function() {
1607
+        $(".settingsSubSection").each(function() { $(this).css("display", "none"); });
1608
+        $("#CloseAccountHtml").css("display", "block");
1609
+        $(".SettingsSection").each(function() { $(this).removeClass("selectedSettingsSection"); });
1610
+        $("#CloseAccountRow td").addClass("selectedSettingsSection");
1611
+    });
1612
+
1613
+    var maxWidth = $(window).width() - 192;
1614
+    var maxHeight = $(window).height() - 98;
1615
+
1616
+    if (maxWidth < 520 || maxHeight < 500) { 
1617
+       $.jeegoopopup.width(maxWidth).height(maxHeight);
1618
+       $.jeegoopopup.center();
1619
+       $("#maximizeImg").hide();
1620
+       $("#minimizeImg").hide();
1621
+    } else { 
1622
+       $.jeegoopopup.width(520).height(500);
1623
+       $.jeegoopopup.center();
1624
+       $("#minimizeImg").hide();
1625
+       $("#maximizeImg").show();
1626
+    }
1627
+
1628
+    $(window).resize(function() {
1629
+       maxWidth = $(window).width() - 192;
1630
+       maxHeight = $(window).height() - 98;
1631
+       $.jeegoopopup.center();
1632
+       if (maxWidth < 520 || maxHeight < 500) { 
1633
+           $.jeegoopopup.width(maxWidth).height(maxHeight);
1634
+           $.jeegoopopup.center();
1635
+           $("#maximizeImg").hide();
1636
+           $("#minimizeImg").hide();
1637
+       } else { 
1638
+           $.jeegoopopup.width(520).height(500);
1639
+           $.jeegoopopup.center();
1640
+           $("#minimizeImg").hide();
1641
+           $("#maximizeImg").show();
1642
+       }
1643
+    });
1644
+
1645
+    $("#minimizeImg").click(function() { $.jeegoopopup.width(520).height(500); $.jeegoopopup.center(); $("#maximizeImg").show(); $("#minimizeImg").hide(); });
1646
+    $("#maximizeImg").click(function() { $.jeegoopopup.width(maxWidth).height(maxHeight); $.jeegoopopup.center(); $("#minimizeImg").show(); $("#maximizeImg").hide(); });
1647
+
1648
+
1649
+    // Output
1650
+    var selectAudioScr = $("#playbackSrc");
1651
+
1652
+    var playButton = $("#preview_output_play");
1653
+
1654
+    var playRingButton = $("#preview_ringer_play");
1655
+
1656
+    var pauseButton = $("#preview_output_pause");
1657
+
1658
+    // Microphone
1659
+    var selectMicScr = $("#microphoneSrc");
1660
+    $("#Settings_AutoGainControl").prop("checked", AutoGainControl);
1661
+    $("#Settings_EchoCancellation").prop("checked", EchoCancellation);
1662
+    $("#Settings_NoiseSuppression").prop("checked", NoiseSuppression);
1663
+
1664
+    // Webcam
1665
+    var selectVideoScr = $("#previewVideoSrc");
1666
+
1667
+    // Orientation
1668
+    var OrientationSel = $("input[name=Settings_Oriteation]");
1669
+    OrientationSel.each(function(){
1670
+        if(this.value == MirrorVideo) $(this).prop("checked", true);
1671
+    });
1672
+    $("#local-video-preview").css("transform", MirrorVideo);
1673
+
1674
+    // Frame Rate
1675
+    var frameRateSel = $("input[name=Settings_FrameRate]");
1676
+    frameRateSel.each(function(){
1677
+        if(this.value == maxFrameRate) $(this).prop("checked", true);
1678
+    });
1679
+
1680
+    // Quality
1681
+    var QualitySel = $("input[name=Settings_Quality]");
1682
+    QualitySel.each(function(){
1683
+        if(this.value == videoHeight) $(this).prop("checked", true);
1684
+    });
1685
+
1686
+    // Aspect Ratio
1687
+    var AspectRatioSel = $("input[name=Settings_AspectRatio]");
1688
+    AspectRatioSel.each(function(){
1689
+        if(this.value == videoAspectRatio) $(this).prop("checked", true);
1690
+    });
1691
+
1692
+    // Ring Tone
1693
+    var selectRingTone = $("#ringTone");
1694
+
1695
+    // Ring Device
1696
+    var selectRingDevice = $("#ringDevice");
1697
+
1698
+    // Handle Aspect Ratio Change
1699
+    AspectRatioSel.change(function(){
1700
+        console.log("Call to change Aspect Ratio ("+ this.value +")");
1701
+
1702
+        var localVideo = $("#local-video-preview").get(0);
1703
+        localVideo.muted = true;
1704
+        localVideo.playsinline = true;
1705
+        localVideo.autoplay = true;
1706
+
1707
+        var tracks = localVideo.srcObject.getTracks();
1708
+        tracks.forEach(function(track) {
1709
+            track.stop();
1710
+        });
1711
+
1712
+        var constraints = {
1713
+            audio: false,
1714
+            video: {
1715
+                deviceId: (selectVideoScr.val() != "default")? { exact: selectVideoScr.val() } : "default"
1716
+            }
1717
+        }
1718
+        if($("input[name=Settings_FrameRate]:checked").val() != ""){
1719
+            constraints.video.frameRate = $("input[name=Settings_FrameRate]:checked").val();
1720
+        }
1721
+        if($("input[name=Settings_Quality]:checked").val() != ""){
1722
+            constraints.video.height = $("input[name=Settings_Quality]:checked").val();
1723
+        }
1724
+        if(this.value != ""){
1725
+            constraints.video.aspectRatio = this.value;
1726
+        }        
1727
+        console.log("Constraints:", constraints);
1728
+        var localStream = new MediaStream();
1729
+        if(navigator.mediaDevices){
1730
+            navigator.mediaDevices.getUserMedia(constraints).then(function(newStream){
1731
+                var videoTrack = newStream.getVideoTracks()[0];
1732
+                localStream.addTrack(videoTrack);
1733
+                localVideo.srcObject = localStream;
1734
+                localVideo.onloadedmetadata = function(e) {
1735
+                    localVideo.play();
1736
+                }
1737
+            }).catch(function(e){
1738
+                console.error(e);
1739
+                AlertConfigExtWindow(lang.alert_error_user_media, lang.error);
1740
+            });
1741
+        }
1742
+    });
1743
+
1744
+    // Handle Video Height Change
1745
+    QualitySel.change(function(){    
1746
+        console.log("Call to change Video Height ("+ this.value +")");
1747
+
1748
+        var localVideo = $("#local-video-preview").get(0);
1749
+        localVideo.muted = true;
1750
+        localVideo.playsinline = true;
1751
+        localVideo.autoplay = true;
1752
+
1753
+        var tracks = localVideo.srcObject.getTracks();
1754
+        tracks.forEach(function(track) {
1755
+            track.stop();
1756
+        });
1757
+
1758
+        var constraints = {
1759
+            audio: false,
1760
+            video: {
1761
+                deviceId: (selectVideoScr.val() != "default")? { exact: selectVideoScr.val() } : "default" ,
1762
+            }
1763
+        }
1764
+        if($("input[name=Settings_FrameRate]:checked").val() != ""){
1765
+            constraints.video.frameRate = $("input[name=Settings_FrameRate]:checked").val();
1766
+        }
1767
+        if(this.value){
1768
+            constraints.video.height = this.value;
1769
+        }
1770
+        if($("input[name=Settings_AspectRatio]:checked").val() != ""){
1771
+            constraints.video.aspectRatio = $("input[name=Settings_AspectRatio]:checked").val();
1772
+        } 
1773
+        console.log("Constraints:", constraints);
1774
+        var localStream = new MediaStream();
1775
+        if(navigator.mediaDevices){
1776
+            navigator.mediaDevices.getUserMedia(constraints).then(function(newStream){
1777
+                var videoTrack = newStream.getVideoTracks()[0];
1778
+                localStream.addTrack(videoTrack);
1779
+                localVideo.srcObject = localStream;
1780
+                localVideo.onloadedmetadata = function(e) {
1781
+                    localVideo.play();
1782
+                }
1783
+            }).catch(function(e){
1784
+                console.error(e);
1785
+                AlertConfigExtWindow(lang.alert_error_user_media, lang.error);
1786
+            });
1787
+        }
1788
+    });    
1789
+
1790
+    // Handle Frame Rate Change 
1791
+    frameRateSel.change(function(){
1792
+        console.log("Call to change Frame Rate ("+ this.value +")");
1793
+
1794
+        var localVideo = $("#local-video-preview").get(0);
1795
+        localVideo.muted = true;
1796
+        localVideo.playsinline = true;
1797
+        localVideo.autoplay = true;
1798
+
1799
+        var tracks = localVideo.srcObject.getTracks();
1800
+        tracks.forEach(function(track) {
1801
+            track.stop();
1802
+        });
1803
+
1804
+        var constraints = {
1805
+            audio: false,
1806
+            video: {
1807
+                deviceId: (selectVideoScr.val() != "default")? { exact: selectVideoScr.val() } : "default" ,
1808
+            }
1809
+        }
1810
+        if(this.value != ""){
1811
+            constraints.video.frameRate = this.value;
1812
+        }
1813
+        if($("input[name=Settings_Quality]:checked").val() != ""){
1814
+            constraints.video.height = $("input[name=Settings_Quality]:checked").val();
1815
+        }
1816
+        if($("input[name=Settings_AspectRatio]:checked").val() != ""){
1817
+            constraints.video.aspectRatio = $("input[name=Settings_AspectRatio]:checked").val();
1818
+        } 
1819
+        console.log("Constraints:", constraints);
1820
+        var localStream = new MediaStream();
1821
+        if(navigator.mediaDevices){
1822
+            navigator.mediaDevices.getUserMedia(constraints).then(function(newStream){
1823
+                var videoTrack = newStream.getVideoTracks()[0];
1824
+                localStream.addTrack(videoTrack);
1825
+                localVideo.srcObject = localStream;
1826
+                localVideo.onloadedmetadata = function(e) {
1827
+                    localVideo.play();
1828
+                }
1829
+            }).catch(function(e){
1830
+                console.error(e);
1831
+                AlertConfigExtWindow(lang.alert_error_user_media, lang.error);
1832
+            });
1833
+        }
1834
+    });
1835
+
1836
+    // Handle Audio Source changes (Microphone)
1837
+    selectMicScr.change(function(){
1838
+        console.log("Call to change Microphone ("+ this.value +")");
1839
+
1840
+        // Change and update visual preview
1841
+        try{
1842
+            var tracks = window.SettingsMicrophoneStream.getTracks();
1843