From 4f67a99678473556dbb7de3dda1e3f5bacec3f82 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Wed, 30 Jan 2019 17:36:00 +0100 Subject: [PATCH 001/150] Use the non-deprecated UNUserNotification framework on iOS 10 and up --- Riot/AppDelegate.m | 62 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 7f67cc7b0..dc2c6ff84 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -71,6 +71,8 @@ #endif #ifdef CALL_STACK_JINGLE #import +#import + #endif #define CALL_STATUS_BAR_HEIGHT 44 @@ -1076,23 +1078,55 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { if (!isPushRegistered) { - NSMutableSet* notificationCategories = [NSMutableSet set]; + if (@available(iOS 10, *)) { + UNTextInputNotificationAction *quickReply = [UNTextInputNotificationAction + actionWithIdentifier:@"inline-reply" + title:NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil) + options:UNNotificationActionOptionAuthenticationRequired + ]; - UIMutableUserNotificationAction* quickReply = [[UIMutableUserNotificationAction alloc] init]; - quickReply.title = NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); - quickReply.identifier = @"inline-reply"; - quickReply.activationMode = UIUserNotificationActivationModeBackground; - quickReply.authenticationRequired = true; - quickReply.behavior = UIUserNotificationActionBehaviorTextInput; + UNNotificationCategory *quickReplyCategory = [UNNotificationCategory + categoryWithIdentifier:@"QUICK_REPLY" + actions:@[quickReply] + intentIdentifiers:NULL + options:UNNotificationCategoryOptionNone]; - UIMutableUserNotificationCategory* quickReplyCategory = [[UIMutableUserNotificationCategory alloc] init]; - quickReplyCategory.identifier = @"QUICK_REPLY"; - [quickReplyCategory setActions:@[quickReply] forContext:UIUserNotificationActionContextDefault]; - [notificationCategories addObject:quickReplyCategory]; + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center setNotificationCategories:[[NSSet alloc] initWithArray:@[quickReplyCategory]]]; + [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) + completionHandler:^(BOOL granted, NSError *error) + { // code here is equivalent to self:application:didRegisterUserNotificationSettings: + if (granted) { + [self registerForRemoteNotificationsWithCompletion:nil]; + } + else + { + // Clear existing token + MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; + [accountManager setPushDeviceToken:nil withPushOptions:nil]; + } + }]; + } + else + { + NSMutableSet *notificationCategories = [NSMutableSet set]; - // Registration on iOS 8 and later - UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:notificationCategories]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + UIMutableUserNotificationAction *quickReply = [[UIMutableUserNotificationAction alloc] init]; + quickReply.title = NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); + quickReply.identifier = @"inline-reply"; + quickReply.activationMode = UIUserNotificationActivationModeBackground; + quickReply.authenticationRequired = true; + quickReply.behavior = UIUserNotificationActionBehaviorTextInput; + + UIMutableUserNotificationCategory *quickReplyCategory = [[UIMutableUserNotificationCategory alloc] init]; + quickReplyCategory.identifier = @"QUICK_REPLY"; + [quickReplyCategory setActions:@[quickReply] forContext:UIUserNotificationActionContextDefault]; + [notificationCategories addObject:quickReplyCategory]; + + // Registration on iOS 8 and later + UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:notificationCategories]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + } } } From 8402106bba1f1f0eff038f28286c2cba5ba6f60a Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Wed, 30 Jan 2019 22:01:55 +0100 Subject: [PATCH 002/150] Implement UNUserNotificationCenterDelegate methods --- Riot/AppDelegate.h | 3 +- Riot/AppDelegate.m | 116 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 2 deletions(-) diff --git a/Riot/AppDelegate.h b/Riot/AppDelegate.h index 811161010..df985fa13 100644 --- a/Riot/AppDelegate.h +++ b/Riot/AppDelegate.h @@ -17,6 +17,7 @@ #import #import +#import #import "MasterTabBarController.h" #import "JitsiViewController.h" @@ -37,7 +38,7 @@ extern NSString *const kAppDelegateDidTapStatusBarNotification; */ extern NSString *const kAppDelegateNetworkStatusDidChangeNotification; -@interface AppDelegate : UIResponder +@interface AppDelegate : UIResponder { BOOL isPushRegistered; diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index dc2c6ff84..a1cf1684c 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1093,6 +1093,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center setNotificationCategories:[[NSSet alloc] initWithArray:@[quickReplyCategory]]]; + [center setDelegate:self]; // commenting this out will fall back to using the same AppDelegate methods as the iOS 9 way of doing this [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError *error) { // code here is equivalent to self:application:didRegisterUserNotificationSettings: @@ -1107,7 +1108,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } }]; } - else + else // DEPRECATED, for iOS 9 { NSMutableSet *notificationCategories = [NSMutableSet set]; @@ -1139,6 +1140,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN self.pushRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP]; } +// DEPRECATED, for iOS 9 - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { // Register for remote notifications only if user provide access to notification feature @@ -1154,6 +1156,70 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } +// iOS 10+, see application:handleActionWithIdentifier:forLocalNotification:withResponseInfo:completionHandler: +- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler +{ + UNNotification *notification = response.notification; + UNNotificationContent *content = notification.request.content; + if ([[response actionIdentifier] isEqualToString:@"inline-reply"]) { + UNTextInputNotificationResponse *textResponse = (UNTextInputNotificationResponse *) response; + NSString* roomId = content.userInfo[@"room_id"]; + + if (roomId.length) { + NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts; + + MXKRoomDataSourceManager* manager; + for (MXKAccount* account in mxAccounts) + { + MXRoom* room = [account.mxSession roomWithRoomId:roomId]; + if (room) + { + manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession]; + if (manager) + { + break; + } + } + } + if (manager == nil) + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found", roomId); + } + else + { + [manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { + NSString* responseText = [textResponse userText]; + if (responseText != nil && responseText.length != 0) + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@", roomId); + [roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) { + UNMutableNotificationContent *failureNotificationContent = [[UNMutableNotificationContent alloc] init]; + failureNotificationContent.userInfo = content.userInfo; + failureNotificationContent.body = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); + + UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest + requestWithIdentifier:@"failureNotification" + content:failureNotificationContent + trigger:nil]; + + [center addNotificationRequest:failureNotificationRequest withCompletionHandler:nil]; + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error sending text message: %@", error); + }]; + } + + completionHandler(); + }]; + } + } + } + else + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", [response actionIdentifier]); + } + completionHandler(); +} + +// DEPRECATED, for iOS 9 // "This block is not a prototype" - don't fix this, or it won't match Apple's definition - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { @@ -1210,6 +1276,54 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN completionHandler(); } +// iOS 10+, see application:didReceiveLocalNotification: +- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler +{ + NSLog(@"[AppDelegate][Push] willPresentNotification: applicationState: %@", @([UIApplication sharedApplication].applicationState)); + + NSString* roomId = notification.request.content.userInfo[@"room_id"]; + if (roomId.length) + { + // TODO retrieve the right matrix session + // We can use the "user_id" value in notification.userInfo + + //************** + // Patch consider the first session which knows the room id + MXKAccount *dedicatedAccount = nil; + + NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; + + if (mxAccounts.count == 1) + { + dedicatedAccount = mxAccounts.firstObject; + } + else + { + for (MXKAccount *account in mxAccounts) + { + if ([account.mxSession roomWithRoomId:roomId]) + { + dedicatedAccount = account; + break; + } + } + } + + // sanity checks + if (dedicatedAccount && dedicatedAccount.mxSession) + { + NSLog(@"[AppDelegate][Push] willPresentNotification: open the roomViewController %@", roomId); + + [self showRoom:roomId andEventId:nil withMatrixSession:dedicatedAccount.mxSession]; + } + else + { + NSLog(@"[AppDelegate][Push] willPresentNotification : no linked session / account has been found."); + } + } +} + +// DEPRECATED, for iOS 9 - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification { NSLog(@"[AppDelegate][Push] didReceiveLocalNotification: applicationState: %@", @(application.applicationState)); From 54dc486541f67b9fdaf5ceda0c5cae7dd11b2550 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Wed, 30 Jan 2019 23:01:59 +0100 Subject: [PATCH 003/150] add iOS 10+ alternatives to all uses of the old notification system --- Riot/AppDelegate.m | 183 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 35 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index a1cf1684c..0d509b8fa 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1500,7 +1500,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN MXEvent *event; // Ignore event already notified to the user - if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) + if (@available(iOS 10, *)) + { + if ([self displayedNotificationRequestForEvent:eventId andUser:account.mxCredentials.userId type:nil]) + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); + continue; + } + } + else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; @@ -1569,41 +1577,88 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // to include a percent symbol (%) in the message, use two percent symbols (%%). notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; - eventNotification.alertBody = notificationBody; - eventNotification.userInfo = @{ - @"type": @"full", - @"room_id": event.roomId, - @"event_id": event.eventId, - @"user_id": account.mxCredentials.userId - }; - - BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; - - if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) + if (@available(iOS 10, *)) { - eventNotification.category = @"QUICK_REPLY"; - } + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - // Set sound name based on the value provided in action of MXPushRule - for (MXPushRuleAction *action in rule.actions) - { - if (action.actionType == MXPushRuleActionTypeSetTweak) + content.body = notificationBody; + content.userInfo = @{ + @"type": @"full", + @"room_id": event.roomId, + @"event_id": event.eventId, + @"user_id": account.mxCredentials.userId + }; + + BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; + + UNNotificationRequest *request = [UNNotificationRequest + requestWithIdentifier:event.eventId + content:content + trigger:nil]; + + if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) { - if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) - { - NSString *soundName = action.parameters[@"value"]; - if ([soundName isEqualToString:@"default"]) - soundName = @"message.mp3"; + content.categoryIdentifier = @"QUICK_REPLY"; + } - eventNotification.soundName = soundName; + // Set sound name based on the value provided in action of MXPushRule + for (MXPushRuleAction *action in rule.actions) + { + if (action.actionType == MXPushRuleActionTypeSetTweak) + { + if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) + { + NSString *soundName = action.parameters[@"value"]; + if ([soundName isEqualToString:@"default"]) + soundName = @"message.mp3"; + + content.sound = [UNNotificationSound soundNamed:soundName]; + } } } - } - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); - [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; - scheduledNotifications++; + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; + scheduledNotifications++; + } + else // DEPRECATED, for iOS 9 + { + UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; + eventNotification.alertBody = notificationBody; + eventNotification.userInfo = @{ + @"type": @"full", + @"room_id": event.roomId, + @"event_id": event.eventId, + @"user_id": account.mxCredentials.userId + }; + + BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; + + if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) + { + eventNotification.category = @"QUICK_REPLY"; + } + + // Set sound name based on the value provided in action of MXPushRule + for (MXPushRuleAction *action in rule.actions) + { + if (action.actionType == MXPushRuleActionTypeSetTweak) + { + if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) + { + NSString *soundName = action.parameters[@"value"]; + if ([soundName isEqualToString:@"default"]) + soundName = @"message.mp3"; + + eventNotification.soundName = soundName; + } + } + } + + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); + [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; + scheduledNotifications++; + } } else { @@ -1770,7 +1825,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN for (NSString *eventId in events) { // Ignore event already notified to the user - if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) + if (@available(iOS 10, *)) + { + if ([self displayedNotificationRequestForEvent:eventId andUser:userId type:nil]) + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); + continue; + } + } + else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; @@ -1795,12 +1858,26 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: room_id is missing for event %@ in payload %@", eventId, payload); } - UILocalNotification *localNotificationForFailedSync = [[UILocalNotification alloc] init]; - localNotificationForFailedSync.userInfo = userInfo; - localNotificationForFailedSync.alertBody = [self limitedNotificationBodyForEvent:eventId inMatrixSession:mxSession]; + if (@available(iOS 10, *)) + { + UNMutableNotificationContent *localNotificationContentForFailedSync = [[UNMutableNotificationContent alloc] init]; + localNotificationContentForFailedSync.userInfo = userInfo; + localNotificationContentForFailedSync.body = [self limitedNotificationBodyForEvent:eventId inMatrixSession:mxSession]; - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@", eventId); - [[UIApplication sharedApplication] scheduleLocalNotification:localNotificationForFailedSync]; + UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:eventId content:localNotificationContentForFailedSync trigger:nil]; + + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@", eventId); + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; + } + else // DEPRECATED, for iOS 9 + { + UILocalNotification *localNotificationForFailedSync = [[UILocalNotification alloc] init]; + localNotificationForFailedSync.userInfo = userInfo; + localNotificationForFailedSync.alertBody = [self limitedNotificationBodyForEvent:eventId inMatrixSession:mxSession]; + + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForFailedSync: Display notification for event %@", eventId); + [[UIApplication sharedApplication] scheduleLocalNotification:localNotificationForFailedSync]; + } } } @@ -1840,6 +1917,41 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN return notificationBody; } +/** + Return the already displayed notification for an event. + + @param eventId the id of the event attached to the notification to find. + @param userId the id of the user attached to the notification to find. + @param type the type of notification. @"full" or @"limited". nil for any type. + @return the local notification request if any. + */ +// iOS 10+ only! +- (UNNotificationRequest *)displayedNotificationRequestForEvent:(NSString *)eventId andUser:(NSString *)userId type:(NSString*)type +{ + __block UNNotificationRequest *foundRequest; + [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray *requests) + { + NSLog(@"[AppDelegate] displayedNotificationRequestForEvent: %@ andUser: %@. Current delivered notifications: %@", eventId, userId, requests); + + for (UNNotificationRequest *request in requests) + { + UNNotificationContent *content = request.content; + NSLog(@" - %@", content.userInfo); + + if ([content.userInfo[@"event_id"] isEqualToString:eventId] + && [content.userInfo[@"user_id"] isEqualToString:userId] + && (!type || [content.userInfo[@"type"] isEqualToString:type])) + { + foundRequest = request; + break; + } + } + }]; + + NSLog(@"[AppDelegate] displayedNotificationRequestForEvent: found: %@", foundRequest); + return foundRequest; +} + /** Return the already displayed notification for an event. @@ -1848,6 +1960,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN @param type the type of notification. @"full" or @"limited". nil for any type. @return the local notification if any. */ +// DEPRECATED, for iOS 9 // TODO: This method does not work: [[UIApplication sharedApplication] scheduledLocalNotifications] is not reliable - (UILocalNotification*)displayedLocalNotificationForEvent:(NSString*)eventId andUser:(NSString*)userId type:(NSString*)type { From 2ba91d6f991124101edb3d9d822d95b00a773522 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 31 Jan 2019 02:02:09 +0100 Subject: [PATCH 004/150] specify empty array instead of NULL for intentIdentifiers --- Riot/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 0d509b8fa..f2cb20d3e 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1088,7 +1088,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNNotificationCategory *quickReplyCategory = [UNNotificationCategory categoryWithIdentifier:@"QUICK_REPLY" actions:@[quickReply] - intentIdentifiers:NULL + intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; From 6806953a815cc490ae19a17e9967ce2127450115 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 31 Jan 2019 02:08:03 +0100 Subject: [PATCH 005/150] added changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 17139e6d7..013e7384a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Improvements: * RoomVC: Remove the beta warning modal when opening an e2e room (#2239). * RoomVC: `Redact` has been renamed to `Remove` to match riot/web (#2134). * Clean up iOS version checking (#2190). + * Use newer notification mechanisms where possible (#2207). * Key backup: Implement setup screen (#2198). * Key backup: Implement recover screen (#2196). * Key backup: Add a dedicated section to settings (#2193). From b75c25c047a615558b1400dd45f9d4cdd1246f01 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Fri, 8 Feb 2019 12:42:55 +0100 Subject: [PATCH 006/150] separate notification content generation for iOS 10+ - this will make using iOS 10+ features easier --- Riot/AppDelegate.m | 183 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 162 insertions(+), 21 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index f2cb20d3e..37c277825 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1569,20 +1569,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Prepare the local notification MXPushRule *rule = eventDict[@"push_rule"]; - [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString * _Nullable notificationBody) { - - if (notificationBody) + if (@available(iOS 10, *)) { + [self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNMutableNotificationContent * _Nullable notificationContent) { - // Printf style escape characters are stripped from the string prior to display; - // to include a percent symbol (%) in the message, use two percent symbols (%%). - notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - - if (@available(iOS 10, *)) + if (notificationContent) { - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + // Printf style escape characters are stripped from the string prior to display; + // to include a percent symbol (%) in the message, use two percent symbols (%%). + // TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc + // use this - maybe not necessary to replace %s + notificationContent.body = [notificationContent.body stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - content.body = notificationBody; - content.userInfo = @{ + notificationContent.userInfo = @{ @"type": @"full", @"room_id": event.roomId, @"event_id": event.eventId, @@ -1593,12 +1591,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId - content:content + content:notificationContent trigger:nil]; if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) { - content.categoryIdentifier = @"QUICK_REPLY"; + notificationContent.categoryIdentifier = @"QUICK_REPLY"; } // Set sound name based on the value provided in action of MXPushRule @@ -1612,7 +1610,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN if ([soundName isEqualToString:@"default"]) soundName = @"message.mp3"; - content.sound = [UNNotificationSound soundNamed:soundName]; + notificationContent.sound = [UNNotificationSound soundNamed:soundName]; } } } @@ -1621,8 +1619,22 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; scheduledNotifications++; } - else // DEPRECATED, for iOS 9 + else { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationContent. Event id: %@", event.eventId); + } + }]; + } + else + { + [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) + { + if (notificationBody) + { + // Printf style escape characters are stripped from the string prior to display; + // to include a percent symbol (%) in the message, use two percent symbols (%%). + notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; + UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; eventNotification.alertBody = notificationBody; eventNotification.userInfo = @{ @@ -1658,13 +1670,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; scheduledNotifications++; + } else + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); } - } - else - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); - } - }]; + }]; + } } } @@ -1799,6 +1810,136 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN }]; } +// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete: for now +- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete; +{ + if (!event.content || !event.content.count) + { + NSLog(@"[AppDelegate][Push] notificationContentForEvent: empty event content"); + onComplete (nil); + return; + } + + MXRoom *room = [account.mxSession roomWithRoomId:event.roomId]; + if (!room) + { + NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Unknown room"); + onComplete (nil); + return; + } + + [room state:^(MXRoomState *roomState) { + + NSString *notificationBody; + NSString *eventSenderName = [roomState.members memberName:event.sender]; + + if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) + { + if (room.isMentionsOnly) + { + // A local notification will be displayed only for highlighted notification. + BOOL isHighlighted = NO; + + // Check whether is there an highlight tweak on it + for (MXPushRuleAction *ruleAction in rule.actions) + { + if (ruleAction.actionType == MXPushRuleActionTypeSetTweak) + { + if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"]) + { + // Check the highlight tweak "value" + // If not present, highlight. Else check its value before highlighting + if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue]) + { + isHighlighted = YES; + break; + } + } + } + } + + if (!isHighlighted) + { + // Ignore this notif. + NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room"); + onComplete(nil); + return; + } + } + + NSString *msgType = event.content[@"msgtype"]; + NSString *content = event.content[@"body"]; + + if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications) + { + // Hide the content + msgType = nil; + } + + NSString *roomDisplayName = room.summary.displayname; + + // Display the room name only if it is different than the sender name + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) + { + if ([msgType isEqualToString:@"m.text"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content]; + else if ([msgType isEqualToString:@"m.emote"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content]; + else if ([msgType isEqualToString:@"m.image"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName]; + else + // Encrypted messages falls here + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; + } + else + { + if ([msgType isEqualToString:@"m.text"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content]; + else if ([msgType isEqualToString:@"m.emote"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content]; + else if ([msgType isEqualToString:@"m.image"]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content]; + else + // Encrypted messages falls here + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; + } + } + else if (event.eventType == MXEventTypeCallInvite) + { + NSString *sdp = event.content[@"offer"][@"sdp"]; + BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound; + + if (!isVideoCall) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName]; + else + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName]; + } + else if (event.eventType == MXEventTypeRoomMember) + { + NSString *roomDisplayName = room.summary.displayname; + + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName]; + else + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName]; + } + else if (event.eventType == MXEventTypeSticker) + { + NSString *roomDisplayName = room.summary.displayname; + + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; + else + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; + } + + UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + [content setBody:notificationBody]; + + onComplete(content); + }]; +} + /** Display "limited" notifications for events the app was not able to get data (because of /sync failure). From 43cc6145664aa6767cdc2942ed8a2ef21b32ba26 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:23:43 +0100 Subject: [PATCH 007/150] more accurately match the old notification action handler in the iOS 10+ code --- Riot/AppDelegate.m | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 37c277825..c79c85272 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1209,6 +1209,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN completionHandler(); }]; + return; } } } From c807fa7273600df9a496135a952bca38d27cb137 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:35:36 +0100 Subject: [PATCH 008/150] don't bother ignoring notifications on iOS 10+ --- Riot/AppDelegate.m | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index c79c85272..38135e758 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1501,15 +1501,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN MXEvent *event; // Ignore event already notified to the user - if (@available(iOS 10, *)) - { - if ([self displayedNotificationRequestForEvent:eventId andUser:account.mxCredentials.userId type:nil]) - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); - continue; - } - } - else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) + // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs + if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; From cefd63d8042e82feb814f3856b3300b703857dae Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:49:02 +0100 Subject: [PATCH 009/150] don't bother ignoring notifications on iOS 10+ - no 2 --- Riot/AppDelegate.m | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 38135e758..4683ea9ee 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1960,15 +1960,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN for (NSString *eventId in events) { // Ignore event already notified to the user - if (@available(iOS 10, *)) - { - if ([self displayedNotificationRequestForEvent:eventId andUser:userId type:nil]) - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); - continue; - } - } - else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) + // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs + if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; From 0580a0713b750b09e76a1765793582df123bd826 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:50:04 +0100 Subject: [PATCH 010/150] factor out common code --- Riot/AppDelegate.m | 98 ++++++++++++++++++---------------------------- 1 file changed, 39 insertions(+), 59 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 4683ea9ee..fa5e2d1e6 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1563,6 +1563,39 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Prepare the local notification MXPushRule *rule = eventDict[@"push_rule"]; + NSDictionary *notificationUserInfo = @{ + @"type": @"full", + @"room_id": event.roomId, + @"event_id": event.eventId, + @"user_id": account.mxCredentials.userId + }; + + BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; + + NSString *categoryIdentifier; + + if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) + { + categoryIdentifier = @"QUICK_REPLY"; + } + + + NSString *soundName; + + // Set sound name based on the value provided in action of MXPushRule + for (MXPushRuleAction *action in rule.actions) + { + if (action.actionType == MXPushRuleActionTypeSetTweak) + { + if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) + { + soundName = action.parameters[@"value"]; + if ([soundName isEqualToString:@"default"]) + soundName = @"message.mp3"; + } + } + } + if (@available(iOS 10, *)) { [self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNMutableNotificationContent * _Nullable notificationContent) { @@ -1573,42 +1606,15 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc // use this - maybe not necessary to replace %s notificationContent.body = [notificationContent.body stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - - notificationContent.userInfo = @{ - @"type": @"full", - @"room_id": event.roomId, - @"event_id": event.eventId, - @"user_id": account.mxCredentials.userId - }; - - BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; + notificationContent.userInfo = notificationUserInfo; + notificationContent.categoryIdentifier = categoryIdentifier; + notificationContent.sound = [UNNotificationSound soundNamed:soundName]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId content:notificationContent trigger:nil]; - if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) - { - notificationContent.categoryIdentifier = @"QUICK_REPLY"; - } - - // Set sound name based on the value provided in action of MXPushRule - for (MXPushRuleAction *action in rule.actions) - { - if (action.actionType == MXPushRuleActionTypeSetTweak) - { - if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) - { - NSString *soundName = action.parameters[@"value"]; - if ([soundName isEqualToString:@"default"]) - soundName = @"message.mp3"; - - notificationContent.sound = [UNNotificationSound soundNamed:soundName]; - } - } - } - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; scheduledNotifications++; @@ -1631,35 +1637,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; eventNotification.alertBody = notificationBody; - eventNotification.userInfo = @{ - @"type": @"full", - @"room_id": event.roomId, - @"event_id": event.eventId, - @"user_id": account.mxCredentials.userId - }; - - BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; - - if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) - { - eventNotification.category = @"QUICK_REPLY"; - } - - // Set sound name based on the value provided in action of MXPushRule - for (MXPushRuleAction *action in rule.actions) - { - if (action.actionType == MXPushRuleActionTypeSetTweak) - { - if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) - { - NSString *soundName = action.parameters[@"value"]; - if ([soundName isEqualToString:@"default"]) - soundName = @"message.mp3"; - - eventNotification.soundName = soundName; - } - } - } + eventNotification.userInfo = notificationUserInfo; + eventNotification.category = categoryIdentifier; + eventNotification.soundName = soundName; NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; From a9ded46d085e1aad2807cfe156afb28dd1a18318 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:53:41 +0100 Subject: [PATCH 011/150] don't display notifications when in foreground --- Riot/AppDelegate.m | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index fa5e2d1e6..efab4d20e 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1277,51 +1277,12 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN completionHandler(); } -// iOS 10+, see application:didReceiveLocalNotification: +// iOS 10+, this is called when a notification is about to display in foreground. - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { NSLog(@"[AppDelegate][Push] willPresentNotification: applicationState: %@", @([UIApplication sharedApplication].applicationState)); - NSString* roomId = notification.request.content.userInfo[@"room_id"]; - if (roomId.length) - { - // TODO retrieve the right matrix session - // We can use the "user_id" value in notification.userInfo - - //************** - // Patch consider the first session which knows the room id - MXKAccount *dedicatedAccount = nil; - - NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; - - if (mxAccounts.count == 1) - { - dedicatedAccount = mxAccounts.firstObject; - } - else - { - for (MXKAccount *account in mxAccounts) - { - if ([account.mxSession roomWithRoomId:roomId]) - { - dedicatedAccount = account; - break; - } - } - } - - // sanity checks - if (dedicatedAccount && dedicatedAccount.mxSession) - { - NSLog(@"[AppDelegate][Push] willPresentNotification: open the roomViewController %@", roomId); - - [self showRoom:roomId andEventId:nil withMatrixSession:dedicatedAccount.mxSession]; - } - else - { - NSLog(@"[AppDelegate][Push] willPresentNotification : no linked session / account has been found."); - } - } + completionHandler(UNNotificationPresentationOptionNone); } // DEPRECATED, for iOS 9 From 2da9e08a592f90143defb225b010083829859d95 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 00:57:09 +0100 Subject: [PATCH 012/150] remove displayedNotificationRequestForEvent:andUser:type: (unused as of c807fa7 and cefd63d) --- Riot/AppDelegate.m | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index efab4d20e..e74673909 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1986,41 +1986,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN return notificationBody; } -/** - Return the already displayed notification for an event. - - @param eventId the id of the event attached to the notification to find. - @param userId the id of the user attached to the notification to find. - @param type the type of notification. @"full" or @"limited". nil for any type. - @return the local notification request if any. - */ -// iOS 10+ only! -- (UNNotificationRequest *)displayedNotificationRequestForEvent:(NSString *)eventId andUser:(NSString *)userId type:(NSString*)type -{ - __block UNNotificationRequest *foundRequest; - [[UNUserNotificationCenter currentNotificationCenter] getDeliveredNotificationsWithCompletionHandler:^(NSArray *requests) - { - NSLog(@"[AppDelegate] displayedNotificationRequestForEvent: %@ andUser: %@. Current delivered notifications: %@", eventId, userId, requests); - - for (UNNotificationRequest *request in requests) - { - UNNotificationContent *content = request.content; - NSLog(@" - %@", content.userInfo); - - if ([content.userInfo[@"event_id"] isEqualToString:eventId] - && [content.userInfo[@"user_id"] isEqualToString:userId] - && (!type || [content.userInfo[@"type"] isEqualToString:type])) - { - foundRequest = request; - break; - } - } - }]; - - NSLog(@"[AppDelegate] displayedNotificationRequestForEvent: found: %@", foundRequest); - return foundRequest; -} - /** Return the already displayed notification for an event. From d0b4c56a3941a875c0f2dbc44b48b440df7d8326 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 14 Mar 2019 01:05:30 +0100 Subject: [PATCH 013/150] navigate to the room when a notification is tapped on iOS 10 --- Riot/AppDelegate.m | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index e74673909..d2be8d38e 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1213,6 +1213,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } } + else if ([[response actionIdentifier] isEqualToString:UNNotificationDefaultActionIdentifier]) + { + NSString *roomId = content.userInfo[@"room_id"]; + [self navigateToRoomById:roomId]; + } else { NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", [response actionIdentifier]); @@ -1291,17 +1296,22 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSLog(@"[AppDelegate][Push] didReceiveLocalNotification: applicationState: %@", @(application.applicationState)); NSString* roomId = notification.userInfo[@"room_id"]; + [self navigateToRoomById:roomId]; +} + +- (void)navigateToRoomById:(NSString *)roomId +{ if (roomId.length) { // TODO retrieve the right matrix session // We can use the "user_id" value in notification.userInfo - + //************** // Patch consider the first session which knows the room id MXKAccount *dedicatedAccount = nil; - + NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; - + if (mxAccounts.count == 1) { dedicatedAccount = mxAccounts.firstObject; @@ -1317,17 +1327,17 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } } - + // sanity checks if (dedicatedAccount && dedicatedAccount.mxSession) { - NSLog(@"[AppDelegate][Push] didReceiveLocalNotification: open the roomViewController %@", roomId); - + NSLog(@"[AppDelegate][Push] navigateToRoomById: open the roomViewController %@", roomId); + [self showRoom:roomId andEventId:nil withMatrixSession:dedicatedAccount.mxSession]; } else { - NSLog(@"[AppDelegate][Push] didReceiveLocalNotification : no linked session / account has been found."); + NSLog(@"[AppDelegate][Push] navigateToRoomById : no linked session / account has been found."); } } } From 4ebae92cb86b3f37456f78dd626ba0da4c194670 Mon Sep 17 00:00:00 2001 From: Andrew Morgan <1342360+anoadragon453@users.noreply.github.com> Date: Thu, 21 Mar 2019 15:26:14 +0000 Subject: [PATCH 014/150] Fix grammar in registration text I assume some script needs to be run to generate the other string files, but if you want to just do that yourselves and take the text in this PR I'm totally fine with that. --- Riot/Assets/en.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 81ca9d9fa..f1bc2ab91 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -83,7 +83,7 @@ "auth_invalid_email" = "This doesn't look like a valid email address"; "auth_invalid_phone" = "This doesn't look like a valid phone number"; "auth_missing_password" = "Missing password"; -"auth_add_email_message" = "Add an email address to your account to let users discover you, and let you reset password."; +"auth_add_email_message" = "Add an email address to your account to let users discover you, and to reset your password."; "auth_add_phone_message" = "Add a phone number to your account to let users discover you."; "auth_add_email_phone_message" = "Add an email address and/or a phone number to your account to let users discover you. Email address will also let you reset your password."; "auth_add_email_and_phone_message" = "Add an email address and a phone number to your account to let users discover you. Email address will also let you reset your password."; From 8ea9bf13d6fc41ab02adbf1fc007748068090b13 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 21 Mar 2019 16:32:34 +0100 Subject: [PATCH 015/150] version++ --- Riot/SupportingFiles/Info.plist | 4 ++-- RiotShareExtension/SupportingFiles/Info.plist | 4 ++-- SiriIntents/Info.plist | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index fd464b9fe..4be0869c4 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.8.4 + 0.8.5 CFBundleSignature ???? CFBundleVersion - 0.8.4 + 0.8.5 ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode diff --git a/RiotShareExtension/SupportingFiles/Info.plist b/RiotShareExtension/SupportingFiles/Info.plist index 1897f23fb..703bfc159 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.4 + 0.8.5 CFBundleVersion - 0.8.4 + 0.8.5 NSExtension NSExtensionAttributes diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index 5effb4123..a032749a7 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.4 + 0.8.5 CFBundleVersion - 0.8.4 + 0.8.5 NSExtension NSExtensionAttributes From 5d56fd791f841d39249c0de43e997e65f74a6f7a Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Fri, 22 Mar 2019 00:40:27 +0100 Subject: [PATCH 016/150] add a threadIdentifier to notifications and implement titles --- Riot/AppDelegate.m | 51 ++++++++++++++++++------ Riot/Assets/en.lproj/Localizable.strings | 11 +++++ 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 7985de290..c6a01d15a 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1815,7 +1815,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN }]; } -// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete: for now +// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features - (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete; { if (!event.content || !event.content.count) @@ -1835,7 +1835,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [room state:^(MXRoomState *roomState) { + NSString *notificationTitle; NSString *notificationBody; + + NSString *threadIdentifier = room.roomId; NSString *eventSenderName = [roomState.members memberName:event.sender]; if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) @@ -1873,7 +1876,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } NSString *msgType = event.content[@"msgtype"]; - NSString *content = event.content[@"body"]; + NSString *messageContent = event.content[@"body"]; if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications) { @@ -1886,27 +1889,44 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Display the room name only if it is different than the sender name if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) { + notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; if ([msgType isEqualToString:@"m.text"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content]; + notificationBody = messageContent; else if ([msgType isEqualToString:@"m.emote"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content]; + { + notificationTitle = nil; + // TODO: how should this look? /me style messages don't look right with a title + // maybe like this: + // title = roomDisplayName + // body = * eventSenderName messageContent + + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, messageContent]; + } else if ([msgType isEqualToString:@"m.image"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName]; + notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); else // Encrypted messages falls here - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; + notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); } else { + notificationTitle = eventSenderName; if ([msgType isEqualToString:@"m.text"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content]; + notificationBody = messageContent; else if ([msgType isEqualToString:@"m.emote"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content]; + { + notificationTitle = nil; + // TODO: how should this look? /me style messages look weird with a title + // maybe like this: + // title = eventSenderName + // body = * messageContent + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, messageContent]; + } else if ([msgType isEqualToString:@"m.image"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content]; + notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); else // Encrypted messages falls here - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; + notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); } } else if (event.eventType == MXEventTypeCallInvite) @@ -1918,6 +1938,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName]; else notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName]; + + threadIdentifier = nil; // call notifications should probably stand out from normal messages } else if (event.eventType == MXEventTypeRoomMember) { @@ -1933,13 +1955,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSString *roomDisplayName = room.summary.displayname; if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; + notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; + notificationTitle = eventSenderName; + + notificationBody = NSLocalizedString(@"STICKER_TEXT_WITH_TITLE", nil); } UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; + + [content setTitle:notificationTitle]; [content setBody:notificationBody]; + [content setThreadIdentifier:threadIdentifier]; onComplete(content); }]; diff --git a/Riot/Assets/en.lproj/Localizable.strings b/Riot/Assets/en.lproj/Localizable.strings index 33e5d39f4..53b8a579c 100644 --- a/Riot/Assets/en.lproj/Localizable.strings +++ b/Riot/Assets/en.lproj/Localizable.strings @@ -22,6 +22,9 @@ /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ posted in %@"; +/* New message when a notification title is used */ +"MSG_TEXT_WITH_TITLE" = "Encrypted message"; + /** Single, unencrypted messages (where we can include the content */ /* New message from a specific person, not referencing a room. Content included. */ @@ -44,12 +47,18 @@ /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ posted a picture %@ in %@"; +/* New action message, but the sender (and room) are already in the notification title */ +"IMAGE_TEXT_WITH_TITLE" = "📷 Picture"; + /* A single unread message in a room */ "SINGLE_UNREAD_IN_ROOM" = "You received a message in %@"; /* A single unread message */ "SINGLE_UNREAD" = "You received a message"; +/* Sticker, but with the sender (and room) already in the title */ +"STICKER_TEXT_WITH_TITLE" = "Sticker"; + /** Coalesced messages **/ /* Multiple unread messages in a room */ @@ -103,3 +112,5 @@ /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Video group call from %@: '%@'"; + +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@"; \ No newline at end of file From 3f21c790bf827140014528740a384a3127950a02 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:10:57 +0100 Subject: [PATCH 017/150] add additional notification titles --- Riot/AppDelegate.m | 19 ++++++++++++------- Riot/Assets/en.lproj/Localizable.strings | 12 ++++++++++-- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index c6a01d15a..ed80fd1d2 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1934,21 +1934,26 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSString *sdp = event.content[@"offer"][@"sdp"]; BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound; - if (!isVideoCall) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName]; - else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName]; + notificationTitle = eventSenderName; - threadIdentifier = nil; // call notifications should probably stand out from normal messages + if (!isVideoCall) + notificationBody = NSLocalizedString(@"VOICE_CALL", nil); + else + notificationBody = NSLocalizedString(@"VIDEO_CALL", nil); + + // call notifications should stand out from normal messages, so we don't stack them + threadIdentifier = nil; } else if (event.eventType == MXEventTypeRoomMember) { NSString *roomDisplayName = room.summary.displayname; + notificationTitle = roomDisplayName; + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName]; + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"INVITE_BY_USER_TO_ROOM", nil), eventSenderName]; else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName]; + notificationBody = NSLocalizedString(@"INVITE_TO_CHAT", nil); } else if (event.eventType == MXEventTypeSticker) { diff --git a/Riot/Assets/en.lproj/Localizable.strings b/Riot/Assets/en.lproj/Localizable.strings index 53b8a579c..38fca6c83 100644 --- a/Riot/Assets/en.lproj/Localizable.strings +++ b/Riot/Assets/en.lproj/Localizable.strings @@ -23,7 +23,7 @@ "MSG_FROM_USER_IN_ROOM" = "%@ posted in %@"; /* New message when a notification title is used */ -"MSG_TEXT_WITH_TITLE" = "Encrypted message"; +"MSG_TEXT_WITH_TITLE" = "Message"; /** Single, unencrypted messages (where we can include the content */ @@ -57,7 +57,7 @@ "SINGLE_UNREAD" = "You received a message"; /* Sticker, but with the sender (and room) already in the title */ -"STICKER_TEXT_WITH_TITLE" = "Sticker"; +"STICKER_TEXT_WITH_TITLE" = "💟 Sticker"; /** Coalesced messages **/ @@ -93,14 +93,22 @@ /* A user has invited you to a named room */ "USER_INVITE_TO_NAMED_ROOM" = "%@ has invited you to %@"; +"INVITE_TO_CHAT" = "You were invited to chat"; + +"INVITE_BY_USER_TO_ROOM" = "You were invited by %@"; + /** Calls **/ /* Incoming one-to-one voice call */ "VOICE_CALL_FROM_USER" = "Call from %@"; +"VOICE_CALL" = "📞 Call"; + /* Incoming one-to-one video call */ "VIDEO_CALL_FROM_USER" = "Video call from %@"; +"VIDEO_CALL" = "📹 Video call"; + /* Incoming unnamed voice conference invite from a specific person */ "VOICE_CONF_FROM_USER" = "Group call from %@"; From 689d05259023944f39257fb6fc817f4fa8eb1140 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:20:50 +0100 Subject: [PATCH 018/150] finalize emote style notifications --- Riot/AppDelegate.m | 17 ++++------------- Riot/Assets/en.lproj/Localizable.strings | 5 +++++ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index ed80fd1d2..1bd28e594 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1894,13 +1894,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN notificationBody = messageContent; else if ([msgType isEqualToString:@"m.emote"]) { - notificationTitle = nil; - // TODO: how should this look? /me style messages don't look right with a title - // maybe like this: - // title = roomDisplayName - // body = * eventSenderName messageContent - - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, messageContent]; + notificationTitle = roomDisplayName; + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, messageContent]; } else if ([msgType isEqualToString:@"m.image"]) notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); @@ -1915,12 +1910,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN notificationBody = messageContent; else if ([msgType isEqualToString:@"m.emote"]) { - notificationTitle = nil; - // TODO: how should this look? /me style messages look weird with a title - // maybe like this: - // title = eventSenderName - // body = * messageContent - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, messageContent]; + notificationTitle = eventSenderName; + notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION", nil), messageContent]; } else if ([msgType isEqualToString:@"m.image"]) notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); diff --git a/Riot/Assets/en.lproj/Localizable.strings b/Riot/Assets/en.lproj/Localizable.strings index 38fca6c83..b387bfa82 100644 --- a/Riot/Assets/en.lproj/Localizable.strings +++ b/Riot/Assets/en.lproj/Localizable.strings @@ -39,6 +39,9 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; +/* New action message, sender is specified in notification title */ +"ACTION" = "* %@"; + /** Image Messages **/ /* New action message from a specific person, not referencing a room. */ @@ -93,8 +96,10 @@ /* A user has invited you to a named room */ "USER_INVITE_TO_NAMED_ROOM" = "%@ has invited you to %@"; +/* Same as USER_INVITE_TO_CHAT but the username is already displayed in the notification title */ "INVITE_TO_CHAT" = "You were invited to chat"; +/* Same as USER_INVITE_TO_NAMED_ROOM but the room name is already displayed in the notification title */ "INVITE_BY_USER_TO_ROOM" = "You were invited by %@"; /** Calls **/ From b8a314d7dbe0c7194cb3adc994e3556bfd74bd83 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Fri, 22 Mar 2019 01:30:20 +0100 Subject: [PATCH 019/150] update CHANGES.rst --- CHANGES.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index f060e739d..fc8ea25b4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,8 @@ Changes in 0.8.5 (2019-xx-xx) =============================================== Improvements: + * Added titles to notifications on iOS 10+ (#2347). + * Implemented notification grouping (#2347). Bug fix: From d4f9dfef4d890c9d22057845a5fd92d17707ef3c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 22 Mar 2019 10:21:16 +0100 Subject: [PATCH 020/150] Factorize and fix some issues with local notifications code --- Riot/AppDelegate.m | 458 +++++++++++++++++---------------------------- 1 file changed, 168 insertions(+), 290 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 7985de290..7c5486524 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1163,8 +1163,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else { // Clear existing token - MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; - [accountManager setPushDeviceToken:nil withPushOptions:nil]; + [self clearPushNotificationToken]; } }]; } @@ -1211,8 +1210,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else { // Clear existing token - MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; - [accountManager setPushDeviceToken:nil withPushOptions:nil]; + [self clearPushNotificationToken]; } } @@ -1221,125 +1219,81 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { UNNotification *notification = response.notification; UNNotificationContent *content = notification.request.content; - if ([[response actionIdentifier] isEqualToString:@"inline-reply"]) { - UNTextInputNotificationResponse *textResponse = (UNTextInputNotificationResponse *) response; - NSString* roomId = content.userInfo[@"room_id"]; - - if (roomId.length) { - NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts; - - MXKRoomDataSourceManager* manager; - for (MXKAccount* account in mxAccounts) - { - MXRoom* room = [account.mxSession roomWithRoomId:roomId]; - if (room) - { - manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession]; - if (manager) - { - break; - } - } - } - if (manager == nil) - { - NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found", roomId); - } - else - { - [manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { - NSString* responseText = [textResponse userText]; - if (responseText != nil && responseText.length != 0) - { - NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@", roomId); - [roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) { - UNMutableNotificationContent *failureNotificationContent = [[UNMutableNotificationContent alloc] init]; - failureNotificationContent.userInfo = content.userInfo; - failureNotificationContent.body = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); - - UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest - requestWithIdentifier:@"failureNotification" - content:failureNotificationContent - trigger:nil]; - - [center addNotificationRequest:failureNotificationRequest withCompletionHandler:nil]; - NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error sending text message: %@", error); - }]; - } - - completionHandler(); - }]; - return; - } + NSString *actionIdentifier = [response actionIdentifier]; + NSString *roomId = content.userInfo[@"room_id"]; + + if ([actionIdentifier isEqualToString:@"inline-reply"]) + { + if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) + { + UNTextInputNotificationResponse *textInputNotificationResponse = (UNTextInputNotificationResponse *)response; + NSString *responseText = [textInputNotificationResponse userText]; + + [self handleNotificationInlineReplyForRoomId:roomId withResponseText:responseText success:^(NSString *eventId) { + completionHandler(); + } failure:^(NSError *error) { + + UNMutableNotificationContent *failureNotificationContent = [[UNMutableNotificationContent alloc] init]; + failureNotificationContent.userInfo = content.userInfo; + failureNotificationContent.body = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); + + NSString *uuid = [[NSUUID UUID] UUIDString]; + UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest requestWithIdentifier:uuid + content:failureNotificationContent + trigger:nil]; + + [center addNotificationRequest:failureNotificationRequest withCompletionHandler:nil]; + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error sending text message: %@", error); + + completionHandler(); + }]; + } + else + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error, expect a response of type UNTextInputNotificationResponse"); + completionHandler(); } } - else if ([[response actionIdentifier] isEqualToString:UNNotificationDefaultActionIdentifier]) + else if ([actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) { - NSString *roomId = content.userInfo[@"room_id"]; [self navigateToRoomById:roomId]; + completionHandler(); } else { NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", [response actionIdentifier]); + completionHandler(); } - completionHandler(); } // DEPRECATED, for iOS 9 // "This block is not a prototype" - don't fix this, or it won't match Apple's definition - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { + NSString* roomId = notification.userInfo[@"room_id"]; + if ([identifier isEqualToString: @"inline-reply"]) { - NSString* roomId = notification.userInfo[@"room_id"]; - if (roomId.length) - { - NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts; - MXKRoomDataSource* roomDataSource = nil; - MXKRoomDataSourceManager* manager; - for (MXKAccount* account in mxAccounts) - { - MXRoom* room = [account.mxSession roomWithRoomId:roomId]; - if (room) - { - manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession]; - if (manager) - { - break; - } - } - } - if (manager == nil) - { - NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: room with id %@ not found", roomId); - } - else - { - [manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { - NSString* responseText = responseInfo[UIUserNotificationActionResponseTypedTextKey]; - if (responseText != nil && responseText.length != 0) - { - NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: sending message to room: %@", roomId); - [roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) { - UILocalNotification* failureNotification = [[UILocalNotification alloc] init]; - failureNotification.alertBody = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); - failureNotification.userInfo = notification.userInfo; - [[UIApplication sharedApplication] scheduleLocalNotification: failureNotification]; - NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: error sending text message: %@", error); - }]; - } - - completionHandler(); - }]; - return; - } - } + NSString* responseText = responseInfo[UIUserNotificationActionResponseTypedTextKey]; + + [self handleNotificationInlineReplyForRoomId:roomId withResponseText:responseText success:^(NSString *eventId) { + completionHandler(); + } failure:^(NSError *error) { + + UILocalNotification* failureNotification = [[UILocalNotification alloc] init]; + failureNotification.alertBody = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); + failureNotification.userInfo = notification.userInfo; + [[UIApplication sharedApplication] scheduleLocalNotification: failureNotification]; + NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: error sending text message: %@", error); + + completionHandler(); + }]; } else { NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: unhandled identifier %@", identifier); + completionHandler(); } - completionHandler(); } // iOS 10+, this is called when a notification is about to display in foreground. @@ -1423,8 +1377,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type { - MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; - [accountManager setPushDeviceToken:nil withPushOptions:nil]; + [self clearPushNotificationToken]; } - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type @@ -1533,7 +1486,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Ignore event already notified to the user // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs - if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) + if (@available(iOS 10, *)) {} + else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; @@ -1626,61 +1580,54 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } } - - if (@available(iOS 10, *)) { - [self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNMutableNotificationContent * _Nullable notificationContent) - { - if (notificationContent) - { - // Printf style escape characters are stripped from the string prior to display; - // to include a percent symbol (%) in the message, use two percent symbols (%%). - // TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc - // use this - maybe not necessary to replace %s - notificationContent.body = [notificationContent.body stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - notificationContent.userInfo = notificationUserInfo; - notificationContent.categoryIdentifier = categoryIdentifier; - notificationContent.sound = [UNNotificationSound soundNamed:soundName]; - - UNNotificationRequest *request = [UNNotificationRequest - requestWithIdentifier:event.eventId - content:notificationContent - trigger:nil]; - - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; - scheduledNotifications++; - } - else - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationContent. Event id: %@", event.eventId); - } - }]; - } - else - { - [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) - { - if (notificationBody) - { - // Printf style escape characters are stripped from the string prior to display; - // to include a percent symbol (%) in the message, use two percent symbols (%%). - notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - - UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; - eventNotification.alertBody = notificationBody; - eventNotification.userInfo = notificationUserInfo; - eventNotification.category = categoryIdentifier; - eventNotification.soundName = soundName; - - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); - [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; - scheduledNotifications++; - } else - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); - } - }]; - } + + [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) + { + if (notificationBody) + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); + + // Printf style escape characters are stripped from the string prior to display; + // to include a percent symbol (%) in the message, use two percent symbols (%%). + // TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc + // use this - maybe not necessary to replace %s + NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; + + if (@available(iOS 10, *)) + { + UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init]; + notificationContent.body = fixedNotificationBody; + notificationContent.userInfo = notificationUserInfo; + notificationContent.categoryIdentifier = categoryIdentifier; + if (soundName) + { + notificationContent.sound = [UNNotificationSound soundNamed:soundName]; + } + + UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId + content:notificationContent + trigger:nil]; + + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; + } + else + { + UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; + eventNotification.alertBody = fixedNotificationBody; + eventNotification.userInfo = notificationUserInfo; + eventNotification.category = categoryIdentifier; + eventNotification.soundName = soundName; + + [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; + } + + scheduledNotifications++; + } + else + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); + } + }]; } } @@ -1815,136 +1762,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN }]; } -// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete: for now -- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete; -{ - if (!event.content || !event.content.count) - { - NSLog(@"[AppDelegate][Push] notificationContentForEvent: empty event content"); - onComplete (nil); - return; - } - - MXRoom *room = [account.mxSession roomWithRoomId:event.roomId]; - if (!room) - { - NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Unknown room"); - onComplete (nil); - return; - } - - [room state:^(MXRoomState *roomState) { - - NSString *notificationBody; - NSString *eventSenderName = [roomState.members memberName:event.sender]; - - if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) - { - if (room.isMentionsOnly) - { - // A local notification will be displayed only for highlighted notification. - BOOL isHighlighted = NO; - - // Check whether is there an highlight tweak on it - for (MXPushRuleAction *ruleAction in rule.actions) - { - if (ruleAction.actionType == MXPushRuleActionTypeSetTweak) - { - if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"]) - { - // Check the highlight tweak "value" - // If not present, highlight. Else check its value before highlighting - if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue]) - { - isHighlighted = YES; - break; - } - } - } - } - - if (!isHighlighted) - { - // Ignore this notif. - NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room"); - onComplete(nil); - return; - } - } - - NSString *msgType = event.content[@"msgtype"]; - NSString *content = event.content[@"body"]; - - if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications) - { - // Hide the content - msgType = nil; - } - - NSString *roomDisplayName = room.summary.displayname; - - // Display the room name only if it is different than the sender name - if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - { - if ([msgType isEqualToString:@"m.text"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content]; - else if ([msgType isEqualToString:@"m.emote"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content]; - else if ([msgType isEqualToString:@"m.image"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName]; - else - // Encrypted messages falls here - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; - } - else - { - if ([msgType isEqualToString:@"m.text"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content]; - else if ([msgType isEqualToString:@"m.emote"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content]; - else if ([msgType isEqualToString:@"m.image"]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content]; - else - // Encrypted messages falls here - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; - } - } - else if (event.eventType == MXEventTypeCallInvite) - { - NSString *sdp = event.content[@"offer"][@"sdp"]; - BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound; - - if (!isVideoCall) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName]; - else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName]; - } - else if (event.eventType == MXEventTypeRoomMember) - { - NSString *roomDisplayName = room.summary.displayname; - - if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName]; - else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName]; - } - else if (event.eventType == MXEventTypeSticker) - { - NSString *roomDisplayName = room.summary.displayname; - - if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName]; - else - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName]; - } - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - [content setBody:notificationBody]; - - onComplete(content); - }]; -} - /** Display "limited" notifications for events the app was not able to get data (because of /sync failure). @@ -1972,7 +1789,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { // Ignore event already notified to the user // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs - if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) + if (@available(iOS 10, *)) {} + else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; @@ -2099,6 +1917,66 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [UIApplication sharedApplication].applicationIconBadgeNumber = count; } +- (void)handleNotificationInlineReplyForRoomId:(NSString*)roomId + withResponseText:(NSString*)responseText + success:(void(^)(NSString *eventId))success + failure:(void(^)(NSError *error))failure +{ + if (!roomId.length) + { + failure(nil); + return; + } + + NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts; + + MXKRoomDataSourceManager* manager; + + for (MXKAccount* account in mxAccounts) + { + MXRoom* room = [account.mxSession roomWithRoomId:roomId]; + if (room) + { + manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession]; + if (manager) + { + break; + } + } + } + + if (manager == nil) + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found", roomId); + failure(nil); + } + else + { + [manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) { + if (responseText != nil && responseText.length != 0) + { + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@", roomId); + [roomDataSource sendTextMessage:responseText success:^(NSString* eventId) { + success(eventId); + } failure:^(NSError* error) { + failure(error); + }]; + } + else + { + failure(nil); + } + }]; + } +} + +- (void)clearPushNotificationToken +{ + // Clear existing token + MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; + [accountManager setPushDeviceToken:nil withPushOptions:nil]; +} + #pragma mark - Universal link - (BOOL)handleUniversalLink:(NSUserActivity*)userActivity From 0679e0af8d35ead2777e84323747b8d0722a730b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 22 Mar 2019 11:06:15 +0100 Subject: [PATCH 021/150] Remove pending and delivered notifications containing a redacted event (Fix #1725) --- Riot/AppDelegate.m | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 7c5486524..9df3ccaf9 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1475,6 +1475,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSMutableArray *eventsArray = eventsToNotify[@(account.mxSession.hash)]; + NSMutableArray *redactedEventIds = [NSMutableArray array]; + // Display a local notification for each event retrieved by the bg sync. for (NSUInteger index = 0; index < eventsArray.count; index++) { @@ -1500,10 +1502,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN if (event) { - // Ignore redacted event. if (event.isRedactedEvent) { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", event.eventId); + if (@available(iOS 10, *)) + { + // Collect redacted event ids to remove possible delivered redacted notifications + [redactedEventIds addObject:eventId]; + } + else + { + // Ignore redacted event. + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", event.eventId); + } continue; } @@ -1630,6 +1640,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN }]; } } + + if (@available(iOS 10, *)) + { + // Remove possible pending and delivered notifications having a redacted event id + if (redactedEventIds.count) + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Remove possible notification with redacted event ids: %@", redactedEventIds); + + [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:redactedEventIds]; + [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:redactedEventIds]; + } + } NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events", scheduledNotifications, eventsArray.count); From 5514dbeecd6b548dbc94fd164bfff8c38876d06d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 22 Mar 2019 11:18:07 +0100 Subject: [PATCH 022/150] Handle iOS 12 grouped notifications. Group by room id. --- Riot/AppDelegate.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 9df3ccaf9..39e8a8a98 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1236,6 +1236,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNMutableNotificationContent *failureNotificationContent = [[UNMutableNotificationContent alloc] init]; failureNotificationContent.userInfo = content.userInfo; failureNotificationContent.body = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil); + failureNotificationContent.threadIdentifier = roomId; NSString *uuid = [[NSUUID UUID] UUIDString]; UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest requestWithIdentifier:uuid @@ -1609,6 +1610,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN notificationContent.body = fixedNotificationBody; notificationContent.userInfo = notificationUserInfo; notificationContent.categoryIdentifier = categoryIdentifier; + notificationContent.threadIdentifier = roomId; if (soundName) { notificationContent.sound = [UNNotificationSound soundNamed:soundName]; @@ -1842,6 +1844,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNMutableNotificationContent *localNotificationContentForFailedSync = [[UNMutableNotificationContent alloc] init]; localNotificationContentForFailedSync.userInfo = userInfo; localNotificationContentForFailedSync.body = [self limitedNotificationBodyForEvent:eventId inMatrixSession:mxSession]; + localNotificationContentForFailedSync.threadIdentifier = roomId; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:eventId content:localNotificationContentForFailedSync trigger:nil]; From c3af90be0030662042661f2cf03675e68c8e2fef Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 22 Mar 2019 11:49:20 +0100 Subject: [PATCH 023/150] Update changes --- CHANGES.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f060e739d..26dc93173 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,7 @@ Changes in 0.8.5 (2019-xx-xx) =============================================== Improvements: + * Use UserNotifications framework for local notifications (iOS 10+), thanks to @fridtjof (PR #2207). Bug fix: @@ -74,7 +75,6 @@ Improvements: * RoomVC: Remove the beta warning modal when opening an e2e room (#2239). * RoomVC: `Redact` has been renamed to `Remove` to match riot/web (#2134). * Clean up iOS version checking (#2190). - * Use newer notification mechanisms where possible (#2207). * Key backup: Implement setup screen (#2198). * Key backup: Implement recover screen (#2196). * Key backup: Add a dedicated section to settings (#2193). From 9babadfb85ab77cc79443c69e79197fbbde8b198 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 22 Mar 2019 16:00:24 +0100 Subject: [PATCH 024/150] Prepare iOS 12 implicit notifications permission --- Riot/AppDelegate.m | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 39e8a8a98..dc8e1b5ad 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1154,7 +1154,18 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; [center setNotificationCategories:[[NSSet alloc] initWithArray:@[quickReplyCategory]]]; [center setDelegate:self]; // commenting this out will fall back to using the same AppDelegate methods as the iOS 9 way of doing this - [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge) + + UNAuthorizationOptions authorizationOptions = (UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge); + + // FIXME: Uncomment lines below when issue https://github.com/matrix-org/matrix-ios-kit/issues/533 will be done. +// // Authorize sending notifications without explicit permission (iOS 12+). +// // User can still disable Riot notifications later in settings or directly from a Riot notification. +// if (@available(iOS 12.0, *)) +// { +// authorizationOptions = authorizationOptions | UNAuthorizationOptionProvisional; +// } + + [center requestAuthorizationWithOptions:authorizationOptions completionHandler:^(BOOL granted, NSError *error) { // code here is equivalent to self:application:didRegisterUserNotificationSettings: if (granted) { From d6fa09968d7c356706da6d8f5622d1b2d634251f Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 27 Mar 2019 11:53:31 +0100 Subject: [PATCH 025/150] Push: Add more logs to track spontaneously disabling #2348 --- CHANGES.rst | 1 + Riot/AppDelegate.m | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 0b65764ff..bff47de2c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,6 +2,7 @@ Changes in 0.8.5 (2019-xx-xx) =============================================== Improvements: + * Push: Add more logs to track spontaneously disabling (#2348). Bug fix: diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index c099b2719..8930181e1 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1134,6 +1134,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)registerUserNotificationSettings { + NSLog(@"[AppDelegate][Push] registerUserNotificationSettings: isPushRegistered: %@", @(isPushRegistered)); + if (!isPushRegistered) { NSMutableSet* notificationCategories = [NSMutableSet set]; @@ -1158,6 +1160,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)registerForRemoteNotificationsWithCompletion:(nullable void (^)(NSError *))completion { + NSLog(@"[AppDelegate][Push] registerForRemoteNotificationsWithCompletion"); + self.registrationForRemoteNotificationsCompletion = completion; self.pushRegistry = [[PKPushRegistry alloc] initWithQueue:nil]; @@ -1167,6 +1171,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings { + NSLog(@"[AppDelegate][Push] didRegisterUserNotificationSettings: notificationSettings.types: %@", @(notificationSettings.types)); + // Register for remote notifications only if user provide access to notification feature if (notificationSettings.types != UIUserNotificationTypeNone) { @@ -1183,6 +1189,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // "This block is not a prototype" - don't fix this, or it won't match Apple's definition - (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler { + NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: identifier: %@", identifier); + if ([identifier isEqualToString: @"inline-reply"]) { NSString* roomId = notification.userInfo[@"room_id"]; @@ -1285,9 +1293,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(PKPushType)type { NSData *token = credentials.token; - - NSUInteger len = ((token.length > 8) ? 8 : token.length / 2); - NSLog(@"[AppDelegate][Push] Got Push token! (%@ ...)", [token subdataWithRange:NSMakeRange(0, len)]); + + NSLog(@"[AppDelegate][Push] didUpdatePushCredentials: Got Push token: %@. Type: %@", [MXKTools logForPushToken:token], type); MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; [accountManager setPushDeviceToken:token withPushOptions:@{@"format": @"event_id_only"}]; @@ -1303,6 +1310,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type { + NSLog(@"[AppDelegate][Push] didInvalidatePushTokenForType: Type: %@", type); + MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; [accountManager setPushDeviceToken:nil withPushOptions:nil]; } From b59dad527e8d8a1f19e797261f215e99410996e0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 27 Mar 2019 12:16:43 +0100 Subject: [PATCH 026/150] Push: more logs --- Riot/AppDelegate.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 8930181e1..c2eaac61f 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1772,7 +1772,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // TODO: This method does not work: [[UIApplication sharedApplication] scheduledLocalNotifications] is not reliable - (UILocalNotification*)displayedLocalNotificationForEvent:(NSString*)eventId andUser:(NSString*)userId type:(NSString*)type { - NSLog(@"[AppDelegate] displayedLocalNotificationForEvent: %@ andUser: %@. Current scheduledLocalNotifications: %@", eventId, userId, [[UIApplication sharedApplication] scheduledLocalNotifications]); + NSLog(@"[AppDelegate][Push] displayedLocalNotificationForEvent: %@ andUser: %@. Current scheduledLocalNotifications: %@", eventId, userId, [[UIApplication sharedApplication] scheduledLocalNotifications]); UILocalNotification *limitedLocalNotification; for (UILocalNotification *localNotification in [[UIApplication sharedApplication] scheduledLocalNotifications]) @@ -1788,7 +1788,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } } - NSLog(@"[AppDelegate] displayedLocalNotificationForEvent: found: %@", limitedLocalNotification); + NSLog(@"[AppDelegate][Push] displayedLocalNotificationForEvent: found: %@", limitedLocalNotification); return limitedLocalNotification; } From da62b7c2059aaddeff68b3f82ee16984fb691191 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 27 Mar 2019 15:44:01 +0100 Subject: [PATCH 027/150] Fix grammar in registration text I assume some script needs to be run to generate the other string files, but if you want to just do that yourselves and take the text in this PR I'm totally fine with that. --- Riot/Generated/Strings.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 846f237d3..cb71c944e 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -38,7 +38,7 @@ internal enum VectorL10n { internal static var authAddEmailAndPhoneWarning: String { return VectorL10n.tr("Vector", "auth_add_email_and_phone_warning") } - /// Add an email address to your account to let users discover you, and let you reset password. + /// Add an email address to your account to let users discover you, and to reset your password. internal static var authAddEmailMessage: String { return VectorL10n.tr("Vector", "auth_add_email_message") } From 2199da49a0ec8afc694fa8f1e345d4eaa9b39993 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 27 Mar 2019 15:45:54 +0100 Subject: [PATCH 028/150] Widgets: Use scalar prod urls in Riot mobile apps #2349 --- CHANGES.rst | 1 + Riot/Assets/Riot-Defaults.plist | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index bff47de2c..145aa4776 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,7 @@ Changes in 0.8.5 (2019-xx-xx) Improvements: * Push: Add more logs to track spontaneously disabling (#2348). + * Widgets: Use scalar prod urls in Riot mobile apps (#2349). Bug fix: diff --git a/Riot/Assets/Riot-Defaults.plist b/Riot/Assets/Riot-Defaults.plist index 7ad072f6b..fb433da71 100644 --- a/Riot/Assets/Riot-Defaults.plist +++ b/Riot/Assets/Riot-Defaults.plist @@ -25,9 +25,9 @@ matrixApps integrationsUiUrl - https://scalar-staging.riot.im/scalar-web/ + https://scalar.vector.im/ integrationsRestUrl - https://scalar-staging.riot.im/scalar/api + https://scalar.vector.im/api integrationsWidgetsUrls https://scalar-staging.riot.im/scalar/api From d8b555b895d0a962c5d79647034e1b012043255e Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 2 Apr 2019 09:35:02 +0200 Subject: [PATCH 029/150] Push: More logs --- Riot/AppDelegate.m | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index c2eaac61f..a7391d82f 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1154,6 +1154,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Registration on iOS 8 and later UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeSound |UIUserNotificationTypeAlert) categories:notificationCategories]; + + NSLog(@"[AppDelegate][Push] registerUserNotificationSettings: %@", settings); [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; } } @@ -2428,7 +2430,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Set the push gateway URL. account.pushGatewayURL = [[NSUserDefaults standardUserDefaults] objectForKey:@"pushGatewayURL"]; - + + NSLog(@"[AppDelegate][Push] didAddAccountNotification: isPushRegistered: %@", @(isPushRegistered)); + if (isPushRegistered) { // Enable push notifications by default on new added account From c4e19c07c6d58e184f49295db577fdc20c06764d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 2 Apr 2019 14:20:05 +0200 Subject: [PATCH 030/150] Fix a crash when receive memory warning in ShareExtensionManager --- RiotShareExtension/Managers/ShareExtensionManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 51d020ecb..6450187b2 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -70,7 +70,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) [[NSNotificationCenter defaultCenter] addObserver:sharedInstance selector:@selector(checkUserAccount) name:NSExtensionHostWillEnterForegroundNotification object:nil]; // Add observer to handle memory warning - [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; + [NSNotificationCenter.defaultCenter addObserver:sharedInstance selector:@selector(didReceiveMemoryWarning:) name:UIApplicationDidReceiveMemoryWarningNotification object:nil]; MXSDKOptions *sdkOptions = [MXSDKOptions sharedInstance]; // Apply the application group From e9e0a092319d1e8450b7de571de0499587184089 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 2 Apr 2019 14:42:43 +0200 Subject: [PATCH 031/150] Log memory usage when receive a memory warning --- .../Managers/ShareExtensionManager.m | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 6450187b2..38120b2c6 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -20,6 +20,7 @@ @import MobileCoreServices; #import "objc/runtime.h" #include +#import NSString *const kShareExtensionManagerDidUpdateAccountDataNotification = @"kShareExtensionManagerDidUpdateAccountDataNotification"; @@ -818,6 +819,31 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) return isImageNotOrientedUp; } +// Log memory usage. +// NOTE: This result may not be reliable for all iOS versions (see https://forums.developer.apple.com/thread/64665 for more information). +- (void)logMemoryUsage +{ + struct task_basic_info basicInfo; + mach_msg_type_number_t size = TASK_BASIC_INFO_COUNT; + kern_return_t kerr = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&basicInfo, + &size); + + vm_size_t memoryUsedInBytes = basicInfo.resident_size; + CGFloat memoryUsedInMegabytes = memoryUsedInBytes / (1024*1024); + + if (kerr == KERN_SUCCESS) + { + NSLog(@"[ShareExtensionManager] Memory in use (in MiB): %f", memoryUsedInMegabytes); + } + else + { + NSLog(@"[ShareExtensionManager] Error with task_info(): %s", mach_error_string(kerr)); + } +} + + #pragma mark - Notifications - (void)onMediaLoaderStateDidChange:(NSNotification *)notification @@ -850,6 +876,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) - (void)didReceiveMemoryWarning:(NSNotification*)notification { NSLog(@"[ShareExtensionManager] Did receive memory warning"); + [self logMemoryUsage]; } #pragma mark - Sharing From 8d02f5799a80306cc55c7fe8349342289be9cb45 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 2 Apr 2019 15:01:50 +0200 Subject: [PATCH 032/150] Log image compression size choice in share extension to help debug memory warnings --- .../Managers/ShareExtensionManager.m | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 38120b2c6..6c3ed44dd 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -529,6 +529,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) // Send the small image self.imageCompressionMode = ImageCompressionModeSmall; + + [self logCompressionSizeChoice:compressionSizes.large]; + if (shareBlock) { shareBlock(); @@ -554,6 +557,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) // Send the medium image self.imageCompressionMode = ImageCompressionModeMedium; + + [self logCompressionSizeChoice:compressionSizes.large]; + if (shareBlock) { shareBlock(); @@ -582,6 +588,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) // Send the large image self.imageCompressionMode = ImageCompressionModeLarge; self.actualLargeSize = compressionSizes.actualLargeSize; + + [self logCompressionSizeChoice:compressionSizes.large]; + if (shareBlock) { shareBlock(); @@ -607,6 +616,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) typeof(self) self = weakSelf; self.imageCompressionMode = ImageCompressionModeNone; + + [self logCompressionSizeChoice:compressionSizes.large]; if (shareBlock) { shareBlock(); @@ -633,6 +644,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) self.imageCompressionMode = ImageCompressionModeNone; } + NSLog(@"[ShareExtensionManager] Send %lu image(s) without compression prompt using compression mode: %ld", (unsigned long)self.pendingImages.count, (long)self.imageCompressionMode); + if (shareBlock) { shareBlock(); @@ -819,6 +832,16 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) return isImageNotOrientedUp; } +- (void)logCompressionSizeChoice:(MXKImageCompressionSize)compressionSize +{ + NSString *fileSize = [MXTools fileSizeToString:compressionSize.fileSize round:NO]; + NSUInteger imageWidth = compressionSize.imageSize.width; + NSUInteger imageHeight = compressionSize.imageSize.height; + + NSLog(@"[ShareExtensionManager] User choose image compression with output size %lu x %lu (output file size: %@)", (unsigned long)imageWidth, (unsigned long)imageHeight, fileSize); + NSLog(@"[ShareExtensionManager] Number of images to send: %lu", (unsigned long)self.pendingImages.count); +} + // Log memory usage. // NOTE: This result may not be reliable for all iOS versions (see https://forums.developer.apple.com/thread/64665 for more information). - (void)logMemoryUsage From 13f77af2d5f4621d5a1c3a5d98596471d4e9fced Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 2 Apr 2019 15:10:15 +0200 Subject: [PATCH 033/150] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 145aa4776..835b55239 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Improvements: * Widgets: Use scalar prod urls in Riot mobile apps (#2349). Bug fix: + * Share extension: Fix a crash when receive a memory warning (PR #2352). Changes in 0.8.4 (2019-03-21) =============================================== From 7b17bc12c4e34a19d8d6bae91b803b2a9cee81b6 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 2 Apr 2019 15:44:59 +0200 Subject: [PATCH 034/150] Update memory usage log in `ShareExtensionManager` --- RiotShareExtension/Managers/ShareExtensionManager.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotShareExtension/Managers/ShareExtensionManager.m b/RiotShareExtension/Managers/ShareExtensionManager.m index 6c3ed44dd..c861ff4a0 100644 --- a/RiotShareExtension/Managers/ShareExtensionManager.m +++ b/RiotShareExtension/Managers/ShareExtensionManager.m @@ -858,7 +858,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode) if (kerr == KERN_SUCCESS) { - NSLog(@"[ShareExtensionManager] Memory in use (in MiB): %f", memoryUsedInMegabytes); + NSLog(@"[ShareExtensionManager] Memory in use (in MB): %f", memoryUsedInMegabytes); } else { From 6c48ecf0ccdd0006ba8a0ebe73ca872bacd27cca Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 16:51:28 +0200 Subject: [PATCH 035/150] Productivity: Create templates for screens and flow root coordinators --- Riot.xcodeproj/project.pbxproj | 121 ++++++++++++ Riot/Generated/Storyboards.swift | 5 + Tools/SwiftGen/swiftgen-config.yml | 1 + Tools/Templates/README.md | 24 +++ .../FlowTemplateCoordinator.swift | 77 ++++++++ ...owTemplateCoordinatorBridgePresenter.swift | 77 ++++++++ .../FlowTemplateCoordinatorType.swift | 26 +++ .../TemplateScreenCoordinator.swift | 68 +++++++ .../TemplateScreenCoordinatorType.swift | 27 +++ .../TemplateScreenViewAction.swift | 24 +++ .../TemplateScreenViewController.storyboard | 92 +++++++++ .../TemplateScreenViewController.swift | 185 ++++++++++++++++++ .../TemplateScreenViewModel.swift | 85 ++++++++ .../TemplateScreenViewModelType.swift | 37 ++++ .../TemplateScreenViewState.swift | 24 +++ Tools/Templates/createScreen.sh | 0 16 files changed, 873 insertions(+) create mode 100644 Tools/Templates/README.md create mode 100644 Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift create mode 100644 Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift create mode 100644 Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift create mode 100644 Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift create mode 100644 Tools/Templates/createScreen.sh diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index f4f6d4f54..13c95d8fc 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -22,6 +22,20 @@ 32242F1721E8FBE500725742 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0D21E8FBA900725742 /* Theme.swift */; }; 32242F1821E8FBF800725742 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0F21E8FBA900725742 /* DefaultTheme.swift */; }; 32242F1921E8FBFB00725742 /* DarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F1021E8FBA900725742 /* DarkTheme.swift */; }; + 3232AB1422564D9100AD6A5C /* swiftgen-config.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB0022564D9100AD6A5C /* swiftgen-config.yml */; }; + 3232AB1522564D9100AD6A5C /* flat-swift4-vector.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB0322564D9100AD6A5C /* flat-swift4-vector.stencil */; }; + 3232AB2122564D9100AD6A5C /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB1322564D9100AD6A5C /* README.md */; }; + 3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB3C2256558300AD6A5C /* FlowTemplateCoordinatorType.swift */; }; + 3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB3D2256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift */; }; + 3232AB4A2256558300AD6A5C /* FlowTemplateCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB3E2256558300AD6A5C /* FlowTemplateCoordinator.swift */; }; + 3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB402256558300AD6A5C /* TemplateScreenViewController.storyboard */; }; + 3232AB4C2256558300AD6A5C /* TemplateScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB412256558300AD6A5C /* TemplateScreenCoordinator.swift */; }; + 3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB422256558300AD6A5C /* TemplateScreenCoordinatorType.swift */; }; + 3232AB4E2256558300AD6A5C /* TemplateScreenViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB432256558300AD6A5C /* TemplateScreenViewModelType.swift */; }; + 3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB442256558300AD6A5C /* TemplateScreenViewController.swift */; }; + 3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB452256558300AD6A5C /* TemplateScreenViewState.swift */; }; + 3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB462256558300AD6A5C /* TemplateScreenViewAction.swift */; }; + 3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB472256558300AD6A5C /* TemplateScreenViewModel.swift */; }; 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; @@ -477,6 +491,20 @@ 32242F0F21E8FBA900725742 /* DefaultTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = ""; }; 32242F1021E8FBA900725742 /* DarkTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkTheme.swift; sourceTree = ""; }; 32242F1121E8FBA900725742 /* ThemeService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThemeService.h; sourceTree = ""; }; + 3232AB0022564D9100AD6A5C /* swiftgen-config.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "swiftgen-config.yml"; sourceTree = ""; }; + 3232AB0322564D9100AD6A5C /* flat-swift4-vector.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "flat-swift4-vector.stencil"; sourceTree = ""; }; + 3232AB1322564D9100AD6A5C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 3232AB3C2256558300AD6A5C /* FlowTemplateCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowTemplateCoordinatorType.swift; sourceTree = ""; }; + 3232AB3D2256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowTemplateCoordinatorBridgePresenter.swift; sourceTree = ""; }; + 3232AB3E2256558300AD6A5C /* FlowTemplateCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FlowTemplateCoordinator.swift; sourceTree = ""; }; + 3232AB402256558300AD6A5C /* TemplateScreenViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TemplateScreenViewController.storyboard; sourceTree = ""; }; + 3232AB412256558300AD6A5C /* TemplateScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenCoordinator.swift; sourceTree = ""; }; + 3232AB422256558300AD6A5C /* TemplateScreenCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenCoordinatorType.swift; sourceTree = ""; }; + 3232AB432256558300AD6A5C /* TemplateScreenViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModelType.swift; sourceTree = ""; }; + 3232AB442256558300AD6A5C /* TemplateScreenViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewController.swift; sourceTree = ""; }; + 3232AB452256558300AD6A5C /* TemplateScreenViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewState.swift; sourceTree = ""; }; + 3232AB462256558300AD6A5C /* TemplateScreenViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewAction.swift; sourceTree = ""; }; + 3232AB472256558300AD6A5C /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = ""; }; 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -1161,6 +1189,83 @@ path = Themes; sourceTree = ""; }; + 3232AAFE22564D9100AD6A5C /* Tools */ = { + isa = PBXGroup; + children = ( + 3232AAFF22564D9100AD6A5C /* SwiftGen */, + 3232AB0422564D9100AD6A5C /* Templates */, + ); + path = Tools; + sourceTree = ""; + }; + 3232AAFF22564D9100AD6A5C /* SwiftGen */ = { + isa = PBXGroup; + children = ( + 3232AB0022564D9100AD6A5C /* swiftgen-config.yml */, + 3232AB0122564D9100AD6A5C /* Templates */, + ); + path = SwiftGen; + sourceTree = ""; + }; + 3232AB0122564D9100AD6A5C /* Templates */ = { + isa = PBXGroup; + children = ( + 3232AB0222564D9100AD6A5C /* Strings */, + ); + path = Templates; + sourceTree = ""; + }; + 3232AB0222564D9100AD6A5C /* Strings */ = { + isa = PBXGroup; + children = ( + 3232AB0322564D9100AD6A5C /* flat-swift4-vector.stencil */, + ); + path = Strings; + sourceTree = ""; + }; + 3232AB0422564D9100AD6A5C /* Templates */ = { + isa = PBXGroup; + children = ( + 3232AB1322564D9100AD6A5C /* README.md */, + 3232AB0522564D9100AD6A5C /* buildable */, + ); + path = Templates; + sourceTree = ""; + }; + 3232AB0522564D9100AD6A5C /* buildable */ = { + isa = PBXGroup; + children = ( + 3232AB3B2256558300AD6A5C /* FlowCoordinatorTemplate */, + 3232AB3F2256558300AD6A5C /* ScreenTemplate */, + ); + path = buildable; + sourceTree = ""; + }; + 3232AB3B2256558300AD6A5C /* FlowCoordinatorTemplate */ = { + isa = PBXGroup; + children = ( + 3232AB3C2256558300AD6A5C /* FlowTemplateCoordinatorType.swift */, + 3232AB3D2256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift */, + 3232AB3E2256558300AD6A5C /* FlowTemplateCoordinator.swift */, + ); + path = FlowCoordinatorTemplate; + sourceTree = ""; + }; + 3232AB3F2256558300AD6A5C /* ScreenTemplate */ = { + isa = PBXGroup; + children = ( + 3232AB402256558300AD6A5C /* TemplateScreenViewController.storyboard */, + 3232AB412256558300AD6A5C /* TemplateScreenCoordinator.swift */, + 3232AB422256558300AD6A5C /* TemplateScreenCoordinatorType.swift */, + 3232AB432256558300AD6A5C /* TemplateScreenViewModelType.swift */, + 3232AB442256558300AD6A5C /* TemplateScreenViewController.swift */, + 3232AB452256558300AD6A5C /* TemplateScreenViewState.swift */, + 3232AB462256558300AD6A5C /* TemplateScreenViewAction.swift */, + 3232AB472256558300AD6A5C /* TemplateScreenViewModel.swift */, + ); + path = ScreenTemplate; + sourceTree = ""; + }; 3233F7291F31F3B4006ACA81 /* libs */ = { isa = PBXGroup; children = ( @@ -2920,6 +3025,7 @@ F094A9A31B78D8F000B1FBBF /* Products */, 5FC42FA41F5186AFFB6A2404 /* Frameworks */, A237FB70534FB8ADA0D7CFEE /* Pods */, + 3232AAFE22564D9100AD6A5C /* Tools */, ); sourceTree = ""; usesTabs = 0; @@ -3076,6 +3182,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, Base, nl, @@ -3193,17 +3300,21 @@ B1B5573720EE6C4D00210D55 /* GroupParticipantsViewController.xib in Resources */, B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */, B1B5573320EE6C4D00210D55 /* GroupHomeViewController.xib in Resources */, + 3232AB2122564D9100AD6A5C /* README.md in Resources */, B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */, B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */, B1664DA320F4F96200808783 /* Vector.strings in Resources */, B1B557C720EF5CD400210D55 /* DirectoryServerDetailTableViewCell.xib in Resources */, B1B5582620EF638A00210D55 /* RoomMemberTitleView.xib in Resources */, + 3232AB1522564D9100AD6A5C /* flat-swift4-vector.stencil in Resources */, F083BDE61E7009ED00A9B29C /* busy.mp3 in Resources */, B1B5574C20EE6C4D00210D55 /* MediaAlbumContentViewController.xib in Resources */, B1B557E820EF60F500210D55 /* MessagesSearchResultTextMsgBubbleCell.xib in Resources */, B1B558D920EF768F00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.xib in Resources */, B1B5573020EE6C4D00210D55 /* BugReportViewController.xib in Resources */, B169329B20F39E6300746532 /* Main.storyboard in Resources */, + 3232AB1422564D9100AD6A5C /* swiftgen-config.yml in Resources */, + 3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */, 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */, B1B5578E20EF568D00210D55 /* GroupInviteTableViewCell.xib in Resources */, B1B5582020EF625800210D55 /* SimpleRoomTitleView.xib in Resources */, @@ -3467,6 +3578,7 @@ B1FDF56021F5FE5500BA3834 /* KeyBackupSetupPassphraseViewAction.swift in Sources */, B1B5573120EE6C4D00210D55 /* BugReportViewController.m in Sources */, B16932A520F3A21C00746532 /* empty.mm in Sources */, + 3232AB4A2256558300AD6A5C /* FlowTemplateCoordinator.swift in Sources */, B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */, B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, @@ -3479,6 +3591,7 @@ B16932B120F3AC9200746532 /* RoomSearchDataSource.m in Sources */, B16932A320F3A21C00746532 /* main.m in Sources */, B1B5574520EE6C4D00210D55 /* StartChatViewController.m in Sources */, + 3232AB4C2256558300AD6A5C /* TemplateScreenCoordinator.swift in Sources */, B1B5575920EE6C4D00210D55 /* HomeMessagesSearchViewController.m in Sources */, B1B558DE20EF768F00210D55 /* RoomIncomingAttachmentBubbleCell.m in Sources */, B1B5574820EE6C4D00210D55 /* PeopleViewController.m in Sources */, @@ -3499,6 +3612,7 @@ B1B5572320EE6C4D00210D55 /* AttachmentsViewController.m in Sources */, F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */, B1B5598620EFC3E000210D55 /* RiotSettings.swift in Sources */, + 3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */, B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */, B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */, B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, @@ -3546,6 +3660,8 @@ B169330020F3C97D00746532 /* RoomDataSource.m in Sources */, B1B558ED20EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderNameBubbleCell.m in Sources */, B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */, + 3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */, + 3232AB4E2256558300AD6A5C /* TemplateScreenViewModelType.swift in Sources */, B1B5590520EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */, B1098BE521ECE1FC000DDA48 /* Storyboards.swift in Sources */, @@ -3567,6 +3683,7 @@ B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */, + 3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */, B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */, B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */, B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */, @@ -3637,11 +3754,13 @@ B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */, B1098BE121ECE09F000DDA48 /* Images.swift in Sources */, B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */, + 3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */, B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */, B1B558C720EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, B1B558F020EF768F00210D55 /* RoomOutgoingAttachmentWithPaginationTitleBubbleCell.m in Sources */, 926FA53F1F4C132000F826C2 /* MXSession+Riot.m in Sources */, B1B5593820EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.m in Sources */, + 3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */, B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */, B1B557C620EF5CD400210D55 /* DirectoryServerDetailTableViewCell.m in Sources */, B1B5590920EF768F00210D55 /* RoomEmptyBubbleCell.m in Sources */, @@ -3683,10 +3802,12 @@ B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, B1DB4F0B223131600065DBFA /* String.swift in Sources */, + 3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */, B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */, B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */, B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */, B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */, + 3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */, B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, 32242F0921E8B05F00725742 /* UIColor.swift in Sources */, B16932E720F3C37100746532 /* HomeMessagesSearchDataSource.m in Sources */, diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index afafd57f1..80f0725b6 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -47,6 +47,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self) } + internal enum TemplateScreenViewController: StoryboardType { + internal static let storyboardName = "TemplateScreenViewController" + + internal static let initialScene = InitialSceneType(storyboard: TemplateScreenViewController.self) + } } // swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name diff --git a/Tools/SwiftGen/swiftgen-config.yml b/Tools/SwiftGen/swiftgen-config.yml index 7da2a05f6..b9d717b2f 100755 --- a/Tools/SwiftGen/swiftgen-config.yml +++ b/Tools/SwiftGen/swiftgen-config.yml @@ -3,6 +3,7 @@ output_dir: ../../Riot/Generated/ ib: - inputs: - Modules/KeyBackup/ + - ../Tools/Templates/buildable/ outputs: - templateName: scenes-swift4 output: Storyboards.swift diff --git a/Tools/Templates/README.md b/Tools/Templates/README.md new file mode 100644 index 000000000..e48d45706 --- /dev/null +++ b/Tools/Templates/README.md @@ -0,0 +1,24 @@ +The `buildable` contains templates with source files that build. + +The goal is to turn these templates as Xcode templates. They are part of the Riot project in order to ensure they build + +# ScreenTemplate +This is the boilerplate to create a screen that follows the MVVM-C pattern within the Riot app. + +To use it (before it becomes an Xcode template): + +- `./createScreen.sh MyScreen [subFolder]` +- Import the created folder in the Xcode project + +`subFolder` is an option subfolder under `Riot/Modules/` + + +# FlowCoordinatorTemplate +The boilerplate to create a root coordinator and its presenter bridge that can be used from Objective-C. + +To use it (before it becomes an Xcode template): + +- `./createFlowCoordinator.sh MyFlowCoordinator [subFolder]` +- Import the created folder in the Xcode project + +`subFolder` is an option subfolder under `Riot/Modules/` \ No newline at end of file diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift new file mode 100644 index 000000000..e96de671a --- /dev/null +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift @@ -0,0 +1,77 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +@objcMembers +final class FlowTemplateCoordinator: FlowTemplateCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let session: MXSession + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: FlowTemplateCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.session = session + } + + // MARK: - Public methods + + func start() { + + let rootCoordinator = self.createTemplateScreenCoordinator() + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + self.navigationRouter.setRootModule(rootCoordinator) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createTemplateScreenCoordinator() -> TemplateScreenCoordinator { + let coordinator = TemplateScreenCoordinator(session: self.session) + coordinator.delegate = self + return coordinator + } +} + +// MARK: - TemplateScreenCoordinatorDelegate +extension FlowTemplateCoordinator: TemplateScreenCoordinatorDelegate { + func templateScreenCoordinator(_ templateScreenCoordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) { + self.delegate?.flowTemplateCoordinatorDidComplete(self) + } + + func templateScreenCoordinatorDidCancel(_ templateScreenCoordinator: TemplateScreenCoordinatorType) { + self.delegate?.flowTemplateCoordinatorDidComplete(self) + } +} diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..6d5eb3242 --- /dev/null +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift @@ -0,0 +1,77 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc protocol FlowTemplateCoordinatorBridgePresenterDelegate { + func flowTemplateCoordinatorBridgePresenterDelegateDidComplete(_ flowTemplateCoordinatorBridgePresenter: FlowTemplateCoordinatorBridgePresenter) +} + +/// FlowTemplateCoordinatorBridgePresenter enables to start FlowTemplateCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class FlowTemplateCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var coordinator: FlowTemplateCoordinator? + + // MARK: Public + + weak var delegate: FlowTemplateCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, isStartedFromSignOut: false, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let flowTemplateCoordinator = FlowTemplateCoordinator(session: self.session) + flowTemplateCoordinator.delegate = self + viewController.present(flowTemplateCoordinator.toPresentable(), animated: animated, completion: nil) + flowTemplateCoordinator.start() + + self.coordinator = flowTemplateCoordinator + } + + func dismiss(animated: Bool) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + } + } +} + +// MARK: - FlowTemplateCoordinatorDelegate +extension FlowTemplateCoordinatorBridgePresenter: FlowTemplateCoordinatorDelegate { + func flowTemplateCoordinatorDidComplete(_ flowTemplateCoordinator: FlowTemplateCoordinatorType) { + self.delegate?.flowTemplateCoordinatorBridgePresenterDelegateDidComplete(self) + } +} diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift new file mode 100644 index 000000000..e4b63d80d --- /dev/null +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift @@ -0,0 +1,26 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol FlowTemplateCoordinatorDelegate: class { + func flowTemplateCoordinatorDidComplete(_ flowTemplateCoordinator: FlowTemplateCoordinatorType) +} + +/// `FlowTemplateCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol FlowTemplateCoordinatorType: Coordinator, Presentable { + var delegate: FlowTemplateCoordinatorDelegate? { get } +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift new file mode 100644 index 000000000..f7c599f3a --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift @@ -0,0 +1,68 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +import UIKit + +final class TemplateScreenCoordinator: TemplateScreenCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var templateScreenViewModel: TemplateScreenViewModelType + private let templateScreenViewController: TemplateScreenViewController + + // MARK: Public + + var childCoordinators: [Coordinator] = [] + + weak var delegate: TemplateScreenCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + + let templateScreenViewModel = TemplateScreenViewModel(session: self.session) + let templateScreenViewController = TemplateScreenViewController.instantiate(with: templateScreenViewModel) + self.templateScreenViewModel = templateScreenViewModel + self.templateScreenViewController = templateScreenViewController + } + + // MARK: - Public methods + + func start() { + self.templateScreenViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.templateScreenViewController + } +} + +// MARK: - TemplateScreenViewModelCoordinatorDelegate +extension TemplateScreenCoordinator: TemplateScreenViewModelCoordinatorDelegate { + + func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didCompleteWithMessage message: String) { + self.delegate?.templateScreenCoordinator(self, didCompleteWithMessage: message) + } + + func templateScreenViewModelDidCancel(_ viewModel: TemplateScreenViewModelType) { + self.delegate?.templateScreenCoordinatorDidCancel(self) + } +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift new file mode 100644 index 000000000..1701eee2a --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift @@ -0,0 +1,27 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol TemplateScreenCoordinatorDelegate: class { + func templateScreenCoordinator(_ templateScreenCoordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) + func templateScreenCoordinatorDidCancel(_ templateScreenCoordinator: TemplateScreenCoordinatorType) +} + +/// `TemplateScreenCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol TemplateScreenCoordinatorType: Coordinator, Presentable { + var delegate: TemplateScreenCoordinatorDelegate? { get } +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift new file mode 100644 index 000000000..8dca49e5c --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewAction.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// TemplateScreenViewController view actions exposed to view model +enum TemplateScreenViewAction { + case sayHello + case complete + case cancel +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard new file mode 100644 index 000000000..35b09e221 --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift new file mode 100644 index 000000000..4d1e3b3c6 --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift @@ -0,0 +1,185 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +final class TemplateScreenViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private weak var okButton: UIButton! + + // MARK: Private + + private var viewModel: TemplateScreenViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: TemplateScreenViewModelType) -> TemplateScreenViewController { + let viewController = StoryboardScene.TemplateScreenViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = "Template" + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .sayHello) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: + self.messageLabel.textColor = theme.textPrimaryColor + + self.okButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.okButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + self.messageLabel.text = "VectorL10n.templateScreenTitle" + self.messageLabel.isHidden = true + } + + private func render(viewState: TemplateScreenViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.messageLabel.text = self.viewModel.message + self.messageLabel.isHidden = false + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func okButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .complete) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - TemplateScreenViewModelViewDelegate +extension TemplateScreenViewController: TemplateScreenViewModelViewDelegate { + + func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didUpdateViewState viewSate: TemplateScreenViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift new file mode 100644 index 000000000..9b716cddf --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModel.swift @@ -0,0 +1,85 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +final class TemplateScreenViewModel: TemplateScreenViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + + // MARK: Public + + var message: String? + + weak var viewDelegate: TemplateScreenViewModelViewDelegate? + weak var coordinatorDelegate: TemplateScreenViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + self.message = nil + } + + deinit { + } + + // MARK: - Public + + func process(viewAction: TemplateScreenViewAction) { + switch viewAction { + case .sayHello: + self.setupHelloMessage() + case .complete: + if let message = self.message { + self.coordinatorDelegate?.templateScreenViewModel(self, didCompleteWithMessage: message) + } + case .cancel: + self.coordinatorDelegate?.templateScreenViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func setupHelloMessage() { + + self.update(viewState: .loading) + + // Check first that the user homeserver is federated with the Riot-bot homeserver + self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in + + guard let sself = self else { + return + } + + switch response { + case .success: + sself.message = "Hello \(response.value ?? "you")" + sself.update(viewState: .loaded) + case .failure(let error): + sself.update(viewState: .error(error)) + } + } + } + + private func update(viewState: TemplateScreenViewState) { + self.viewDelegate?.templateScreenViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift new file mode 100644 index 000000000..fd76d45b1 --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewModelType.swift @@ -0,0 +1,37 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol TemplateScreenViewModelViewDelegate: class { + func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didUpdateViewState viewSate: TemplateScreenViewState) +} + +protocol TemplateScreenViewModelCoordinatorDelegate: class { + func templateScreenViewModel(_ viewModel: TemplateScreenViewModelType, didCompleteWithMessage message: String) + func templateScreenViewModelDidCancel(_ viewModel: TemplateScreenViewModelType) +} + +/// Protocol describing the view model used by `TemplateScreenViewController` +protocol TemplateScreenViewModelType { + + var message: String? { get set } + + var viewDelegate: TemplateScreenViewModelViewDelegate? { get set } + var coordinatorDelegate: TemplateScreenViewModelCoordinatorDelegate? { get set } + + func process(viewAction: TemplateScreenViewAction) +} diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift new file mode 100644 index 000000000..815fd8196 --- /dev/null +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewState.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// TemplateScreenViewController view state +enum TemplateScreenViewState { + case loading + case loaded + case error(Error) +} diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh new file mode 100644 index 000000000..e69de29bb From 5645dfa43f458c330eee7f9bca9fc602303c0538 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Apr 2019 17:45:13 +0200 Subject: [PATCH 036/150] Update notification strings for notification titles --- Riot/Assets/en.lproj/Localizable.strings | 32 ++++++------------------ 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/Riot/Assets/en.lproj/Localizable.strings b/Riot/Assets/en.lproj/Localizable.strings index b387bfa82..fc5cc015a 100644 --- a/Riot/Assets/en.lproj/Localizable.strings +++ b/Riot/Assets/en.lproj/Localizable.strings @@ -14,17 +14,17 @@ limitations under the License. */ +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@"; + /** Single, end-to-end encrypted messages (ie. we don't know what they say) */ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Message from %@"; +"MSG_FROM_USER" = "%@ sent a message"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ posted in %@"; -/* New message when a notification title is used */ -"MSG_TEXT_WITH_TITLE" = "Message"; - /** Single, unencrypted messages (where we can include the content */ /* New message from a specific person, not referencing a room. Content included. */ @@ -39,28 +39,22 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; -/* New action message, sender is specified in notification title */ -"ACTION" = "* %@"; - /** Image Messages **/ /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ sent you a picture %@"; +"IMAGE_FROM_USER" = "%@ sent a picture %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ posted a picture %@ in %@"; -/* New action message, but the sender (and room) are already in the notification title */ -"IMAGE_TEXT_WITH_TITLE" = "📷 Picture"; - /* A single unread message in a room */ "SINGLE_UNREAD_IN_ROOM" = "You received a message in %@"; /* A single unread message */ "SINGLE_UNREAD" = "You received a message"; -/* Sticker, but with the sender (and room) already in the title */ -"STICKER_TEXT_WITH_TITLE" = "💟 Sticker"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ sent a sticker"; /** Coalesced messages **/ @@ -96,24 +90,14 @@ /* A user has invited you to a named room */ "USER_INVITE_TO_NAMED_ROOM" = "%@ has invited you to %@"; -/* Same as USER_INVITE_TO_CHAT but the username is already displayed in the notification title */ -"INVITE_TO_CHAT" = "You were invited to chat"; - -/* Same as USER_INVITE_TO_NAMED_ROOM but the room name is already displayed in the notification title */ -"INVITE_BY_USER_TO_ROOM" = "You were invited by %@"; - /** Calls **/ /* Incoming one-to-one voice call */ "VOICE_CALL_FROM_USER" = "Call from %@"; -"VOICE_CALL" = "📞 Call"; - /* Incoming one-to-one video call */ "VIDEO_CALL_FROM_USER" = "Video call from %@"; -"VIDEO_CALL" = "📹 Video call"; - /* Incoming unnamed voice conference invite from a specific person */ "VOICE_CONF_FROM_USER" = "Group call from %@"; @@ -125,5 +109,3 @@ /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Video group call from %@: '%@'"; - -"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@"; \ No newline at end of file From 576060fba9ff25c1f5c0d1e3801cac1657c3c392 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:01:46 +0200 Subject: [PATCH 037/150] Productivity: SwiftGen: expose all storyboards from Modules/ except one that is in Objc --- Tools/SwiftGen/swiftgen-config.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Tools/SwiftGen/swiftgen-config.yml b/Tools/SwiftGen/swiftgen-config.yml index b9d717b2f..bb711f244 100755 --- a/Tools/SwiftGen/swiftgen-config.yml +++ b/Tools/SwiftGen/swiftgen-config.yml @@ -1,9 +1,10 @@ input_dir: ../../Riot/ output_dir: ../../Riot/Generated/ ib: - - inputs: - - Modules/KeyBackup/ + - inputs: - ../Tools/Templates/buildable/ + - Modules/ + filter: ^((?!DeactivateAccountViewController).)*\.(storyboard) outputs: - templateName: scenes-swift4 output: Storyboards.swift From df3261545607e8244f39a32e5ab05cbe0b4305c4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:17:19 +0200 Subject: [PATCH 038/150] Productivity: Create createScreen.sh --- Tools/Templates/createScreen.sh | 44 +++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) mode change 100644 => 100755 Tools/Templates/createScreen.sh diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh old mode 100644 new mode 100755 index e69de29bb..6307d3565 --- a/Tools/Templates/createScreen.sh +++ b/Tools/Templates/createScreen.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +if [ $# -eq 0 ]; then + echo "Usage: ./createScreen.sh MyScreen [subFolder]" + exit 1 +fi + +SCREEN_NAME=$1 +SCREEN_VAR_NAME=`echo $SCREEN_NAME | awk '{ print tolower(substr($0, 1, 1)) substr($0, 2) }'` + +MODULE_DIR="../../Riot/Modules" + +OUTPUT_DIR="$MODULE_DIR" +if [ $# -eq 2 ]; +then + OUTPUT_DIR="$OUTPUT_DIR/$1" + if [ ! -e $OUTPUT_DIR ]; then + echo "Create folder ${OUTPUT_DIR}" + mkdir $OUTPUT_DIR + fi + + OUTPUT_DIR="$OUTPUT_DIR/$2" +else + OUTPUT_DIR="$OUTPUT_DIR/$1" +fi + + +if [ -e $OUTPUT_DIR ]; then + echo "Error: Folder ${OUTPUT_DIR} already exists" + exit 1 +fi + +echo "Create folder ${OUTPUT_DIR}" + +cp -R buildable/ScreenTemplate $OUTPUT_DIR + +cd $OUTPUT_DIR +for file in * +do + echo "Building ${file/TemplateScreen/$SCREEN_NAME}..." + perl -p -i -e "s/TemplateScreen/"$SCREEN_NAME"/g" $file + perl -p -i -e "s/templateScreen/"$SCREEN_VAR_NAME"/g" $file + mv ${file} ${file/TemplateScreen/$SCREEN_NAME} +done \ No newline at end of file From 6967b918ec4de4ae2c12258c4fa9175478ae6af6 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:20:21 +0200 Subject: [PATCH 039/150] CHANGES --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 835b55239..1c0af777e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes in 0.8.5 (2019-xx-xx) Improvements: * Push: Add more logs to track spontaneously disabling (#2348). * Widgets: Use scalar prod urls in Riot mobile apps (#2349). + * Productiviy: Create templates (see Tools/Templates/README.md). Bug fix: * Share extension: Fix a crash when receive a memory warning (PR #2352). From 9e30a49e608b7ebf75bb93018fef0994053a4e1e Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:35:58 +0200 Subject: [PATCH 040/150] Productivity: createScreen.sh: Fix subFolder parameter --- Tools/Templates/createScreen.sh | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh index 6307d3565..18019a48e 100755 --- a/Tools/Templates/createScreen.sh +++ b/Tools/Templates/createScreen.sh @@ -13,16 +13,13 @@ MODULE_DIR="../../Riot/Modules" OUTPUT_DIR="$MODULE_DIR" if [ $# -eq 2 ]; then - OUTPUT_DIR="$OUTPUT_DIR/$1" + OUTPUT_DIR="$OUTPUT_DIR/$2" if [ ! -e $OUTPUT_DIR ]; then echo "Create folder ${OUTPUT_DIR}" mkdir $OUTPUT_DIR fi - - OUTPUT_DIR="$OUTPUT_DIR/$2" -else - OUTPUT_DIR="$OUTPUT_DIR/$1" fi +OUTPUT_DIR="$OUTPUT_DIR/$1" if [ -e $OUTPUT_DIR ]; then From e4b05606bbb8e27d5058f42851540f10d5fcd65e Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:58:32 +0200 Subject: [PATCH 041/150] Productivity: createScreen.sh: Fix subFolder parameter again --- Tools/Templates/README.md | 6 +++--- Tools/Templates/createScreen.sh | 22 ++++++---------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/Tools/Templates/README.md b/Tools/Templates/README.md index e48d45706..931671c79 100644 --- a/Tools/Templates/README.md +++ b/Tools/Templates/README.md @@ -7,10 +7,10 @@ This is the boilerplate to create a screen that follows the MVVM-C pattern withi To use it (before it becomes an Xcode template): -- `./createScreen.sh MyScreen [subFolder]` +- `./createScreen.sh ScreenFolder MyScreenName` - Import the created folder in the Xcode project -`subFolder` is an option subfolder under `Riot/Modules/` +This will create ScreenFolder within the Riot/Modules. Files inside will be called `MyScreenNameXxx`. # FlowCoordinatorTemplate @@ -21,4 +21,4 @@ To use it (before it becomes an Xcode template): - `./createFlowCoordinator.sh MyFlowCoordinator [subFolder]` - Import the created folder in the Xcode project -`subFolder` is an option subfolder under `Riot/Modules/` \ No newline at end of file +`subFolder` is an option subfolder under `Riot/Modules/` diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh index 18019a48e..1c42a0aec 100755 --- a/Tools/Templates/createScreen.sh +++ b/Tools/Templates/createScreen.sh @@ -1,27 +1,16 @@ #!/bin/bash -if [ $# -eq 0 ]; then - echo "Usage: ./createScreen.sh MyScreen [subFolder]" +if [ ! $# -eq 2 ]; then + echo "Usage: ./createScreen.sh Folder MyScreenName" exit 1 fi -SCREEN_NAME=$1 +OUTPUT_DIR="../../Riot/Modules"/$1 +SCREEN_NAME=$2 SCREEN_VAR_NAME=`echo $SCREEN_NAME | awk '{ print tolower(substr($0, 1, 1)) substr($0, 2) }'` MODULE_DIR="../../Riot/Modules" -OUTPUT_DIR="$MODULE_DIR" -if [ $# -eq 2 ]; -then - OUTPUT_DIR="$OUTPUT_DIR/$2" - if [ ! -e $OUTPUT_DIR ]; then - echo "Create folder ${OUTPUT_DIR}" - mkdir $OUTPUT_DIR - fi -fi -OUTPUT_DIR="$OUTPUT_DIR/$1" - - if [ -e $OUTPUT_DIR ]; then echo "Error: Folder ${OUTPUT_DIR} already exists" exit 1 @@ -29,7 +18,8 @@ fi echo "Create folder ${OUTPUT_DIR}" -cp -R buildable/ScreenTemplate $OUTPUT_DIR +mkdir -p $OUTPUT_DIR +cp -R buildable/ScreenTemplate/ $OUTPUT_DIR/ cd $OUTPUT_DIR for file in * From c7566865774a77c1ffcd7de5883ffbef38c49af8 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 18:59:06 +0200 Subject: [PATCH 042/150] Productivity: Tweak templates a bit --- .../FlowCoordinatorTemplate/FlowTemplateCoordinator.swift | 3 ++- .../FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift | 2 +- .../ScreenTemplate/TemplateScreenCoordinatorType.swift | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift index e96de671a..92264ce19 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift @@ -27,7 +27,8 @@ final class FlowTemplateCoordinator: FlowTemplateCoordinatorType { private let session: MXSession // MARK: Public - + + // Must be used only internally var childCoordinators: [Coordinator] = [] weak var delegate: FlowTemplateCoordinatorDelegate? diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift index e4b63d80d..cd754d8d4 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorType.swift @@ -17,7 +17,7 @@ import Foundation protocol FlowTemplateCoordinatorDelegate: class { - func flowTemplateCoordinatorDidComplete(_ flowTemplateCoordinator: FlowTemplateCoordinatorType) + func flowTemplateCoordinatorDidComplete(_ coordinator: FlowTemplateCoordinatorType) } /// `FlowTemplateCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift index 1701eee2a..b8869aafd 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinatorType.swift @@ -17,8 +17,8 @@ import Foundation protocol TemplateScreenCoordinatorDelegate: class { - func templateScreenCoordinator(_ templateScreenCoordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) - func templateScreenCoordinatorDidCancel(_ templateScreenCoordinator: TemplateScreenCoordinatorType) + func templateScreenCoordinator(_ coordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) + func templateScreenCoordinatorDidCancel(_ coordinator: TemplateScreenCoordinatorType) } /// `TemplateScreenCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. From 33bfc1bb2b5cb32d5cb35c2344c7402a65cd51ff Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 21:06:46 +0200 Subject: [PATCH 043/150] Productivity: Tweak templates a bit more --- .../FlowCoordinatorTemplate/FlowTemplateCoordinator.swift | 4 ++-- .../FlowTemplateCoordinatorBridgePresenter.swift | 4 ++-- .../buildable/ScreenTemplate/TemplateScreenCoordinator.swift | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift index 92264ce19..720ed34f4 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinator.swift @@ -68,11 +68,11 @@ final class FlowTemplateCoordinator: FlowTemplateCoordinatorType { // MARK: - TemplateScreenCoordinatorDelegate extension FlowTemplateCoordinator: TemplateScreenCoordinatorDelegate { - func templateScreenCoordinator(_ templateScreenCoordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) { + func templateScreenCoordinator(_ coordinator: TemplateScreenCoordinatorType, didCompleteWithMessage message: String) { self.delegate?.flowTemplateCoordinatorDidComplete(self) } - func templateScreenCoordinatorDidCancel(_ templateScreenCoordinator: TemplateScreenCoordinatorType) { + func templateScreenCoordinatorDidCancel(_ coordinator: TemplateScreenCoordinatorType) { self.delegate?.flowTemplateCoordinatorDidComplete(self) } } diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift index 6d5eb3242..4e6939611 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift @@ -17,7 +17,7 @@ import Foundation @objc protocol FlowTemplateCoordinatorBridgePresenterDelegate { - func flowTemplateCoordinatorBridgePresenterDelegateDidComplete(_ flowTemplateCoordinatorBridgePresenter: FlowTemplateCoordinatorBridgePresenter) + func flowTemplateCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: FlowTemplateCoordinatorBridgePresenter) } /// FlowTemplateCoordinatorBridgePresenter enables to start FlowTemplateCoordinator from a view controller. @@ -71,7 +71,7 @@ final class FlowTemplateCoordinatorBridgePresenter: NSObject { // MARK: - FlowTemplateCoordinatorDelegate extension FlowTemplateCoordinatorBridgePresenter: FlowTemplateCoordinatorDelegate { - func flowTemplateCoordinatorDidComplete(_ flowTemplateCoordinator: FlowTemplateCoordinatorType) { + func flowTemplateCoordinatorDidComplete(_ coordinator: FlowTemplateCoordinatorType) { self.delegate?.flowTemplateCoordinatorBridgePresenterDelegateDidComplete(self) } } diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift index f7c599f3a..8cd322e45 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenCoordinator.swift @@ -28,7 +28,8 @@ final class TemplateScreenCoordinator: TemplateScreenCoordinatorType { private let templateScreenViewController: TemplateScreenViewController // MARK: Public - + + // Must be used only internally var childCoordinators: [Coordinator] = [] weak var delegate: TemplateScreenCoordinatorDelegate? From e5c9a262251b2f2f4fda42d805c48dc919b422be Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 4 Apr 2019 22:11:52 +0200 Subject: [PATCH 044/150] Productivity: Create createRootCoordinator.sh --- Tools/Templates/README.md | 42 +++++++++++++++++++++--- Tools/Templates/createRootCoordinator.sh | 37 +++++++++++++++++++++ 2 files changed, 74 insertions(+), 5 deletions(-) create mode 100755 Tools/Templates/createRootCoordinator.sh diff --git a/Tools/Templates/README.md b/Tools/Templates/README.md index 931671c79..0bcde6578 100644 --- a/Tools/Templates/README.md +++ b/Tools/Templates/README.md @@ -1,9 +1,9 @@ -The `buildable` contains templates with source files that build. +The `buildable` folder contains templates with source files that build. The goal is to turn these templates as Xcode templates. They are part of the Riot project in order to ensure they build # ScreenTemplate -This is the boilerplate to create a screen that follows the MVVM-C pattern within the Riot app. +This is the boilerplate to create a screen that follows the MVVM-C pattern used within the Riot app. To use it (before it becomes an Xcode template): @@ -18,7 +18,39 @@ The boilerplate to create a root coordinator and its presenter bridge that can b To use it (before it becomes an Xcode template): -- `./createFlowCoordinator.sh MyFlowCoordinator [subFolder]` -- Import the created folder in the Xcode project +- `./createRootCoordinator.sh Folder MyRootCoordinatorName [DefaultScreenName]` +- Import created files in the Xcode project -`subFolder` is an option subfolder under `Riot/Modules/` + +# Usage example +Following commands: + +``` +./createScreen.sh MyFlowDir/MyFirstScreenDir MyFirstScreen +./createRootCoordinator.sh MyFlowDir MyFlow MyFirstScreen +``` + +generate in `Riot/Modules`: + +``` +Riot/Modules/MyFlowDir +├── MyFirstScreenDir +│   ├── MyFirstScreenCoordinator.swift +│   ├── MyFirstScreenCoordinatorType.swift +│   ├── MyFirstScreenViewAction.swift +│   ├── MyFirstScreenViewController.storyboard +│   ├── MyFirstScreenViewController.swift +│   ├── MyFirstScreenViewModel.swift +│   ├── MyFirstScreenViewModelType.swift +│   └── MyFirstScreenViewState.swift +├── MyFlowCoordinator.swift +├── MyFlowCoordinatorBridgePresenter.swift +└── MyFlowCoordinatorType.swift +``` + +It is then ready to use: + +``` +MyFlowCoordinatorBridgePresenter *presenter = [[MyFlowCoordinatorBridgePresenter alloc] initWithSession:session]; +[presenter presentFrom:self animated:YES]; +``` diff --git a/Tools/Templates/createRootCoordinator.sh b/Tools/Templates/createRootCoordinator.sh new file mode 100755 index 000000000..b82b20bfc --- /dev/null +++ b/Tools/Templates/createRootCoordinator.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +if [ ! $# -eq 2 ] && [ ! $# -eq 3 ] ; then + echo "Usage: ./createRootCoordinator.sh Folder MyRootCoordinatorName [DefaultScreenName]" + exit 1 +fi + + +OUTPUT_DIR="../../Riot/Modules"/$1 +COORDINATOR_NAME=$2 +COORDINATOR_VAR_NAME=`echo $COORDINATOR_NAME | awk '{ print tolower(substr($0, 1, 1)) substr($0, 2) }'` +SCREEN_NAME=$3 +SCREEN_VAR_NAME=`echo $SCREEN_NAME | awk '{ print tolower(substr($0, 1, 1)) substr($0, 2) }'` + +MODULE_DIR="../../Riot/Modules" + +echo "Create folder ${OUTPUT_DIR}" + +mkdir -p $OUTPUT_DIR +cp -R buildable/FlowCoordinatorTemplate/ $OUTPUT_DIR/ + +cd $OUTPUT_DIR +for file in FlowTemplate* +do + if [ -f "$file" ]; then + echo "Building ${file/FlowTemplate/$COORDINATOR_NAME}..." + perl -p -i -e "s/FlowTemplate/"$COORDINATOR_NAME"/g" $file + perl -p -i -e "s/flowTemplate/"$COORDINATOR_VAR_NAME"/g" $file + + if [ -n "$SCREEN_NAME" ]; then + perl -p -i -e "s/TemplateScreen/"$SCREEN_NAME"/g" $file + perl -p -i -e "s/templateScreen/"$SCREEN_VAR_NAME"/g" $file + fi + + mv ${file} ${file/FlowTemplate/$COORDINATOR_NAME} + fi +done \ No newline at end of file From ad561a06de2f9e768205b3e8b1e602dea740632c Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 07:57:53 +0200 Subject: [PATCH 045/150] Productivity: Improve Templates/README.md --- Tools/Templates/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Tools/Templates/README.md b/Tools/Templates/README.md index 0bcde6578..54e33f1aa 100644 --- a/Tools/Templates/README.md +++ b/Tools/Templates/README.md @@ -5,12 +5,12 @@ The goal is to turn these templates as Xcode templates. They are part of the Rio # ScreenTemplate This is the boilerplate to create a screen that follows the MVVM-C pattern used within the Riot app. -To use it (before it becomes an Xcode template): +To create a screen from this template (before it becomes an Xcode template): - `./createScreen.sh ScreenFolder MyScreenName` - Import the created folder in the Xcode project -This will create ScreenFolder within the Riot/Modules. Files inside will be called `MyScreenNameXxx`. +This will create ScreenFolder within the Riot/Modules. Files inside will be named `MyScreenNameXxx`. # FlowCoordinatorTemplate @@ -48,7 +48,7 @@ Riot/Modules/MyFlowDir └── MyFlowCoordinatorType.swift ``` -It is then ready to use: +The generated code is ready to use. The screen provided by `ScreenTemplate` can by displayed by: ``` MyFlowCoordinatorBridgePresenter *presenter = [[MyFlowCoordinatorBridgePresenter alloc] initWithSession:session]; From a157a9c2a98b742ffb8e44e03667961fe3aa7c5d Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 08:40:10 +0200 Subject: [PATCH 046/150] Productivity: Tweak templates a bit more --- Tools/Templates/createRootCoordinator.sh | 3 +++ Tools/Templates/createScreen.sh | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/Tools/Templates/createRootCoordinator.sh b/Tools/Templates/createRootCoordinator.sh index b82b20bfc..f8d3deaa4 100755 --- a/Tools/Templates/createRootCoordinator.sh +++ b/Tools/Templates/createRootCoordinator.sh @@ -31,6 +31,9 @@ do perl -p -i -e "s/TemplateScreen/"$SCREEN_NAME"/g" $file perl -p -i -e "s/templateScreen/"$SCREEN_VAR_NAME"/g" $file fi + + echo "// $ createRootCoordinator.sh $@" | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + echo '// File created from FlowTemplate' | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} mv ${file} ${file/FlowTemplate/$COORDINATOR_NAME} fi diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh index 1c42a0aec..acc05a232 100755 --- a/Tools/Templates/createScreen.sh +++ b/Tools/Templates/createScreen.sh @@ -27,5 +27,9 @@ do echo "Building ${file/TemplateScreen/$SCREEN_NAME}..." perl -p -i -e "s/TemplateScreen/"$SCREEN_NAME"/g" $file perl -p -i -e "s/templateScreen/"$SCREEN_VAR_NAME"/g" $file + + echo "// $ createScreen.sh $@" | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + echo '// File created from ScreenTemplate' | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + mv ${file} ${file/TemplateScreen/$SCREEN_NAME} done \ No newline at end of file From d4f9995cbac8d705d12bd455ff516bba876779ac Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 08:52:59 +0200 Subject: [PATCH 047/150] Productivity: Tweak templates: No comments in a storyboard --- Tools/Templates/createScreen.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Tools/Templates/createScreen.sh b/Tools/Templates/createScreen.sh index acc05a232..3e3062f4b 100755 --- a/Tools/Templates/createScreen.sh +++ b/Tools/Templates/createScreen.sh @@ -28,8 +28,11 @@ do perl -p -i -e "s/TemplateScreen/"$SCREEN_NAME"/g" $file perl -p -i -e "s/templateScreen/"$SCREEN_VAR_NAME"/g" $file - echo "// $ createScreen.sh $@" | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} - echo '// File created from ScreenTemplate' | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + if [[ ! $file == *.storyboard ]]; + then + echo "// $ createScreen.sh $@" | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + echo '// File created from ScreenTemplate' | cat - ${file} > /tmp/$$ && mv /tmp/$$ ${file} + fi mv ${file} ${file/TemplateScreen/$SCREEN_NAME} done \ No newline at end of file From d92d0407cbefaffb3421ee8a4f2570798b42c86f Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 09:34:48 +0200 Subject: [PATCH 048/150] Productivity: Fix templates small issue --- .../FlowTemplateCoordinatorBridgePresenter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift index 4e6939611..042c29106 100644 --- a/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift +++ b/Tools/Templates/buildable/FlowCoordinatorTemplate/FlowTemplateCoordinatorBridgePresenter.swift @@ -47,7 +47,7 @@ final class FlowTemplateCoordinatorBridgePresenter: NSObject { // NOTE: Default value feature is not compatible with Objective-C. // func present(from viewController: UIViewController, animated: Bool) { - // self.present(from: viewController, isStartedFromSignOut: false, animated: animated) + // self.present(from: viewController, animated: animated) // } func present(from viewController: UIViewController, animated: Bool) { From 970a03ff9c976a6468ce851a11a610f5422c75b9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 09:37:15 +0200 Subject: [PATCH 049/150] Device Verification: boilerplate for the start screen --- Riot.xcodeproj/project.pbxproj | 60 ++++++ Riot/Generated/Storyboards.swift | 5 + .../DeviceVerificationCoordinator.swift | 80 ++++++++ ...rificationCoordinatorBridgePresenter.swift | 79 ++++++++ .../DeviceVerificationCoordinatorType.swift | 28 +++ .../DeviceVerificationStartCoordinator.swift | 71 +++++++ ...viceVerificationStartCoordinatorType.swift | 29 +++ .../DeviceVerificationStartViewAction.swift | 26 +++ ...VerificationStartViewController.storyboard | 92 +++++++++ ...eviceVerificationStartViewController.swift | 187 ++++++++++++++++++ .../DeviceVerificationStartViewModel.swift | 87 ++++++++ ...DeviceVerificationStartViewModelType.swift | 39 ++++ .../DeviceVerificationStartViewState.swift | 26 +++ 13 files changed, 809 insertions(+) create mode 100644 Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift create mode 100644 Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift create mode 100644 Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorType.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift create mode 100644 Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewState.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 13c95d8fc..abd1ef9ad 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -36,6 +36,17 @@ 3232AB502256558300AD6A5C /* TemplateScreenViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB452256558300AD6A5C /* TemplateScreenViewState.swift */; }; 3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB462256558300AD6A5C /* TemplateScreenViewAction.swift */; }; 3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB472256558300AD6A5C /* TemplateScreenViewModel.swift */; }; + 3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB95225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift */; }; + 3232ABA2225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB97225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard */; }; + 3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB98225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift */; }; + 3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB99225730E100AD6A5C /* DeviceVerificationStartViewAction.swift */; }; + 3232ABA5225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9A225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift */; }; + 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9B225730E100AD6A5C /* DeviceVerificationStartViewController.swift */; }; + 3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9C225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift */; }; + 3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9D225730E100AD6A5C /* DeviceVerificationStartViewState.swift */; }; + 3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9E225730E100AD6A5C /* DeviceVerificationStartViewModel.swift */; }; + 3232ABAA225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */; }; + 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */; }; 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; @@ -505,6 +516,17 @@ 3232AB452256558300AD6A5C /* TemplateScreenViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewState.swift; sourceTree = ""; }; 3232AB462256558300AD6A5C /* TemplateScreenViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewAction.swift; sourceTree = ""; }; 3232AB472256558300AD6A5C /* TemplateScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TemplateScreenViewModel.swift; sourceTree = ""; }; + 3232AB95225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationCoordinatorType.swift; sourceTree = ""; }; + 3232AB97225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationStartViewController.storyboard; sourceTree = ""; }; + 3232AB98225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartCoordinatorType.swift; sourceTree = ""; }; + 3232AB99225730E100AD6A5C /* DeviceVerificationStartViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewAction.swift; sourceTree = ""; }; + 3232AB9A225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewModelType.swift; sourceTree = ""; }; + 3232AB9B225730E100AD6A5C /* DeviceVerificationStartViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewController.swift; sourceTree = ""; }; + 3232AB9C225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartCoordinator.swift; sourceTree = ""; }; + 3232AB9D225730E100AD6A5C /* DeviceVerificationStartViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewState.swift; sourceTree = ""; }; + 3232AB9E225730E100AD6A5C /* DeviceVerificationStartViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewModel.swift; sourceTree = ""; }; + 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationCoordinatorBridgePresenter.swift; sourceTree = ""; }; + 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationCoordinator.swift; sourceTree = ""; }; 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -1266,6 +1288,32 @@ path = ScreenTemplate; sourceTree = ""; }; + 3232AB94225730E100AD6A5C /* DeviceVerification */ = { + isa = PBXGroup; + children = ( + 3232AB96225730E100AD6A5C /* Start */, + 3232AB95225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift */, + 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */, + 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */, + ); + path = DeviceVerification; + sourceTree = ""; + }; + 3232AB96225730E100AD6A5C /* Start */ = { + isa = PBXGroup; + children = ( + 3232AB97225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard */, + 3232AB98225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift */, + 3232AB99225730E100AD6A5C /* DeviceVerificationStartViewAction.swift */, + 3232AB9A225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift */, + 3232AB9B225730E100AD6A5C /* DeviceVerificationStartViewController.swift */, + 3232AB9C225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift */, + 3232AB9D225730E100AD6A5C /* DeviceVerificationStartViewState.swift */, + 3232AB9E225730E100AD6A5C /* DeviceVerificationStartViewModel.swift */, + ); + path = Start; + sourceTree = ""; + }; 3233F7291F31F3B4006ACA81 /* libs */ = { isa = PBXGroup; children = ( @@ -1730,6 +1778,7 @@ B1B5567620EE6C4C00210D55 /* Modules */ = { isa = PBXGroup; children = ( + 3232AB94225730E100AD6A5C /* DeviceVerification */, B1B556EA20EE6C4C00210D55 /* Main */, B1B556CA20EE6C4C00210D55 /* TabBar */, B1B556F920EE6C4C00210D55 /* Authentication */, @@ -3359,6 +3408,7 @@ B1B558C020EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, B1B5572420EE6C4D00210D55 /* RoomViewController.xib in Resources */, B169331520F3CAFC00746532 /* PublicRoomTableViewCell.xib in Resources */, + 3232ABA2225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard in Resources */, 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */, B1B557A220EF58AD00210D55 /* ContactTableViewCell.xib in Resources */, B1B558EB20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.xib in Resources */, @@ -3580,6 +3630,7 @@ B16932A520F3A21C00746532 /* empty.mm in Sources */, 3232AB4A2256558300AD6A5C /* FlowTemplateCoordinator.swift in Sources */, B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */, + 3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */, B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, @@ -3598,6 +3649,7 @@ B1B5598720EFC3E000210D55 /* Widget.m in Sources */, B1B557E320EF60B900210D55 /* MessagesSearchResultAttachmentBubbleCell.m in Sources */, B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */, + 3232ABAA225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift in Sources */, B1B5574420EE6C4D00210D55 /* CallViewController.m in Sources */, B1B5572220EE6C4D00210D55 /* RoomSettingsViewController.m in Sources */, B1B5577320EE702800210D55 /* JitsiViewController.m in Sources */, @@ -3612,6 +3664,7 @@ B1B5572320EE6C4D00210D55 /* AttachmentsViewController.m in Sources */, F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */, B1B5598620EFC3E000210D55 /* RiotSettings.swift in Sources */, + 3232ABA3225730E100AD6A5C /* DeviceVerificationStartCoordinatorType.swift in Sources */, 3232AB4D2256558300AD6A5C /* TemplateScreenCoordinatorType.swift in Sources */, B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */, B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */, @@ -3631,6 +3684,7 @@ B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */, B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */, B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */, + 3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */, B1B558D420EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169331420F3CAFC00746532 /* PublicRoomTableViewCell.m in Sources */, 32BF995721FB07A400698084 /* SettingsKeyBackupTableViewSection.swift in Sources */, @@ -3663,8 +3717,10 @@ 3232AB512256558300AD6A5C /* TemplateScreenViewAction.swift in Sources */, 3232AB4E2256558300AD6A5C /* TemplateScreenViewModelType.swift in Sources */, B1B5590520EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, + 3232ABA5225730E100AD6A5C /* DeviceVerificationStartViewModelType.swift in Sources */, B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */, B1098BE521ECE1FC000DDA48 /* Storyboards.swift in Sources */, + 3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */, B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */, @@ -3693,6 +3749,7 @@ F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */, + 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */, @@ -3702,6 +3759,7 @@ B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, + 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */, B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */, B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */, B1B5574020EE6C4D00210D55 /* SegmentedViewController.m in Sources */, @@ -3753,6 +3811,7 @@ B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */, B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */, B1098BE121ECE09F000DDA48 /* Images.swift in Sources */, + 3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */, B1B5575A20EE6C4D00210D55 /* UnifiedSearchViewController.m in Sources */, 3232AB492256558300AD6A5C /* FlowTemplateCoordinatorBridgePresenter.swift in Sources */, B1B5572820EE6C4D00210D55 /* RoomViewController.m in Sources */, @@ -3769,6 +3828,7 @@ B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */, B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */, B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */, + 3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */, B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */, B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */, B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */, diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 80f0725b6..ee9cee1c8 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -12,6 +12,11 @@ import UIKit // swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name internal enum StoryboardScene { + internal enum DeviceVerificationStartViewController: StoryboardType { + internal static let storyboardName = "DeviceVerificationStartViewController" + + internal static let initialScene = InitialSceneType(storyboard: DeviceVerificationStartViewController.self) + } internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType { internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController" diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift new file mode 100644 index 000000000..5c7e27748 --- /dev/null +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift @@ -0,0 +1,80 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +@objcMembers +final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let navigationRouter: NavigationRouterType + private let session: MXSession + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: DeviceVerificationCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) + self.session = session + } + + // MARK: - Public methods + + func start() { + + let rootCoordinator = self.createDeviceVerificationStartCoordinator() + + rootCoordinator.start() + + self.add(childCoordinator: rootCoordinator) + + self.navigationRouter.setRootModule(rootCoordinator) + } + + func toPresentable() -> UIViewController { + return self.navigationRouter.toPresentable() + } + + // MARK: - Private methods + + private func createDeviceVerificationStartCoordinator() -> DeviceVerificationStartCoordinator { + let coordinator = DeviceVerificationStartCoordinator(session: self.session) + coordinator.delegate = self + return coordinator + } +} + +// MARK: - DeviceVerificationStartCoordinatorDelegate +extension DeviceVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate { + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithMessage message: String) { + self.delegate?.deviceVerificationCoordinatorDidComplete(self) + } + + func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) { + self.delegate?.deviceVerificationCoordinatorDidComplete(self) + } +} diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift new file mode 100644 index 000000000..0c8b4fbc3 --- /dev/null +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift @@ -0,0 +1,79 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +@objc protocol DeviceVerificationCoordinatorBridgePresenterDelegate { + func deviceVerificationCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: DeviceVerificationCoordinatorBridgePresenter) +} + +/// DeviceVerificationCoordinatorBridgePresenter enables to start DeviceVerificationCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class DeviceVerificationCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var coordinator: DeviceVerificationCoordinator? + + // MARK: Public + + weak var delegate: DeviceVerificationCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session) + deviceVerificationCoordinator.delegate = self + viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil) + deviceVerificationCoordinator.start() + + self.coordinator = deviceVerificationCoordinator + } + + func dismiss(animated: Bool) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + } + } +} + +// MARK: - DeviceVerificationCoordinatorDelegate +extension DeviceVerificationCoordinatorBridgePresenter: DeviceVerificationCoordinatorDelegate { + func deviceVerificationCoordinatorDidComplete(_ coordinator: DeviceVerificationCoordinatorType) { + self.delegate?.deviceVerificationCoordinatorBridgePresenterDelegateDidComplete(self) + } +} diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorType.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorType.swift new file mode 100644 index 000000000..bc925808b --- /dev/null +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorType.swift @@ -0,0 +1,28 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh DeviceVerification DeviceVerification DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol DeviceVerificationCoordinatorDelegate: class { + func deviceVerificationCoordinatorDidComplete(_ coordinator: DeviceVerificationCoordinatorType) +} + +/// `DeviceVerificationCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol DeviceVerificationCoordinatorType: Coordinator, Presentable { + var delegate: DeviceVerificationCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift new file mode 100644 index 000000000..9115c6d1b --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift @@ -0,0 +1,71 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +import UIKit + +final class DeviceVerificationStartCoordinator: DeviceVerificationStartCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var deviceVerificationStartViewModel: DeviceVerificationStartViewModelType + private let deviceVerificationStartViewController: DeviceVerificationStartViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: DeviceVerificationStartCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + + let deviceVerificationStartViewModel = DeviceVerificationStartViewModel(session: self.session) + let deviceVerificationStartViewController = DeviceVerificationStartViewController.instantiate(with: deviceVerificationStartViewModel) + self.deviceVerificationStartViewModel = deviceVerificationStartViewModel + self.deviceVerificationStartViewController = deviceVerificationStartViewController + } + + // MARK: - Public methods + + func start() { + self.deviceVerificationStartViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.deviceVerificationStartViewController + } +} + +// MARK: - DeviceVerificationStartViewModelCoordinatorDelegate +extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCoordinatorDelegate { + + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithMessage message: String) { + self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithMessage: message) + } + + func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) { + self.delegate?.deviceVerificationStartCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift new file mode 100644 index 000000000..2a82dc6ed --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol DeviceVerificationStartCoordinatorDelegate: class { + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithMessage message: String) + func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) +} + +/// `DeviceVerificationStartCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol DeviceVerificationStartCoordinatorType: Coordinator, Presentable { + var delegate: DeviceVerificationStartCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift new file mode 100644 index 000000000..5923c882d --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// DeviceVerificationStartViewController view actions exposed to view model +enum DeviceVerificationStartViewAction { + case sayHello + case complete + case cancel +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard new file mode 100644 index 000000000..8dd82b992 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift new file mode 100644 index 000000000..472da5243 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -0,0 +1,187 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +final class DeviceVerificationStartViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private weak var okButton: UIButton! + + // MARK: Private + + private var viewModel: DeviceVerificationStartViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: DeviceVerificationStartViewModelType) -> DeviceVerificationStartViewController { + let viewController = StoryboardScene.DeviceVerificationStartViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = "Template" + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .sayHello) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: + self.messageLabel.textColor = theme.textPrimaryColor + + self.okButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.okButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + self.messageLabel.text = "VectorL10n.deviceVerificationStartTitle" + self.messageLabel.isHidden = true + } + + private func render(viewState: DeviceVerificationStartViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.messageLabel.text = self.viewModel.message + self.messageLabel.isHidden = false + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func okButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .complete) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - DeviceVerificationStartViewModelViewDelegate +extension DeviceVerificationStartViewController: DeviceVerificationStartViewModelViewDelegate { + + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didUpdateViewState viewSate: DeviceVerificationStartViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift new file mode 100644 index 000000000..dbc9cb9f1 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift @@ -0,0 +1,87 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + + // MARK: Public + + var message: String? + + weak var viewDelegate: DeviceVerificationStartViewModelViewDelegate? + weak var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession) { + self.session = session + self.message = nil + } + + deinit { + } + + // MARK: - Public + + func process(viewAction: DeviceVerificationStartViewAction) { + switch viewAction { + case .sayHello: + self.setupHelloMessage() + case .complete: + if let message = self.message { + self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithMessage: message) + } + case .cancel: + self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func setupHelloMessage() { + + self.update(viewState: .loading) + + // Check first that the user homeserver is federated with the Riot-bot homeserver + self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in + + guard let sself = self else { + return + } + + switch response { + case .success: + sself.message = "Hello \(response.value ?? "you")" + sself.update(viewState: .loaded) + case .failure(let error): + sself.update(viewState: .error(error)) + } + } + } + + private func update(viewState: DeviceVerificationStartViewState) { + self.viewDelegate?.deviceVerificationStartViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift new file mode 100644 index 000000000..866a7ddd8 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift @@ -0,0 +1,39 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol DeviceVerificationStartViewModelViewDelegate: class { + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didUpdateViewState viewSate: DeviceVerificationStartViewState) +} + +protocol DeviceVerificationStartViewModelCoordinatorDelegate: class { + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithMessage message: String) + func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) +} + +/// Protocol describing the view model used by `DeviceVerificationStartViewController` +protocol DeviceVerificationStartViewModelType { + + var message: String? { get set } + + var viewDelegate: DeviceVerificationStartViewModelViewDelegate? { get set } + var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate? { get set } + + func process(viewAction: DeviceVerificationStartViewAction) +} diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewState.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewState.swift new file mode 100644 index 000000000..5536d4bf9 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Start DeviceVerificationStart +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// DeviceVerificationStartViewController view state +enum DeviceVerificationStartViewState { + case loading + case loaded + case error(Error) +} From 374b45fead0385ee8ed79573d2bf6530ab14bdef Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 10:59:18 +0200 Subject: [PATCH 050/150] Update iOS 10+ notification titles --- Riot/AppDelegate.m | 303 +++++++++++++++++++++++++++------------------ 1 file changed, 183 insertions(+), 120 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 892acd89d..ba281a040 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1478,19 +1478,21 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)handleLocalNotificationsForAccount:(MXKAccount*)account { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: %@", account.mxCredentials.userId); + NSString *userId = account.mxCredentials.userId; + + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: %@", userId); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@", eventsToNotify[@(account.mxSession.hash)]); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@", self.incomingPushEventIds[@(account.mxSession.hash)]); - + __block NSUInteger scheduledNotifications = 0; - + // The call invite are handled here only when the callkit is not active. BOOL isCallKitActive = [MXCallKitAdapter callKitAvailable] && [MXKAppSettings standardAppSettings].isCallKitEnabled; NSMutableArray *eventsArray = eventsToNotify[@(account.mxSession.hash)]; NSMutableArray *redactedEventIds = [NSMutableArray array]; - + // Display a local notification for each event retrieved by the bg sync. for (NSUInteger index = 0; index < eventsArray.count; index++) { @@ -1499,11 +1501,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSString *roomId = eventDict[@"room_id"]; BOOL checkReadEvent = YES; MXEvent *event; - + // Ignore event already notified to the user // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs if (@available(iOS 10, *)) {} - else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) + else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil]) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); continue; @@ -1526,7 +1528,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN else { // Ignore redacted event. - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", event.eventId); + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", eventId); } continue; } @@ -1537,7 +1539,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Ignore call invite when callkit is active. if (isCallKitActive) { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@", event.eventId); + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@", eventId); continue; } else @@ -1557,13 +1559,13 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN if (checkReadEvent) { // Ignore event which has been read on another device. - MXReceiptData *readReceipt = [account.mxSession.store getReceiptInRoom:roomId forUserId:account.mxCredentials.userId]; + MXReceiptData *readReceipt = [account.mxSession.store getReceiptInRoom:roomId forUserId:userId]; if (readReceipt) { MXEvent *readReceiptEvent = [account.mxSession.store eventWithEventId:readReceipt.eventId inRoom:roomId]; if (event.originServerTs <= readReceiptEvent.originServerTs) { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@", event.eventId); + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@", eventId); continue; } } @@ -1571,108 +1573,137 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Prepare the local notification MXPushRule *rule = eventDict[@"push_rule"]; - - NSDictionary *notificationUserInfo = @{ - @"type": @"full", - @"room_id": event.roomId, - @"event_id": event.eventId, - @"user_id": account.mxCredentials.userId - }; - - BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; - - NSString *categoryIdentifier; - - if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) + + if (@available(iOS 10, *)) { - categoryIdentifier = @"QUICK_REPLY"; - } - - - NSString *soundName; - - // Set sound name based on the value provided in action of MXPushRule - for (MXPushRuleAction *action in rule.actions) - { - if (action.actionType == MXPushRuleActionTypeSetTweak) - { - if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) + [self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNNotificationContent * _Nullable notificationContent) { + + if (notificationContent) { - soundName = action.parameters[@"value"]; - if ([soundName isEqualToString:@"default"]) - soundName = @"message.mp3"; + UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:eventId + content:notificationContent + trigger:nil]; + + [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { + + if (error) + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Fail to display notification for event %@ with error: %@", eventId, error); + } + else + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", eventId); + } + }]; + + scheduledNotifications++; } - } + else + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated content. Event id: %@", eventId); + } + }]; } - - [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) - { - if (notificationBody) + else + { + [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); - - // Printf style escape characters are stripped from the string prior to display; - // to include a percent symbol (%) in the message, use two percent symbols (%%). - // TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc - // use this - maybe not necessary to replace %s - NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; - - if (@available(iOS 10, *)) - { - UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init]; - notificationContent.body = fixedNotificationBody; - notificationContent.userInfo = notificationUserInfo; - notificationContent.categoryIdentifier = categoryIdentifier; - notificationContent.threadIdentifier = roomId; - if (soundName) - { - notificationContent.sound = [UNNotificationSound soundNamed:soundName]; - } - - UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId - content:notificationContent - trigger:nil]; - - [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil]; - } - else + if (notificationBody) { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", eventId); + + // Printf style escape characters are stripped from the string prior to display; + // to include a percent symbol (%) in the message, use two percent symbols (%%). + NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"]; + + NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:userId]; + NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event]; + NSString *soundName = [self notificationSoundNameFromPushRule:rule]; + UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; eventNotification.alertBody = fixedNotificationBody; eventNotification.userInfo = notificationUserInfo; eventNotification.category = categoryIdentifier; eventNotification.soundName = soundName; - + [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; + + scheduledNotifications++; } - - scheduledNotifications++; - } - else - { - NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); - } - }]; + else + { + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", eventId); + } + }]; + } } } - + if (@available(iOS 10, *)) { // Remove possible pending and delivered notifications having a redacted event id if (redactedEventIds.count) { NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Remove possible notification with redacted event ids: %@", redactedEventIds); - + [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:redactedEventIds]; [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:redactedEventIds]; } } - + NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events", scheduledNotifications, eventsArray.count); - + [eventsArray removeAllObjects]; } +- (NSString*)notificationSoundNameFromPushRule:(MXPushRule*)pushRule +{ + NSString *soundName; + + // Set sound name based on the value provided in action of MXPushRule + for (MXPushRuleAction *action in pushRule.actions) + { + if (action.actionType == MXPushRuleActionTypeSetTweak) + { + if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"]) + { + soundName = action.parameters[@"value"]; + if ([soundName isEqualToString:@"default"]) + { + soundName = @"message.mp3"; + } + } + } + } + + return soundName; +} + +- (NSString*)notificationCategoryIdentifierForEvent:(MXEvent*)event +{ + BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications; + + NSString *categoryIdentifier; + + if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown) + { + categoryIdentifier = @"QUICK_REPLY"; + } + + return categoryIdentifier; +} + +- (NSDictionary*)notificationUserInfoForEvent:(MXEvent*)event andUserId:(NSString*)userId +{ + NSDictionary *notificationUserInfo = @{ + @"type": @"full", + @"room_id": event.roomId, + @"event_id": event.eventId, + @"user_id": userId + }; + return notificationUserInfo; +} + - (void)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account onComplete:(void (^)(NSString * _Nullable notificationBody))onComplete; { if (!event.content || !event.content.count) @@ -1800,7 +1831,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } // iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features -- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete; +- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNNotificationContent * _Nullable notificationContent))onComplete; { if (!event.content || !event.content.count) { @@ -1858,97 +1889,129 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN return; } } - + NSString *msgType = event.content[@"msgtype"]; NSString *messageContent = event.content[@"body"]; - + if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications) { // Hide the content msgType = nil; } - + NSString *roomDisplayName = room.summary.displayname; - + // Display the room name only if it is different than the sender name if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) { - notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; + notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]];// + if ([msgType isEqualToString:@"m.text"]) + { notificationBody = messageContent; + } else if ([msgType isEqualToString:@"m.emote"]) { - notificationTitle = roomDisplayName; - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, messageContent]; + notificationBody = [NSString localizedUserNotificationStringForKey:@"ACTION_FROM_USER" arguments:@[eventSenderName, messageContent]]; } else if ([msgType isEqualToString:@"m.image"]) - notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"IMAGE_FROM_USER" arguments:@[eventSenderName, messageContent]]; + } else + { // Encrypted messages falls here - notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); + notificationBody = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER" arguments:@[eventSenderName]]; + } } else { notificationTitle = eventSenderName; + if ([msgType isEqualToString:@"m.text"]) + { notificationBody = messageContent; + } else if ([msgType isEqualToString:@"m.emote"]) { - notificationTitle = eventSenderName; - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION", nil), messageContent]; + notificationBody = [NSString localizedUserNotificationStringForKey:@"ACTION_FROM_USER" arguments:@[eventSenderName, messageContent]]; } else if ([msgType isEqualToString:@"m.image"]) - notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"IMAGE_FROM_USER" arguments:@[eventSenderName, messageContent]]; + } else + { // Encrypted messages falls here - notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); + notificationBody = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER" arguments:@[eventSenderName]]; + } } } else if (event.eventType == MXEventTypeCallInvite) { NSString *sdp = event.content[@"offer"][@"sdp"]; BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound; - - notificationTitle = eventSenderName; - + if (!isVideoCall) - notificationBody = NSLocalizedString(@"VOICE_CALL", nil); + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"VOICE_CALL_FROM_USER" arguments:@[eventSenderName]]; + } else - notificationBody = NSLocalizedString(@"VIDEO_CALL", nil); - + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"VIDEO_CALL_FROM_USER" arguments:@[eventSenderName]]; + } + // call notifications should stand out from normal messages, so we don't stack them threadIdentifier = nil; } else if (event.eventType == MXEventTypeRoomMember) { NSString *roomDisplayName = room.summary.displayname; - - notificationTitle = roomDisplayName; - + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationBody = [NSString stringWithFormat:NSLocalizedString(@"INVITE_BY_USER_TO_ROOM", nil), eventSenderName]; + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"USER_INVITE_TO_NAMED_ROOM" arguments:@[eventSenderName, roomDisplayName]]; + } else - notificationBody = NSLocalizedString(@"INVITE_TO_CHAT", nil); + { + notificationBody = [NSString localizedUserNotificationStringForKey:@"USER_INVITE_TO_CHAT" arguments:@[eventSenderName]]; + } } else if (event.eventType == MXEventTypeSticker) { NSString *roomDisplayName = room.summary.displayname; - + if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) - notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; + { + notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]]; + } else + { notificationTitle = eventSenderName; - - notificationBody = NSLocalizedString(@"STICKER_TEXT_WITH_TITLE", nil); + } + + notificationBody = [NSString localizedUserNotificationStringForKey:@"STICKER_FROM_USER" arguments:@[eventSenderName]]; } - - UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; - - [content setTitle:notificationTitle]; - [content setBody:notificationBody]; - [content setThreadIdentifier:threadIdentifier]; - - onComplete(content); + + UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init]; + + NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:account.mxCredentials.userId]; + NSString *notificationSoundName = [self notificationSoundNameFromPushRule:rule]; + NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event]; + + notificationContent.title = notificationTitle; + notificationContent.subtitle = notificationSubTitle; + notificationContent.body = notificationBody; + notificationContent.threadIdentifier = threadIdentifier; + notificationContent.userInfo = notificationUserInfo; + notificationContent.categoryIdentifier = categoryIdentifier; + + if (notificationSoundName) + { + notificationContent.sound = [UNNotificationSound soundNamed:notificationSoundName]; + } + + onComplete([notificationContent copy]); }]; } From 73f00428f0f05b9de8b0d1bae564737efe997e5d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 14:51:56 +0200 Subject: [PATCH 051/150] When navigate to room, remove associated delivered notifications. --- Riot/AppDelegate.m | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index ba281a040..a6d335fb9 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1273,7 +1273,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } else { - NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", [response actionIdentifier]); + NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", actionIdentifier); completionHandler(); } } @@ -2231,6 +2231,34 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [accountManager setPushDeviceToken:nil withPushOptions:nil]; } +// Remove delivred notifications for a given room id except call notifications +- (void)removeDeliveredNotificationsWithRoomId:(NSString*)roomId completion:(dispatch_block_t)completion +{ + NSMutableArray *notificationRequestIdentifiersToRemove = [NSMutableArray new]; + + UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; + + [notificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) { + + for (UNNotification *notification in notifications) + { + NSString *threadIdentifier = notification.request.content.threadIdentifier; + + if ([threadIdentifier isEqualToString:roomId]) + { + [notificationRequestIdentifiersToRemove addObject:notification.request.identifier]; + } + } + + [notificationCenter removeDeliveredNotificationsWithIdentifiers:notificationRequestIdentifiersToRemove]; + + if (completion) + { + completion(); + } + }]; +} + #pragma mark - Universal link - (BOOL)handleUniversalLink:(NSUserActivity*)userActivity @@ -3710,8 +3738,10 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [self restoreInitialDisplay:^{ // Select room to display its details (dispatch this action in order to let TabBarController end its refresh) - [_masterTabBarController selectRoomWithId:roomId andEventId:eventId inMatrixSession:mxSession]; - + [_masterTabBarController selectRoomWithId:roomId andEventId:eventId inMatrixSession:mxSession completion:^{ + // Remove delivered notifications for this room + [self removeDeliveredNotificationsWithRoomId:roomId completion:nil]; + }]; }]; } From 5e0f7121a96aebe37c886d8ff0987f380226e81f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 14:57:55 +0200 Subject: [PATCH 052/150] Remove commented code about notification provisional authorization. --- Riot/AppDelegate.m | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index a6d335fb9..13cf6fd99 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1157,14 +1157,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN UNAuthorizationOptions authorizationOptions = (UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge); - // FIXME: Uncomment lines below when issue https://github.com/matrix-org/matrix-ios-kit/issues/533 will be done. -// // Authorize sending notifications without explicit permission (iOS 12+). -// // User can still disable Riot notifications later in settings or directly from a Riot notification. -// if (@available(iOS 12.0, *)) -// { -// authorizationOptions = authorizationOptions | UNAuthorizationOptionProvisional; -// } - [center requestAuthorizationWithOptions:authorizationOptions completionHandler:^(BOOL granted, NSError *error) { // code here is equivalent to self:application:didRegisterUserNotificationSettings: From a963a92b055c7a78a0e52d90fa3f93c9fb9621a2 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 15:11:50 +0200 Subject: [PATCH 053/150] Update changes --- CHANGES.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 97f51f2e4..e0053a560 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -2,9 +2,10 @@ Changes in 0.8.5 (2019-xx-xx) =============================================== Improvements: - * Added titles to notifications on iOS 10+ (#2347). - * Implemented notification grouping (#2347). - * Use UserNotifications framework for local notifications (iOS 10+), thanks to @fridtjof (PR #2207). + * Notifications: Use UserNotifications framework for local notifications (iOS 10+), thanks to @fridtjof (PR #2207). + * Notifications: Added titles to notifications on iOS 10+, thanks to @fridtjof (PR #2347). + * iOS 12 Notification: Group them by room (#2337 and PR #2347 thanks to @fridtjof). + * Notifications: When navigate to a room, remove associated delivered notifications (#2337). Bug fix: From c20831c31ae4f972c5e297f481dcbd28d4de7d32 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 15:15:06 +0200 Subject: [PATCH 054/150] Device Verification: Start start screen business logic --- Riot/Assets/en.lproj/Vector.strings | 5 ++ Riot/Generated/Strings.swift | 4 ++ .../DeviceVerificationCoordinator.swift | 72 ++++++++++++++++--- ...rificationCoordinatorBridgePresenter.swift | 4 +- .../DeviceVerificationStartCoordinator.swift | 12 ++-- ...viceVerificationStartCoordinatorType.swift | 4 +- .../DeviceVerificationStartViewAction.swift | 2 +- ...VerificationStartViewController.storyboard | 10 +-- ...eviceVerificationStartViewController.swift | 12 ++-- .../DeviceVerificationStartViewModel.swift | 68 +++++++++++++----- ...DeviceVerificationStartViewModelType.swift | 4 +- 11 files changed, 149 insertions(+), 48 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f1bc2ab91..da5664a40 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -792,3 +792,8 @@ "sign_out_key_backup_in_progress_alert_title" = "Key backup in progress. If you sign out now you’ll lose access to your encrypted messages."; "sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "I don't want my encrypted messages"; "sign_out_key_backup_in_progress_alert_cancel_action" = "I'll wait"; + +// MARK: - Device Verification + +// MARK: - Start +"device_verification_start_verify_button" = "Verify"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index cb71c944e..29015294a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -426,6 +426,10 @@ internal enum VectorL10n { internal static var decline: String { return VectorL10n.tr("Vector", "decline") } + /// Verify + internal static var deviceVerificationStartVerifyButton: String { + return VectorL10n.tr("Vector", "device_verification_start_verify_button") + } /// %tu rooms internal static func directoryCellDescription(_ p1: Int) -> String { return VectorL10n.tr("Vector", "directory_cell_description", p1) diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift index 5c7e27748..cb61de551 100644 --- a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift @@ -27,7 +27,11 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { private let navigationRouter: NavigationRouterType private let session: MXSession - + private let otherUserId: String + private let otherDeviceId: String + + private var transaction: MXSASTransaction! + // MARK: Public // Must be used only internally @@ -36,23 +40,60 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { weak var delegate: DeviceVerificationCoordinatorDelegate? // MARK: - Setup - - init(session: MXSession) { + + /// Contrustor to start a verification of another device. + /// + /// - Parameters: + /// - session: the MXSession + /// - otherUserId: the device user id + /// - otherDevice: the device id + init(session: MXSession, otherUserId: String, otherDeviceId: String) { self.navigationRouter = NavigationRouter(navigationController: RiotNavigationController()) self.session = session - } + self.otherUserId = otherUserId + self.otherDeviceId = otherDeviceId + } + + /// Contrustor to manage an existing SAS device verification transaction + /// + /// - Parameters: + /// - session: the MXSession + /// - transaction: an existing device verification transaction + convenience init(session: MXSession, transaction: MXSASTransaction) { + self.init(session: session, + otherUserId: transaction.otherUser, + otherDeviceId: transaction.otherDevice) + self.transaction = transaction + } // MARK: - Public methods func start() { - let rootCoordinator = self.createDeviceVerificationStartCoordinator() + guard let otherUser = self.session.user(withUserId: otherUserId) else { + return // TODO + } - rootCoordinator.start() + // Before starting make sure we have device crypto informatino + self.session.crypto?.downloadKeys([self.otherUserId], forceDownload: false, success: { [weak self] (usersDevicesMap) in + guard let sself = self else { + return + } - self.add(childCoordinator: rootCoordinator) + guard let otherDevice = usersDevicesMap?.object(forDevice: sself.otherDeviceId, forUser: sself.otherUserId) else { + return // TODO + } - self.navigationRouter.setRootModule(rootCoordinator) + let rootCoordinator = sself.createDeviceVerificationStartCoordinator(otherUser: otherUser, otherDevice: otherDevice) + rootCoordinator.start() + + sself.add(childCoordinator: rootCoordinator) + + sself.navigationRouter.setRootModule(rootCoordinator) + + }, failure: { (error) in + // TODO + }) } func toPresentable() -> UIViewController { @@ -61,8 +102,8 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { // MARK: - Private methods - private func createDeviceVerificationStartCoordinator() -> DeviceVerificationStartCoordinator { - let coordinator = DeviceVerificationStartCoordinator(session: self.session) + private func createDeviceVerificationStartCoordinator(otherUser: MXUser, otherDevice: MXDeviceInfo) -> DeviceVerificationStartCoordinator { + let coordinator = DeviceVerificationStartCoordinator(session: self.session, otherUser: otherUser, otherDevice: otherDevice) coordinator.delegate = self return coordinator } @@ -70,7 +111,16 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { // MARK: - DeviceVerificationStartCoordinatorDelegate extension DeviceVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate { - func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithMessage message: String) { + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) { + self.transaction = transaction + + // TODO + self.delegate?.deviceVerificationCoordinatorDidComplete(self) + } + + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) { + + // TODO self.delegate?.deviceVerificationCoordinatorDidComplete(self) } diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift index 0c8b4fbc3..a644ee49a 100644 --- a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinatorBridgePresenter.swift @@ -52,8 +52,8 @@ final class DeviceVerificationCoordinatorBridgePresenter: NSObject { // self.present(from: viewController, animated: animated) // } - func present(from viewController: UIViewController, animated: Bool) { - let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session) + func present(from viewController: UIViewController, otherUserId: String, otherDeviceId: String, animated: Bool) { + let deviceVerificationCoordinator = DeviceVerificationCoordinator(session: self.session, otherUserId: otherUserId, otherDeviceId: otherDeviceId) deviceVerificationCoordinator.delegate = self viewController.present(deviceVerificationCoordinator.toPresentable(), animated: animated, completion: nil) deviceVerificationCoordinator.start() diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift index 9115c6d1b..8f283cc91 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift @@ -38,10 +38,10 @@ final class DeviceVerificationStartCoordinator: DeviceVerificationStartCoordinat // MARK: - Setup - init(session: MXSession) { + init(session: MXSession, otherUser: MXUser, otherDevice: MXDeviceInfo) { self.session = session - let deviceVerificationStartViewModel = DeviceVerificationStartViewModel(session: self.session) + let deviceVerificationStartViewModel = DeviceVerificationStartViewModel(session: self.session, otherUser: otherUser, otherDevice: otherDevice) let deviceVerificationStartViewController = DeviceVerificationStartViewController.instantiate(with: deviceVerificationStartViewModel) self.deviceVerificationStartViewModel = deviceVerificationStartViewModel self.deviceVerificationStartViewController = deviceVerificationStartViewController @@ -61,8 +61,12 @@ final class DeviceVerificationStartCoordinator: DeviceVerificationStartCoordinat // MARK: - DeviceVerificationStartViewModelCoordinatorDelegate extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCoordinatorDelegate { - func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithMessage message: String) { - self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithMessage: message) + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) { + self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction) + } + + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) { + self.delegate?.deviceVerificationStartCoordinator(self, didTransactionCancelled: transaction) } func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) { diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift index 2a82dc6ed..72871ee60 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinatorType.swift @@ -19,7 +19,9 @@ import Foundation protocol DeviceVerificationStartCoordinatorDelegate: class { - func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithMessage message: String) + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) + func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) + func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift index 5923c882d..90d6d2c28 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift @@ -21,6 +21,6 @@ import Foundation /// DeviceVerificationStartViewController view actions exposed to view model enum DeviceVerificationStartViewAction { case sayHello - case complete + case beginVerifying case cancel } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard index 8dd82b992..1a6ffab2d 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard @@ -10,7 +10,7 @@ - + @@ -35,11 +35,11 @@ @@ -80,13 +80,13 @@ - + - + diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift index 472da5243..403505b2c 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -33,7 +33,7 @@ final class DeviceVerificationStartViewController: UIViewController { @IBOutlet private weak var scrollView: UIScrollView! @IBOutlet private weak var messageLabel: UILabel! - @IBOutlet private weak var okButton: UIButton! + @IBOutlet private weak var verifyButton: UIButton! // MARK: Private @@ -113,8 +113,8 @@ final class DeviceVerificationStartViewController: UIViewController { // TODO: self.messageLabel.textColor = theme.textPrimaryColor - self.okButton.backgroundColor = theme.backgroundColor - theme.applyStyle(onButton: self.okButton) + self.verifyButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.verifyButton) } private func registerThemeServiceDidChangeThemeNotification() { @@ -133,6 +133,8 @@ final class DeviceVerificationStartViewController: UIViewController { self.navigationItem.rightBarButtonItem = cancelBarButtonItem self.scrollView.keyboardDismissMode = .interactive + + self.verifyButton.setTitle(VectorL10n.deviceVerificationStartVerifyButton, for: .normal) self.messageLabel.text = "VectorL10n.deviceVerificationStartTitle" self.messageLabel.isHidden = true @@ -168,8 +170,8 @@ final class DeviceVerificationStartViewController: UIViewController { // MARK: - Actions - @IBAction private func okButtonAction(_ sender: Any) { - self.viewModel.process(viewAction: .complete) + @IBAction private func verifyButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .beginVerifying) } private func cancelButtonAction() { diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift index dbc9cb9f1..cbbd63935 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift @@ -25,6 +25,9 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy // MARK: Private private let session: MXSession + private let verificationManager: MXDeviceVerificationManager + private let otherUser: MXUser + private let otherDevice: MXDeviceInfo // MARK: Public @@ -35,8 +38,11 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy // MARK: - Setup - init(session: MXSession) { + init(session: MXSession, otherUser: MXUser, otherDevice: MXDeviceInfo) { self.session = session + self.verificationManager = session.crypto.deviceVerificationManager + self.otherUser = otherUser + self.otherDevice = otherDevice self.message = nil } @@ -47,41 +53,67 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy func process(viewAction: DeviceVerificationStartViewAction) { switch viewAction { - case .sayHello: - self.setupHelloMessage() - case .complete: - if let message = self.message { - self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithMessage: message) - } + case .beginVerifying: + self.beginVerifying() case .cancel: self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self) + case .sayHello: + break } } // MARK: - Private - private func setupHelloMessage() { + private func beginVerifying() { self.update(viewState: .loading) - // Check first that the user homeserver is federated with the Riot-bot homeserver - self.session.matrixRestClient.displayName(forUser: self.session.myUser.userId) { [weak self] (response) in + self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: kMXKeyVerificationMethodSAS, complete: { [weak self] (transaction) in guard let sself = self else { return } - - switch response { - case .success: - sself.message = "Hello \(response.value ?? "you")" - sself.update(viewState: .loaded) - case .failure(let error): - sself.update(viewState: .error(error)) + guard let sasTransaction: MXOutgoingSASTransaction = transaction as? MXOutgoingSASTransaction else { + return } - } + + sself.message = transaction?.description + + sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction) + sself.update(viewState: .loaded) + + print("\(String(describing: transaction))") + }) } private func update(viewState: DeviceVerificationStartViewState) { self.viewDelegate?.deviceVerificationStartViewModel(self, didUpdateViewState: viewState) } + + + // MARK: - MXDeviceVerificationTransactionDidChange + + private func registerTransactionDidStateChangeNotification(transaction: MXOutgoingSASTransaction) { + NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXDeviceVerificationTransactionDidChange, object: transaction) + } + + @objc private func transactionDidStateChange(notification: Notification) { + guard let transaction = notification.object as? MXOutgoingSASTransaction else { + return + } + + // TODO: To remove + self.message = transaction.description + self.update(viewState: .loaded) + + switch transaction.state { + case MXOutgoingSASTransactionStateShowSAS: + self.message = transaction.sasEmoji?.description + self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction) + case MXOutgoingSASTransactionStateCancelled: + self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didTransactionCancelled: transaction) + default: + break + } + } } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift index 866a7ddd8..b9b6c5859 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift @@ -23,7 +23,9 @@ protocol DeviceVerificationStartViewModelViewDelegate: class { } protocol DeviceVerificationStartViewModelCoordinatorDelegate: class { - func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithMessage message: String) + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) + func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) } From 3daa0fa7abcdd246407396e1ff675fca943d1606 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 15:55:05 +0200 Subject: [PATCH 055/150] Update Riot/AppDelegate.m Co-Authored-By: SBiOSoftWhare --- Riot/AppDelegate.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 13cf6fd99..c8268ba4a 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1896,7 +1896,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Display the room name only if it is different than the sender name if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) { - notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]];// + notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]]; if ([msgType isEqualToString:@"m.text"]) { From 74c6e746b2bbf61895b5ac73a3353b06b889297f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 16:03:27 +0200 Subject: [PATCH 056/150] Add logs on related push methods --- Riot/AppDelegate.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index c8268ba4a..42aa818b8 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2218,6 +2218,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN - (void)clearPushNotificationToken { + NSLog(@"[AppDelegate][Push] clearPushNotificationToken: Clear existing token"); + // Clear existing token MXKAccountManager* accountManager = [MXKAccountManager sharedManager]; [accountManager setPushDeviceToken:nil withPushOptions:nil]; @@ -2226,6 +2228,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Remove delivred notifications for a given room id except call notifications - (void)removeDeliveredNotificationsWithRoomId:(NSString*)roomId completion:(dispatch_block_t)completion { + NSLog(@"[AppDelegate][Push] removeDeliveredNotificationsWithRoomId: Remove potential delivered notifications for room id: %@", roomId); + NSMutableArray *notificationRequestIdentifiersToRemove = [NSMutableArray new]; UNUserNotificationCenter *notificationCenter = [UNUserNotificationCenter currentNotificationCenter]; From c5df3cd5cb803592412f24cbb7993b31c1abd9e8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Apr 2019 16:48:24 +0200 Subject: [PATCH 057/150] Fix an issue in AppDelegate --- Riot/AppDelegate.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index bfec8e010..fddfe1bd9 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2000,8 +2000,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN NSString *notificationSoundName = [self notificationSoundNameFromPushRule:rule]; NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event]; - notificationContent.title = notificationTitle; - notificationContent.subtitle = notificationSubTitle; + notificationContent.title = notificationTitle; notificationContent.body = notificationBody; notificationContent.threadIdentifier = threadIdentifier; notificationContent.userInfo = notificationUserInfo; From b1404b9dbb46bd0959eac3ac32e6ee12be0de699 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 17:10:54 +0200 Subject: [PATCH 058/150] Device Verification: Start start screen UI --- Riot/Assets/en.lproj/Vector.strings | 9 ++- Riot/Generated/Strings.swift | 22 +++++- .../DeviceVerificationStartCoordinator.swift | 6 +- .../DeviceVerificationStartViewAction.swift | 2 +- ...VerificationStartViewController.storyboard | 77 +++++++++++++++---- ...eviceVerificationStartViewController.swift | 35 +++++---- .../DeviceVerificationStartViewModel.swift | 4 +- ...DeviceVerificationStartViewModelType.swift | 2 + 8 files changed, 120 insertions(+), 37 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index da5664a40..5535ce24b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -794,6 +794,13 @@ "sign_out_key_backup_in_progress_alert_cancel_action" = "I'll wait"; // MARK: - Device Verification +"device_verification_title" = "Verify device"; // MARK: - Start -"device_verification_start_verify_button" = "Verify"; +"device_verification_start_title" = "Verify by comparing a short text string"; +"device_verification_start_security_advise" = "For maximum security, we recommend you do this in person or use another trusted means of communication"; +"device_verification_start_wait_partner" = "Waiting for partner to accept..."; +"device_verification_start_verify_button" = "Begin Verifying"; +"device_verification_start_use_legacy_action" = "Use Legacy Verification (for older clients)"; + + diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 29015294a..04a9c6a07 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -426,10 +426,30 @@ internal enum VectorL10n { internal static var decline: String { return VectorL10n.tr("Vector", "decline") } - /// Verify + /// For maximum security, we recommend you do this in person or use another trusted means of communication + internal static var deviceVerificationStartSecurityAdvise: String { + return VectorL10n.tr("Vector", "device_verification_start_security_advise") + } + /// Verify by comparing a short text string + internal static var deviceVerificationStartTitle: String { + return VectorL10n.tr("Vector", "device_verification_start_title") + } + /// Use Legacy Verification (for older clients) + internal static var deviceVerificationStartUseLegacyAction: String { + return VectorL10n.tr("Vector", "device_verification_start_use_legacy_action") + } + /// Begin Verifying internal static var deviceVerificationStartVerifyButton: String { return VectorL10n.tr("Vector", "device_verification_start_verify_button") } + /// Waiting for partner to accept... + internal static var deviceVerificationStartWaitPartner: String { + return VectorL10n.tr("Vector", "device_verification_start_wait_partner") + } + /// Verify device + internal static var deviceVerificationTitle: String { + return VectorL10n.tr("Vector", "device_verification_title") + } /// %tu rooms internal static func directoryCellDescription(_ p1: Int) -> String { return VectorL10n.tr("Vector", "directory_cell_description", p1) diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift index 8f283cc91..b3259b0b4 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartCoordinator.swift @@ -60,7 +60,11 @@ final class DeviceVerificationStartCoordinator: DeviceVerificationStartCoordinat // MARK: - DeviceVerificationStartViewModelCoordinatorDelegate extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCoordinatorDelegate { - + func deviceVerificationStartViewModelUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) { + // TODO + self.delegate?.deviceVerificationStartCoordinatorDidCancel(self) + } + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) { self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction) } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift index 90d6d2c28..ce7024a06 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewAction.swift @@ -20,7 +20,7 @@ import Foundation /// DeviceVerificationStartViewController view actions exposed to view model enum DeviceVerificationStartViewAction { - case sayHello + case useLegacyVerification case beginVerifying case cancel } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard index 1a6ffab2d..621055610 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard @@ -22,30 +22,72 @@ - + - + - + + + + + + + + - + - + + + + + + + + + + + + + @@ -55,7 +97,7 @@ - + @@ -63,13 +105,13 @@ - + - + @@ -79,9 +121,12 @@ - + + + + diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift index 403505b2c..ee617ad48 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -31,10 +31,13 @@ final class DeviceVerificationStartViewController: UIViewController { // MARK: Outlets @IBOutlet private weak var scrollView: UIScrollView! - - @IBOutlet private weak var messageLabel: UILabel! + + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet weak var verifyButtonBackgroundView: UIView! @IBOutlet private weak var verifyButton: UIButton! - + @IBOutlet weak var useLegacyVerificationButton: UIButton! + // MARK: Private private var viewModel: DeviceVerificationStartViewModelType! @@ -59,7 +62,7 @@ final class DeviceVerificationStartViewController: UIViewController { // Do any additional setup after loading the view. - self.title = "Template" + self.title = VectorL10n.deviceVerificationTitle self.setupViews() self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) @@ -70,8 +73,6 @@ final class DeviceVerificationStartViewController: UIViewController { self.update(theme: self.theme) self.viewModel.viewDelegate = self - - self.viewModel.process(viewAction: .sayHello) } override func viewWillAppear(_ animated: Bool) { @@ -109,12 +110,13 @@ final class DeviceVerificationStartViewController: UIViewController { theme.applyStyle(onNavigationBar: navigationBar) } - - // TODO: - self.messageLabel.textColor = theme.textPrimaryColor + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor self.verifyButton.backgroundColor = theme.backgroundColor theme.applyStyle(onButton: self.verifyButton) + + theme.applyStyle(onButton: self.useLegacyVerificationButton) } private func registerThemeServiceDidChangeThemeNotification() { @@ -134,10 +136,11 @@ final class DeviceVerificationStartViewController: UIViewController { self.scrollView.keyboardDismissMode = .interactive + self.titleLabel.text = VectorL10n.deviceVerificationStartTitle + self.informationLabel.text = VectorL10n.deviceVerificationStartSecurityAdvise + self.verifyButton.setTitle(VectorL10n.deviceVerificationStartVerifyButton, for: .normal) - - self.messageLabel.text = "VectorL10n.deviceVerificationStartTitle" - self.messageLabel.isHidden = true + self.useLegacyVerificationButton.setTitle(VectorL10n.deviceVerificationStartUseLegacyAction, for: .normal) } private func render(viewState: DeviceVerificationStartViewState) { @@ -157,9 +160,7 @@ final class DeviceVerificationStartViewController: UIViewController { private func renderLoaded() { self.activityPresenter.removeCurrentActivityIndicator(animated: true) - - self.messageLabel.text = self.viewModel.message - self.messageLabel.isHidden = false + //self.informationLabel.text = self.viewModel.message } private func render(error: Error) { @@ -174,6 +175,10 @@ final class DeviceVerificationStartViewController: UIViewController { self.viewModel.process(viewAction: .beginVerifying) } + @IBAction private func useLegacyVerificationButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .useLegacyVerification) + } + private func cancelButtonAction() { self.viewModel.process(viewAction: .cancel) } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift index cbbd63935..e048d0f7c 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift @@ -53,12 +53,12 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy func process(viewAction: DeviceVerificationStartViewAction) { switch viewAction { + case .useLegacyVerification: + self.coordinatorDelegate?.deviceVerificationStartViewModelUseLegacyVerification(self) case .beginVerifying: self.beginVerifying() case .cancel: self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self) - case .sayHello: - break } } diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift index b9b6c5859..c4a6c64de 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift @@ -23,6 +23,8 @@ protocol DeviceVerificationStartViewModelViewDelegate: class { } protocol DeviceVerificationStartViewModelCoordinatorDelegate: class { + func deviceVerificationStartViewModelUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) + func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) From daeb864025ddb2615d122c92e1823a21b9c3ae71 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 18:15:05 +0200 Subject: [PATCH 059/150] Device Verification: Start screen: add "Waiting for partner to accept..." --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 +++ ...VerificationStartViewController.storyboard | 26 +++++++++++++++++++ ...eviceVerificationStartViewController.swift | 14 +++++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 5535ce24b..80dc593fc 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -800,6 +800,7 @@ "device_verification_start_title" = "Verify by comparing a short text string"; "device_verification_start_security_advise" = "For maximum security, we recommend you do this in person or use another trusted means of communication"; "device_verification_start_wait_partner" = "Waiting for partner to accept..."; +"device_verification_start_use_legacy" = "Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification."; "device_verification_start_verify_button" = "Begin Verifying"; "device_verification_start_use_legacy_action" = "Use Legacy Verification (for older clients)"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 04a9c6a07..ccfeb1b31 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -434,6 +434,10 @@ internal enum VectorL10n { internal static var deviceVerificationStartTitle: String { return VectorL10n.tr("Vector", "device_verification_start_title") } + /// Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification. + internal static var deviceVerificationStartUseLegacy: String { + return VectorL10n.tr("Vector", "device_verification_start_use_legacy") + } /// Use Legacy Verification (for older clients) internal static var deviceVerificationStartUseLegacyAction: String { return VectorL10n.tr("Vector", "device_verification_start_use_legacy_action") diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard index 621055610..5c8a7e211 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard @@ -42,6 +42,16 @@ + @@ -72,14 +82,28 @@ + + + + + @@ -125,8 +149,10 @@ + + diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift index ee617ad48..bdee146ac 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -34,6 +34,8 @@ final class DeviceVerificationStartViewController: UIViewController { @IBOutlet private weak var titleLabel: UILabel! @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet weak var waitingPartnerLabel: UILabel! + @IBOutlet weak var useLegacyVerificationLabel: UILabel! @IBOutlet weak var verifyButtonBackgroundView: UIView! @IBOutlet private weak var verifyButton: UIButton! @IBOutlet weak var useLegacyVerificationButton: UIButton! @@ -112,6 +114,8 @@ final class DeviceVerificationStartViewController: UIViewController { self.titleLabel.textColor = theme.textPrimaryColor self.informationLabel.textColor = theme.textPrimaryColor + self.waitingPartnerLabel.textColor = theme.textPrimaryColor + self.useLegacyVerificationLabel.textColor = theme.textPrimaryColor self.verifyButton.backgroundColor = theme.backgroundColor theme.applyStyle(onButton: self.verifyButton) @@ -138,6 +142,11 @@ final class DeviceVerificationStartViewController: UIViewController { self.titleLabel.text = VectorL10n.deviceVerificationStartTitle self.informationLabel.text = VectorL10n.deviceVerificationStartSecurityAdvise + self.waitingPartnerLabel.text = VectorL10n.deviceVerificationStartWaitPartner + self.useLegacyVerificationLabel.text = VectorL10n.deviceVerificationStartUseLegacy + + self.waitingPartnerLabel.isHidden = true + self.useLegacyVerificationLabel.isHidden = true self.verifyButton.setTitle(VectorL10n.deviceVerificationStartVerifyButton, for: .normal) self.useLegacyVerificationButton.setTitle(VectorL10n.deviceVerificationStartUseLegacyAction, for: .normal) @@ -160,7 +169,10 @@ final class DeviceVerificationStartViewController: UIViewController { private func renderLoaded() { self.activityPresenter.removeCurrentActivityIndicator(animated: true) - //self.informationLabel.text = self.viewModel.message + + self.verifyButtonBackgroundView.isHidden = true + self.waitingPartnerLabel.isHidden = false + self.useLegacyVerificationLabel.isHidden = false } private func render(error: Error) { From 9cf47e7a2eaceeea2ea7d5561fa656c5ed1501bd Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 18:39:57 +0200 Subject: [PATCH 060/150] Device Verification: Start screen: add transaction cancel --- .../DeviceVerificationStartViewModel.swift | 25 ++++++++++--------- ...DeviceVerificationStartViewModelType.swift | 5 +--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift index e048d0f7c..c449e1758 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModel.swift @@ -28,10 +28,10 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy private let verificationManager: MXDeviceVerificationManager private let otherUser: MXUser private let otherDevice: MXDeviceInfo + + private var transaction: MXSASTransaction! // MARK: Public - - var message: String? weak var viewDelegate: DeviceVerificationStartViewModelViewDelegate? weak var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate? @@ -43,7 +43,6 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy self.verificationManager = session.crypto.deviceVerificationManager self.otherUser = otherUser self.otherDevice = otherDevice - self.message = nil } deinit { @@ -54,10 +53,12 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy func process(viewAction: DeviceVerificationStartViewAction) { switch viewAction { case .useLegacyVerification: + self.cancelTransaction() self.coordinatorDelegate?.deviceVerificationStartViewModelUseLegacyVerification(self) case .beginVerifying: self.beginVerifying() case .cancel: + self.cancelTransaction() self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self) } } @@ -65,7 +66,6 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy // MARK: - Private private func beginVerifying() { - self.update(viewState: .loading) self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: kMXKeyVerificationMethodSAS, complete: { [weak self] (transaction) in @@ -77,14 +77,20 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy return } - sself.message = transaction?.description + sself.transaction = sasTransaction sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction) sself.update(viewState: .loaded) - - print("\(String(describing: transaction))") }) } + + private func cancelTransaction() { + guard let transaction = self.transaction else { + return + } + + transaction.cancel(with: MXTransactionCancelCode.user()) + } private func update(viewState: DeviceVerificationStartViewState) { self.viewDelegate?.deviceVerificationStartViewModel(self, didUpdateViewState: viewState) @@ -102,13 +108,8 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy return } - // TODO: To remove - self.message = transaction.description - self.update(viewState: .loaded) - switch transaction.state { case MXOutgoingSASTransactionStateShowSAS: - self.message = transaction.sasEmoji?.description self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction) case MXOutgoingSASTransactionStateCancelled: self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didTransactionCancelled: transaction) diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift index c4a6c64de..be3bf3b29 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewModelType.swift @@ -32,10 +32,7 @@ protocol DeviceVerificationStartViewModelCoordinatorDelegate: class { } /// Protocol describing the view model used by `DeviceVerificationStartViewController` -protocol DeviceVerificationStartViewModelType { - - var message: String? { get set } - +protocol DeviceVerificationStartViewModelType { var viewDelegate: DeviceVerificationStartViewModelViewDelegate? { get set } var coordinatorDelegate: DeviceVerificationStartViewModelCoordinatorDelegate? { get set } From e50aee0cf2aae4c5012c899794a9337806fe55be Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 18:44:07 +0200 Subject: [PATCH 061/150] Template: Fix storyboard --- .../TemplateScreenViewController.storyboard | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard index 35b09e221..120c8ca35 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.storyboard @@ -22,10 +22,10 @@ - + - + - + @@ -55,7 +55,7 @@ - + @@ -63,7 +63,7 @@ - + From 0f93acf59f979ebf431319fdad45606a24c5911e Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 19:09:29 +0200 Subject: [PATCH 062/150] Device Verification: Start verify screen --- Riot.xcodeproj/project.pbxproj | 40 ++++ Riot/Generated/Storyboards.swift | 5 + .../DeviceVerificationCoordinator.swift | 30 ++- .../DeviceVerificationVerifyCoordinator.swift | 71 +++++++ ...iceVerificationVerifyCoordinatorType.swift | 29 +++ .../DeviceVerificationVerifyViewAction.swift | 26 +++ ...erificationVerifyViewController.storyboard | 92 +++++++++ ...viceVerificationVerifyViewController.swift | 185 ++++++++++++++++++ .../DeviceVerificationVerifyViewModel.swift | 69 +++++++ ...eviceVerificationVerifyViewModelType.swift | 37 ++++ .../DeviceVerificationVerifyViewState.swift | 26 +++ 11 files changed, 607 insertions(+), 3 deletions(-) create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinator.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinatorType.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewAction.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModel.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModelType.swift create mode 100644 Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewState.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index abd1ef9ad..e3de014c9 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -47,6 +47,14 @@ 3232ABA9225730E100AD6A5C /* DeviceVerificationStartViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9E225730E100AD6A5C /* DeviceVerificationStartViewModel.swift */; }; 3232ABAA225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */; }; 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */; }; + 3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABAD2257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift */; }; + 3232ABB62257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3232ABAE2257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard */; }; + 3232ABB72257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABAF2257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift */; }; + 3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB02257BE6400AD6A5C /* DeviceVerificationVerifyCoordinator.swift */; }; + 3232ABB92257BE6500AD6A5C /* DeviceVerificationVerifyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB12257BE6400AD6A5C /* DeviceVerificationVerifyViewController.swift */; }; + 3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB22257BE6400AD6A5C /* DeviceVerificationVerifyViewModel.swift */; }; + 3232ABBB2257BE6500AD6A5C /* DeviceVerificationVerifyViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB32257BE6400AD6A5C /* DeviceVerificationVerifyViewState.swift */; }; + 3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB42257BE6400AD6A5C /* DeviceVerificationVerifyViewAction.swift */; }; 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; @@ -527,6 +535,14 @@ 3232AB9E225730E100AD6A5C /* DeviceVerificationStartViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationStartViewModel.swift; sourceTree = ""; }; 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationCoordinatorBridgePresenter.swift; sourceTree = ""; }; 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationCoordinator.swift; sourceTree = ""; }; + 3232ABAD2257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyCoordinatorType.swift; sourceTree = ""; }; + 3232ABAE2257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationVerifyViewController.storyboard; sourceTree = ""; }; + 3232ABAF2257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewModelType.swift; sourceTree = ""; }; + 3232ABB02257BE6400AD6A5C /* DeviceVerificationVerifyCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyCoordinator.swift; sourceTree = ""; }; + 3232ABB12257BE6400AD6A5C /* DeviceVerificationVerifyViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewController.swift; sourceTree = ""; }; + 3232ABB22257BE6400AD6A5C /* DeviceVerificationVerifyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewModel.swift; sourceTree = ""; }; + 3232ABB32257BE6400AD6A5C /* DeviceVerificationVerifyViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewState.swift; sourceTree = ""; }; + 3232ABB42257BE6400AD6A5C /* DeviceVerificationVerifyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewAction.swift; sourceTree = ""; }; 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -1292,6 +1308,7 @@ isa = PBXGroup; children = ( 3232AB96225730E100AD6A5C /* Start */, + 3232ABAC2257BE6400AD6A5C /* Verify */, 3232AB95225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift */, 3232AB9F225730E100AD6A5C /* DeviceVerificationCoordinatorBridgePresenter.swift */, 3232ABA0225730E100AD6A5C /* DeviceVerificationCoordinator.swift */, @@ -1314,6 +1331,21 @@ path = Start; sourceTree = ""; }; + 3232ABAC2257BE6400AD6A5C /* Verify */ = { + isa = PBXGroup; + children = ( + 3232ABAD2257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift */, + 3232ABAE2257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard */, + 3232ABAF2257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift */, + 3232ABB02257BE6400AD6A5C /* DeviceVerificationVerifyCoordinator.swift */, + 3232ABB12257BE6400AD6A5C /* DeviceVerificationVerifyViewController.swift */, + 3232ABB22257BE6400AD6A5C /* DeviceVerificationVerifyViewModel.swift */, + 3232ABB32257BE6400AD6A5C /* DeviceVerificationVerifyViewState.swift */, + 3232ABB42257BE6400AD6A5C /* DeviceVerificationVerifyViewAction.swift */, + ); + path = Verify; + sourceTree = ""; + }; 3233F7291F31F3B4006ACA81 /* libs */ = { isa = PBXGroup; children = ( @@ -3403,6 +3435,7 @@ B1B557B320EF5AEF00210D55 /* EventDetailsView.xib in Resources */, B1B557DD20EF5FBB00210D55 /* FilesSearchTableViewCell.xib in Resources */, B1B5590320EF768F00210D55 /* RoomSelectedStickerBubbleCell.xib in Resources */, + 3232ABB62257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard in Resources */, B1B5573F20EE6C4D00210D55 /* SegmentedViewController.xib in Resources */, B1B5581E20EF625800210D55 /* RoomTitleView.xib in Resources */, B1B558C020EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, @@ -3639,6 +3672,7 @@ B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */, B1DB4F0E22316FFF0065DBFA /* UserNameColorGenerator.swift in Sources */, B1057789221304EC00334B1E /* KeyBackupSetupSuccessFromPassphraseViewController.swift in Sources */, + 3232ABB72257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift in Sources */, B16932B120F3AC9200746532 /* RoomSearchDataSource.m in Sources */, B16932A320F3A21C00746532 /* main.m in Sources */, B1B5574520EE6C4D00210D55 /* StartChatViewController.m in Sources */, @@ -3707,6 +3741,7 @@ B1B5573D20EE6C4D00210D55 /* WebViewViewController.m in Sources */, 3209451221F1C1430088CAA2 /* BlackTheme.swift in Sources */, B1B5572720EE6C4D00210D55 /* RoomSearchViewController.m in Sources */, + 3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */, F05927C91FDED836009F2A68 /* MXGroup+Riot.m in Sources */, B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, @@ -3731,6 +3766,7 @@ B1B5582C20EF666100210D55 /* DirectoryRecentTableViewCell.m in Sources */, B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */, B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */, + 3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */, B1B5572A20EE6C4D00210D55 /* RoomMemberDetailsViewController.m in Sources */, B1B5590120EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */, B1B558C920EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, @@ -3742,6 +3778,7 @@ 3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */, B1B558FC20EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleBubbleCell.m in Sources */, B1B5572920EE6C4D00210D55 /* RoomFilesViewController.m in Sources */, + 3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */, B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */, B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */, B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, @@ -3749,6 +3786,7 @@ F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */, + 3232ABBB2257BE6500AD6A5C /* DeviceVerificationVerifyViewState.swift in Sources */, 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, @@ -3823,6 +3861,7 @@ B1B558C820EF768F00210D55 /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */, B1B557C620EF5CD400210D55 /* DirectoryServerDetailTableViewCell.m in Sources */, B1B5590920EF768F00210D55 /* RoomEmptyBubbleCell.m in Sources */, + 3232ABB92257BE6500AD6A5C /* DeviceVerificationVerifyViewController.swift in Sources */, B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */, B1B5574720EE6C4D00210D55 /* UsersDevicesViewController.m in Sources */, B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */, @@ -3858,6 +3897,7 @@ F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, B1B5572C20EE6C4D00210D55 /* RoomParticipantsViewController.m in Sources */, B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, + 3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */, 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index ee9cee1c8..53dd3798f 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -17,6 +17,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: DeviceVerificationStartViewController.self) } + internal enum DeviceVerificationVerifyViewController: StoryboardType { + internal static let storyboardName = "DeviceVerificationVerifyViewController" + + internal static let initialScene = InitialSceneType(storyboard: DeviceVerificationVerifyViewController.self) + } internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType { internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController" diff --git a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift index cb61de551..79c3ac493 100644 --- a/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift +++ b/Riot/Modules/DeviceVerification/DeviceVerificationCoordinator.swift @@ -107,15 +107,29 @@ final class DeviceVerificationCoordinator: DeviceVerificationCoordinatorType { coordinator.delegate = self return coordinator } + + private func showVerify(animated: Bool) { + guard let transaction = self.transaction else { + return + } + + let coordinator = DeviceVerificationVerifyCoordinator(session: self.session, transaction: transaction) + coordinator.delegate = self + coordinator.start() + + // TODO: Do not push, replace + self.add(childCoordinator: coordinator) + self.navigationRouter.push(coordinator, animated: animated) { [weak self] in + self?.remove(childCoordinator: coordinator) + } + } } -// MARK: - DeviceVerificationStartCoordinatorDelegate extension DeviceVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate { func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) { self.transaction = transaction - // TODO - self.delegate?.deviceVerificationCoordinatorDidComplete(self) + self.showVerify(animated: true) } func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) { @@ -128,3 +142,13 @@ extension DeviceVerificationCoordinator: DeviceVerificationStartCoordinatorDeleg self.delegate?.deviceVerificationCoordinatorDidComplete(self) } } + +extension DeviceVerificationCoordinator: DeviceVerificationVerifyCoordinatorDelegate { + func deviceVerificationVerifyCoordinatorDidComplete(_ coordinator: DeviceVerificationVerifyCoordinatorType) { + self.delegate?.deviceVerificationCoordinatorDidComplete(self) + } + + func deviceVerificationVerifyCoordinatorDidCancel(_ coordinator: DeviceVerificationVerifyCoordinatorType) { + self.delegate?.deviceVerificationCoordinatorDidComplete(self) + } +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinator.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinator.swift new file mode 100644 index 000000000..932ea64c6 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinator.swift @@ -0,0 +1,71 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +import UIKit + +final class DeviceVerificationVerifyCoordinator: DeviceVerificationVerifyCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private var deviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModelType + private let deviceVerificationVerifyViewController: DeviceVerificationVerifyViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: DeviceVerificationVerifyCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, transaction: MXSASTransaction) { + self.session = session + + let deviceVerificationVerifyViewModel = DeviceVerificationVerifyViewModel(session: self.session, transaction:transaction) + let deviceVerificationVerifyViewController = DeviceVerificationVerifyViewController.instantiate(with: deviceVerificationVerifyViewModel) + self.deviceVerificationVerifyViewModel = deviceVerificationVerifyViewModel + self.deviceVerificationVerifyViewController = deviceVerificationVerifyViewController + } + + // MARK: - Public methods + + func start() { + self.deviceVerificationVerifyViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.deviceVerificationVerifyViewController + } +} + +// MARK: - DeviceVerificationVerifyViewModelCoordinatorDelegate +extension DeviceVerificationVerifyCoordinator: DeviceVerificationVerifyViewModelCoordinatorDelegate { + + func deviceVerificationVerifyViewModelDidComplete(_ viewModel: DeviceVerificationVerifyViewModelType) { + self.delegate?.deviceVerificationVerifyCoordinatorDidComplete(self) + } + + func deviceVerificationVerifyViewModelDidCancel(_ viewModel: DeviceVerificationVerifyViewModelType) { + self.delegate?.deviceVerificationVerifyCoordinatorDidCancel(self) + } +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinatorType.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinatorType.swift new file mode 100644 index 000000000..d9816480b --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyCoordinatorType.swift @@ -0,0 +1,29 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol DeviceVerificationVerifyCoordinatorDelegate: class { + func deviceVerificationVerifyCoordinatorDidComplete(_ coordinator: DeviceVerificationVerifyCoordinatorType) + func deviceVerificationVerifyCoordinatorDidCancel(_ coordinator: DeviceVerificationVerifyCoordinatorType) +} + +/// `DeviceVerificationVerifyCoordinatorType` is a protocol describing a Coordinator that handle key backup setup passphrase navigation flow. +protocol DeviceVerificationVerifyCoordinatorType: Coordinator, Presentable { + var delegate: DeviceVerificationVerifyCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewAction.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewAction.swift new file mode 100644 index 000000000..5ed5933c6 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewAction.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// DeviceVerificationVerifyViewController view actions exposed to view model +enum DeviceVerificationVerifyViewAction { + case confirm + case complete + case cancel +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard new file mode 100644 index 000000000..9b95e7e5c --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift new file mode 100644 index 000000000..00760d4fb --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift @@ -0,0 +1,185 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import UIKit + +final class DeviceVerificationVerifyViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private weak var okButton: UIButton! + + // MARK: Private + + private var viewModel: DeviceVerificationVerifyViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: DeviceVerificationVerifyViewModelType) -> DeviceVerificationVerifyViewController { + let viewController = StoryboardScene.DeviceVerificationVerifyViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = "Template" + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: + self.messageLabel.textColor = theme.textPrimaryColor + + self.okButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.okButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + self.messageLabel.text = "VectorL10n.deviceVerificationVerifyTitle" + self.messageLabel.isHidden = true + } + + private func render(viewState: DeviceVerificationVerifyViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded: + self.renderLoaded() + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded() { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + self.messageLabel.text = "TODO" + self.messageLabel.isHidden = false + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func okButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .complete) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .cancel) + } +} + + +// MARK: - DeviceVerificationVerifyViewModelViewDelegate +extension DeviceVerificationVerifyViewController: DeviceVerificationVerifyViewModelViewDelegate { + + func deviceVerificationVerifyViewModel(_ viewModel: DeviceVerificationVerifyViewModelType, didUpdateViewState viewSate: DeviceVerificationVerifyViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModel.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModel.swift new file mode 100644 index 000000000..65f56aeda --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModel.swift @@ -0,0 +1,69 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +final class DeviceVerificationVerifyViewModel: DeviceVerificationVerifyViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let session: MXSession + private let transaction: MXSASTransaction + + // MARK: Public + + weak var viewDelegate: DeviceVerificationVerifyViewModelViewDelegate? + weak var coordinatorDelegate: DeviceVerificationVerifyViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(session: MXSession, transaction: MXSASTransaction) { + self.session = session + self.transaction = transaction + } + + deinit { + } + + // MARK: - Public + + func process(viewAction: DeviceVerificationVerifyViewAction) { + switch viewAction { + case .confirm: + self.confirm() + case .complete: + self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidComplete(self) + case .cancel: + self.coordinatorDelegate?.deviceVerificationVerifyViewModelDidCancel(self) + } + } + + // MARK: - Private + + private func confirm() { + self.update(viewState: .loading) + + // TODO + } + + private func update(viewState: DeviceVerificationVerifyViewState) { + self.viewDelegate?.deviceVerificationVerifyViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModelType.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModelType.swift new file mode 100644 index 000000000..348e7f927 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewModelType.swift @@ -0,0 +1,37 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +protocol DeviceVerificationVerifyViewModelViewDelegate: class { + func deviceVerificationVerifyViewModel(_ viewModel: DeviceVerificationVerifyViewModelType, didUpdateViewState viewSate: DeviceVerificationVerifyViewState) +} + +protocol DeviceVerificationVerifyViewModelCoordinatorDelegate: class { + func deviceVerificationVerifyViewModelDidComplete(_ viewModel: DeviceVerificationVerifyViewModelType) + func deviceVerificationVerifyViewModelDidCancel(_ viewModel: DeviceVerificationVerifyViewModelType) +} + +/// Protocol describing the view model used by `DeviceVerificationVerifyViewController` +protocol DeviceVerificationVerifyViewModelType { + + var viewDelegate: DeviceVerificationVerifyViewModelViewDelegate? { get set } + var coordinatorDelegate: DeviceVerificationVerifyViewModelCoordinatorDelegate? { get set } + + func process(viewAction: DeviceVerificationVerifyViewAction) +} diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewState.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewState.swift new file mode 100644 index 000000000..bccd66485 --- /dev/null +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh DeviceVerification/Verify DeviceVerificationVerify +/* + Copyright 2019 New Vector Ltd + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation + +/// DeviceVerificationVerifyViewController view state +enum DeviceVerificationVerifyViewState { + case loading + case loaded + case error(Error) +} From ffe384080572767807b52e281420e578ef6fe3ad Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 5 Apr 2019 23:48:06 +0200 Subject: [PATCH 063/150] Device Verification: Verify screen: add wording --- Riot/Assets/en.lproj/Vector.strings | 8 +- Riot/Generated/Strings.swift | 20 ++++- ...eviceVerificationStartViewController.swift | 2 +- ...erificationVerifyViewController.storyboard | 87 ++++++++++++++----- ...viceVerificationVerifyViewController.swift | 42 +++++---- 5 files changed, 116 insertions(+), 43 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 80dc593fc..5c8224246 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -795,13 +795,17 @@ // MARK: - Device Verification "device_verification_title" = "Verify device"; +"device_verification_security_advice" = "For maximum security, we recommend you do this in person or use another trusted means of communication"; // MARK: - Start "device_verification_start_title" = "Verify by comparing a short text string"; -"device_verification_start_security_advise" = "For maximum security, we recommend you do this in person or use another trusted means of communication"; "device_verification_start_wait_partner" = "Waiting for partner to accept..."; "device_verification_start_use_legacy" = "Nothing appearing? Not all clients supports interactive verification yet. Use legacy verification."; "device_verification_start_verify_button" = "Begin Verifying"; "device_verification_start_use_legacy_action" = "Use Legacy Verification (for older clients)"; - +// MARK: - Verify +"device_verification_verify_title_emoji" = "Verify this user by confirming the following emoji appear on their screen"; +"device_verification_verify_title_number" = "Verify this user by confirming the following number appears on their screen"; +"device_verification_verify_wait_partner" = "Waiting for partner to confirm..."; +"device_verification_verify_continue_button" = "Continue"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index ccfeb1b31..786804bc8 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -427,8 +427,8 @@ internal enum VectorL10n { return VectorL10n.tr("Vector", "decline") } /// For maximum security, we recommend you do this in person or use another trusted means of communication - internal static var deviceVerificationStartSecurityAdvise: String { - return VectorL10n.tr("Vector", "device_verification_start_security_advise") + internal static var deviceVerificationSecurityAdvice: String { + return VectorL10n.tr("Vector", "device_verification_security_advice") } /// Verify by comparing a short text string internal static var deviceVerificationStartTitle: String { @@ -454,6 +454,22 @@ internal enum VectorL10n { internal static var deviceVerificationTitle: String { return VectorL10n.tr("Vector", "device_verification_title") } + /// Continue + internal static var deviceVerificationVerifyContinueButton: String { + return VectorL10n.tr("Vector", "device_verification_verify_continue_button") + } + /// Verify this user by confirming the following emoji appear on their screen + internal static var deviceVerificationVerifyTitleEmoji: String { + return VectorL10n.tr("Vector", "device_verification_verify_title_emoji") + } + /// Verify this user by confirming the following number appears on their screen + internal static var deviceVerificationVerifyTitleNumber: String { + return VectorL10n.tr("Vector", "device_verification_verify_title_number") + } + /// Waiting for partner to confirm... + internal static var deviceVerificationVerifyWaitPartner: String { + return VectorL10n.tr("Vector", "device_verification_verify_wait_partner") + } /// %tu rooms internal static func directoryCellDescription(_ p1: Int) -> String { return VectorL10n.tr("Vector", "directory_cell_description", p1) diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift index bdee146ac..0078b56ce 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -141,7 +141,7 @@ final class DeviceVerificationStartViewController: UIViewController { self.scrollView.keyboardDismissMode = .interactive self.titleLabel.text = VectorL10n.deviceVerificationStartTitle - self.informationLabel.text = VectorL10n.deviceVerificationStartSecurityAdvise + self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice self.waitingPartnerLabel.text = VectorL10n.deviceVerificationStartWaitPartner self.useLegacyVerificationLabel.text = VectorL10n.deviceVerificationStartUseLegacy diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard index 9b95e7e5c..c12aae8ba 100644 --- a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard @@ -10,7 +10,7 @@ - + @@ -22,30 +22,72 @@ - + - + - - + + + + + + + + + + + + @@ -55,7 +97,7 @@ - + @@ -63,13 +105,13 @@ - + - + @@ -79,14 +121,17 @@ - - + + + + + - + diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift index 00760d4fb..082edb36e 100644 --- a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift @@ -31,10 +31,13 @@ final class DeviceVerificationVerifyViewController: UIViewController { // MARK: Outlets @IBOutlet private weak var scrollView: UIScrollView! - - @IBOutlet private weak var messageLabel: UILabel! - @IBOutlet private weak var okButton: UIButton! - + + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var informationLabel: UILabel! + @IBOutlet weak var waitingPartnerLabel: UILabel! + @IBOutlet weak var continueButtonBackgroundView: UIView! + @IBOutlet private weak var continueButton: UIButton! + // MARK: Private private var viewModel: DeviceVerificationVerifyViewModelType! @@ -59,7 +62,7 @@ final class DeviceVerificationVerifyViewController: UIViewController { // Do any additional setup after loading the view. - self.title = "Template" + self.title = VectorL10n.deviceVerificationTitle self.setupViews() self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) @@ -107,12 +110,12 @@ final class DeviceVerificationVerifyViewController: UIViewController { theme.applyStyle(onNavigationBar: navigationBar) } + self.titleLabel.textColor = theme.textPrimaryColor + self.informationLabel.textColor = theme.textPrimaryColor + self.waitingPartnerLabel.textColor = theme.textPrimaryColor - // TODO: - self.messageLabel.textColor = theme.textPrimaryColor - - self.okButton.backgroundColor = theme.backgroundColor - theme.applyStyle(onButton: self.okButton) + self.continueButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.continueButton) } private func registerThemeServiceDidChangeThemeNotification() { @@ -131,9 +134,14 @@ final class DeviceVerificationVerifyViewController: UIViewController { self.navigationItem.rightBarButtonItem = cancelBarButtonItem self.scrollView.keyboardDismissMode = .interactive - - self.messageLabel.text = "VectorL10n.deviceVerificationVerifyTitle" - self.messageLabel.isHidden = true + + self.titleLabel.text = VectorL10n.deviceVerificationVerifyTitleEmoji + self.informationLabel.text = VectorL10n.deviceVerificationSecurityAdvice + self.waitingPartnerLabel.text = VectorL10n.deviceVerificationVerifyWaitPartner + + self.waitingPartnerLabel.isHidden = true + + self.continueButton.setTitle(VectorL10n.deviceVerificationVerifyContinueButton, for: .normal) } private func render(viewState: DeviceVerificationVerifyViewState) { @@ -154,8 +162,8 @@ final class DeviceVerificationVerifyViewController: UIViewController { private func renderLoaded() { self.activityPresenter.removeCurrentActivityIndicator(animated: true) - self.messageLabel.text = "TODO" - self.messageLabel.isHidden = false + self.continueButtonBackgroundView.isHidden = true + self.waitingPartnerLabel.isHidden = false } private func render(error: Error) { @@ -166,8 +174,8 @@ final class DeviceVerificationVerifyViewController: UIViewController { // MARK: - Actions - @IBAction private func okButtonAction(_ sender: Any) { - self.viewModel.process(viewAction: .complete) + @IBAction private func continueButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .confirm) } private func cancelButtonAction() { From c0c8227f07d0ef61a8a7f50a23ac3ff712a23f95 Mon Sep 17 00:00:00 2001 From: manuroe Date: Sun, 7 Apr 2019 00:00:49 +0200 Subject: [PATCH 064/150] Device Verification: Start screen: Improve a bit layout (maybe) --- ...viceVerificationStartViewController.storyboard | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard index 5c8a7e211..a8b74949f 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.storyboard @@ -37,13 +37,16 @@ - + From 1893b3c69f22e386d15c933af8f222ee3bb620b4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Sun, 7 Apr 2019 00:01:19 +0200 Subject: [PATCH 065/150] Device Verification: Verify screen: display emojis --- Riot.xcodeproj/project.pbxproj | 12 ++++ ...erificationVerifyViewController.storyboard | 67 ++++++++++++++++++- ...viceVerificationVerifyViewController.swift | 29 +++++++- .../DeviceVerificationVerifyViewModel.swift | 2 + ...eviceVerificationVerifyViewModelType.swift | 2 + .../Views/VerifyEmojiCollectionViewCell.swift | 22 ++++++ 6 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 Riot/Modules/DeviceVerification/Verify/Views/VerifyEmojiCollectionViewCell.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index e3de014c9..a67e4e94c 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -55,6 +55,7 @@ 3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB22257BE6400AD6A5C /* DeviceVerificationVerifyViewModel.swift */; }; 3232ABBB2257BE6500AD6A5C /* DeviceVerificationVerifyViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB32257BE6400AD6A5C /* DeviceVerificationVerifyViewState.swift */; }; 3232ABBC2257BE6500AD6A5C /* DeviceVerificationVerifyViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABB42257BE6400AD6A5C /* DeviceVerificationVerifyViewAction.swift */; }; + 3232ABC022594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3232ABBF22594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift */; }; 3233F7461F3497E2006ACA81 /* JitsiMeet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; }; 3233F7471F3497E2006ACA81 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; @@ -543,6 +544,7 @@ 3232ABB22257BE6400AD6A5C /* DeviceVerificationVerifyViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewModel.swift; sourceTree = ""; }; 3232ABB32257BE6400AD6A5C /* DeviceVerificationVerifyViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewState.swift; sourceTree = ""; }; 3232ABB42257BE6400AD6A5C /* DeviceVerificationVerifyViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifyViewAction.swift; sourceTree = ""; }; + 3232ABBF22594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VerifyEmojiCollectionViewCell.swift; sourceTree = ""; }; 3233F7441F3497DA006ACA81 /* JitsiMeet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = JitsiMeet.framework; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; @@ -1334,6 +1336,7 @@ 3232ABAC2257BE6400AD6A5C /* Verify */ = { isa = PBXGroup; children = ( + 3232ABBE22594B5C00AD6A5C /* Views */, 3232ABAD2257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift */, 3232ABAE2257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard */, 3232ABAF2257BE6400AD6A5C /* DeviceVerificationVerifyViewModelType.swift */, @@ -1346,6 +1349,14 @@ path = Verify; sourceTree = ""; }; + 3232ABBE22594B5C00AD6A5C /* Views */ = { + isa = PBXGroup; + children = ( + 3232ABBF22594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift */, + ); + path = Views; + sourceTree = ""; + }; 3233F7291F31F3B4006ACA81 /* libs */ = { isa = PBXGroup; children = ( @@ -3703,6 +3714,7 @@ B1B5581720EF625800210D55 /* PreviewRoomTitleView.m in Sources */, B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */, B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, + 3232ABC022594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift in Sources */, B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */, B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169330B20F3CA3A00746532 /* Contact.m in Sources */, diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard index c12aae8ba..a087135a4 100644 --- a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.storyboard @@ -37,13 +37,16 @@ - + '),c.document.close(),a.addEvent(c.document.getElementById("okay"),"click",function(e){i&&a.renderNotificationBar(a.TEXT.EXTENSION?a.TEXT.EXTENSION.REQUIRE_REFRESH:a.TEXT.REFRESH.REQUIRE_REFRESH,a.TEXT.REFRESH.BUTTON,"javascript:location.reload()"),window.open(n,r?"_blank":"_top"),e.preventDefault();try{e.cancelBubble=!0}catch(e){}var t=setInterval(function(){isIE||navigator.plugins.refresh(!1),a.WebRTCPlugin.isPluginInstalled(a.WebRTCPlugin.pluginInfo.prefix,a.WebRTCPlugin.pluginInfo.plugName,a.WebRTCPlugin.pluginInfo.type,function(){clearInterval(t),a.WebRTCPlugin.defineWebRTCInterface()},function(){})},500)}),a.addEvent(c.document.getElementById("cancel"),"click",function(e){o.document.body.removeChild(s)})):c.document.close(),setTimeout(function(){"string"==typeof s.style.webkitTransform?s.style.webkitTransform="translateY(40px)":"string"==typeof s.style.transform?s.style.transform="translateY(40px)":s.style.top="0px"},300)}},webrtcDetectedType=null,checkMediaDataChannelSettings=function(e,t,n,r){if("function"==typeof n){var i=!0,a="firefox"===webrtcDetectedBrowser,s="moz"===webrtcDetectedType&&webrtcDetectedVersion>30,c="firefox"===e;if(a&&c||s)try{delete r.mandatory.MozDontOfferDataChannel}catch(e){o.error("Failed deleting MozDontOfferDataChannel"),o.error(e)}else a&&!c&&(r.mandatory.MozDontOfferDataChannel=!0);if(!a)for(var u in r.mandatory)r.mandatory.hasOwnProperty(u)&&-1!==u.indexOf("Moz")&&delete r.mandatory[u];!a||c||s||(i=!1),n(i,r)}},checkIceConnectionState=function(e,t,n){"function"==typeof n?(e=e||"peer",a._iceConnectionFiredStates[e]&&t!==a._iceConnectionStates.disconnected&&t!==a._iceConnectionStates.failed&&t!==a._iceConnectionStates.closed||(a._iceConnectionFiredStates[e]=[]),t=a._iceConnectionStates[t],a._iceConnectionFiredStates[e].indexOf(t)<0&&(a._iceConnectionFiredStates[e].push(t),t===a._iceConnectionStates.connected&&setTimeout(function(){a._iceConnectionFiredStates[e].push(a._iceConnectionStates.done),n(a._iceConnectionStates.done)},1e3),n(t))):o.warn("No callback specified in checkIceConnectionState. Aborted.")},createIceServer=null,createIceServers=null,RTCPeerConnection=null,RTCSessionDescription="function"==typeof RTCSessionDescription?RTCSessionDescription:null,RTCIceCandidate="function"==typeof RTCIceCandidate?RTCIceCandidate:null,getUserMedia=null,attachMediaStream=null,reattachMediaStream=null,webrtcDetectedBrowser=null,webrtcDetectedVersion=null,webrtcMinimumVersion=null,!(navigator.mozGetUserMedia||navigator.webkitGetUserMedia||navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/))||0===(navigator.userAgent.match(/android/gi)||[]).length&&0===(navigator.userAgent.match(/chrome/gi)||[]).length&&navigator.userAgent.indexOf("Safari/")>0?("object"==typeof o&&"function"==typeof o.log||((o={}||o).log=function(e){},o.info=function(e){},o.error=function(e){},o.dir=function(e){},o.exception=function(e){},o.trace=function(e){},o.warn=function(e){},o.count=function(e){},o.debug=function(e){},o.count=function(e){},o.time=function(e){},o.timeEnd=function(e){},o.group=function(e){},o.groupCollapsed=function(e){},o.groupEnd=function(e){}),a.parseWebrtcDetectedBrowser(),isIE="IE"===webrtcDetectedBrowser,a.WebRTCPlugin.WaitForPluginReady=function(){for(;a.WebRTCPlugin.pluginState!==a.WebRTCPlugin.PLUGIN_STATES.READY;);},a.WebRTCPlugin.callWhenPluginReady=function(e){if(a.WebRTCPlugin.pluginState===a.WebRTCPlugin.PLUGIN_STATES.READY)e();else var t=setInterval(function(){a.WebRTCPlugin.pluginState===a.WebRTCPlugin.PLUGIN_STATES.READY&&(clearInterval(t),e())},100)},a.WebRTCPlugin.setLogLevel=function(e){a.WebRTCPlugin.callWhenPluginReady(function(){a.WebRTCPlugin.plugin.setLogLevel(e)})},a.WebRTCPlugin.injectPlugin=function(){if("complete"===document.readyState&&a.WebRTCPlugin.pluginState===a.WebRTCPlugin.PLUGIN_STATES.INITIALIZING){if(a.WebRTCPlugin.pluginState=a.WebRTCPlugin.PLUGIN_STATES.INJECTING,"IE"===webrtcDetectedBrowser&&webrtcDetectedVersion<=10){var e=document.createDocumentFragment();for(a.WebRTCPlugin.plugin=document.createElement("div"),a.WebRTCPlugin.plugin.innerHTML=' '+(a.options.getAllCams?'':"")+"";a.WebRTCPlugin.plugin.firstChild;)e.appendChild(a.WebRTCPlugin.plugin.firstChild);document.body.appendChild(e),a.WebRTCPlugin.plugin=document.getElementById(a.WebRTCPlugin.pluginInfo.pluginId)}else a.WebRTCPlugin.plugin=document.createElement("object"),a.WebRTCPlugin.plugin.id=a.WebRTCPlugin.pluginInfo.pluginId,isIE?(a.WebRTCPlugin.plugin.width="1px",a.WebRTCPlugin.plugin.height="1px"):(a.WebRTCPlugin.plugin.width="0px",a.WebRTCPlugin.plugin.height="0px"),a.WebRTCPlugin.plugin.type=a.WebRTCPlugin.pluginInfo.type,a.WebRTCPlugin.plugin.innerHTML=' '+(a.options.getAllCams?'':"")+'',document.body.appendChild(a.WebRTCPlugin.plugin);a.WebRTCPlugin.pluginState=a.WebRTCPlugin.PLUGIN_STATES.INJECTED}},a.WebRTCPlugin.isPluginInstalled=function(e,t,n,r,i){if(isIE){try{new ActiveXObject(e+"."+t)}catch(e){return void i()}r()}else{for(var o=navigator.mimeTypes,a=0;a=0)return void r();i()}},a.WebRTCPlugin.defineWebRTCInterface=function(){if(a.WebRTCPlugin.pluginState!==a.WebRTCPlugin.PLUGIN_STATES.READY){a.WebRTCPlugin.pluginState=a.WebRTCPlugin.PLUGIN_STATES.INITIALIZING,a.isDefined=function(e){return null!==e&&void 0!==e},createIceServer=function(e,t,n){var r=null,i=e.split(":");return 0===i[0].indexOf("stun")?r={url:e,hasCredentials:!1}:0===i[0].indexOf("turn")&&(r={url:e,hasCredentials:!0,credential:n,username:t}),r},createIceServers=function(e,t,n){for(var r=[],i=0;i1)return r&&(e.iceServers=r),a.WebRTCPlugin.plugin.PeerConnection(e);var o=t&&t.mandatory?t.mandatory:null,s=t&&t.optional?t.optional:null;return a.WebRTCPlugin.plugin.PeerConnection(a.WebRTCPlugin.pageId,r,o,s)},MediaStreamTrack=function(){},MediaStreamTrack.getSources=function(e){a.WebRTCPlugin.callWhenPluginReady(function(){a.WebRTCPlugin.plugin.GetSources(e)})};var e=function(e){if("object"!=typeof e||e.mandatory||e.optional)return e;var t={};return Object.keys(e).forEach(function(n){if("require"!==n&&"advanced"!==n&&"mediaSource"!==n){var r="object"==typeof e[n]?e[n]:{ideal:e[n]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var i=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==r.ideal){t.optional=t.optional||[];var o={};"number"==typeof r.ideal?(o[i("min",n)]=r.ideal,t.optional.push(o),(o={})[i("max",n)]=r.ideal,t.optional.push(o)):(o[i("",n)]=r.ideal,t.optional.push(o))}void 0!==r.exact&&"number"!=typeof r.exact?(t.mandatory=t.mandatory||{},t.mandatory[i("",n)]=r.exact):["min","max"].forEach(function(e){void 0!==r[e]&&(t.mandatory=t.mandatory||{},t.mandatory[i(e,n)]=r[e])})}}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t};getUserMedia=function(t,n,r){var i={};i.audio=!!t.audio&&e(t.audio),i.video=!!t.video&&e(t.video),a.WebRTCPlugin.callWhenPluginReady(function(){a.WebRTCPlugin.plugin.getUserMedia(i,n,r)})},window.navigator.getUserMedia=getUserMedia,navigator.mediaDevices||"undefined"==typeof Promise||(requestUserMedia=function(e){return new Promise(function(t,n){getUserMedia(e,t,n)})},navigator.mediaDevices={getUserMedia:requestUserMedia,enumerateDevices:function(){return new Promise(function(e){var t={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(n){e(n.map(function(e){return{label:e.label,kind:t[e.kind],id:e.id,deviceId:e.id,groupId:""}}))})})}}),attachMediaStream=function(e,t){if(e&&e.parentNode){var n;null===t?n="":(void 0!==t.enableSoundTracks&&t.enableSoundTracks(!0),n=t.id);var r=0===e.id.length?Math.random().toString(36).slice(2):e.id,i=e.nodeName.toLowerCase();if("object"!==i){var o;switch(i){case"audio":o=a.WebRTCPlugin.TAGS.AUDIO;break;case"video":o=a.WebRTCPlugin.TAGS.VIDEO;break;default:o=a.WebRTCPlugin.TAGS.NONE}var s=document.createDocumentFragment(),c=document.createElement("div"),u="";for(e.className?u='class="'+e.className+'" ':e.attributes&&e.attributes.class&&(u='class="'+e.attributes.class.value+'" '),c.innerHTML=' ';c.firstChild;)s.appendChild(c.firstChild);var l="",d="";e.clientWidth||e.clientHeight?(d=e.clientWidth,l=e.clientHeight):(e.width||e.height)&&(d=e.width,l=e.height),e.parentNode.insertBefore(s,e),(s=document.getElementById(r)).width=d,s.height=l,e.parentNode.removeChild(e)}else{for(var p=e.children,h=0;h!==p.length;++h)if("streamId"===p[h].name){p[h].value=n;break}e.setStreamId(n)}var f=document.getElementById(r);return a.forwardEventHandlers(f,e,Object.getPrototypeOf(e)),f}},reattachMediaStream=function(e,t){for(var n=null,r=t.children,i=0;i!==r.length;++i)if("streamId"===r[i].name){a.WebRTCPlugin.WaitForPluginReady(),n=a.WebRTCPlugin.plugin.getStreamWithId(a.WebRTCPlugin.pageId,r[i].value);break}if(null!==n)return attachMediaStream(e,n);o.log("Could not find the stream associated with this element")},window.attachMediaStream=attachMediaStream,window.reattachMediaStream=reattachMediaStream,window.getUserMedia=getUserMedia,a.attachMediaStream=attachMediaStream,a.reattachMediaStream=reattachMediaStream,a.getUserMedia=getUserMedia,a.forwardEventHandlers=function(e,t,n){properties=Object.getOwnPropertyNames(n);for(var r in properties)r&&(propName=properties[r],"function"==typeof propName.slice&&"on"===propName.slice(0,2)&&"function"==typeof t[propName]&&a.addEvent(e,propName.slice(2),t[propName]));var i=Object.getPrototypeOf(n);i&&a.forwardEventHandlers(e,t,i)},RTCIceCandidate=function(e){return e.sdpMid||(e.sdpMid=""),a.WebRTCPlugin.WaitForPluginReady(),a.WebRTCPlugin.plugin.ConstructIceCandidate(e.sdpMid,e.sdpMLineIndex,e.candidate)},a.addEvent(document,"readystatechange",a.WebRTCPlugin.injectPlugin),a.WebRTCPlugin.injectPlugin()}else o.error("AdapterJS - WebRTC interface has already been defined")},a.WebRTCPlugin.pluginNeededButNotInstalledCb=a.WebRTCPlugin.pluginNeededButNotInstalledCb||function(){a.addEvent(document,"readystatechange",a.WebRTCPlugin.pluginNeededButNotInstalledCbPriv),a.WebRTCPlugin.pluginNeededButNotInstalledCbPriv()},a.WebRTCPlugin.pluginNeededButNotInstalledCbPriv=function(){if(!a.options.hidePluginInstallPrompt){var e=a.WebRTCPlugin.pluginInfo.downloadLink;if(e){var t;t=a.WebRTCPlugin.pluginInfo.portalLink?'This website requires you to install the '+a.WebRTCPlugin.pluginInfo.companyName+" WebRTC Plugin to work on this browser.":a.TEXT.PLUGIN.REQUIRE_INSTALLATION,a.renderNotificationBar(t,a.TEXT.PLUGIN.BUTTON,e)}else a.renderNotificationBar(a.TEXT.PLUGIN.NOT_SUPPORTED)}},a.WebRTCPlugin.isPluginInstalled(a.WebRTCPlugin.pluginInfo.prefix,a.WebRTCPlugin.pluginInfo.plugName,a.WebRTCPlugin.pluginInfo.type,a.WebRTCPlugin.defineWebRTCInterface,a.WebRTCPlugin.pluginNeededButNotInstalledCb)):(s=function(){return(function e(t,n,i){function o(s,c){if(!n[s]){if(!t[s]){if(!c&&"function"==typeof r&&r)return r(s,!0);if(a)return a(s,!0);var u=new Error("Cannot find module '"+s+"'");throw u.code="MODULE_NOT_FOUND",u}var l=n[s]={exports:{}};t[s][0].call(l.exports,function(e){return o(t[s][1][e]||e)},l,l.exports,e,t,n,i)}return n[s].exports}for(var a="function"==typeof r&&r,s=0;s-1?(n.attribute=e.substr(t+1,r-t-1),n.value=e.substr(r+1)):n.attribute=e.substr(t+1),n},r.getDtlsParameters=function(e,t){var n=r.splitLines(e),i=(n=n.concat(r.splitLines(t))).filter(function(e){return 0===e.indexOf("a=fingerprint:")})[0].substr(14);return{role:"auto",fingerprints:[{algorithm:i.split(" ")[0],value:i.split(" ")[1]}]}},r.writeDtlsParameters=function(e,t){var n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(function(e){n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},r.getIceParameters=function(e,t){var n=r.splitLines(e);return{usernameFragment:(n=n.concat(r.splitLines(t))).filter(function(e){return 0===e.indexOf("a=ice-ufrag:")})[0].substr(12),password:n.filter(function(e){return 0===e.indexOf("a=ice-pwd:")})[0].substr(10)}},r.writeIceParameters=function(e){return"a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n"},r.parseRtpParameters=function(e){for(var t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},n=r.splitLines(e)[0].split(" "),i=3;i0?"9":"0",n+=" UDP/TLS/RTP/SAVPF ",n+=t.codecs.map(function(e){return void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType}).join(" ")+"\r\n",n+="c=IN IP4 0.0.0.0\r\n",n+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach(function(e){n+=r.writeRtpMap(e),n+=r.writeFmtp(e),n+=r.writeRtcpFb(e)}),n+="a=rtcp-mux\r\n"},r.parseRtpEncodingParameters=function(e){var t,n=[],i=r.parseRtpParameters(e),o=-1!==i.fecMechanisms.indexOf("RED"),a=-1!==i.fecMechanisms.indexOf("ULPFEC"),s=r.matchPrefix(e,"a=ssrc:").map(function(e){return r.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute}),c=s.length>0&&s[0].ssrc,u=r.matchPrefix(e,"a=ssrc-group:FID").map(function(e){var t=e.split(" ");return t.shift(),t.map(function(e){return parseInt(e,10)})});u.length>0&&u[0].length>1&&u[0][0]===c&&(t=u[0][1]),i.codecs.forEach(function(e){if("RTX"===e.name.toUpperCase()&&e.parameters.apt){var r={ssrc:c,codecPayloadType:parseInt(e.parameters.apt,10),rtx:{payloadType:e.payloadType,ssrc:t}};n.push(r),o&&((r=JSON.parse(JSON.stringify(r))).fec={ssrc:t,mechanism:a?"red+ulpfec":"red"},n.push(r))}}),0===n.length&&c&&n.push({ssrc:c});var l=r.matchPrefix(e,"b=");return l.length&&(0===l[0].indexOf("b=TIAS:")?l=parseInt(l[0].substr(7),10):0===l[0].indexOf("b=AS:")&&(l=parseInt(l[0].substr(5),10)),n.forEach(function(e){e.maxBitrate=l})),n},r.writeSessionBoilerplate=function(){return"v=0\r\no=thisisadapterortc 8169639915646943137 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},r.writeMediaSection=function(e,t,n,i){var o=r.writeRtpDescription(e.kind,t);if(o+=r.writeIceParameters(e.iceGatherer.getLocalParameters()),o+=r.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===n?"actpass":"active"),o+="a=mid:"+e.mid+"\r\n",e.rtpSender&&e.rtpReceiver?o+="a=sendrecv\r\n":e.rtpSender?o+="a=sendonly\r\n":e.rtpReceiver?o+="a=recvonly\r\n":o+="a=inactive\r\n",e.rtpSender){var a="msid:"+i.id+" "+e.rtpSender.track.id+"\r\n";o+="a="+a,o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+a}return o+"a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+r.localCName+"\r\n"},r.getDirection=function(e,t){for(var n=r.splitLines(e),i=0;i0&&"function"==typeof e)return i(e,t);var a=function(e){var t={};return e.result().forEach(function(e){var n={id:e.id,timestamp:e.timestamp,type:e.type};e.names().forEach(function(t){n[t]=e.stat(t)}),t[n.id]=n}),t},s=function(e,t){var n=new Map(Object.keys(e).map(function(t){return[t,e[t]]}));return t=t||e,Object.keys(t).forEach(function(e){n[e]=t[e]}),n};if(arguments.length>=2){var c=function(e){o[1](s(a(e)))};return i.apply(this,[c,arguments[0]])}return new Promise(function(t,n){1===o.length&&"object"==typeof e?i.apply(r,[function(e){t(s(a(e)))},n]):i.apply(r,[function(e){t(s(a(e),e.result()))},n])}).then(t,n)},n},window.RTCPeerConnection.prototype=webkitRTCPeerConnection.prototype,webkitRTCPeerConnection.generateCertificate&&Object.defineProperty(window.RTCPeerConnection,"generateCertificate",{get:function(){return webkitRTCPeerConnection.generateCertificate}}),["createOffer","createAnswer"].forEach(function(e){var t=webkitRTCPeerConnection.prototype[e];webkitRTCPeerConnection.prototype[e]=function(){var e=this;if(arguments.length<1||1===arguments.length&&"object"==typeof arguments[0]){var n=1===arguments.length?arguments[0]:void 0;return new Promise(function(r,i){t.apply(e,[r,i,n])})}return t.apply(this,arguments)}}),i.version<51&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(e){var t=webkitRTCPeerConnection.prototype[e];webkitRTCPeerConnection.prototype[e]=function(){var e=arguments,n=this,r=new Promise(function(r,i){t.apply(n,[e[0],r,i])});return e.length<2?r:r.then(function(){e[1].apply(null,[])},function(t){e.length>=3&&e[2].apply(null,[t])})}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(e){var t=webkitRTCPeerConnection.prototype[e];webkitRTCPeerConnection.prototype[e]=function(){return arguments[0]=new("addIceCandidate"===e?RTCIceCandidate:RTCSessionDescription)(arguments[0]),t.apply(this,arguments)}});var e=RTCPeerConnection.prototype.addIceCandidate;RTCPeerConnection.prototype.addIceCandidate=function(){return null===arguments[0]?Promise.resolve():e.apply(this,arguments)}}};t.exports={shimMediaStream:o.shimMediaStream,shimOnTrack:o.shimOnTrack,shimSourceObject:o.shimSourceObject,shimPeerConnection:o.shimPeerConnection,shimGetUserMedia:e("./getusermedia")}},{"../utils.js":10,"./getusermedia":4}],4:[function(e,t,n){"use strict";var r=e("../utils.js").log;t.exports=function(){var e=function(e){if("object"!=typeof e||e.mandatory||e.optional)return e;var t={};return Object.keys(e).forEach(function(n){if("require"!==n&&"advanced"!==n&&"mediaSource"!==n){var r="object"==typeof e[n]?e[n]:{ideal:e[n]};void 0!==r.exact&&"number"==typeof r.exact&&(r.min=r.max=r.exact);var i=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==r.ideal){t.optional=t.optional||[];var o={};"number"==typeof r.ideal?(o[i("min",n)]=r.ideal,t.optional.push(o),(o={})[i("max",n)]=r.ideal,t.optional.push(o)):(o[i("",n)]=r.ideal,t.optional.push(o))}void 0!==r.exact&&"number"!=typeof r.exact?(t.mandatory=t.mandatory||{},t.mandatory[i("",n)]=r.exact):["min","max"].forEach(function(e){void 0!==r[e]&&(t.mandatory=t.mandatory||{},t.mandatory[i(e,n)]=r[e])})}}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},t=function(t,n){if((t=JSON.parse(JSON.stringify(t)))&&t.audio&&(t.audio=e(t.audio)),t&&"object"==typeof t.video){var i=t.video.facingMode;if((i=i&&("object"==typeof i?i:{ideal:i}))&&("user"===i.exact||"environment"===i.exact||"user"===i.ideal||"environment"===i.ideal)&&(!navigator.mediaDevices.getSupportedConstraints||!navigator.mediaDevices.getSupportedConstraints().facingMode)&&(delete t.video.facingMode,"environment"===i.exact||"environment"===i.ideal))return navigator.mediaDevices.enumerateDevices().then(function(o){var a=(o=o.filter(function(e){return"videoinput"===e.kind})).find(function(e){return-1!==e.label.toLowerCase().indexOf("back")})||o.length&&o[o.length-1];return a&&(t.video.deviceId=i.exact?{exact:a.deviceId}:{ideal:a.deviceId}),t.video=e(t.video),r("chrome: "+JSON.stringify(t)),n(t)});t.video=e(t.video)}return r("chrome: "+JSON.stringify(t)),n(t)},n=function(e){return{name:{PermissionDeniedError:"NotAllowedError",ConstraintNotSatisfiedError:"OverconstrainedError"}[e.name]||e.name,message:e.message,constraint:e.constraintName,toString:function(){return this.name+(this.message&&": ")+this.message}}};navigator.getUserMedia=function(e,r,i){t(e,function(e){navigator.webkitGetUserMedia(e,r,function(e){i(n(e))})})};var i=function(e){return new Promise(function(t,n){navigator.getUserMedia(e,t,n)})};if(navigator.mediaDevices||(navigator.mediaDevices={getUserMedia:i,enumerateDevices:function(){return new Promise(function(e){var t={audio:"audioinput",video:"videoinput"};return MediaStreamTrack.getSources(function(n){e(n.map(function(e){return{label:e.label,kind:t[e.kind],deviceId:e.id,groupId:""}}))})})}}),navigator.mediaDevices.getUserMedia){var o=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(e){return t(e,function(e){return o(e).catch(function(e){return Promise.reject(n(e))})})}}else navigator.mediaDevices.getUserMedia=function(e){return i(e)};void 0===navigator.mediaDevices.addEventListener&&(navigator.mediaDevices.addEventListener=function(){r("Dummy mediaDevices.addEventListener called.")}),void 0===navigator.mediaDevices.removeEventListener&&(navigator.mediaDevices.removeEventListener=function(){r("Dummy mediaDevices.removeEventListener called.")})}},{"../utils.js":10}],5:[function(e,t,n){"use strict";var r=e("sdp"),i=e("../utils").browserDetails,o={shimPeerConnection:function(){window.RTCIceGatherer&&(window.RTCIceCandidate||(window.RTCIceCandidate=function(e){return e}),window.RTCSessionDescription||(window.RTCSessionDescription=function(e){return e})),window.RTCPeerConnection=function(e){var t=this,n=document.createDocumentFragment();if(["addEventListener","removeEventListener","dispatchEvent"].forEach(function(e){t[e]=n[e].bind(n)}),this.onicecandidate=null,this.onaddstream=null,this.ontrack=null,this.onremovestream=null,this.onsignalingstatechange=null,this.oniceconnectionstatechange=null,this.onnegotiationneeded=null,this.ondatachannel=null,this.localStreams=[],this.remoteStreams=[],this.getLocalStreams=function(){return t.localStreams},this.getRemoteStreams=function(){return t.remoteStreams},this.localDescription=new RTCSessionDescription({type:"",sdp:""}),this.remoteDescription=new RTCSessionDescription({type:"",sdp:""}),this.signalingState="stable",this.iceConnectionState="new",this.iceGatheringState="new",this.iceOptions={gatherPolicy:"all",iceServers:[]},e&&e.iceTransportPolicy)switch(e.iceTransportPolicy){case"all":case"relay":this.iceOptions.gatherPolicy=e.iceTransportPolicy;break;case"none":throw new TypeError('iceTransportPolicy "none" not supported')}if(this.usingBundle=e&&"max-bundle"===e.bundlePolicy,e&&e.iceServers){var r=JSON.parse(JSON.stringify(e.iceServers));this.iceOptions.iceServers=r.filter(function(e){if(e&&e.urls){var t=e.urls;return"string"==typeof t&&(t=[t]),!!(t=t.filter(function(e){return 0===e.indexOf("turn:")&&-1!==e.indexOf("transport=udp")&&-1===e.indexOf("turn:[")||0===e.indexOf("stun:")&&i.version>=14393})[0])}return!1})}this.transceivers=[],this._localIceCandidatesBuffer=[]},window.RTCPeerConnection.prototype._emitBufferedCandidates=function(){var e=this,t=r.splitSections(e.localDescription.sdp);this._localIceCandidatesBuffer.forEach(function(n){if(n.candidate&&0!==Object.keys(n.candidate).length)-1===n.candidate.candidate.indexOf("typ endOfCandidates")&&(t[n.candidate.sdpMLineIndex+1]+="a="+n.candidate.candidate+"\r\n");else for(var r=1;r-1&&(this.localStreams.splice(t,1),this._maybeFireNegotiationNeeded())},window.RTCPeerConnection.prototype.getSenders=function(){return this.transceivers.filter(function(e){return!!e.rtpSender}).map(function(e){return e.rtpSender})},window.RTCPeerConnection.prototype.getReceivers=function(){return this.transceivers.filter(function(e){return!!e.rtpReceiver}).map(function(e){return e.rtpReceiver})},window.RTCPeerConnection.prototype._getCommonCapabilities=function(e,t){var n={codecs:[],headerExtensions:[],fecMechanisms:[]};return e.codecs.forEach(function(e){for(var r=0;r0;t.forEach(function(e,t){var a=i.transceivers[t],s=a.iceGatherer,c=a.iceTransport,u=a.dtlsTransport,l=a.localCapabilities,d=a.remoteCapabilities;if("0"!==e.split("\n",1)[0].split(" ",2)[1]&&!a.isDatachannel){var p=r.getIceParameters(e,n);if(o){var h=r.matchPrefix(e,"a=candidate:").map(function(e){return r.parseCandidate(e)}).filter(function(e){return"1"===e.component});h.length&&c.setRemoteCandidates(h)}var f=r.getDtlsParameters(e,n);o&&(f.role="server"),i.usingBundle&&0!==t||(c.start(s,p,o?"controlling":"controlled"),u.start(f));var m=i._getCommonCapabilities(l,d);i._transceive(a,m.codecs.length>0,!1)}})}switch(this.localDescription={type:e.type,sdp:e.sdp},e.type){case"offer":this._updateSignalingState("have-local-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+e.type+'"')}var a=arguments.length>1&&"function"==typeof arguments[1];if(a){var s=arguments[1];window.setTimeout(function(){s(),"new"===i.iceGatheringState&&(i.iceGatheringState="gathering"),i._emitBufferedCandidates()},0)}var c=Promise.resolve();return c.then(function(){a||("new"===i.iceGatheringState&&(i.iceGatheringState="gathering"),window.setTimeout(i._emitBufferedCandidates.bind(i),500))}),c},window.RTCPeerConnection.prototype.setRemoteDescription=function(e){var t=this,n=new MediaStream,i=[],o=r.splitSections(e.sdp),a=o.shift(),s=r.matchPrefix(a,"a=ice-lite").length>0;switch(this.usingBundle=r.matchPrefix(a,"a=group:BUNDLE ").length>0,o.forEach(function(o,c){var u=r.splitLines(o)[0].substr(2).split(" "),l=u[0],d="0"===u[1],p=r.getDirection(o,a),h=r.matchPrefix(o,"a=mid:");if(h=h.length?h[0].substr(6):r.generateIdentifier(),"application"!==l||"DTLS/SCTP"!==u[2]){var f,m,v,g,y,b,S,_,T,C,E,w,R=r.parseRtpParameters(o);d||(E=r.getIceParameters(o,a),(w=r.getDtlsParameters(o,a)).role="client"),_=r.parseRtpEncodingParameters(o);var k,I=r.matchPrefix(o,"a=ssrc:").map(function(e){return r.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute})[0];I&&(k=I.value);var P=r.matchPrefix(o,"a=end-of-candidates",a).length>0,A=r.matchPrefix(o,"a=candidate:").map(function(e){return r.parseCandidate(e)}).filter(function(e){return"1"===e.component});if("offer"!==e.type||d)"answer"!==e.type||d||(m=(f=t.transceivers[c]).iceGatherer,v=f.iceTransport,g=f.dtlsTransport,y=f.rtpSender,b=f.rtpReceiver,S=f.sendEncodingParameters,T=f.localCapabilities,t.transceivers[c].recvEncodingParameters=_,t.transceivers[c].remoteCapabilities=R,t.transceivers[c].cname=k,(s||P)&&A.length&&v.setRemoteCandidates(A),t.usingBundle&&0!==c||(v.start(m,E,"controlling"),g.start(w)),t._transceive(f,"sendrecv"===p||"recvonly"===p,"sendrecv"===p||"sendonly"===p),!b||"sendrecv"!==p&&"sendonly"!==p?delete f.rtpReceiver:(C=b.track,i.push([C,b]),n.addTrack(C)));else{var O=t.usingBundle&&c>0?{iceGatherer:t.transceivers[0].iceGatherer,iceTransport:t.transceivers[0].iceTransport,dtlsTransport:t.transceivers[0].dtlsTransport}:t._createIceAndDtlsTransports(h,c);if(P&&O.iceTransport.setRemoteCandidates(A),T=RTCRtpReceiver.getCapabilities(l),S=[{ssrc:1001*(2*c+2)}],C=(b=new RTCRtpReceiver(O.dtlsTransport,l)).track,i.push([C,b]),n.addTrack(C),t.localStreams.length>0&&t.localStreams[0].getTracks().length>=c){var D;"audio"===l?D=t.localStreams[0].getAudioTracks()[0]:"video"===l&&(D=t.localStreams[0].getVideoTracks()[0]),D&&(y=new RTCRtpSender(D,O.dtlsTransport))}t.transceivers[c]={iceGatherer:O.iceGatherer,iceTransport:O.iceTransport,dtlsTransport:O.dtlsTransport,localCapabilities:T,remoteCapabilities:R,rtpSender:y,rtpReceiver:b,kind:l,mid:h,cname:k,sendEncodingParameters:S,recvEncodingParameters:_},t._transceive(t.transceivers[c],!1,"sendrecv"===p||"sendonly"===p)}}else t.transceivers[c]={mid:h,isDatachannel:!0}}),this.remoteDescription={type:e.type,sdp:e.sdp},e.type){case"offer":this._updateSignalingState("have-remote-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+e.type+'"')}return n.getTracks().length&&(t.remoteStreams.push(n),window.setTimeout(function(){var e=new Event("addstream");e.stream=n,t.dispatchEvent(e),null!==t.onaddstream&&window.setTimeout(function(){t.onaddstream(e)},0),i.forEach(function(r){var i=r[0],o=r[1],a=new Event("track");a.track=i,a.receiver=o,a.streams=[n],t.dispatchEvent(e),null!==t.ontrack&&window.setTimeout(function(){t.ontrack(a)},0)})},0)),arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.close=function(){this.transceivers.forEach(function(e){e.iceTransport&&e.iceTransport.stop(),e.dtlsTransport&&e.dtlsTransport.stop(),e.rtpSender&&e.rtpSender.stop(),e.rtpReceiver&&e.rtpReceiver.stop()}),this._updateSignalingState("closed")},window.RTCPeerConnection.prototype._updateSignalingState=function(e){this.signalingState=e;var t=new Event("signalingstatechange");this.dispatchEvent(t),null!==this.onsignalingstatechange&&this.onsignalingstatechange(t)},window.RTCPeerConnection.prototype._maybeFireNegotiationNeeded=function(){var e=new Event("negotiationneeded");this.dispatchEvent(e),null!==this.onnegotiationneeded&&this.onnegotiationneeded(e)},window.RTCPeerConnection.prototype._updateConnectionState=function(){var e,t={new:0,closed:0,connecting:0,checking:0,connected:0,completed:0,failed:0};if(this.transceivers.forEach(function(e){t[e.iceTransport.state]++,t[e.dtlsTransport.state]++}),t.connected+=t.completed,e="new",t.failed>0?e="failed":t.connecting>0||t.checking>0?e="connecting":t.disconnected>0?e="disconnected":t.new>0?e="new":(t.connected>0||t.completed>0)&&(e="connected"),e!==this.iceConnectionState){this.iceConnectionState=e;var n=new Event("iceconnectionstatechange");this.dispatchEvent(n),null!==this.oniceconnectionstatechange&&this.oniceconnectionstatechange(n)}},window.RTCPeerConnection.prototype.createOffer=function(){var e=this;if(this._pendingOffer)throw new Error("createOffer called while there is a pending offer.");var t;1===arguments.length&&"function"!=typeof arguments[0]?t=arguments[0]:3===arguments.length&&(t=arguments[2]);var n=[],i=0,o=0;if(this.localStreams.length&&(i=this.localStreams[0].getAudioTracks().length,o=this.localStreams[0].getVideoTracks().length),t){if(t.mandatory||t.optional)throw new TypeError("Legacy mandatory/optional constraints not supported.");void 0!==t.offerToReceiveAudio&&(i=t.offerToReceiveAudio),void 0!==t.offerToReceiveVideo&&(o=t.offerToReceiveVideo)}for(this.localStreams.length&&this.localStreams[0].getTracks().forEach(function(e){n.push({kind:e.kind,track:e,wantReceive:"audio"===e.kind?i>0:o>0}),"audio"===e.kind?i--:"video"===e.kind&&o--});i>0||o>0;)i>0&&(n.push({kind:"audio",wantReceive:!0}),i--),o>0&&(n.push({kind:"video",wantReceive:!0}),o--);var a=r.writeSessionBoilerplate(),s=[];n.forEach(function(t,n){var i,o,a=t.track,c=t.kind,u=r.generateIdentifier(),l=e.usingBundle&&n>0?{iceGatherer:s[0].iceGatherer,iceTransport:s[0].iceTransport,dtlsTransport:s[0].dtlsTransport}:e._createIceAndDtlsTransports(u,n),d=RTCRtpSender.getCapabilities(c),p=[{ssrc:1001*(2*n+1)}];a&&(i=new RTCRtpSender(a,l.dtlsTransport)),t.wantReceive&&(o=new RTCRtpReceiver(l.dtlsTransport,c)),s[n]={iceGatherer:l.iceGatherer,iceTransport:l.iceTransport,dtlsTransport:l.dtlsTransport,localCapabilities:d,remoteCapabilities:null,rtpSender:i,rtpReceiver:o,kind:c,mid:u,sendEncodingParameters:p,recvEncodingParameters:null}}),this.usingBundle&&(a+="a=group:BUNDLE "+s.map(function(e){return e.mid}).join(" ")+"\r\n"),n.forEach(function(t,n){var i=s[n];a+=r.writeMediaSection(i,i.localCapabilities,"offer",e.localStreams[0])}),this._pendingOffer=s;var c=new RTCSessionDescription({type:"offer",sdp:a});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,c),Promise.resolve(c)},window.RTCPeerConnection.prototype.createAnswer=function(){var e=this,t=r.writeSessionBoilerplate();this.usingBundle&&(t+="a=group:BUNDLE "+this.transceivers.map(function(e){return e.mid}).join(" ")+"\r\n"),this.transceivers.forEach(function(n){if(n.isDatachannel)t+="m=application 0 DTLS/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\na=mid:"+n.mid+"\r\n";else{var i=e._getCommonCapabilities(n.localCapabilities,n.remoteCapabilities);t+=r.writeMediaSection(n,i,"answer",e.localStreams[0])}});var n=new RTCSessionDescription({type:"answer",sdp:t});return arguments.length&&"function"==typeof arguments[0]&&window.setTimeout(arguments[0],0,n),Promise.resolve(n)},window.RTCPeerConnection.prototype.addIceCandidate=function(e){if(null===e)this.transceivers.forEach(function(e){e.iceTransport.addRemoteCandidate({})});else{var t=e.sdpMLineIndex;if(e.sdpMid)for(var n=0;n0?r.parseCandidate(e.candidate):{};if("tcp"===o.protocol&&(0===o.port||9===o.port))return;if("1"!==o.component)return;"endOfCandidates"===o.type&&(o={}),i.iceTransport.addRemoteCandidate(o);var a=r.splitSections(this.remoteDescription.sdp);a[t+1]+=(o.type?e.candidate.trim():"a=end-of-candidates")+"\r\n",this.remoteDescription.sdp=a.join("")}}return arguments.length>1&&"function"==typeof arguments[1]&&window.setTimeout(arguments[1],0),Promise.resolve()},window.RTCPeerConnection.prototype.getStats=function(){var e=[];this.transceivers.forEach(function(t){["rtpSender","rtpReceiver","iceGatherer","iceTransport","dtlsTransport"].forEach(function(n){t[n]&&e.push(t[n].getStats())})});var t=arguments.length>1&&"function"==typeof arguments[1]&&arguments[1];return new Promise(function(n){var r=new Map;Promise.all(e).then(function(e){e.forEach(function(e){Object.keys(e).forEach(function(t){r.set(t,e[t]),r[t]=e[t]})}),t&&window.setTimeout(t,0,r),n(r)})})}}};t.exports={shimPeerConnection:o.shimPeerConnection,shimGetUserMedia:e("./getusermedia")}},{"../utils":10,"./getusermedia":6,sdp:1}],6:[function(e,t,n){"use strict";t.exports=function(){var e=navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);navigator.mediaDevices.getUserMedia=function(t){return e(t).catch(function(e){return Promise.reject({name:{PermissionDeniedError:"NotAllowedError"}[(t=e).name]||t.name,message:t.message,constraint:t.constraint,toString:function(){return this.name}});var t})}}},{}],7:[function(e,t,n){"use strict";var r=e("../utils").browserDetails,i={shimOnTrack:function(){"object"!=typeof window||!window.RTCPeerConnection||"ontrack"in window.RTCPeerConnection.prototype||Object.defineProperty(window.RTCPeerConnection.prototype,"ontrack",{get:function(){return this._ontrack},set:function(e){this._ontrack&&(this.removeEventListener("track",this._ontrack),this.removeEventListener("addstream",this._ontrackpoly)),this.addEventListener("track",this._ontrack=e),this.addEventListener("addstream",this._ontrackpoly=function(e){e.stream.getTracks().forEach(function(t){var n=new Event("track");n.track=t,n.receiver={track:t},n.streams=[e.stream],this.dispatchEvent(n)}.bind(this))}.bind(this))}})},shimSourceObject:function(){"object"==typeof window&&(!window.HTMLMediaElement||"srcObject"in window.HTMLMediaElement.prototype||Object.defineProperty(window.HTMLMediaElement.prototype,"srcObject",{get:function(){return this.mozSrcObject},set:function(e){this.mozSrcObject=e}}))},shimPeerConnection:function(){if("object"==typeof window&&(window.RTCPeerConnection||window.mozRTCPeerConnection)){window.RTCPeerConnection||(window.RTCPeerConnection=function(e,t){if(r.version<38&&e&&e.iceServers){for(var n=[],i=0;i=n&&parseInt(r[n],10)},detectBrowser:function(){var e={};if(e.browser=null,e.version=null,"undefined"==typeof window||!window.navigator)return e.browser="Not a browser.",e;if(navigator.mozGetUserMedia)e.browser="firefox",e.version=this.extractVersion(navigator.userAgent,/Firefox\/([0-9]+)\./,1);else if(navigator.webkitGetUserMedia)if(window.webkitRTCPeerConnection)e.browser="chrome",e.version=this.extractVersion(navigator.userAgent,/Chrom(e|ium)\/([0-9]+)\./,2);else{if(!navigator.userAgent.match(/Version\/(\d+).(\d+)/))return e.browser="Unsupported webkit-based browser with GUM support but no WebRTC support.",e;e.browser="safari",e.version=this.extractVersion(navigator.userAgent,/AppleWebKit\/([0-9]+)\./,1)}else{if(!navigator.mediaDevices||!navigator.userAgent.match(/Edge\/(\d+).(\d+)$/))return e.browser="Not a supported browser.",e;e.browser="edge",e.version=this.extractVersion(navigator.userAgent,/Edge\/(\d+).(\d+)$/,2)}return e}};t.exports={log:i.log,disableLog:i.disableLog,browserDetails:i.detectBrowser(),extractVersion:i.extractVersion}},{}]},{},[2])(2)},e.exports=s(),a.parseWebrtcDetectedBrowser(),navigator.mozGetUserMedia?(MediaStreamTrack.getSources=function(e){setTimeout(function(){e([{kind:"audio",id:"default",label:"",facing:""},{kind:"video",id:"default",label:"",facing:""}])},0)},attachMediaStream=function(e,t){return e.srcObject=t,e},reattachMediaStream=function(e,t){return e.srcObject=t.srcObject,e},createIceServer=function(e,t,n){o.warn("createIceServer is deprecated. It should be replaced with an application level implementation.");var r=null,i=e.split(":");if(0===i[0].indexOf("stun"))r={urls:[e]};else if(0===i[0].indexOf("turn"))if(webrtcDetectedVersion<27){var a=e.split("?");1!==a.length&&0!==a[1].indexOf("transport=udp")||(r={urls:[a[0]],credential:n,username:t})}else r={urls:[e],credential:n,username:t};return r},createIceServers=function(e,t,n){o.warn("createIceServers is deprecated. It should be replaced with an application level implementation.");var r=[];for(i=0;i=43?e.srcObject=t:void 0!==e.src?e.src=URL.createObjectURL(t):o.error("Error attaching stream to element."),e},reattachMediaStream=function(e,t){return webrtcDetectedVersion>=43?e.srcObject=t.srcObject:e.src=t.src,e},createIceServer=function(e,t,n){o.warn("createIceServer is deprecated. It should be replaced with an application level implementation.");var r=null,i=e.split(":");return 0===i[0].indexOf("stun")?r={url:e}:0===i[0].indexOf("turn")&&(r={url:e,credential:n,username:t}),r},createIceServers=function(e,t,n){o.warn("createIceServers is deprecated. It should be replaced with an application level implementation.");var r=[];if(webrtcDetectedVersion>=34)r={urls:e,credential:n,username:t};else for(i=0;i38?e.srcObject=t:void 0!==e.src&&(e.src=URL.createObjectURL(t))}),attachMediaStream=function(e,t){return"chrome"!==webrtcDetectedBrowser&&"opera"!==webrtcDetectedBrowser||t?attachMediaStream_base(e,t):e.src="",e},reattachMediaStream_base=reattachMediaStream,reattachMediaStream=function(e,t){return reattachMediaStream_base(e,t),e},window.attachMediaStream=attachMediaStream,window.reattachMediaStream=reattachMediaStream,window.getUserMedia=function(e,t,n){navigator.getUserMedia(e,t,n)},a.attachMediaStream=attachMediaStream,a.reattachMediaStream=reattachMediaStream,a.getUserMedia=getUserMedia,"undefined"==typeof Promise&&(requestUserMedia=null),a.maybeThroughWebRTCReady());var s;!(function(){"use strict";var e=null;a.TEXT.EXTENSION={REQUIRE_INSTALLATION_FF:"To enable screensharing you need to install the Skylink WebRTC tools Firefox Add-on.",REQUIRE_INSTALLATION_CHROME:"To enable screensharing you need to install the Skylink WebRTC tools Chrome Extension.",REQUIRE_REFRESH:"Please refresh this page after the Skylink WebRTC tools extension has been installed.",BUTTON_FF:"Install Now",BUTTON_CHROME:"Go to Chrome Web Store"};var t=function(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t};if(window.navigator.mozGetUserMedia?(e=window.navigator.getUserMedia,navigator.getUserMedia=function(n,r,i){if(n&&n.video&&n.video.mediaSource){if("screen"!==n.video.mediaSource&&"window"!==n.video.mediaSource)return void i(new Error('GetUserMedia: Only "screen" and "window" are supported as mediaSource constraints'));var o=t(n);o.video.mozMediaSource=o.video.mediaSource;var s=setInterval(function(){"complete"===document.readyState&&(clearInterval(s),e(o,r,function(e){["PermissionDeniedError","SecurityError"].indexOf(e.name)>-1&&"https:"===window.parent.location.protocol?a.renderNotificationBar(a.TEXT.EXTENSION.REQUIRE_INSTALLATION_FF,a.TEXT.EXTENSION.BUTTON_FF,"https://addons.mozilla.org/en-US/firefox/addon/skylink-webrtc-tools/",!0,!0):i(e)}))},1)}else e(n,r,i)},a.getUserMedia=window.getUserMedia=navigator.getUserMedia):window.navigator.webkitGetUserMedia&&"safari"!==window.webrtcDetectedBrowser?(e=window.navigator.getUserMedia,navigator.getUserMedia=function(n,i,o){if(n&&n.video&&n.video.mediaSource){if("chrome"!==window.webrtcDetectedBrowser)return void o(new Error("Current browser does not support screensharing"));var s=t(n),c=function(t,n){t?o("permission-denied"===t?new Error("Permission denied for screen retrieval"):new Error("Failed retrieving selected screen")):(s.video.mandatory=s.video.mandatory||{},s.video.mandatory.chromeMediaSource="desktop",s.video.mandatory.maxWidth=window.screen.width>1920?window.screen.width:1920,s.video.mandatory.maxHeight=window.screen.height>1080?window.screen.height:1080,n&&(s.video.mandatory.chromeMediaSourceId=n),delete s.video.mediaSource,e(s,i,o))},u=function e(t){t.data&&(t.data.chromeMediaSourceId&&("PermissionDeniedError"===t.data.chromeMediaSourceId?c("permission-denied"):c(null,t.data.chromeMediaSourceId)),t.data.chromeExtensionStatus&&("not-installed"===t.data.chromeExtensionStatus?a.renderNotificationBar(a.TEXT.EXTENSION.REQUIRE_INSTALLATION_CHROME,a.TEXT.EXTENSION.BUTTON_CHROME,t.data.data,!0,!0):c(t.data.chromeExtensionStatus,null)),window.removeEventListener("message",e))};window.addEventListener("message",u),r({captureSourceId:!0})}else e(n,i,o)},a.getUserMedia=window.getUserMedia=navigator.getUserMedia,navigator.mediaDevices.getUserMedia=function(e){return new Promise(function(t,n){window.getUserMedia(e,t,n)})}):navigator.mediaDevices&&navigator.userAgent.match(/Edge\/(\d+).(\d+)$/)?o.warn("Edge does not support screensharing feature in getUserMedia"):(e=window.navigator.getUserMedia,navigator.getUserMedia=function(n,r,i){if(n&&n.video&&n.video.mediaSource){var o=t(n);a.WebRTCPlugin.callWhenPluginReady(function(){a.WebRTCPlugin.plugin.HasScreensharingFeature&&a.WebRTCPlugin.plugin.isScreensharingAvailable?(o.video.optional=o.video.optional||[],o.video.optional.push({sourceId:a.WebRTCPlugin.plugin.screensharingKey||"Screensharing"}),delete o.video.mediaSource,e(o,r,i)):i(new Error("Your version of the WebRTC plugin does not support screensharing"))})}else e(n,r,i)},a.getUserMedia=getUserMedia=window.getUserMedia=navigator.getUserMedia,navigator.mediaDevices&&"undefined"!=typeof Promise&&(navigator.mediaDevices.getUserMedia="undefined"==typeof requestUserMedia?void 0:requestUserMedia)),"chrome"===window.webrtcDetectedBrowser){var n=document.createElement("iframe");n.onload=function(){n.isLoaded=!0},n.src="https://cdn.temasys.com.sg/skylink/extensions/detectRTC.html",n.style.display="none",(document.body||document.documentElement).appendChild(n);var r=function(e){e=e||{},n.isLoaded?n.contentWindow.postMessage(e,"*"):setTimeout(function(){n.contentWindow.postMessage(e,"*")},100)}}else"opera"===window.webrtcDetectedBrowser&&o.warn("Opera does not support screensharing feature in getUserMedia")})()}).call(t,"modules/RTC/adapter.screenshare.js")},function(e,t){function n(){var e=arguments[0],t=arguments[1],n=Array.prototype.slice.call(arguments,2);if(!(i[t]: ").apply(c,n)}}function r(e,t,r,o){this.id=t,this.format=o,this.transports=r,this.transports||(this.transports=[]),this.level=i[e];for(var a=Object.keys(i),s=0;s=0&&(e._idleTimeoutId=setTimeout(function(){e._onTimeout&&e._onTimeout()},t))},n(90),t.setImmediate=setImmediate,t.clearImmediate=clearImmediate},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:1200;(function(t,n){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")})(this),this.message={timestamp:"",sentBytes:1e4,padding:""};var n=r(t-o.getCurrent().toString().length-JSON.stringify(this.message).length);this.message.padding=n}return i(e,[{key:"make",value:function(e){return this.message.timestamp=o.getCurrent(),this.message.sentBytes=e,JSON.stringify(this.message)}}]),e})();t.MessageMaker=a,t.randomAsciiString=r},function(e,t,n){"use strict";function r(e,t,n){this.stream=e,this.intervalId=null,this.intervalMilis=t,this.audioLevel=0,this.callback=n}t.a=r;var i=n(1);window.AudioContext=window.AudioContext||window.webkitAudioContext;var o=null;window.AudioContext&&(o=new AudioContext).suspend&&o.suspend(),r.prototype.start=function(){if(r.isLocalStatsSupported()){o.resume();var e=o.createAnalyser();e.smoothingTimeConstant=.8,e.fftSize=2048,o.createMediaStreamSource(this.stream).connect(e);var t=this;this.intervalId=setInterval(function(){var n=new Uint8Array(e.frequencyBinCount);e.getByteTimeDomainData(n);var r=(function(e){for(var t=0,n=e.length,r=0;r.2?t-.2:n<-.4?t+.4:e).toFixed(3))})(r,t.audioLevel),t.callback(t.audioLevel))},this.intervalMilis)}},r.prototype.stop=function(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)},r.isLocalStatsSupported=function(){return Boolean(o&&!i.b.isTemasysPluginUsed())}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return i}),n.d(t,"c",function(){return o}),n.d(t,"d",function(){return a});var r="statistics.audioLevel",i="statistics.before_disposed",o="statistics.byte_sent_stats",a="statistics.connectionstats"},function(e,t,n){var r=n(96),i={loadScript:function(e,t,n,i,o,a){var s=document,c=s.createElement("script"),u=s.getElementsByTagName("script")[0];if(c.async=t,i){var l=r();if(l){var d=l.src,p=d.substring(0,d.lastIndexOf("/")+1);d&&p&&(e=p+e)}}o&&(c.onload=o),a&&(c.onerror=a),c.src=e,n?u.parentNode.insertBefore(c,u):u.parentNode.appendChild(c)}};e.exports=i},function(e,t,n){"use strict";(function(e){var r=n(0),i=(n.n(r),n(2)),o=(n.n(i),n(101)),a=(n.n(o),n(19)),s=n.n(a),c=n(47),u=n(26),l=n(1),d=n(102),p=n(105),h=n(113),f=n(114),m=n(115),v=n(116),g=n(10),y=n(117),b=(function(){function e(e,t){for(var n=0;n1&&void 0!==arguments[1]?arguments[1]:"/http-bind";e&&(t+=(-1===t.indexOf("?")?"?":"&")+"token="+e);var n=new i.Strophe.Connection(t);return n.maxRetries=3,n})(n,e.bosh),r.caps=new y.a(r.connection,r.options.clientNode),r.initFeaturesList(),$(window).on("beforeunload unload",r.disconnect.bind(r)),r}return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)})(t,g.a),b(t,[{key:"initFeaturesList",value:function(){this.caps.addFeature("urn:xmpp:jingle:1"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:1"),this.caps.addFeature("urn:xmpp:jingle:transports:ice-udp:1"),this.caps.addFeature("urn:xmpp:jingle:apps:dtls:0"),this.caps.addFeature("urn:xmpp:jingle:transports:dtls-sctp:1"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:audio"),this.caps.addFeature("urn:xmpp:jingle:apps:rtp:video"),!this.options.disableRtx&&l.b.supportsRtx()&&this.caps.addFeature("urn:ietf:rfc:4588"),this.caps.addFeature("urn:ietf:rfc:5761"),this.caps.addFeature("urn:ietf:rfc:5888"),l.b.isChrome()&&!1!==this.options.enableLipSync&&(S.info("Lip-sync enabled !"),this.caps.addFeature("http://jitsi.org/meet/lipsync")),this.connection.rayo&&this.caps.addFeature("urn:xmpp:rayo:client:1")}},{key:"getConnection",value:function(){return this.connection}},{key:"connectionHandler",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=this,n=arguments[1],r=arguments[2],o=window.performance.now(),a=i.Strophe.getStatusString(n).toLowerCase();if(this.connectionTimes[a]=o,S.log("(TIME) Strophe "+a+(r?"["+r+"]":"")+":\t",o),n===i.Strophe.Status.CONNECTED||n===i.Strophe.Status.ATTACHED){(this.options.useStunTurn||this.options.p2p&&this.options.p2p.useStunTurn)&&this.connection.jingle.getStunAndTurnCredentials(),S.info("My Jabber ID: "+this.connection.jid);var s=this.connection.domain;this.connection.ping.hasPingSupport(s,function(e){e?t.connection.ping.startInterval(s):S.warn("Ping NOT supported by "+s)}),e.password&&(this.authenticatedUser=!0),this.connection&&this.connection.connected&&i.Strophe.getResourceFromJid(this.connection.jid)&&this.eventEmitter.emit(u.CONNECTION_ESTABLISHED,i.Strophe.getResourceFromJid(this.connection.jid))}else if(n===i.Strophe.Status.CONNFAIL)"x-strophe-bad-non-anon-jid"===r?this.anonymousConnectionFailed=!0:this.connectionFailed=!0,this.lastErrorMsg=r,"giving-up"===r&&this.eventEmitter.emit(u.CONNECTION_FAILED,c.OTHER_ERROR,r);else if(n===i.Strophe.Status.DISCONNECTED){this.connection.ping.stopInterval();var l=this.disconnectInProgress,d=r||this.lastErrorMsg;if(this.disconnectInProgress=!1,this.anonymousConnectionFailed)this.eventEmitter.emit(u.CONNECTION_FAILED,c.PASSWORD_REQUIRED);else if(this.connectionFailed)this.eventEmitter.emit(u.CONNECTION_FAILED,c.OTHER_ERROR,d,void 0,this._getConnectionFailedReasonDetails());else if(l)this.eventEmitter.emit(u.CONNECTION_DISCONNECTED,d);else{S.error("XMPP connection dropped!");var p=i.Strophe.getLastErrorStatus();p>=500&&p<600?this.eventEmitter.emit(u.CONNECTION_FAILED,c.SERVER_ERROR,d||"server-error"):this.eventEmitter.emit(u.CONNECTION_FAILED,c.CONNECTION_DROPPED_ERROR,d||"connection-dropped-error")}}else n===i.Strophe.Status.AUTHFAIL&&this.eventEmitter.emit(u.CONNECTION_FAILED,c.PASSWORD_REQUIRED,r,e)}},{key:"_connect",value:function(e,t){this.anonymousConnectionFailed=!1,this.connectionFailed=!1,this.lastErrorMsg=void 0,this.connection.connect(e,t,this.connectionHandler.bind(this,{jid:e,password:t}))}},{key:"attach",value:function(e){var t=this.connectionTimes.attaching=window.performance.now();S.log("(TIME) Strophe Attaching\t:"+t),this.connection.attach(e.jid,e.sid,parseInt(e.rid,10)+1,this.connectionHandler.bind(this,{jid:e.jid,password:e.password}))}},{key:"connect",value:function(e,t){if(!e){var n=this.options.hosts,r=n.anonymousdomain,i=n.domain,o=r||i,a=window.location;if(r){var s=a&&a.search;(s&&-1!==s.indexOf("login=true")||this.token)&&(o=i)}e=o||a&&a.hostname}return this._connect(e,t)}},{key:"createRoom",value:function(e,t){var n=i.Strophe.getNodeFromJid(this.connection.jid),r=e+"@"+this.options.hosts.muc+"/",o=t.useNicks&&t.nick?t.nick:null;return o?n=t.nick:this.authenticatedUser||(n=n.substr(0,8)),(this.authenticatedUser||null!==o)&&(n+="-"+s.a.randomHexString(6)),r+=n,this.connection.emuc.createRoom(r,null,t)}},{key:"getJingleLog",value:function(){var e=this.connection.jingle;return e?e.getLog():{}}},{key:"getXmppLog",value:function(){return(this.connection.logger||{}).log||null}},{key:"dial",value:function(){var e;(e=this.connection.rayo).dial.apply(e,arguments)}},{key:"setMute",value:function(e,t){this.connection.moderate.setMute(e,t)}},{key:"eject",value:function(e){this.connection.moderate.eject(e)}},{key:"getSessions",value:function(){return this.connection.jingle.sessions}},{key:"disconnect",value:function(e){if(!this.disconnectInProgress&&this.connection&&this.connection.connected){if(this.disconnectInProgress=!0,this.connection.flush(),null!==e&&void 0!==e){var t=e.type;"beforeunload"!==t&&"unload"!==t||(this.connection.options.sync=!0)}this.connection.disconnect(),!0!==this.connection.options.sync&&this.connection.flush()}else this.eventEmitter.emit(u.WRONG_STATE)}},{key:"_initStrophePlugins",value:function(){var e={jvb:{iceServers:[]},p2p:{iceServers:[]}},t=this.options.p2p&&this.options.p2p.stunServers||[{urls:"stun:stun.l.google.com:19302"},{urls:"stun:stun1.l.google.com:19302"},{urls:"stun:stun2.l.google.com:19302"}];Array.isArray(t)&&(S.info("P2P STUN servers: ",t),e.p2p.iceServers=t),this.options.p2p&&this.options.p2p.iceTransportPolicy&&(S.info("P2P ICE transport policy: ",this.options.p2p.iceTransportPolicy),e.p2p.iceTransportPolicy=this.options.p2p.iceTransportPolicy),Object(d.a)(this),Object(p.a)(this,this.eventEmitter,e),Object(h.a)(),Object(f.a)(this),Object(m.a)(),Object(v.a)()}},{key:"_getConnectionFailedReasonDetails",value:function(){var e={};if(this.options.deploymentInfo&&this.options.deploymentInfo.shard&&this.connection._proto&&this.connection._proto.lastResponseHeaders){var t={};this.connection._proto.lastResponseHeaders.trim().split(/[\r\n]+/).forEach(function(e){var n=e.split(": "),r=n.shift(),i=n.join(": ");t[r]=i}),e.shard_changed=this.options.deploymentInfo.shard!==t["x-jitsi-shard"]}return e.suspend_time=this.connection.ping.getPingSuspendTime(),e}}]),t})();t.a=_}).call(t,"modules/xmpp/xmpp.js")},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"CONNECTION_DROPPED_ERROR",function(){return r}),n.d(t,"OTHER_ERROR",function(){return i}),n.d(t,"PASSWORD_REQUIRED",function(){return o}),n.d(t,"SERVER_ERROR",function(){return a});var r="connection.droppedError",i="connection.otherError",o="connection.passwordRequired",a="connection.serverError"},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),n.d(t,"ON",function(){return r}),n.d(t,"OFF",function(){return i});var r="on",i="off"},function(e,t){e.exports={IDENTITY_UPDATED:"authentication.identity_updated"}},function(e,t,n){"use strict";(function(e){function r(e){if("function"==typeof e.querySelector){var t=e.querySelector("error"),n=t&&t.querySelector("text");return t&&{code:t.attributes.code&&t.attributes.code.value,type:t.attributes.type&&t.attributes.type.value,message:n&&n.textContent}}var r=null,i=!0,o=!1,a=void 0;try{for(var s,c=e.children["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value;if("error"===u.tagName){r=u;break}}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}if(!r)return null;var l=null,d=!0,p=!1,h=void 0;try{for(var f,m=r.children["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(f=m.next()).done);d=!0){var v=f.value;if("text"===v.tagName){l=v.value;break}}}catch(e){p=!0,h=e}finally{try{!d&&m.return&&m.return()}finally{if(p)throw h}}return{code:r.attributes.code,type:r.attributes.type,message:l}}function i(e,t,n,r,o,a){this.eventEmitter=t,this.connection=n,this.state=null,this.focusMucJid=r,this.jirecon=o,this.url=null,this.type=e,this._isSupported=!(e===i.types.JIRECON&&!this.jirecon||e!==i.types.JIBRI&&e!==i.types.JIBRI_FILE&&e!==i.types.COLIBRI),this.jireconRid=null,this.roomjid=a}t.a=i;var o=n(0),a=(n.n(o),n(2)),s=(n.n(a),n(8)),c=n(51),u=n(3),l=Object(o.getLogger)(e);i.types={COLIBRI:"colibri",JIRECON:"jirecon",JIBRI:"jibri",JIBRI_FILE:"jibri_file"},i.status={ON:"on",OFF:"off",AVAILABLE:"available",UNAVAILABLE:"unavailable",PENDING:"pending",RETRYING:"retrying",ERROR:"error",BUSY:"busy",FAILED:"failed"},i.action={START:"start",STOP:"stop"},i.prototype.handleJibriPresence=function(e){var t=e.attributes;if(t){var n=t.status,o=r(e);l.log("Handle Jibri presence : "+n,o),n!==this.state&&("undefined"===n?this.state=i.status.UNAVAILABLE:n===i.status.OFF?this.state&&"undefined"!==this.state&&this.state!==i.status.UNAVAILABLE?this.state=i.status.OFF:this.state=i.status.AVAILABLE:this.state=n,this.eventEmitter.emit(s.RECORDER_STATE_CHANGED,this.state))}},i.prototype.setRecordingJibri=function(e,t,n){var o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{};e===this.state&&n(c.INVALID_STATE);var s=Object(a.$iq)({to:this.focusMucJid,type:"set"}).c("jibri",{xmlns:"http://jitsi.org/protocol/jibri",action:e===i.status.ON?i.action.START:i.action.STOP,recording_mode:this.type===i.types.JIBRI_FILE?"file":"stream",streamid:this.type===i.types.JIBRI?o.streamId:void 0}).up();l.log("Set jibri recording: "+e,s.nodeTree),l.log(s.nodeTree),this.connection.sendIQ(s,function(e){l.log("Result",e);var n=$(e).find("jibri");t(n.attr("state"),n.attr("url"))},function(e){l.log("Failed to start recording, error: ",r(e)),n(e)})},i.prototype.setRecordingJirecon=function(e,t,n){e===this.state&&n(new Error("Invalid state!"));var r=Object(a.$iq)({to:this.jirecon,type:"set"}).c("recording",{xmlns:"http://jitsi.org/protocol/jirecon",action:e===i.status.ON?i.action.START:i.action.STOP,mucjid:this.roomjid});e===i.status.OFF&&r.attrs({rid:this.jireconRid}),l.log("Start recording");var o=this;this.connection.sendIQ(r,function(n){o.jireconRid=$(n).find("recording").attr("rid");var r=e===i.status.ON?"started":"stopped";l.log("Recording "+r+"(jirecon)"+n),o.state=e,e===i.status.OFF&&(o.jireconRid=null),t(e)},function(e){l.log("Failed to start recording, error: ",e),n(e)})},i.prototype.setRecordingColibri=function(e,t,n,r){var i=Object(a.$iq)({to:this.focusMucJid,type:"set"});i.c("conference",{xmlns:"http://jitsi.org/protocol/colibri"}),i.c("recording",{state:e,token:r.token});var o=this;this.connection.sendIQ(i,function(n){l.log('Set recording "',e,'". Result:',n);var r=$(n).find(">conference>recording").attr("state");o.state=r,t(r),"pending"===r&&o.connection.addHandler(function(e){var n=$(e).find("recording").attr("state");n&&(o.state=r,t(n))},"http://jitsi.org/protocol/colibri","iq",null,null,null)},function(e){l.warn(e),n(e)})},i.prototype.setRecording=function(){switch(this.type){case i.types.JIRECON:this.setRecordingJirecon.apply(this,arguments);break;case i.types.COLIBRI:this.setRecordingColibri.apply(this,arguments);break;case i.types.JIBRI:case i.types.JIBRI_FILE:this.setRecordingJibri.apply(this,arguments);break;default:var e="Unknown recording type!";u.callErrorHandler(new Error(e)),l.error(e)}},i.prototype.toggleRecording=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1],n=this.state;if(n===i.status.UNAVAILABLE||n===i.status.FAILED?t(i.status.FAILED,c.RECORDER_UNAVAILABLE):n===i.status.BUSY&&t(i.status.BUSY,c.RECORDER_BUSY),(n===i.status.OFF||n===i.status.AVAILABLE)&&(!e.token&&this.type===i.types.COLIBRI||!e.streamId&&this.type===i.types.JIBRI))return t(i.status.FAILED,c.NO_TOKEN),void l.error("No token passed!");var r=n===i.status.AVAILABLE||n===i.status.OFF?i.status.ON:i.status.OFF,o=this;l.log("Toggle recording (old state, new state): ",n,r),this.setRecording(r,function(e,r){e&&e!==n&&(o.state=e,o.url=r,t(e))},function(e){return t(i.status.FAILED,e)},e)},i.prototype.isSupported=function(){return this._isSupported},i.prototype.getState=function(){return this.state},i.prototype.getURL=function(){return this.url}}).call(t,"modules/xmpp/recording.js")},function(e,t){e.exports={RECORDER_UNAVAILABLE:"recorder.unavailable",RECORDER_BUSY:"recorder.busy",NO_TOKEN:"recorder.noToken",STATE_CHANGE_FAILED:"recorder.stateChangeFailed",INVALID_STATE:"recorder.invalidState"}},function(e,t,n){"use strict";n.d(t,"c",function(){return r}),n.d(t,"a",function(){return i}),n.d(t,"b",function(){return o});var r="pending",i="active",o="ended"},function(e,t,n){"use strict";function r(e){for(var t=e.split("\r\nm="),n=1,r=t.length;n2&&t.attrs({streams:a[2]}),t.up()}if(i.a.findLines(this.media[e],"a=fingerprint:",this.session).forEach(function(o){(n=i.a.parseFingerprint(o)).xmlns="urn:xmpp:jingle:apps:dtls:0",t.c("fingerprint").t(n.fingerprint),delete n.fingerprint,(o=i.a.findLine(r.media[e],"a=setup:",r.session))&&(n.setup=o.substr(8)),t.attrs(n),t.up()}),n=i.a.iceparams(this.media[e],this.session)){n.xmlns="urn:xmpp:jingle:transports:ice-udp:1",t.attrs(n);var s=i.a.findLines(this.media[e],"a=candidate:",this.session);s.length&&s.forEach(function(e){var n=i.a.candidateToJingle(e);r.failICE&&(n.ip="1.1.1.1");var o=n&&"string"==typeof n.protocol?n.protocol.toLowerCase():"";r.removeTcpCandidates&&("tcp"===o||"ssltcp"===o)||r.removeUdpCandidates&&"udp"===o||t.c("candidate",n).up()})}t.up()},r.prototype.rtcpFbToJingle=function(e,t,n){i.a.findLines(this.media[e],"a=rtcp-fb:"+n).forEach(function(e){var n=i.a.parseRTCPFB(e);"trr-int"===n.type?(t.c("rtcp-fb-trr-int",{xmlns:"urn:xmpp:jingle:apps:rtp:rtcp-fb:0",value:n.params[0]}),t.up()):(t.c("rtcp-fb",{xmlns:"urn:xmpp:jingle:apps:rtp:rtcp-fb:0",type:n.type}),n.params.length>0&&t.attrs({subtype:n.params[0]}),t.up())})},r.prototype.rtcpFbFromJingle=function(e,t){var n="",r=e.find('>rtcp-fb-trr-int[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]');return r.length&&(n+="a=rtcp-fb:* trr-int ",r.attr("value")?n+=r.attr("value"):n+="0",n+="\r\n"),(r=e.find('>rtcp-fb[xmlns="urn:xmpp:jingle:apps:rtp:rtcp-fb:0"]')).each(function(){n+="a=rtcp-fb:"+t+" "+$(this).attr("type"),$(this).attr("subtype")&&(n+=" "+$(this).attr("subtype")),n+="\r\n"}),n},r.prototype.fromJingle=function(e){var t=this;this.raw="v=0\r\no=- 1923518516 2 IN IP4 0.0.0.0\r\ns=-\r\nt=0 0\r\n";var n=$(e).find('>group[xmlns="urn:xmpp:jingle:apps:grouping:0"]');n.length&&n.each(function(e,n){var r=$(n).find(">content").map(function(e,t){return t.getAttribute("name")}).get();r.length>0&&(t.raw+="a=group:"+(n.getAttribute("semantics")||n.getAttribute("type"))+" "+r.join(" ")+"\r\n")}),this.session=this.raw,e.find(">content").each(function(){var e=t.jingle2media($(this));t.media.push(e)}),this.raw=this.session+this.media.join("")},r.prototype.jingle2media=function(e){var t=e.find("description"),n="",r=this,o=e.find('>transport>sctpmap[xmlns="urn:xmpp:jingle:transports:dtls-sctp:1"]'),a={media:t.attr("media")};if(a.port="1","rejected"===e.attr("senders")&&(a.port="0"),e.find(">transport>fingerprint").length||t.find("encryption").length?a.proto=o.length?"DTLS/SCTP":"RTP/SAVPF":a.proto="RTP/AVPF",o.length){n+="m=application 1 DTLS/SCTP "+o.attr("number")+"\r\n",n+="a=sctpmap:"+o.attr("number")+" "+o.attr("protocol");var s=o.attr("streams");n+=s?" "+s+"\r\n":"\r\n"}else a.fmt=t.find("payload-type").map(function(){return this.getAttribute("id")}).get(),n+=i.a.buildMLine(a)+"\r\n";switch(n+="c=IN IP4 0.0.0.0\r\n",o.length||(n+="a=rtcp:1 IN IP4 0.0.0.0\r\n"),(a=e.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]')).length&&(a.attr("ufrag")&&(n+=i.a.buildICEUfrag(a.attr("ufrag"))+"\r\n"),a.attr("pwd")&&(n+=i.a.buildICEPwd(a.attr("pwd"))+"\r\n"),a.find(">fingerprint").each(function(){n+="a=fingerprint:"+this.getAttribute("hash"),n+=" "+$(this).text(),n+="\r\n",this.getAttribute("setup")&&(n+="a=setup:"+this.getAttribute("setup")+"\r\n")})),e.attr("senders")){case"initiator":n+="a=sendonly\r\n";break;case"responder":n+="a=recvonly\r\n";break;case"none":n+="a=inactive\r\n";break;case"both":n+="a=sendrecv\r\n"}return n+="a=mid:"+e.attr("name")+"\r\n",t.find("rtcp-mux").length&&(n+="a=rtcp-mux\r\n"),t.find("encryption").length&&t.find("encryption>crypto").each(function(){n+="a=crypto:"+this.getAttribute("tag"),n+=" "+this.getAttribute("crypto-suite"),n+=" "+this.getAttribute("key-params"),this.getAttribute("session-params")&&(n+=" "+this.getAttribute("session-params")),n+="\r\n"}),t.find("payload-type").each(function(){n+=i.a.buildRTPMap(this)+"\r\n",$(this).find(">parameter").length&&(n+="a=fmtp:"+this.getAttribute("id")+" ",n+=$(this).find("parameter").map(function(){var e=this.getAttribute("name");return(e?e+"=":"")+this.getAttribute("value")}).get().join("; "),n+="\r\n"),n+=r.rtcpFbFromJingle($(this),this.getAttribute("id"))}),n+=r.rtcpFbFromJingle(t,"*"),(a=t.find('>rtp-hdrext[xmlns="urn:xmpp:jingle:apps:rtp:rtp-hdrext:0"]')).each(function(){n+="a=extmap:"+this.getAttribute("id")+" "+this.getAttribute("uri")+"\r\n"}),e.find('>transport[xmlns="urn:xmpp:jingle:transports:ice-udp:1"]>candidate').each(function(){var e=this.getAttribute("protocol");e="string"==typeof e?e.toLowerCase():"",r.removeTcpCandidates&&("tcp"===e||"ssltcp"===e)||r.removeUdpCandidates&&"udp"===e||(r.failICE&&this.setAttribute("ip","1.1.1.1"),n+=i.a.candidateFromJingle(this))}),e.find('description>ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(n+="a=ssrc-group:"+e+" "+t.join(" ")+"\r\n")}),(a=e.find('description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]')).each(function(){var e=this.getAttribute("ssrc");$(this).find(">parameter").each(function(){var t=this.getAttribute("name"),r=this.getAttribute("value");r=i.a.filterSpecialChars(r),n+="a=ssrc:"+e+" "+t,r&&r.length&&(n+=":"+r),n+="\r\n"})}),n}},function(e,t,n){"use strict";n.d(t,"a",function(){return r}),n.d(t,"b",function(){return i});var r="signaling.peerMuted",i="signaling.peerVideoType"},function(e,t,n){"use strict";(function(e){function r(e,t){void 0===e.active?e.onended=t:e.oninactive=t}var i=n(16),o=n.n(i),a=n(0),s=(n.n(a),n(18)),c=n(4),u=n(28),l=(function(){function e(e,t){for(var n=0;n=0;--n){var r=t[n];e||u.a.attachMediaStream(r,null),e&&r!==e||t.splice(n,1)}e&&u.a.attachMediaStream(e,null)}},{key:"_attachTTFMTracker",value:function(e){}},{key:"dispose",value:function(){return this.removeAllListeners(),this.disposed=!0,Promise.resolve()}},{key:"isScreenSharing",value:function(){}},{key:"getId",value:function(){return this.stream?u.a.getStreamID(this.stream):null}},{key:"isActive",value:function(){return void 0===this.stream.active||this.stream.active}},{key:"setAudioLevel",value:function(e,t){this.audioLevel!==e&&(this.audioLevel=e,this.emit(s.TRACK_AUDIO_LEVEL_CHANGED,e,t))}},{key:"getMSID",value:function(){var e=this.getStreamId(),t=this.getTrackId();return e&&t?e+" "+t:null}},{key:"setAudioOutput",value:function(e){var t=this;return u.a.isDeviceChangeAvailable("output")?this.isVideoTrack()?Promise.resolve():Promise.all(this.containers.map(function(t){return t.setSinkId(e).catch(function(e){throw d.warn("Failed to change audio output device on element. Default or previously set audio output device will be used.",t,e),e})})).then(function(){t.emit(s.TRACK_AUDIO_OUTPUT_CHANGED,e)}):Promise.reject(new Error("Audio output device change is not supported"))}}]),t})();t.a=h}).call(t,"modules/RTC/JitsiTrack.js")},function(e,t){e.exports={ENVIRONMENT:"environment",USER:"user"}},function(e,t){var n=e.exports={v:[{name:"version",reg:/^(\d*)$/}],o:[{name:"origin",reg:/^(\S*) (\d*) (\d*) (\S*) IP(\d) (\S*)/,names:["username","sessionId","sessionVersion","netType","ipVer","address"],format:"%s %s %d %s IP%d %s"}],s:[{name:"name"}],i:[{name:"description"}],u:[{name:"uri"}],e:[{name:"email"}],p:[{name:"phone"}],z:[{name:"timezones"}],r:[{name:"repeats"}],t:[{name:"timing",reg:/^(\d*) (\d*)/,names:["start","stop"],format:"%d %d"}],c:[{name:"connection",reg:/^IN IP(\d) (\S*)/,names:["version","ip"],format:"IN IP%d %s"}],b:[{push:"bandwidth",reg:/^(TIAS|AS|CT|RR|RS):(\d*)/,names:["type","limit"],format:"%s:%s"}],m:[{reg:/^(\w*) (\d*) ([\w\/]*)(?: (.*))?/,names:["type","port","protocol","payloads"],format:"%s %d %s %s"}],a:[{push:"rtp",reg:/^rtpmap:(\d*) ([\w\-\.]*)(?:\s*\/(\d*)(?:\s*\/(\S*))?)?/,names:["payload","codec","rate","encoding"],format:function(e){return e.encoding?"rtpmap:%d %s/%s/%s":e.rate?"rtpmap:%d %s/%s":"rtpmap:%d %s"}},{push:"fmtp",reg:/^fmtp:(\d*) ([\S| ]*)/,names:["payload","config"],format:"fmtp:%d %s"},{name:"control",reg:/^control:(.*)/,format:"control:%s"},{name:"rtcp",reg:/^rtcp:(\d*)(?: (\S*) IP(\d) (\S*))?/,names:["port","netType","ipVer","address"],format:function(e){return null!=e.address?"rtcp:%d %s IP%d %s":"rtcp:%d"}},{push:"rtcpFbTrrInt",reg:/^rtcp-fb:(\*|\d*) trr-int (\d*)/,names:["payload","value"],format:"rtcp-fb:%d trr-int %d"},{push:"rtcpFb",reg:/^rtcp-fb:(\*|\d*) ([\w-_]*)(?: ([\w-_]*))?/,names:["payload","type","subtype"],format:function(e){return null!=e.subtype?"rtcp-fb:%s %s %s":"rtcp-fb:%s %s"}},{push:"ext",reg:/^extmap:(\d+)(?:\/(\w+))? (\S*)(?: (\S*))?/,names:["value","direction","uri","config"],format:function(e){return"extmap:%d"+(e.direction?"/%s":"%v")+" %s"+(e.config?" %s":"")}},{push:"crypto",reg:/^crypto:(\d*) ([\w_]*) (\S*)(?: (\S*))?/,names:["id","suite","config","sessionConfig"],format:function(e){return null!=e.sessionConfig?"crypto:%d %s %s %s":"crypto:%d %s %s"}},{name:"setup",reg:/^setup:(\w*)/,format:"setup:%s"},{name:"mid",reg:/^mid:([^\s]*)/,format:"mid:%s"},{name:"msid",reg:/^msid:(.*)/,format:"msid:%s"},{name:"ptime",reg:/^ptime:(\d*)/,format:"ptime:%d"},{name:"maxptime",reg:/^maxptime:(\d*)/,format:"maxptime:%d"},{name:"direction",reg:/^(sendrecv|recvonly|sendonly|inactive)/},{name:"icelite",reg:/^(ice-lite)/},{name:"iceUfrag",reg:/^ice-ufrag:(\S*)/,format:"ice-ufrag:%s"},{name:"icePwd",reg:/^ice-pwd:(\S*)/,format:"ice-pwd:%s"},{name:"fingerprint",reg:/^fingerprint:(\S*) (\S*)/,names:["type","hash"],format:"fingerprint:%s %s"},{push:"candidates",reg:/^candidate:(\S*) (\d*) (\S*) (\d*) (\S*) (\d*) typ (\S*)(?: raddr (\S*) rport (\d*))?(?: tcptype (\S*))?(?: generation (\d*))?(?: network-id (\d*))?(?: network-cost (\d*))?/,names:["foundation","component","transport","priority","ip","port","type","raddr","rport","tcptype","generation","network-id","network-cost"],format:function(e){var t="candidate:%s %d %s %d %s %d typ %s";return t+=null!=e.raddr?" raddr %s rport %d":"%v%v",t+=null!=e.tcptype?" tcptype %s":"%v",null!=e.generation&&(t+=" generation %d"),(t+=null!=e["network-id"]?" network-id %d":"%v")+(null!=e["network-cost"]?" network-cost %d":"%v")}},{name:"endOfCandidates",reg:/^(end-of-candidates)/},{name:"remoteCandidates",reg:/^remote-candidates:(.*)/,format:"remote-candidates:%s"},{name:"iceOptions",reg:/^ice-options:(\S*)/,format:"ice-options:%s"},{push:"ssrcs",reg:/^ssrc:(\d*) ([\w_]*)(?::(.*))?/,names:["id","attribute","value"],format:function(e){var t="ssrc:%d";return null!=e.attribute&&(t+=" %s",null!=e.value&&(t+=":%s")),t}},{push:"ssrcGroups",reg:/^ssrc-group:([\x21\x23\x24\x25\x26\x27\x2A\x2B\x2D\x2E\w]*) (.*)/,names:["semantics","ssrcs"],format:"ssrc-group:%s %s"},{name:"msidSemantic",reg:/^msid-semantic:\s?(\w*) (\S*)/,names:["semantic","token"],format:"msid-semantic: %s %s"},{push:"groups",reg:/^group:(\w*) (.*)/,names:["type","mids"],format:"group:%s %s"},{name:"rtcpMux",reg:/^(rtcp-mux)/},{name:"rtcpRsize",reg:/^(rtcp-rsize)/},{name:"sctpmap",reg:/^sctpmap:([\w_\/]*) (\S*)(?: (\S*))?/,names:["sctpmapNumber","app","maxMessageSize"],format:function(e){return null!=e.maxMessageSize?"sctpmap:%s %s %s":"sctpmap:%s %s"}},{name:"xGoogleFlag",reg:/^x-google-flag:([^\s]*)/,format:"x-google-flag:%s"},{push:"rids",reg:/^rid:([\d\w]+) (\w+)(?: ([\S| ]*))?/,names:["id","direction","params"],format:function(e){return e.params?"rid:%s %s %s":"rid:%s %s"}},{push:"imageattrs",reg:new RegExp("^imageattr:(\\d+|\\*)[\\s\\t]+(send|recv)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*)(?:[\\s\\t]+(recv|send)[\\s\\t]+(\\*|\\[\\S+\\](?:[\\s\\t]+\\[\\S+\\])*))?"),names:["pt","dir1","attrs1","dir2","attrs2"],format:function(e){return"imageattr:%s %s %s"+(e.dir2?" %s %s":"")}},{name:"simulcast",reg:new RegExp("^simulcast:(send|recv) ([a-zA-Z0-9\\-_~;,]+)(?:\\s?(send|recv) ([a-zA-Z0-9\\-_~;,]+))?$"),names:["dir1","list1","dir2","list2"],format:function(e){return"simulcast:%s %s"+(e.dir2?" %s %s":"")}},{name:"simulcast_03",reg:/^simulcast:[\s\t]+([\S+\s\t]+)$/,names:["value"],format:"simulcast: %s"},{name:"framerate",reg:/^framerate:(\d+(?:$|\.\d+))/,format:"framerate:%s"},{push:"invalid",names:["value"]}]};Object.keys(n).forEach(function(e){n[e].forEach(function(e){e.reg||(e.reg=/(.*)/),e.format||(e.format="%s")})})},function(e,t,n){"use strict";(function(e){var t="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},n={};n.generateIdentifier=function(){return Math.random().toString(36).substr(2,10)},n.localCName=n.generateIdentifier(),n.splitLines=function(e){return e.trim().split("\n").map(function(e){return e.trim()})},n.splitSections=function(e){return e.split("\nm=").map(function(e,t){return(t>0?"m="+e:e).trim()+"\r\n"})},n.matchPrefix=function(e,t){return n.splitLines(e).filter(function(e){return 0===e.indexOf(t)})},n.parseCandidate=function(e){for(var t,n={foundation:(t=0===e.indexOf("a=candidate:")?e.substring(12).split(" "):e.substring(10).split(" "))[0],component:parseInt(t[1],10),protocol:t[2].toLowerCase(),priority:parseInt(t[3],10),ip:t[4],port:parseInt(t[5],10),type:t[7]},r=8;r0?t[0].split("/")[1]:"sendrecv",uri:t[1]}},n.writeExtmap=function(e){return"a=extmap:"+(e.id||e.preferredId)+(e.direction&&"sendrecv"!==e.direction?"/"+e.direction:"")+" "+e.uri+"\r\n"},n.parseFmtp=function(e){for(var t,n={},r=e.substr(e.indexOf(" ")+1).split(";"),i=0;i-1?(n.attribute=e.substr(t+1,r-t-1),n.value=e.substr(r+1)):n.attribute=e.substr(t+1),n},n.getMid=function(e){var t=n.matchPrefix(e,"a=mid:")[0];if(t)return t.substr(6)},n.parseFingerprint=function(e){var t=e.substr(14).split(" ");return{algorithm:t[0].toLowerCase(),value:t[1]}},n.getDtlsParameters=function(e,t){return{role:"auto",fingerprints:n.matchPrefix(e+t,"a=fingerprint:").map(n.parseFingerprint)}},n.writeDtlsParameters=function(e,t){var n="a=setup:"+t+"\r\n";return e.fingerprints.forEach(function(e){n+="a=fingerprint:"+e.algorithm+" "+e.value+"\r\n"}),n},n.getIceParameters=function(e,t){var r=n.splitLines(e);return{usernameFragment:(r=r.concat(n.splitLines(t))).filter(function(e){return 0===e.indexOf("a=ice-ufrag:")})[0].substr(12),password:r.filter(function(e){return 0===e.indexOf("a=ice-pwd:")})[0].substr(10)}},n.writeIceParameters=function(e){return"a=ice-ufrag:"+e.usernameFragment+"\r\na=ice-pwd:"+e.password+"\r\n"},n.parseRtpParameters=function(e){for(var t={codecs:[],headerExtensions:[],fecMechanisms:[],rtcp:[]},r=n.splitLines(e)[0].split(" "),i=3;i0?"9":"0",r+=" UDP/TLS/RTP/SAVPF ",r+=t.codecs.map(function(e){return void 0!==e.preferredPayloadType?e.preferredPayloadType:e.payloadType}).join(" ")+"\r\n",r+="c=IN IP4 0.0.0.0\r\n",r+="a=rtcp:9 IN IP4 0.0.0.0\r\n",t.codecs.forEach(function(e){r+=n.writeRtpMap(e),r+=n.writeFmtp(e),r+=n.writeRtcpFb(e)});var i=0;return t.codecs.forEach(function(e){e.maxptime>i&&(i=e.maxptime)}),i>0&&(r+="a=maxptime:"+i+"\r\n"),r+="a=rtcp-mux\r\n",t.headerExtensions.forEach(function(e){r+=n.writeExtmap(e)}),r},n.parseRtpEncodingParameters=function(e){var t,r=[],i=n.parseRtpParameters(e),o=-1!==i.fecMechanisms.indexOf("RED"),a=-1!==i.fecMechanisms.indexOf("ULPFEC"),s=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute}),c=s.length>0&&s[0].ssrc,u=n.matchPrefix(e,"a=ssrc-group:FID").map(function(e){var t=e.split(" ");return t.shift(),t.map(function(e){return parseInt(e,10)})});u.length>0&&u[0].length>1&&u[0][0]===c&&(t=u[0][1]),i.codecs.forEach(function(e){if("RTX"===e.name.toUpperCase()&&e.parameters.apt){var n={ssrc:c,codecPayloadType:parseInt(e.parameters.apt,10),rtx:{ssrc:t}};r.push(n),o&&((n=JSON.parse(JSON.stringify(n))).fec={ssrc:t,mechanism:a?"red+ulpfec":"red"},r.push(n))}}),0===r.length&&c&&r.push({ssrc:c});var l=n.matchPrefix(e,"b=");return l.length&&(l=0===l[0].indexOf("b=TIAS:")?parseInt(l[0].substr(7),10):0===l[0].indexOf("b=AS:")?1e3*parseInt(l[0].substr(5),10)*.95-16e3:void 0,r.forEach(function(e){e.maxBitrate=l})),r},n.parseRtcpParameters=function(e){var t={},r=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"cname"===e.attribute})[0];r&&(t.cname=r.value,t.ssrc=r.ssrc);var i=n.matchPrefix(e,"a=rtcp-rsize");t.reducedSize=i.length>0,t.compound=0===i.length;var o=n.matchPrefix(e,"a=rtcp-mux");return t.mux=o.length>0,t},n.parseMsid=function(e){var t,r=n.matchPrefix(e,"a=msid:");if(1===r.length)return{stream:(t=r[0].substr(7).split(" "))[0],track:t[1]};var i=n.matchPrefix(e,"a=ssrc:").map(function(e){return n.parseSsrcMedia(e)}).filter(function(e){return"msid"===e.attribute});return i.length>0?{stream:(t=i[0].value.split(" "))[0],track:t[1]}:void 0},n.generateSessionId=function(){return Math.random().toString().substr(2,21)},n.writeSessionBoilerplate=function(e,t){var r=void 0!==t?t:2;return"v=0\r\no=thisisadapterortc "+(e||n.generateSessionId())+" "+r+" IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\n"},n.writeMediaSection=function(e,t,r,i){var o=n.writeRtpDescription(e.kind,t);if(o+=n.writeIceParameters(e.iceGatherer.getLocalParameters()),o+=n.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===r?"actpass":"active"),o+="a=mid:"+e.mid+"\r\n",e.direction?o+="a="+e.direction+"\r\n":e.rtpSender&&e.rtpReceiver?o+="a=sendrecv\r\n":e.rtpSender?o+="a=sendonly\r\n":e.rtpReceiver?o+="a=recvonly\r\n":o+="a=inactive\r\n",e.rtpSender){var a="msid:"+i.id+" "+e.rtpSender.track.id+"\r\n";o+="a="+a,o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+a,e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" "+a,o+="a=ssrc-group:FID "+e.sendEncodingParameters[0].ssrc+" "+e.sendEncodingParameters[0].rtx.ssrc+"\r\n")}return o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+n.localCName+"\r\n",e.rtpSender&&e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" cname:"+n.localCName+"\r\n"),o},n.getDirection=function(e,t){for(var r=n.splitLines(e),i=0;i0&&t.data.push(e.data)},t},a.prototype.removeTrack=function(e){if(!e.isVideoTrack()){var t=this.recorders,n=void 0;for(n=0;n0){var n={};for(var r in t)t.hasOwnProperty(r)&&(n[r]=t[r]);n.id="deployment_info",M.a.sendLog(JSON.stringify(n))}if(this.version){var i={id:"component_version",component:"lib-jitsi-meet",version:this.version};M.a.sendLog(JSON.stringify(i))}return O.a.init(e||{})},isDesktopSharingEnabled:function(){return O.a.isDesktopSharingEnabled()},setLogLevel:function(e){R.a.setLogLevel(e)},setLogLevelById:function(e,t){R.a.setLogLevelById(e,t)},addGlobalLogTransport:function(e){R.a.addGlobalTransport(e)},removeGlobalLogTransport:function(e){R.a.removeGlobalTransport(e)},createLocalTracks:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=!1;return!0===arguments[1]&&window.setTimeout(function(){n||m.a.emitEvent(v.PERMISSION_PROMPT_IS_SHOWN,D.b.getName())},1e3),window.connectionTimes||(window.connectionTimes={}),window.connectionTimes["obtainPermissions.start"]=window.performance.now(),O.a.obtainAudioAndVideoPermissions(t).then(function(e){if(n=!0,window.connectionTimes["obtainPermissions.end"]=window.performance.now(),M.a.sendAnalytics(Object(i.y)("success",r(t))),!O.a.options.disableAudioLevels)for(var o=0;o0&&void 0!==arguments[0]?arguments[0]:{};this.xmpp.connect(e.id,e.password)},r.prototype.attach=function(e){this.xmpp.attach(e)},r.prototype.disconnect=function(){var e;(e=this.xmpp).disconnect.apply(e,arguments)},r.prototype.setToken=function(e){this.token=e},r.prototype.initJitsiConference=function(e,t){return new o.a({name:e,config:t,connection:this})},r.prototype.addEventListener=function(e,t){this.xmpp.addListener(e,t)},r.prototype.removeEventListener=function(e,t){this.xmpp.removeListener(e,t)},r.prototype.getConnectionTimes=function(){return this.xmpp.connectionTimes},r.prototype.addFeature=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return this.xmpp.caps.addFeature(e,t)},r.prototype.removeFeature=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];return this.xmpp.caps.removeFeature(e,t)}},function(e,t,n){"use strict";(function(e){function r(e){if(!e.name||e.name.toLowerCase()!==e.name){var t="Invalid conference name (no conference name passed or it contains invalid characters like capital letters)!";throw B.error(t),new Error(t)}this.eventEmitter=new h.a,this.options=e,this.eventManager=new v.a(this),this.participants={},this._init(e),this.componentsVersions=new s.a(this),this.jvbJingleSession=null,this.lastDominantSpeaker=null,this.dtmfManager=null,this.somebodySupportsDTMF=!1,this.authEnabled=!1,this.startAudioMuted=!1,this.startVideoMuted=!1,this.startMutedPolicy={audio:!1,video:!1},this.availableDevices={audio:void 0,video:void 0},this.isMutedByFocus=!1,this.wasStopped=!1,this.connectionQuality=new c.a(this,this.eventEmitter,e),this.avgRtpStatsReporter=new a.a(this,e.config.avgRtpStatsN||15),this.isJvbConnectionInterrupted=!1,this.speakerStatsCollector=new H.a(this),this.deferredStartP2PTask=null;var n=parseInt(e.config.p2p&&e.config.p2p.backToP2PDelay,10);this.backToP2PDelay=isNaN(n)?5:n,B.info("backToP2PDelay: "+this.backToP2PDelay),this.isP2PConnectionInterrupted=!1,this.p2p=!1,this.p2pJingleSession=null,this.videoSIPGWHandler=new j.a(this.room)}t.a=r;var i=n(2),o=(n.n(i),n(6)),a=n(66),s=n(98),c=n(99),u=n(0),l=(n.n(u),n(3)),d=n.n(l),p=n(16),h=n.n(p),f=n(100),m=n(32),v=n(118),g=n(7),y=n(120),b=n.n(y),S=n(121),_=n(12),T=n(17),C=n(18),E=n(122),w=n(4),R=n(36),k=n(123),I=n(27),P=n(1),A=n(9),O=(n.n(A),n(5)),D=n(160),x=n(161),N=n.n(x),L=n(13),M=n.n(L),j=n(166),F=n(30),U=n(8),H=(n.n(U),n(168)),B=Object(u.getLogger)(e);r.prototype.constructor=r,r.prototype._init=function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};t.connection&&(this.connection=t.connection,this.xmpp=this.connection.xmpp,this.eventManager.setupXMPPListeners());var n=this.options.config;if(this.room=this.xmpp.createRoom(this.options.name,n),this._onIceConnectionInterrupted=this._onIceConnectionInterrupted.bind(this),this.room.addListener(U.CONNECTION_INTERRUPTED,this._onIceConnectionInterrupted),this._onIceConnectionRestored=this._onIceConnectionRestored.bind(this),this.room.addListener(U.CONNECTION_RESTORED,this._onIceConnectionRestored),this._onIceConnectionEstablished=this._onIceConnectionEstablished.bind(this),this.room.addListener(U.CONNECTION_ESTABLISHED,this._onIceConnectionEstablished),this.room.updateDeviceAvailability(I.a.getDeviceAvailability()),this.rtc||(this.rtc=new I.a(this,t),this.eventManager.setupRTCListeners()),this.participantConnectionStatus=new R.b(this.rtc,this,{rtcMuteTimeout:n._peerConnStatusRtcMuteTimeout,outOfLastNTimeout:n._peerConnStatusOutOfLastNTimeout}),this.participantConnectionStatus.init(),!this.statistics){var r=window.location,i=this.myUserId();n.enableDisplayNameInStats&&n.displayName&&(i=n.displayName),this.statistics=new O.a(this.xmpp,{callStatsAliasName:i,callStatsConfIDNamespace:n.callStatsConfIDNamespace||r&&r.hostname||n.hosts&&n.hosts.domain,callStatsCustomScriptUrl:n.callStatsCustomScriptUrl,callStatsID:n.callStatsID,callStatsSecret:n.callStatsSecret,roomName:this.options.name,swapUserNameAndAlias:n.enableStatsID,applicationName:n.applicationName,getWiFiStatsMethod:n.getWiFiStatsMethod})}this.eventManager.setupChatRoomListeners(),this.eventManager.setupStatisticsListeners(),n.enableTalkWhileMuted&&new D.a(this,function(){return e.eventEmitter.emit(g.TALK_WHILE_MUTED)}),"channelLastN"in n&&this.setLastN(n.channelLastN),this.jvb121Status=new E.a(this),this.p2pDominantSpeakerDetection=new k.a(this)},r.prototype.join=function(e){this.room&&this.room.join(e)},r.prototype.authenticateAndUpgradeRole=function(){for(var e=arguments.length,t=Array(e),n=0;n0&&void 0!==arguments[0]&&arguments[0],t=this.getParticipants();return e||(t=t.filter(function(e){return!e.isHidden()})),t.length+1},r.prototype.getParticipantById=function(e){return this.participants[e]},r.prototype.kickParticipant=function(e){var t=this.getParticipantById(e);t&&this.room.kick(t.getJid())},r.prototype.muteParticipant=function(e){var t=this.getParticipantById(e);t&&this.room.muteParticipant(t.getJid(),!0)},r.prototype.onMemberJoined=function(e,t,n,r,o,a){var s=this,c=i.Strophe.getResourceFromJid(e);if("focus"!==c&&this.myUserId()!==c){var u=new S.a(e,this,t,r,o,a);u._role=n,this.participants[c]=u,this.eventEmitter.emit(g.USER_JOINED,c,u),this.xmpp.caps.getFeatures(e).then(function(e){u._supportsDTMF=e.has("urn:xmpp:jingle:dtmf:0"),s.updateDTMFSupport()},function(t){return B.warn("Failed to discover features of "+e,t)}),this._maybeStartOrStopP2P()}},r.prototype.onMemberLeft=function(e){var t=this,n=i.Strophe.getResourceFromJid(e);if("focus"!==n&&this.myUserId()!==n){var r=this.participants[n];delete this.participants[n],this.rtc.removeRemoteTracks(n).forEach(function(e){return t.eventEmitter.emit(g.TRACK_REMOVED,e)}),r&&this.eventEmitter.emit(g.USER_LEFT,n,r),this._maybeStartOrStopP2P(!0)}},r.prototype.onLocalRoleChanged=function(e){this.eventEmitter.emit(g.USER_ROLE_CHANGED,this.myUserId(),e),this._maybeStartOrStopP2P()},r.prototype.onUserRoleChanged=function(e,t){var n=i.Strophe.getResourceFromJid(e),r=this.getParticipantById(n);r&&(r._role=t,this.eventEmitter.emit(g.USER_ROLE_CHANGED,n,t))},r.prototype.onDisplayNameChanged=function(e,t){var n=i.Strophe.getResourceFromJid(e),r=this.getParticipantById(n);r&&r._displayName!==t&&(r._displayName=t,this.eventEmitter.emit(g.DISPLAY_NAME_CHANGED,n,t))},r.prototype.onRemoteTrackAdded=function(e){var t=this;if(!e.isP2P||this.isP2PActive())if(e.isP2P||!this.isP2PActive()){var n=e.getParticipantId(),r=this.getParticipantById(n);if(r){r._tracks.push(e),this.transcriber&&this.transcriber.addTrack(e);var i=this.eventEmitter;e.addEventListener(C.TRACK_MUTE_CHANGED,function(){return i.emit(g.TRACK_MUTE_CHANGED,e)}),e.addEventListener(C.TRACK_AUDIO_LEVEL_CHANGED,function(e,r){t.getActivePeerConnection()===r&&i.emit(g.TRACK_AUDIO_LEVEL_CHANGED,n,e)}),i.emit(g.TRACK_ADDED,e)}else B.error("No participant found for id: "+n)}else B.info("Trying to add remote JVB track, when in P2P - IGNORED");else B.info("Trying to add remote P2P track, when not in P2P - IGNORED")},r.prototype.onCallAccepted=function(e,t){this.p2pJingleSession===e&&(B.info("P2P setAnswer"),this.p2pJingleSession.setAnswer(t))},r.prototype.onTransportInfo=function(e,t){this.p2pJingleSession===e&&(B.info("P2P addIceCandidates"),this.p2pJingleSession.addIceCandidates(t))},r.prototype.onRemoteTrackRemoved=function(e){var t=this;this.getParticipants().forEach(function(n){for(var r=n.getTracks(),i=0;icontent>transport>web-socket").first();1===r.length&&(n=r[0].getAttribute("url"));var i=void 0;switch(this.options.config.openBridgeChannel){case"datachannel":case!0:case void 0:i="datachannel";break;case"websocket":i="websocket"}"datachannel"!==i||P.b.supportsDataChannels()||(i="websocket"),"datachannel"===i?this.rtc.initializeBridgeChannel(t,null):"websocket"===i&&n&&this.rtc.initializeBridgeChannel(null,n)},r.prototype._rejectIncomingCall=function(e,t){t&&t.errorMsg&&d.a.callErrorHandler(new Error(t.errorMsg)),e.terminate(null,function(e){B.warn("An error occurred while trying to terminate invalid Jingle session",e)},{reason:t&&t.reason,reasonDescription:t&&t.reasonDescription,sendSessionTerminate:!0})},r.prototype.onCallEnded=function(e,t,n){B.info("Call ended: "+t+" - "+n+" P2P ?"+e.isP2P),e===this.jvbJingleSession?(this.wasStopped=!0,O.a.sendAnalytics(Object(o.z)(o.d,{p2p:!1})),this.statistics&&(this.statistics.stopRemoteStats(this.jvbJingleSession.peerconnection),B.info("Stopping JVB CallStats"),this.statistics.stopCallStats(this.jvbJingleSession.peerconnection)),this.jvbJingleSession=null,this.rtc.onCallEnded()):e===this.p2pJingleSession?("decline"===t&&"force JVB121"===n?(B.info("In forced JVB 121 mode..."),O.a.analytics.addPermanentProperties({forceJvb121:!0})):"connectivity-error"===t&&"ICE FAILED"===n&&O.a.analytics.addPermanentProperties({p2pFailed:!0}),this._stopP2PSession()):B.error("Received onCallEnded for invalid session",e.sid,e.remoteJid,t,n)},r.prototype.onSuspendDetected=function(e){e.isP2P||(this.leave(),this.eventEmitter.emit(g.SUSPEND_DETECTED))},r.prototype.updateDTMFSupport=function(){for(var e=!1,t=this.getParticipants(),n=0;n "+i),!i&&this.deferredStartP2PTask&&this._maybeClearDeferredStartP2P(),r&&!this.p2pJingleSession&&i){var a=n&&t[0];if(r&&"moderator"===a.getRole()){var s=this.myUserId(),c=a.getId();if(s>c)return void B.debug("Everyone's a moderator - the other peer should start P2P",s,c);if(s===c)return void B.error("The same IDs ? ",s,c)}var u=a.getJid();if(e){if(this.deferredStartP2PTask)return void B.error("Deferred start P2P task's been set already!");B.info("Will start P2P with: "+u+" after "+this.backToP2PDelay+" seconds..."),this.deferredStartP2PTask=setTimeout(this._startP2PSession.bind(this,u),1e3*this.backToP2PDelay)}else B.info("Will start P2P with: "+u),this._startP2PSession(u)}else this.p2pJingleSession&&!i&&(B.info("Will stop P2P with: "+this.p2pJingleSession.remoteJid),this.p2pJingleSession.isInitiator&&n>1&&O.a.sendAnalyticsAndLog(Object(o.B)(o.i)),this._stopP2PSession())}else B.info("Auto P2P disabled")},r.prototype._stopP2PSession=function(e,t){if(this.p2pJingleSession){var n=this.isP2PActive();n&&(this.jvbJingleSession&&this._resumeMediaTransferForJvbConnection(),this._removeRemoteP2PTracks()),B.info("Stopping remote stats for P2P connection"),this.statistics.stopRemoteStats(this.p2pJingleSession.peerconnection),B.info("Stopping CallStats for P2P connection"),this.statistics.stopCallStats(this.p2pJingleSession.peerconnection),this.p2pJingleSession.terminate(function(){B.info("P2P session terminate RESULT")},function(t){e&&B.error("An error occurred while trying to terminate P2P Jingle session",t)},{reason:e||"success",reasonDescription:t||"Turing off P2P session",sendSessionTerminate:this.room&&this.getParticipantById(i.Strophe.getResourceFromJid(this.p2pJingleSession.remoteJid))}),this.p2pJingleSession=null,this._setP2PStatus(!1),n&&(this.jvbJingleSession?this._addRemoteJVBTracks():B.info("Not adding remote JVB tracks - no session yet"))}else B.error("No P2P session to be stopped!")},r.prototype.isP2PActive=function(){return this.p2p},r.prototype.getP2PConnectionState=function(){return this.isP2PActive()?this.p2pJingleSession.peerconnection.getConnectionState():null},r.prototype.startP2PSession=function(){var e=this.getParticipants();if(1!==e.length)throw new Error("There must be exactly 1 participant to start the P2P session !");var t=e[0].getJid();this._startP2PSession(t)},r.prototype.stopP2PSession=function(){this._stopP2PSession()},r.prototype.getSpeakerStats=function(){return this.speakerStatsCollector.getStats()},r.prototype.setReceiverVideoConstraint=function(e){this.rtc.setReceiverVideoConstraint(e)},r.prototype.createVideoSIPGWSession=function(e,t){return this.room?this.videoSIPGWHandler.createVideoSIPGWSession(e,t):new Error(F.ERROR_NO_CONNECTION)}}).call(t,"JitsiConference.js")},function(e,t,n){"use strict";(function(e){function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}var i=n(6),o=n(0),a=(n.n(o),n(31)),s=n(7),c=n(4),u=n(1),l=n(5),d=n(13),p=(n.n(d),(function(){function e(e,t){for(var n=0;n=this._n){if(u.b.supportsRTTStatistics()){var t=this._avgRtpStatsReporter._conference,n={p2p:this.isP2P,conference_size:t.getParticipantCount()};if(e.transport&&e.transport.length&&babelHelpers.extends(n,{local_candidate_type:e.transport[0].localCandidateType,remote_candidate_type:e.transport[0].remoteCandidateType,transport_type:e.transport[0].type}),this._avgRTT.appendReport(n),this.isP2P){var r=this._avgRtpStatsReporter.jvbStatsMonitor._avgEnd2EndRTT;isNaN(r)||(n.rtt_diff=this._avgRTT.calculate()-r)}else{var o=this._calculateAvgRemoteRTT(),a=this._avgRTT.calculate();this._avgEnd2EndRTT=a+o,isNaN(a)||isNaN(o)||(n.end2end_rtt_avg=this._avgEnd2EndRTT)}l.a.sendAnalytics(Object(i.D)(n))}this._resetAvgStats()}}else h.error("No stats")}},{key:"_calculateAvgRemoteRTT",value:function(){var e=0,t=0,n=!0,r=!1,i=void 0;try{for(var o,a=this._avgRemoteRTTMap.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value,c=s.calculate();isNaN(c)||(t+=c,e+=1,s.reset())}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}return t/e}},{key:"_processRemoteStats",value:function(e,t){var n="number"==typeof t.jvbRTT,r=this._avgRemoteRTTMap.get(e);!r&&n&&(r=new f(e+"_stat_rtt"),this._avgRemoteRTTMap.set(e,r)),n?r.addNext(t.jvbRTT):r&&this._avgRemoteRTTMap.delete(e)}},{key:"_resetAvgStats",value:function(){this._avgRTT.reset(),this._avgRemoteRTTMap&&this._avgRemoteRTTMap.clear(),this._sampleIdx=0}},{key:"dispose",value:function(){var e=this._avgRtpStatsReporter._conference;e.statistics.removeConnectionStatsListener(this._onConnectionStats),this.isP2P||(e.off(a.REMOTE_STATS_UPDATED,this._onRemoteStatsUpdated),e.off(s.USER_LEFT,this._onUserLeft))}}]),e})(),v=(function(){function e(t,n){var i=this;r(this,e),this._n=n,n>0?(h.info("Avg RTP stats will be calculated every "+n+" samples"),this._sampleIdx=0,this._conference=t,this._avgAudioBitrateUp=new f("bitrate_audio_upload"),this._avgAudioBitrateDown=new f("bitrate_audio_download"),this._avgVideoBitrateUp=new f("bitrate_video_upload"),this._avgVideoBitrateDown=new f("bitrate_video_download"),this._avgBandwidthUp=new f("bandwidth_upload"),this._avgBandwidthDown=new f("bandwidth_download"),this._avgPacketLossTotal=new f("packet_loss_total"),this._avgPacketLossUp=new f("packet_loss_upload"),this._avgPacketLossDown=new f("packet_loss_download"),this._avgRemoteFPS=new f("framerate_remote"),this._avgRemoteScreenFPS=new f("framerate_screen_remote"),this._avgLocalFPS=new f("framerate_local"),this._avgLocalScreenFPS=new f("framerate_screen_local"),this._avgRemoteCameraPixels=new f("pixels_remote"),this._avgRemoteScreenPixels=new f("pixels_screen_remote"),this._avgLocalCameraPixels=new f("pixels_local"),this._avgLocalScreenPixels=new f("pixels_screen_local"),this._avgCQ=new f("connection_quality"),this._onLocalStatsUpdated=function(e){return i._calculateAvgStats(e)},t.on(a.LOCAL_STATS_UPDATED,this._onLocalStatsUpdated),this._onP2PStatusChanged=function(){h.debug("Resetting average stats calculation"),i._resetAvgStats(),i.jvbStatsMonitor._resetAvgStats(),i.p2pStatsMonitor._resetAvgStats()},t.on(s.P2P_STATUS,this._onP2PStatusChanged),this._onJvb121StatusChanged=function(e,t){!0===t&&(h.info("Resetting JVB avg RTP stats"),i._resetAvgJvbStats())},t.on(s.JVB121_STATUS,this._onJvb121StatusChanged),this.jvbStatsMonitor=new m(this,!1,n),this.p2pStatsMonitor=new m(this,!0,n)):h.info("Avg RTP stats reports are disabled.")}return p(e,[{key:"_calculateAvgStats",value:function(e){if(e){var t=this._conference.isP2PActive(),n=this._conference.getParticipantCount();if(t||!(n<2)){var r=e.bitrate,o=e.bandwidth,a=e.packetLoss,s=e.framerate,c=e.resolution;if(r)if(o)if(a)if(s)if(c){if(this._avgAudioBitrateUp.addNext(r.audio.upload),this._avgAudioBitrateDown.addNext(r.audio.download),this._avgVideoBitrateUp.addNext(r.video.upload),this._avgVideoBitrateDown.addNext(r.video.download),u.b.supportsBandwidthStatistics()&&(this._avgBandwidthUp.addNext(o.upload),this._avgBandwidthDown.addNext(o.download)),this._avgPacketLossUp.addNext(a.upload),this._avgPacketLossDown.addNext(a.download),this._avgPacketLossTotal.addNext(a.total),this._avgCQ.addNext(e.connectionQuality),s&&(this._avgRemoteFPS.addNext(this._calculateAvgVideoFps(s,!1,d.CAMERA)),this._avgRemoteScreenFPS.addNext(this._calculateAvgVideoFps(s,!1,d.DESKTOP)),this._avgLocalFPS.addNext(this._calculateAvgVideoFps(s,!0,d.CAMERA)),this._avgLocalScreenFPS.addNext(this._calculateAvgVideoFps(s,!0,d.DESKTOP))),c&&(this._avgRemoteCameraPixels.addNext(this._calculateAvgVideoPixels(c,!1,d.CAMERA)),this._avgRemoteScreenPixels.addNext(this._calculateAvgVideoPixels(c,!1,d.DESKTOP)),this._avgLocalCameraPixels.addNext(this._calculateAvgVideoPixels(c,!0,d.CAMERA)),this._avgLocalScreenPixels.addNext(this._calculateAvgVideoPixels(c,!0,d.DESKTOP))),this._sampleIdx+=1,this._sampleIdx>=this._n){var p={p2p:t,conference_size:n};e.transport&&e.transport.length&&babelHelpers.extends(p,{local_candidate_type:e.transport[0].localCandidateType,remote_candidate_type:e.transport[0].remoteCandidateType,transport_type:e.transport[0].type}),this._avgAudioBitrateUp.appendReport(p),this._avgAudioBitrateDown.appendReport(p),this._avgVideoBitrateUp.appendReport(p),this._avgVideoBitrateDown.appendReport(p),u.b.supportsBandwidthStatistics()&&(this._avgBandwidthUp.appendReport(p),this._avgBandwidthDown.appendReport(p)),this._avgPacketLossUp.appendReport(p),this._avgPacketLossDown.appendReport(p),this._avgPacketLossTotal.appendReport(p),this._avgRemoteFPS.appendReport(p),isNaN(this._avgRemoteScreenFPS.calculate())||this._avgRemoteScreenFPS.appendReport(p),this._avgLocalFPS.appendReport(p),isNaN(this._avgLocalScreenFPS.calculate())||this._avgLocalScreenFPS.appendReport(p),this._avgRemoteCameraPixels.appendReport(p),isNaN(this._avgRemoteScreenPixels.calculate())||this._avgRemoteScreenPixels.appendReport(p),this._avgLocalCameraPixels.appendReport(p),isNaN(this._avgLocalScreenPixels.calculate())||this._avgLocalScreenPixels.appendReport(p),this._avgCQ.appendReport(p),l.a.sendAnalytics(Object(i.D)(p)),this._resetAvgStats()}}else h.error("No resolution");else h.error('No "framerate"');else h.error('No "packetloss"');else h.error('No "bandwidth"');else h.error('No "bitrate"')}}else h.error("No stats")}},{key:"_calculateAvgVideoPixels",value:function(e,t,n){var r=0,i=0,o=this._conference.myUserId(),a=!0,s=!1,c=void 0;try{for(var u,l=Object.keys(e)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(a=(u=l.next()).done);a=!0){var d=u.value;if(t?d===o:d!==o){var p=t?null:this._conference.getParticipantById(d),h=e[d];if((t||p)&&h){var f=this._calculatePeerAvgVideoPixels(h,p,n);isNaN(f)||(r+=f,i+=1)}}}}catch(e){s=!0,c=e}finally{try{!a&&l.return&&l.return()}finally{if(s)throw c}}return r/i}},{key:"_calculatePeerAvgVideoPixels",value:function(e,t,n){var r=Object.keys(e).map(function(e){return Number(e)}),i=null,o=this._conference.getActivePeerConnection();t?(i=t.getTracksByMediaType(c.b))&&(r=r.filter(function(e){return i.find(function(t){return!t.isMuted()&&t.getSSRC()===e&&t.videoType===n})})):(i=this._conference.getLocalTracks(c.b),r=r.filter(function(e){return i.find(function(t){return!t.isMuted()&&o.getLocalSSRC(t)===e&&t.videoType===n})}));var a=0,s=0,u=!0,l=!1,d=void 0;try{for(var p,h=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=h.next()).done);u=!0){var f=p.value,m=Number(e[f].height)*Number(e[f].width);!isNaN(m)&&m>0&&(a+=m,s+=1)}}catch(e){l=!0,d=e}finally{try{!u&&h.return&&h.return()}finally{if(l)throw d}}return a/s}},{key:"_calculateAvgVideoFps",value:function(e,t,n){var r=0,i=0,o=this._conference.myUserId(),a=!0,s=!1,c=void 0;try{for(var u,l=Object.keys(e)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(a=(u=l.next()).done);a=!0){var d=u.value;if(t?d===o:d!==o){var p=t?null:this._conference.getParticipantById(d),h=e[d];if((t||p)&&h){var f=this._calculatePeerAvgVideoFps(h,p,n);isNaN(f)||(r+=f,i+=1)}}}}catch(e){s=!0,c=e}finally{try{!a&&l.return&&l.return()}finally{if(s)throw c}}return r/i}},{key:"_calculatePeerAvgVideoFps",value:function(e,t,n){var r=Object.keys(e).map(function(e){return Number(e)}),i=null,o=this._conference.getActivePeerConnection();t?(i=t.getTracksByMediaType(c.b))&&(r=r.filter(function(e){return i.find(function(t){return!t.isMuted()&&t.getSSRC()===e&&t.videoType===n})})):(i=this._conference.getLocalTracks(c.b),r=r.filter(function(e){return i.find(function(t){return!t.isMuted()&&o.getLocalSSRC(t)===e&&t.videoType===n})}));var a=0,s=0,u=!0,l=!1,d=void 0;try{for(var p,h=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=h.next()).done);u=!0){var f=p.value,m=Number(e[f]);!isNaN(m)&&m>0&&(a+=m,s+=1)}}catch(e){l=!0,d=e}finally{try{!u&&h.return&&h.return()}finally{if(l)throw d}}return a/s}},{key:"_resetAvgJvbStats",value:function(){this._resetAvgStats(),this.jvbStatsMonitor._resetAvgStats()}},{key:"_resetAvgStats",value:function(){this._avgAudioBitrateUp.reset(),this._avgAudioBitrateDown.reset(),this._avgVideoBitrateUp.reset(),this._avgVideoBitrateDown.reset(),this._avgBandwidthUp.reset(),this._avgBandwidthDown.reset(),this._avgPacketLossUp.reset(),this._avgPacketLossDown.reset(),this._avgPacketLossTotal.reset(),this._avgRemoteFPS.reset(),this._avgRemoteScreenFPS.reset(),this._avgLocalFPS.reset(),this._avgLocalScreenFPS.reset(),this._avgRemoteCameraPixels.reset(),this._avgRemoteScreenPixels.reset(),this._avgLocalCameraPixels.reset(),this._avgLocalScreenPixels.reset(),this._avgCQ.reset(),this._sampleIdx=0}},{key:"dispose",value:function(){this._conference.off(s.P2P_STATUS,this._onP2PStatusChanged),this._conference.off(a.LOCAL_STATS_UPDATED,this._onLocalStatsUpdated),this._conference.off(s.JVB121_STATUS,this._onJvb121StatusChanged),this.jvbStatsMonitor.dispose(),this.p2pStatsMonitor.dispose()}}]),e})();t.a=v}).call(t,"modules/statistics/AvgRTPStatsReporter.js")},function(e,t,n){function r(e,t){this.logStorage=e,this.stringifyObjects=!(!t||!t.stringifyObjects)&&t.stringifyObjects,this.storeInterval=t&&t.storeInterval?t.storeInterval:3e4,this.maxEntryLength=t&&t.maxEntryLength?t.maxEntryLength:1e4,Object.keys(o.levels).forEach(function(e){this[o.levels[e]]=function(e){this._log.apply(this,arguments)}.bind(this,e)}.bind(this)),this.storeLogsIntervalID=null,this.queue=[],this.totalLen=0,this.outputCache=[]}var i="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},o=n(38);r.prototype.stringify=function(e){try{return JSON.stringify(e)}catch(e){return"[object with circular refs?]"}},r.prototype.formatLogMessage=function(e){for(var t="",n=1,r=arguments.length;n=this.maxEntryLength&&this._flush(!0,!0)},r.prototype.start=function(){this._reschedulePublishInterval()},r.prototype._reschedulePublishInterval=function(){this.storeLogsIntervalID&&(window.clearTimeout(this.storeLogsIntervalID),this.storeLogsIntervalID=null),this.storeLogsIntervalID=window.setTimeout(this._flush.bind(this,!1,!0),this.storeInterval)},r.prototype.flush=function(){this._flush(!1,!0)},r.prototype._flush=function(e,t){this.totalLen>0&&(this.logStorage.isReady()||e)&&(this.logStorage.isReady()?(this.outputCache.length&&(this.outputCache.forEach(function(e){this.logStorage.storeLogs(e)}.bind(this)),this.outputCache=[]),this.logStorage.storeLogs(this.queue)):this.outputCache.push(this.queue),this.queue=[],this.totalLen=0),t&&this._reschedulePublishInterval()},r.prototype.stop=function(){this._flush(!1,!1)},e.exports=r},function(e,t,n){"use strict";(function(e){function r(){var e=navigator.userAgent;if(e.match(/Electron/)){var t=e.match(/Electron\/([\d.]+)/)[1];return d.info("This appears to be Electron, ver: "+t),{name:u.ELECTRON,version:t}}}function i(){var e=navigator.userAgent;if(e.match(/JitsiMeetNW/)){var t=e.match(/JitsiMeetNW\/([\d.]+)/)[1];return d.info("This appears to be JitsiMeetNW, ver: "+t),{name:u.NWJS,version:t}}}function o(){var e=navigator.userAgent.match(/\b(react[ \t_-]*native)(?:\/(\S+))?/i),t=void 0;if(e||"ReactNative"===navigator.product){var n=void 0;return e&&e.length>2&&(n=e[1],t=e[2]),n||(n="react-native"),t||(t="unknown"),d.info("This appears to be "+n+", ver: "+t),{name:u.REACT_NATIVE,version:t}}}var a=n(69),s=n.n(a),c=n(0),u=(n.n(c),n(39)),l=(function(){function e(e,t){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:(function(){for(var e=void 0,t=[o,r,i],n=0;n1&&n[1]||""}function n(t){var n=e.match(t);return n&&n.length>1&&n[2]||""}var r,i=t(/(ipod|iphone|ipad)/i).toLowerCase(),a=!/like android/i.test(e)&&/android/i.test(e),s=/nexus\s*[0-6]\s*/i.test(e),c=!s&&/nexus\s*[0-9]+/i.test(e),u=/CrOS/.test(e),l=/silk/i.test(e),d=/sailfish/i.test(e),p=/tizen/i.test(e),h=/(web|hpw)os/i.test(e),f=/windows phone/i.test(e),m=(/SamsungBrowser/i.test(e),!f&&/windows/i.test(e)),v=!i&&!l&&/macintosh/i.test(e),g=!a&&!d&&!p&&!h&&/linux/i.test(e),y=n(/edg([ea]|ios)\/(\d+(\.\d+)?)/i),b=t(/version\/(\d+(\.\d+)?)/i),S=/tablet/i.test(e)&&!/tablet pc/i.test(e),_=!S&&/[^-]mobi/i.test(e),T=/xbox/i.test(e);/opera/i.test(e)?r={name:"Opera",opera:o,version:b||t(/(?:opera|opr|opios)[\s\/](\d+(\.\d+)?)/i)}:/opr\/|opios/i.test(e)?r={name:"Opera",opera:o,version:t(/(?:opr|opios)[\s\/](\d+(\.\d+)?)/i)||b}:/SamsungBrowser/i.test(e)?r={name:"Samsung Internet for Android",samsungBrowser:o,version:b||t(/(?:SamsungBrowser)[\s\/](\d+(\.\d+)?)/i)}:/coast/i.test(e)?r={name:"Opera Coast",coast:o,version:b||t(/(?:coast)[\s\/](\d+(\.\d+)?)/i)}:/yabrowser/i.test(e)?r={name:"Yandex Browser",yandexbrowser:o,version:b||t(/(?:yabrowser)[\s\/](\d+(\.\d+)?)/i)}:/ucbrowser/i.test(e)?r={name:"UC Browser",ucbrowser:o,version:t(/(?:ucbrowser)[\s\/](\d+(?:\.\d+)+)/i)}:/mxios/i.test(e)?r={name:"Maxthon",maxthon:o,version:t(/(?:mxios)[\s\/](\d+(?:\.\d+)+)/i)}:/epiphany/i.test(e)?r={name:"Epiphany",epiphany:o,version:t(/(?:epiphany)[\s\/](\d+(?:\.\d+)+)/i)}:/puffin/i.test(e)?r={name:"Puffin",puffin:o,version:t(/(?:puffin)[\s\/](\d+(?:\.\d+)?)/i)}:/sleipnir/i.test(e)?r={name:"Sleipnir",sleipnir:o,version:t(/(?:sleipnir)[\s\/](\d+(?:\.\d+)+)/i)}:/k-meleon/i.test(e)?r={name:"K-Meleon",kMeleon:o,version:t(/(?:k-meleon)[\s\/](\d+(?:\.\d+)+)/i)}:f?(r={name:"Windows Phone",osname:"Windows Phone",windowsphone:o},y?(r.msedge=o,r.version=y):(r.msie=o,r.version=t(/iemobile\/(\d+(\.\d+)?)/i))):/msie|trident/i.test(e)?r={name:"Internet Explorer",msie:o,version:t(/(?:msie |rv:)(\d+(\.\d+)?)/i)}:u?r={name:"Chrome",osname:"Chrome OS",chromeos:o,chromeBook:o,chrome:o,version:t(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:/edg([ea]|ios)/i.test(e)?r={name:"Microsoft Edge",msedge:o,version:y}:/vivaldi/i.test(e)?r={name:"Vivaldi",vivaldi:o,version:t(/vivaldi\/(\d+(\.\d+)?)/i)||b}:d?r={name:"Sailfish",osname:"Sailfish OS",sailfish:o,version:t(/sailfish\s?browser\/(\d+(\.\d+)?)/i)}:/seamonkey\//i.test(e)?r={name:"SeaMonkey",seamonkey:o,version:t(/seamonkey\/(\d+(\.\d+)?)/i)}:/firefox|iceweasel|fxios/i.test(e)?(r={name:"Firefox",firefox:o,version:t(/(?:firefox|iceweasel|fxios)[ \/](\d+(\.\d+)?)/i)},/\((mobile|tablet);[^\)]*rv:[\d\.]+\)/i.test(e)&&(r.firefoxos=o,r.osname="Firefox OS")):l?r={name:"Amazon Silk",silk:o,version:t(/silk\/(\d+(\.\d+)?)/i)}:/phantom/i.test(e)?r={name:"PhantomJS",phantom:o,version:t(/phantomjs\/(\d+(\.\d+)?)/i)}:/slimerjs/i.test(e)?r={name:"SlimerJS",slimer:o,version:t(/slimerjs\/(\d+(\.\d+)?)/i)}:/blackberry|\bbb\d+/i.test(e)||/rim\stablet/i.test(e)?r={name:"BlackBerry",osname:"BlackBerry OS",blackberry:o,version:b||t(/blackberry[\d]+\/(\d+(\.\d+)?)/i)}:h?(r={name:"WebOS",osname:"WebOS",webos:o,version:b||t(/w(?:eb)?osbrowser\/(\d+(\.\d+)?)/i)},/touchpad\//i.test(e)&&(r.touchpad=o)):/bada/i.test(e)?r={name:"Bada",osname:"Bada",bada:o,version:t(/dolfin\/(\d+(\.\d+)?)/i)}:p?r={name:"Tizen",osname:"Tizen",tizen:o,version:t(/(?:tizen\s?)?browser\/(\d+(\.\d+)?)/i)||b}:/qupzilla/i.test(e)?r={name:"QupZilla",qupzilla:o,version:t(/(?:qupzilla)[\s\/](\d+(?:\.\d+)+)/i)||b}:/chromium/i.test(e)?r={name:"Chromium",chromium:o,version:t(/(?:chromium)[\s\/](\d+(?:\.\d+)?)/i)||b}:/chrome|crios|crmo/i.test(e)?r={name:"Chrome",chrome:o,version:t(/(?:chrome|crios|crmo)\/(\d+(\.\d+)?)/i)}:a?r={name:"Android",version:b}:/safari|applewebkit/i.test(e)?(r={name:"Safari",safari:o},b&&(r.version=b)):i?(r={name:"iphone"==i?"iPhone":"ipad"==i?"iPad":"iPod"},b&&(r.version=b)):r=/googlebot/i.test(e)?{name:"Googlebot",googlebot:o,version:t(/googlebot\/(\d+(\.\d+))/i)||b}:{name:t(/^(.*)\/(.*) /),version:n(/^(.*)\/(.*) /)},!r.msedge&&/(apple)?webkit/i.test(e)?(/(apple)?webkit\/537\.36/i.test(e)?(r.name=r.name||"Blink",r.blink=o):(r.name=r.name||"Webkit",r.webkit=o),!r.version&&b&&(r.version=b)):!r.opera&&/gecko\//i.test(e)&&(r.name=r.name||"Gecko",r.gecko=o,r.version=r.version||t(/gecko\/(\d+(\.\d+)?)/i)),r.windowsphone||!a&&!r.silk?!r.windowsphone&&i?(r[i]=o,r.ios=o,r.osname="iOS"):v?(r.mac=o,r.osname="macOS"):T?(r.xbox=o,r.osname="Xbox"):m?(r.windows=o,r.osname="Windows"):g&&(r.linux=o,r.osname="Linux"):(r.android=o,r.osname="Android");var C="";r.windows?C=(function(e){switch(t(/Windows ((NT|XP)( \d\d?.\d)?)/i)){case"NT":return"NT";case"XP":return"XP";case"NT 5.0":return"2000";case"NT 5.1":return"XP";case"NT 5.2":return"2003";case"NT 6.0":return"Vista";case"NT 6.1":return"7";case"NT 6.2":return"8";case"NT 6.3":return"8.1";case"NT 10.0":return"10";default:return}})():r.windowsphone?C=t(/windows phone (?:os)?\s?(\d+(\.\d+)*)/i):r.mac?C=(C=t(/Mac OS X (\d+([_\.\s]\d+)*)/i)).replace(/[_\s]/g,"."):i?C=(C=t(/os (\d+([_\s]\d+)*) like mac os x/i)).replace(/[_\s]/g,"."):a?C=t(/android[ \/-](\d+(\.\d+)*)/i):r.webos?C=t(/(?:web|hpw)os\/(\d+(\.\d+)*)/i):r.blackberry?C=t(/rim\stablet\sos\s(\d+(\.\d+)*)/i):r.bada?C=t(/bada\/(\d+(\.\d+)*)/i):r.tizen&&(C=t(/tizen[\/\s](\d+(\.\d+)*)/i)),C&&(r.osversion=C);var E=!r.windows&&C.split(".")[0];return S||c||"ipad"==i||a&&(3==E||E>=4&&!_)||r.silk?r.tablet=o:(_||"iphone"==i||"ipod"==i||a||s||r.blackberry||r.webos||r.bada)&&(r.mobile=o),r.msedge||r.msie&&r.version>=10||r.yandexbrowser&&r.version>=15||r.vivaldi&&r.version>=1||r.chrome&&r.version>=20||r.samsungBrowser&&r.version>=4||r.firefox&&r.version>=20||r.safari&&r.version>=6||r.opera&&r.version>=10||r.ios&&r.osversion&&r.osversion.split(".")[0]>=6||r.blackberry&&r.version>=10.1||r.chromium&&r.version>=20?r.a=o:r.msie&&r.version<10||r.chrome&&r.version<20||r.firefox&&r.version<20||r.safari&&r.version<6||r.opera&&r.version<10||r.ios&&r.osversion&&r.osversion.split(".")[0]<6||r.chromium&&r.version<20?r.c=o:r.x=o,r}function t(e){return e.split(".").length}function n(e,t){var n,r=[];if(Array.prototype.map)return Array.prototype.map.call(e,t);for(n=0;n=0;){if(i[0][r]>i[1][r])return 1;if(i[0][r]!==i[1][r])return-1;if(0===r)return 0}}function i(t,n,i){var o=a;"string"==typeof n&&(i=n,n=void 0),void 0===n&&(n=!1),i&&(o=e(i));var s=""+o.version;for(var c in t)if(t.hasOwnProperty(c)&&o[c]){if("string"!=typeof t[c])throw new Error("Browser version in the minVersion map should be a string: "+c+": "+String(t));return r([s,t[c]])<0}return n}var o=!0,a=e("undefined"!=typeof navigator?navigator.userAgent||"":"");return a.test=function(e){for(var t=0;t1&&void 0!==arguments[1]?arguments[1]:{},n=null;"string"==typeof e?n={type:r.q,action:e,actionSubject:e,source:e,attributes:t}:"object"===(void 0===e?"undefined":s(e))&&(n=e),this._verifyRequiredFields(n)?this._sendEvent(n):u.error("Dropping a mis-formatted event: "+JSON.stringify(n))}},{key:"_verifyRequiredFields",value:function(e){if(!e)return!1;e.type||(e.type=r.q);var t=e.type;return t!==r.q&&t!==r.r&&t!==r.t&&t!==r.s?(u.error("Unknown event type: "+t),!1):t===r.r?Boolean(e.name):(e.action=e.action||e.name||e.actionSubject,e.actionSubject=e.actionSubject||e.name||e.action,e.source=e.source||e.name||e.action||e.actionSubject,e.action&&e.actionSubject&&e.source?!!(t!==r.s||(e.objectType=e.objectType||"generic-object-type",e.containerType=e.containerType||"conference","conference"!==e.containerType||e.containerId||(e.containerId=this.conferenceName),e.objectType&&e.objectId&&e.containerType&&e.containerId))||(u.error("Required field missing (containerId, containerType, objectId or objectType)"),!1):(u.error("Required field missing (action, actionSubject or source)"),!1))}},{key:"_maybeCacheEvent",value:function(e){return!!this.cache&&(this.cache.push(e),this.cache.length>100&&this.cache.splice(0,1),!0)}},{key:"_sendEvent",value:function(e){if(this._maybeCacheEvent(e));else{this._appendPermanentProperties(e);var t=!0,n=!1,r=void 0;try{for(var i,o=this.analyticsHandlers["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;try{a.sendEvent(e)}catch(e){u.warn("Error sending analytics event: "+e)}}}catch(e){n=!0,r=e}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}}}},{key:"_appendPermanentProperties",value:function(e){e.attributes||(e.attributes={}),e.attributes=babelHelpers.extends(e.attributes,this.permanentProperties)}}]),e})();t.a=new l}).call(t,"modules/statistics/AnalyticsAdapter.js")},function(e,t,n){var r=n(19),i=["Aaliyah","Aaron","Abagail","Abbey","Abbie","Abbigail","Abby","Abdiel","Abdul","Abdullah","Abe","Abel","Abelardo","Abigail","Abigale","Abigayle","Abner","Abraham","Ada","Adah","Adalberto","Adaline","Adam","Adan","Addie","Addison","Adela","Adelbert","Adele","Adelia","Adeline","Adell","Adella","Adelle","Aditya","Adolf","Adolfo","Adolph","Adolphus","Adonis","Adrain","Adrian","Adriana","Adrianna","Adriel","Adrien","Adrienne","Afton","Aglae","Agnes","Agustin","Agustina","Ahmad","Ahmed","Aida","Aidan","Aiden","Aileen","Aisha","Aiyana","Akeem","Al","Alaina","Alan","Alana","Alanis","Alanna","Alayna","Alba","Albert","Alberta","Albertha","Alberto","Albin","Albina","Alda","Alden","Alec","Aleen","Alejandra","Alejandrin","Alek","Alena","Alene","Alessandra","Alessandro","Alessia","Aletha","Alex","Alexa","Alexander","Alexandra","Alexandre","Alexandrea","Alexandria","Alexandrine","Alexandro","Alexane","Alexanne","Alexie","Alexis","Alexys","Alexzander","Alf","Alfonso","Alfonzo","Alford","Alfred","Alfreda","Alfredo","Ali","Alia","Alice","Alicia","Alisa","Alisha","Alison","Alivia","Aliya","Aliyah","Aliza","Alize","Allan","Allen","Allene","Allie","Allison","Ally","Alphonso","Alta","Althea","Alva","Alvah","Alvena","Alvera","Alverta","Alvina","Alvis","Alyce","Alycia","Alysa","Alysha","Alyson","Alysson","Amalia","Amanda","Amani","Amara","Amari","Amaya","Amber","Ambrose","Amelia","Amelie","Amely","America","Americo","Amie","Amina","Amir","Amira","Amiya","Amos","Amparo","Amy","Amya","Ana","Anabel","Anabelle","Anahi","Anais","Anastacio","Anastasia","Anderson","Andre","Andreane","Andreanne","Andres","Andrew","Andy","Angel","Angela","Angelica","Angelina","Angeline","Angelita","Angelo","Angie","Angus","Anibal","Anika","Anissa","Anita","Aniya","Aniyah","Anjali","Anna","Annabel","Annabell","Annabelle","Annalise","Annamae","Annamarie","Anne","Annetta","Annette","Annie","Ansel","Ansley","Anthony","Antoinette","Antone","Antonetta","Antonette","Antonia","Antonietta","Antonina","Antonio","Antwan","Antwon","Anya","April","Ara","Araceli","Aracely","Arch","Archibald","Ardella","Arden","Ardith","Arely","Ari","Ariane","Arianna","Aric","Ariel","Arielle","Arjun","Arlene","Arlie","Arlo","Armand","Armando","Armani","Arnaldo","Arne","Arno","Arnold","Arnoldo","Arnulfo","Aron","Art","Arthur","Arturo","Arvel","Arvid","Arvilla","Aryanna","Asa","Asha","Ashlee","Ashleigh","Ashley","Ashly","Ashlynn","Ashton","Ashtyn","Asia","Assunta","Astrid","Athena","Aubree","Aubrey","Audie","Audra","Audreanne","Audrey","August","Augusta","Augustine","Augustus","Aurelia","Aurelie","Aurelio","Aurore","Austen","Austin","Austyn","Autumn","Ava","Avery","Avis","Axel","Ayana","Ayden","Ayla","Aylin","Baby","Bailee","Bailey","Barbara","Barney","Baron","Barrett","Barry","Bart","Bartholome","Barton","Baylee","Beatrice","Beau","Beaulah","Bell","Bella","Belle","Ben","Benedict","Benjamin","Bennett","Bennie","Benny","Benton","Berenice","Bernadette","Bernadine","Bernard","Bernardo","Berneice","Bernhard","Bernice","Bernie","Berniece","Bernita","Berry","Bert","Berta","Bertha","Bertram","Bertrand","Beryl","Bessie","Beth","Bethany","Bethel","Betsy","Bette","Bettie","Betty","Bettye","Beulah","Beverly","Bianka","Bill","Billie","Billy","Birdie","Blair","Blaise","Blake","Blanca","Blanche","Blaze","Bo","Bobbie","Bobby","Bonita","Bonnie","Boris","Boyd","Brad","Braden","Bradford","Bradley","Bradly","Brady","Braeden","Brain","Brandi","Brando","Brandon","Brandt","Brandy","Brandyn","Brannon","Branson","Brant","Braulio","Braxton","Brayan","Breana","Breanna","Breanne","Brenda","Brendan","Brenden","Brendon","Brenna","Brennan","Brennon","Brent","Bret","Brett","Bria","Brian","Briana","Brianne","Brice","Bridget","Bridgette","Bridie","Brielle","Brigitte","Brionna","Brisa","Britney","Brittany","Brock","Broderick","Brody","Brook","Brooke","Brooklyn","Brooks","Brown","Bruce","Bryana","Bryce","Brycen","Bryon","Buck","Bud","Buddy","Buford","Bulah","Burdette","Burley","Burnice","Buster","Cade","Caden","Caesar","Caitlyn","Cale","Caleb","Caleigh","Cali","Calista","Callie","Camden","Cameron","Camila","Camilla","Camille","Camren","Camron","Camryn","Camylle","Candace","Candelario","Candice","Candida","Candido","Cara","Carey","Carissa","Carlee","Carleton","Carley","Carli","Carlie","Carlo","Carlos","Carlotta","Carmel","Carmela","Carmella","Carmelo","Carmen","Carmine","Carol","Carolanne","Carole","Carolina","Caroline","Carolyn","Carolyne","Carrie","Carroll","Carson","Carter","Cary","Casandra","Casey","Casimer","Casimir","Casper","Cassandra","Cassandre","Cassidy","Cassie","Catalina","Caterina","Catharine","Catherine","Cathrine","Cathryn","Cathy","Cayla","Ceasar","Cecelia","Cecil","Cecile","Cecilia","Cedrick","Celestine","Celestino","Celia","Celine","Cesar","Chad","Chadd","Chadrick","Chaim","Chance","Chandler","Chanel","Chanelle","Charity","Charlene","Charles","Charley","Charlie","Charlotte","Chase","Chasity","Chauncey","Chaya","Chaz","Chelsea","Chelsey","Chelsie","Chesley","Chester","Chet","Cheyanne","Cheyenne","Chloe","Chris","Christ","Christa","Christelle","Christian","Christiana","Christina","Christine","Christop","Christophe","Christopher","Christy","Chyna","Ciara","Cicero","Cielo","Cierra","Cindy","Citlalli","Clair","Claire","Clara","Clarabelle","Clare","Clarissa","Clark","Claud","Claude","Claudia","Claudie","Claudine","Clay","Clemens","Clement","Clementina","Clementine","Clemmie","Cleo","Cleora","Cleta","Cletus","Cleve","Cleveland","Clifford","Clifton","Clint","Clinton","Clotilde","Clovis","Cloyd","Clyde","Coby","Cody","Colby","Cole","Coleman","Colin","Colleen","Collin","Colt","Colten","Colton","Columbus","Concepcion","Conner","Connie","Connor","Conor","Conrad","Constance","Constantin","Consuelo","Cooper","Cora","Coralie","Corbin","Cordelia","Cordell","Cordia","Cordie","Corene","Corine","Cornelius","Cornell","Corrine","Cortez","Cortney","Cory","Coty","Courtney","Coy","Craig","Crawford","Creola","Cristal","Cristian","Cristina","Cristobal","Cristopher","Cruz","Crystal","Crystel","Cullen","Curt","Curtis","Cydney","Cynthia","Cyril","Cyrus","Dagmar","Dahlia","Daija","Daisha","Daisy","Dakota","Dale","Dallas","Dallin","Dalton","Damaris","Dameon","Damian","Damien","Damion","Damon","Dan","Dana","Dandre","Dane","D'angelo","Dangelo","Danial","Daniela","Daniella","Danielle","Danika","Dannie","Danny","Dante","Danyka","Daphne","Daphnee","Daphney","Darby","Daren","Darian","Dariana","Darien","Dario","Darion","Darius","Darlene","Daron","Darrel","Darrell","Darren","Darrick","Darrin","Darrion","Darron","Darryl","Darwin","Daryl","Dashawn","Dasia","Dave","David","Davin","Davion","Davon","Davonte","Dawn","Dawson","Dax","Dayana","Dayna","Dayne","Dayton","Dean","Deangelo","Deanna","Deborah","Declan","Dedric","Dedrick","Dee","Deion","Deja","Dejah","Dejon","Dejuan","Delaney","Delbert","Delfina","Delia","Delilah","Dell","Della","Delmer","Delores","Delpha","Delphia","Delphine","Delta","Demarco","Demarcus","Demario","Demetris","Demetrius","Demond","Dena","Denis","Dennis","Deon","Deondre","Deontae","Deonte","Dereck","Derek","Derick","Deron","Derrick","Deshaun","Deshawn","Desiree","Desmond","Dessie","Destany","Destin","Destinee","Destiney","Destini","Destiny","Devan","Devante","Deven","Devin","Devon","Devonte","Devyn","Dewayne","Dewitt","Dexter","Diamond","Diana","Dianna","Diego","Dillan","Dillon","Dimitri","Dina","Dino","Dion","Dixie","Dock","Dolly","Dolores","Domenic","Domenica","Domenick","Domenico","Domingo","Dominic","Dominique","Don","Donald","Donato","Donavon","Donna","Donnell","Donnie","Donny","Dora","Dorcas","Dorian","Doris","Dorothea","Dorothy","Dorris","Dortha","Dorthy","Doug","Douglas","Dovie","Doyle","Drake","Drew","Duane","Dudley","Dulce","Duncan","Durward","Dustin","Dusty","Dwight","Dylan","Earl","Earlene","Earline","Earnest","Earnestine","Easter","Easton","Ebba","Ebony","Ed","Eda","Edd","Eddie","Eden","Edgar","Edgardo","Edison","Edmond","Edmund","Edna","Eduardo","Edward","Edwardo","Edwin","Edwina","Edyth","Edythe","Effie","Efrain","Efren","Eileen","Einar","Eino","Eladio","Elaina","Elbert","Elda","Eldon","Eldora","Eldred","Eldridge","Eleanora","Eleanore","Eleazar","Electa","Elena","Elenor","Elenora","Eleonore","Elfrieda","Eli","Elian","Eliane","Elias","Eliezer","Elijah","Elinor","Elinore","Elisa","Elisabeth","Elise","Eliseo","Elisha","Elissa","Eliza","Elizabeth","Ella","Ellen","Ellie","Elliot","Elliott","Ellis","Ellsworth","Elmer","Elmira","Elmo","Elmore","Elna","Elnora","Elody","Eloisa","Eloise","Elouise","Eloy","Elroy","Elsa","Else","Elsie","Elta","Elton","Elva","Elvera","Elvie","Elvis","Elwin","Elwyn","Elyse","Elyssa","Elza","Emanuel","Emelia","Emelie","Emely","Emerald","Emerson","Emery","Emie","Emil","Emile","Emilia","Emiliano","Emilie","Emilio","Emily","Emma","Emmalee","Emmanuel","Emmanuelle","Emmet","Emmett","Emmie","Emmitt","Emmy","Emory","Ena","Enid","Enoch","Enola","Enos","Enrico","Enrique","Ephraim","Era","Eriberto","Eric","Erica","Erich","Erick","Ericka","Erik","Erika","Erin","Erling","Erna","Ernest","Ernestina","Ernestine","Ernesto","Ernie","Ervin","Erwin","Eryn","Esmeralda","Esperanza","Esta","Esteban","Estefania","Estel","Estell","Estella","Estelle","Estevan","Esther","Estrella","Etha","Ethan","Ethel","Ethelyn","Ethyl","Ettie","Eudora","Eugene","Eugenia","Eula","Eulah","Eulalia","Euna","Eunice","Eusebio","Eva","Evalyn","Evan","Evangeline","Evans","Eve","Eveline","Evelyn","Everardo","Everett","Everette","Evert","Evie","Ewald","Ewell","Ezekiel","Ezequiel","Ezra","Fabian","Fabiola","Fae","Fannie","Fanny","Fatima","Faustino","Fausto","Favian","Fay","Faye","Federico","Felicia","Felicita","Felicity","Felipa","Felipe","Felix","Felton","Fermin","Fern","Fernando","Ferne","Fidel","Filiberto","Filomena","Finn","Fiona","Flavie","Flavio","Fleta","Fletcher","Flo","Florence","Florencio","Florian","Florida","Florine","Flossie","Floy","Floyd","Ford","Forest","Forrest","Foster","Frances","Francesca","Francesco","Francis","Francisca","Francisco","Franco","Frank","Frankie","Franz","Fred","Freda","Freddie","Freddy","Frederic","Frederick","Frederik","Frederique","Fredrick","Fredy","Freeda","Freeman","Freida","Frida","Frieda","Friedrich","Fritz","Furman","Gabe","Gabriel","Gabriella","Gabrielle","Gaetano","Gage","Gail","Gardner","Garett","Garfield","Garland","Garnet","Garnett","Garret","Garrett","Garrick","Garrison","Garry","Garth","Gaston","Gavin","Gay","Gayle","Gaylord","Gene","General","Genesis","Genevieve","Gennaro","Genoveva","Geo","Geoffrey","George","Georgette","Georgiana","Georgianna","Geovanni","Geovanny","Geovany","Gerald","Geraldine","Gerard","Gerardo","Gerda","Gerhard","Germaine","German","Gerry","Gerson","Gertrude","Gia","Gianni","Gideon","Gilbert","Gilberto","Gilda","Giles","Gillian","Gina","Gino","Giovani","Giovanna","Giovanni","Giovanny","Gisselle","Giuseppe","Gladyce","Gladys","Glen","Glenda","Glenna","Glennie","Gloria","Godfrey","Golda","Golden","Gonzalo","Gordon","Grace","Gracie","Graciela","Grady","Graham","Grant","Granville","Grayce","Grayson","Green","Greg","Gregg","Gregoria","Gregorio","Gregory","Greta","Gretchen","Greyson","Griffin","Grover","Guadalupe","Gudrun","Guido","Guillermo","Guiseppe","Gunnar","Gunner","Gus","Gussie","Gust","Gustave","Guy","Gwen","Gwendolyn","Hadley","Hailee","Hailey","Hailie","Hal","Haleigh","Haley","Halie","Halle","Hallie","Hank","Hanna","Hannah","Hans","Hardy","Harley","Harmon","Harmony","Harold","Harrison","Harry","Harvey","Haskell","Hassan","Hassie","Hattie","Haven","Hayden","Haylee","Hayley","Haylie","Hazel","Hazle","Heath","Heather","Heaven","Heber","Hector","Heidi","Helen","Helena","Helene","Helga","Hellen","Helmer","Heloise","Henderson","Henri","Henriette","Henry","Herbert","Herman","Hermann","Hermina","Herminia","Herminio","Hershel","Herta","Hertha","Hester","Hettie","Hilario","Hilbert","Hilda","Hildegard","Hillard","Hillary","Hilma","Hilton","Hipolito","Hiram","Hobart","Holden","Hollie","Hollis","Holly","Hope","Horace","Horacio","Hortense","Hosea","Houston","Howard","Howell","Hoyt","Hubert","Hudson","Hugh","Hulda","Humberto","Hunter","Hyman","Ian","Ibrahim","Icie","Ida","Idell","Idella","Ignacio","Ignatius","Ike","Ila","Ilene","Iliana","Ima","Imani","Imelda","Immanuel","Imogene","Ines","Irma","Irving","Irwin","Isaac","Isabel","Isabell","Isabella","Isabelle","Isac","Isadore","Isai","Isaiah","Isaias","Isidro","Ismael","Isobel","Isom","Israel","Issac","Itzel","Iva","Ivah","Ivory","Ivy","Izabella","Izaiah","Jabari","Jace","Jacey","Jacinthe","Jacinto","Jack","Jackeline","Jackie","Jacklyn","Jackson","Jacky","Jaclyn","Jacquelyn","Jacques","Jacynthe","Jada","Jade","Jaden","Jadon","Jadyn","Jaeden","Jaida","Jaiden","Jailyn","Jaime","Jairo","Jakayla","Jake","Jakob","Jaleel","Jalen","Jalon","Jalyn","Jamaal","Jamal","Jamar","Jamarcus","Jamel","Jameson","Jamey","Jamie","Jamil","Jamir","Jamison","Jammie","Jan","Jana","Janae","Jane","Janelle","Janessa","Janet","Janice","Janick","Janie","Janis","Janiya","Jannie","Jany","Jaquan","Jaquelin","Jaqueline","Jared","Jaren","Jarod","Jaron","Jarred","Jarrell","Jarret","Jarrett","Jarrod","Jarvis","Jasen","Jasmin","Jason","Jasper","Jaunita","Javier","Javon","Javonte","Jay","Jayce","Jaycee","Jayda","Jayde","Jayden","Jaydon","Jaylan","Jaylen","Jaylin","Jaylon","Jayme","Jayne","Jayson","Jazlyn","Jazmin","Jazmyn","Jazmyne","Jean","Jeanette","Jeanie","Jeanne","Jed","Jedediah","Jedidiah","Jeff","Jefferey","Jeffery","Jeffrey","Jeffry","Jena","Jenifer","Jennie","Jennifer","Jennings","Jennyfer","Jensen","Jerad","Jerald","Jeramie","Jeramy","Jerel","Jeremie","Jeremy","Jermain","Jermaine","Jermey","Jerod","Jerome","Jeromy","Jerrell","Jerrod","Jerrold","Jerry","Jess","Jesse","Jessica","Jessie","Jessika","Jessy","Jessyca","Jesus","Jett","Jettie","Jevon","Jewel","Jewell","Jillian","Jimmie","Jimmy","Jo","Joan","Joana","Joanie","Joanne","Joannie","Joanny","Joany","Joaquin","Jocelyn","Jodie","Jody","Joe","Joel","Joelle","Joesph","Joey","Johan","Johann","Johanna","Johathan","John","Johnathan","Johnathon","Johnnie","Johnny","Johnpaul","Johnson","Jolie","Jon","Jonas","Jonatan","Jonathan","Jonathon","Jordan","Jordane","Jordi","Jordon","Jordy","Jordyn","Jorge","Jose","Josefa","Josefina","Joseph","Josephine","Josh","Joshua","Joshuah","Josiah","Josiane","Josianne","Josie","Josue","Jovan","Jovani","Jovanny","Jovany","Joy","Joyce","Juana","Juanita","Judah","Judd","Jude","Judge","Judson","Judy","Jules","Julia","Julian","Juliana","Julianne","Julie","Julien","Juliet","Julio","Julius","June","Junior","Junius","Justen","Justice","Justina","Justine","Juston","Justus","Justyn","Juvenal","Juwan","Kacey","Kaci","Kacie","Kade","Kaden","Kadin","Kaela","Kaelyn","Kaia","Kailee","Kailey","Kailyn","Kaitlin","Kaitlyn","Kale","Kaleb","Kaleigh","Kaley","Kali","Kallie","Kameron","Kamille","Kamren","Kamron","Kamryn","Kane","Kara","Kareem","Karelle","Karen","Kari","Kariane","Karianne","Karina","Karine","Karl","Karlee","Karley","Karli","Karlie","Karolann","Karson","Kasandra","Kasey","Kassandra","Katarina","Katelin","Katelyn","Katelynn","Katharina","Katherine","Katheryn","Kathleen","Kathlyn","Kathryn","Kathryne","Katlyn","Katlynn","Katrina","Katrine","Kattie","Kavon","Kay","Kaya","Kaycee","Kayden","Kayla","Kaylah","Kaylee","Kayleigh","Kayley","Kayli","Kaylie","Kaylin","Keagan","Keanu","Keara","Keaton","Keegan","Keeley","Keely","Keenan","Keira","Keith","Kellen","Kelley","Kelli","Kellie","Kelly","Kelsi","Kelsie","Kelton","Kelvin","Ken","Kendall","Kendra","Kendrick","Kenna","Kennedi","Kennedy","Kenneth","Kennith","Kenny","Kenton","Kenya","Kenyatta","Kenyon","Keon","Keshaun","Keshawn","Keven","Kevin","Kevon","Keyon","Keyshawn","Khalid","Khalil","Kian","Kiana","Kianna","Kiara","Kiarra","Kiel","Kiera","Kieran","Kiley","Kim","Kimberly","King","Kip","Kira","Kirk","Kirsten","Kirstin","Kitty","Kobe","Koby","Kody","Kolby","Kole","Korbin","Korey","Kory","Kraig","Kris","Krista","Kristian","Kristin","Kristina","Kristofer","Kristoffer","Kristopher","Kristy","Krystal","Krystel","Krystina","Kurt","Kurtis","Kyla","Kyle","Kylee","Kyleigh","Kyler","Kylie","Kyra","Lacey","Lacy","Ladarius","Lafayette","Laila","Laisha","Lamar","Lambert","Lamont","Lance","Landen","Lane","Laney","Larissa","Laron","Larry","Larue","Laura","Laurel","Lauren","Laurence","Lauretta","Lauriane","Laurianne","Laurie","Laurine","Laury","Lauryn","Lavada","Lavern","Laverna","Laverne","Lavina","Lavinia","Lavon","Lavonne","Lawrence","Lawson","Layla","Layne","Lazaro","Lea","Leann","Leanna","Leanne","Leatha","Leda","Lee","Leif","Leila","Leilani","Lela","Lelah","Leland","Lelia","Lempi","Lemuel","Lenna","Lennie","Lenny","Lenora","Lenore","Leo","Leola","Leon","Leonard","Leonardo","Leone","Leonel","Leonie","Leonor","Leonora","Leopold","Leopoldo","Leora","Lera","Lesley","Leslie","Lesly","Lessie","Lester","Leta","Letha","Letitia","Levi","Lew","Lewis","Lexi","Lexie","Lexus","Lia","Liam","Liana","Libbie","Libby","Lila","Lilian","Liliana","Liliane","Lilla","Lillian","Lilliana","Lillie","Lilly","Lily","Lilyan","Lina","Lincoln","Linda","Lindsay","Lindsey","Linnea","Linnie","Linwood","Lionel","Lisa","Lisandro","Lisette","Litzy","Liza","Lizeth","Lizzie","Llewellyn","Lloyd","Logan","Lois","Lola","Lolita","Loma","Lon","London","Lonie","Lonnie","Lonny","Lonzo","Lora","Loraine","Loren","Lorena","Lorenz","Lorenza","Lorenzo","Lori","Lorine","Lorna","Lottie","Lou","Louie","Louisa","Lourdes","Louvenia","Lowell","Loy","Loyal","Loyce","Lucas","Luciano","Lucie","Lucienne","Lucile","Lucinda","Lucio","Lucious","Lucius","Lucy","Ludie","Ludwig","Lue","Luella","Luigi","Luis","Luisa","Lukas","Lula","Lulu","Luna","Lupe","Lura","Lurline","Luther","Luz","Lyda","Lydia","Lyla","Lynn","Lyric","Lysanne","Mabel","Mabelle","Mable","Mac","Macey","Maci","Macie","Mack","Mackenzie","Macy","Madaline","Madalyn","Maddison","Madeline","Madelyn","Madelynn","Madge","Madie","Madilyn","Madisen","Madison","Madisyn","Madonna","Madyson","Mae","Maegan","Maeve","Mafalda","Magali","Magdalen","Magdalena","Maggie","Magnolia","Magnus","Maia","Maida","Maiya","Major","Makayla","Makenna","Makenzie","Malachi","Malcolm","Malika","Malinda","Mallie","Mallory","Malvina","Mandy","Manley","Manuel","Manuela","Mara","Marc","Marcel","Marcelina","Marcelino","Marcella","Marcelle","Marcellus","Marcelo","Marcia","Marco","Marcos","Marcus","Margaret","Margarete","Margarett","Margaretta","Margarette","Margarita","Marge","Margie","Margot","Margret","Marguerite","Maria","Mariah","Mariam","Marian","Mariana","Mariane","Marianna","Marianne","Mariano","Maribel","Marie","Mariela","Marielle","Marietta","Marilie","Marilou","Marilyne","Marina","Mario","Marion","Marisa","Marisol","Maritza","Marjolaine","Marjorie","Marjory","Mark","Markus","Marlee","Marlen","Marlene","Marley","Marlin","Marlon","Marques","Marquis","Marquise","Marshall","Marta","Martin","Martina","Martine","Marty","Marvin","Mary","Maryam","Maryjane","Maryse","Mason","Mateo","Mathew","Mathias","Mathilde","Matilda","Matilde","Matt","Matteo","Mattie","Maud","Maude","Maudie","Maureen","Maurice","Mauricio","Maurine","Maverick","Mavis","Max","Maxie","Maxime","Maximilian","Maximillia","Maximillian","Maximo","Maximus","Maxine","Maxwell","May","Maya","Maybell","Maybelle","Maye","Maymie","Maynard","Mayra","Mazie","Mckayla","Mckenna","Mckenzie","Meagan","Meaghan","Meda","Megane","Meggie","Meghan","Mekhi","Melany","Melba","Melisa","Melissa","Mellie","Melody","Melvin","Melvina","Melyna","Melyssa","Mercedes","Meredith","Merl","Merle","Merlin","Merritt","Mertie","Mervin","Meta","Mia","Micaela","Micah","Michael","Michaela","Michale","Micheal","Michel","Michele","Michelle","Miguel","Mikayla","Mike","Mikel","Milan","Miles","Milford","Miller","Millie","Milo","Milton","Mina","Minerva","Minnie","Miracle","Mireille","Mireya","Misael","Missouri","Misty","Mitchel","Mitchell","Mittie","Modesta","Modesto","Mohamed","Mohammad","Mohammed","Moises","Mollie","Molly","Mona","Monica","Monique","Monroe","Monserrat","Monserrate","Montana","Monte","Monty","Morgan","Moriah","Morris","Mortimer","Morton","Mose","Moses","Moshe","Mossie","Mozell","Mozelle","Muhammad","Muriel","Murl","Murphy","Murray","Mustafa","Mya","Myah","Mylene","Myles","Myra","Myriam","Myrl","Myrna","Myron","Myrtice","Myrtie","Myrtis","Myrtle","Nadia","Nakia","Name","Nannie","Naomi","Naomie","Napoleon","Narciso","Nash","Nasir","Nat","Natalia","Natalie","Natasha","Nathan","Nathanael","Nathanial","Nathaniel","Nathen","Nayeli","Neal","Ned","Nedra","Neha","Neil","Nelda","Nella","Nelle","Nellie","Nels","Nelson","Neoma","Nestor","Nettie","Neva","Newell","Newton","Nia","Nicholas","Nicholaus","Nichole","Nick","Nicklaus","Nickolas","Nico","Nicola","Nicolas","Nicole","Nicolette","Nigel","Nikita","Nikki","Nikko","Niko","Nikolas","Nils","Nina","Noah","Noble","Noe","Noel","Noelia","Noemi","Noemie","Noemy","Nola","Nolan","Nona","Nora","Norbert","Norberto","Norene","Norma","Norris","Norval","Norwood","Nova","Novella","Nya","Nyah","Nyasia","Obie","Oceane","Ocie","Octavia","Oda","Odell","Odessa","Odie","Ofelia","Okey","Ola","Olaf","Ole","Olen","Oleta","Olga","Olin","Oliver","Ollie","Oma","Omari","Omer","Ona","Onie","Opal","Ophelia","Ora","Oral","Oran","Oren","Orie","Orin","Orion","Orland","Orlando","Orlo","Orpha","Orrin","Orval","Orville","Osbaldo","Osborne","Oscar","Osvaldo","Oswald","Oswaldo","Otha","Otho","Otilia","Otis","Ottilie","Ottis","Otto","Ova","Owen","Ozella","Pablo","Paige","Palma","Pamela","Pansy","Paolo","Paris","Parker","Pascale","Pasquale","Pat","Patience","Patricia","Patrick","Patsy","Pattie","Paul","Paula","Pauline","Paxton","Payton","Pearl","Pearlie","Pearline","Pedro","Peggie","Penelope","Percival","Percy","Perry","Pete","Peter","Petra","Peyton","Philip","Phoebe","Phyllis","Pierce","Pierre","Pietro","Pink","Pinkie","Piper","Polly","Porter","Precious","Presley","Preston","Price","Prince","Princess","Priscilla","Providenci","Prudence","Queen","Queenie","Quentin","Quincy","Quinn","Quinten","Quinton","Rachael","Rachel","Rachelle","Rae","Raegan","Rafael","Rafaela","Raheem","Rahsaan","Rahul","Raina","Raleigh","Ralph","Ramiro","Ramon","Ramona","Randal","Randall","Randi","Randy","Ransom","Raoul","Raphael","Raphaelle","Raquel","Rashad","Rashawn","Rasheed","Raul","Raven","Ray","Raymond","Raymundo","Reagan","Reanna","Reba","Rebeca","Rebecca","Rebeka","Rebekah","Reece","Reed","Reese","Regan","Reggie","Reginald","Reid","Reilly","Reina","Reinhold","Remington","Rene","Renee","Ressie","Reta","Retha","Retta","Reuben","Reva","Rex","Rey","Reyes","Reymundo","Reyna","Reynold","Rhea","Rhett","Rhianna","Rhiannon","Rhoda","Ricardo","Richard","Richie","Richmond","Rick","Rickey","Rickie","Ricky","Rico","Rigoberto","Riley","Rita","River","Robb","Robbie","Robert","Roberta","Roberto","Robin","Robyn","Rocio","Rocky","Rod","Roderick","Rodger","Rodolfo","Rodrick","Rodrigo","Roel","Rogelio","Roger","Rogers","Rolando","Rollin","Roma","Romaine","Roman","Ron","Ronaldo","Ronny","Roosevelt","Rory","Rosa","Rosalee","Rosalia","Rosalind","Rosalinda","Rosalyn","Rosamond","Rosanna","Rosario","Roscoe","Rose","Rosella","Roselyn","Rosemarie","Rosemary","Rosendo","Rosetta","Rosie","Rosina","Roslyn","Ross","Rossie","Rowan","Rowena","Rowland","Roxane","Roxanne","Roy","Royal","Royce","Rozella","Ruben","Rubie","Ruby","Rubye","Rudolph","Rudy","Rupert","Russ","Russel","Russell","Rusty","Ruth","Ruthe","Ruthie","Ryan","Ryann","Ryder","Rylan","Rylee","Ryleigh","Ryley","Sabina","Sabrina","Sabryna","Sadie","Sadye","Sage","Saige","Sallie","Sally","Salma","Salvador","Salvatore","Sam","Samanta","Samantha","Samara","Samir","Sammie","Sammy","Samson","Sandra","Sandrine","Sandy","Sanford","Santa","Santiago","Santina","Santino","Santos","Sarah","Sarai","Sarina","Sasha","Saul","Savanah","Savanna","Savannah","Savion","Scarlett","Schuyler","Scot","Scottie","Scotty","Seamus","Sean","Sebastian","Sedrick","Selena","Selina","Selmer","Serena","Serenity","Seth","Shad","Shaina","Shakira","Shana","Shane","Shanel","Shanelle","Shania","Shanie","Shaniya","Shanna","Shannon","Shanny","Shanon","Shany","Sharon","Shaun","Shawn","Shawna","Shaylee","Shayna","Shayne","Shea","Sheila","Sheldon","Shemar","Sheridan","Sherman","Sherwood","Shirley","Shyann","Shyanne","Sibyl","Sid","Sidney","Sienna","Sierra","Sigmund","Sigrid","Sigurd","Silas","Sim","Simeon","Simone","Sincere","Sister","Skye","Skyla","Skylar","Sofia","Soledad","Solon","Sonia","Sonny","Sonya","Sophia","Sophie","Spencer","Stacey","Stacy","Stan","Stanford","Stanley","Stanton","Stefan","Stefanie","Stella","Stephan","Stephania","Stephanie","Stephany","Stephen","Stephon","Sterling","Steve","Stevie","Stewart","Stone","Stuart","Summer","Sunny","Susan","Susana","Susanna","Susie","Suzanne","Sven","Syble","Sydnee","Sydney","Sydni","Sydnie","Sylvan","Sylvester","Sylvia","Tabitha","Tad","Talia","Talon","Tamara","Tamia","Tania","Tanner","Tanya","Tara","Taryn","Tate","Tatum","Tatyana","Taurean","Tavares","Taya","Taylor","Teagan","Ted","Telly","Terence","Teresa","Terrance","Terrell","Terrence","Terrill","Terry","Tess","Tessie","Tevin","Thad","Thaddeus","Thalia","Thea","Thelma","Theo","Theodora","Theodore","Theresa","Therese","Theresia","Theron","Thomas","Thora","Thurman","Tia","Tiana","Tianna","Tiara","Tierra","Tiffany","Tillman","Timmothy","Timmy","Timothy","Tina","Tito","Titus","Tobin","Toby","Tod","Tom","Tomas","Tomasa","Tommie","Toney","Toni","Tony","Torey","Torrance","Torrey","Toy","Trace","Tracey","Tracy","Travis","Travon","Tre","Tremaine","Tremayne","Trent","Trenton","Tressa","Tressie","Treva","Trever","Trevion","Trevor","Trey","Trinity","Trisha","Tristian","Tristin","Triston","Troy","Trudie","Trycia","Trystan","Turner","Twila","Tyler","Tyra","Tyree","Tyreek","Tyrel","Tyrell","Tyrese","Tyrique","Tyshawn","Tyson","Ubaldo","Ulices","Ulises","Una","Unique","Urban","Uriah","Uriel","Ursula","Vada","Valentin","Valentina","Valentine","Valerie","Vallie","Van","Vance","Vanessa","Vaughn","Veda","Velda","Vella","Velma","Velva","Vena","Verda","Verdie","Vergie","Verla","Verlie","Vern","Verna","Verner","Vernice","Vernie","Vernon","Verona","Veronica","Vesta","Vicenta","Vicente","Vickie","Vicky","Victor","Victoria","Vida","Vidal","Vilma","Vince","Vincent","Vincenza","Vincenzo","Vinnie","Viola","Violet","Violette","Virgie","Virgil","Virginia","Virginie","Vita","Vito","Viva","Vivian","Viviane","Vivianne","Vivien","Vivienne","Vladimir","Wade","Waino","Waldo","Walker","Wallace","Walter","Walton","Wanda","Ward","Warren","Watson","Wava","Waylon","Wayne","Webster","Weldon","Wellington","Wendell","Wendy","Werner","Westley","Weston","Whitney","Wilber","Wilbert","Wilburn","Wiley","Wilford","Wilfred","Wilfredo","Wilfrid","Wilhelm","Wilhelmine","Will","Willa","Willard","William","Willie","Willis","Willow","Willy","Wilma","Wilmer","Wilson","Wilton","Winfield","Winifred","Winnifred","Winona","Winston","Woodrow","Wyatt","Wyman","Xander","Xavier","Xzavier","Yadira","Yasmeen","Yasmin","Yasmine","Yazmin","Yesenia","Yessenia","Yolanda","Yoshiko","Yvette","Yvonne","Zachariah","Zachary","Zachery","Zack","Zackary","Zackery","Zakary","Zander","Zane","Zaria","Zechariah","Zelda","Zella","Zelma","Zena","Zetta","Zion","Zita","Zoe","Zoey","Zoie","Zoila","Zola","Zora","Zula"];e.exports={generateUsername:function(){return r.randomElement(i)+"-"+r.randomAlphanumStr(3)}}},function(e,t,n){"use strict";(function(e){var r=n(1),i=n(3),o=n.n(i),a=(function(){function e(e,t){for(var n=0;n3?a-3:0),l=3;l0||parseInt(T[C].bytesSent,10)>0)&&(I[C].push(T[C]),"true"!==T[C].googActiveConnection&&!0!==T[C].googActiveConnection||(v.activeConnectionIndex=I[C].length)):(I[C].push(T[C]),"true"!==T[C].selected&&!0!==T[C].selected||(v.activeConnectionIndex=I[C].length));else if(T.localCandidate)L[T.localCandidate.id]=T.localCandidate;else if(T.remoteCandidate)M[T.remoteCandidate.id]=T.remoteCandidate;else if(T.bwe)I.bwe=T.bwe;else if(T.trackStats)I.trackStats||(I.trackStats=[]),I.trackStats.push(T.trackStats);else if(T.codec)I.codec||(I.codec=[]),I.codec.push(T.codec);else if(T.candidatePair)I.candidatePair||(I.candidatePair=[]),I.candidatePair.push(T.candidatePair);else{void 0!==(w=nt(o,T.ssrc))&&w.localStartTime||(k=it(S,b,i,o,u,null),k&&(x=!0),w=nt(o,T.ssrc)),w&&(E=w.remoteUserID),void 0===E&&(E=S);var F={userID:E,data:T.data,reportType:T.reportType,streamType:T.streamType,ssrc:T.ssrc};void 0!==w&&(F.cname=w.cname,F.msid=w.msid,F.associatedVideoTag=w.associatedVideoTag,F.usage=w.usageLabel),I.streams.push(F)}if(v.statistics=I,I.Transport&&(A=(function(e,t){var n,r,i,o,a,s,c,u,l,d=!1,p="None",h=!1,f=W.codebase,m=function(e){if(e.typePreference){var t=e.typePreference>>24;return"rtp"===e.protocol&&e.address===n&&(t>=0&&t<=2&&(p=(function(e){var n="None";switch(t){case 0:n="TURN/TLS";break;case 1:n="TURN/TCP";break;case 2:n="TURN/UDP"}return n})()),!0)}return!1};for(c=0;c0&&(t[r].csioReceivedBwKbps=8*(parseInt(t[r].bytesReceived,10)-c)/l,t[r].csioSentBwKbps=8*(parseInt(t[r].bytesSent,10)-u)/l,t[r].csioIntBytesReceived=parseInt(t[r].bytesReceived,10)-c,t[r].csioIntBytesSent=parseInt(t[r].bytesSent,10)-u,void 0!==s.packetsSent&&void 0!==t[r].packetsSent&&(t[r].csioSentPacketRate=(parseInt(t[r].packetsSent,10)-parseInt(s.packetsSent,10))/(l/1e3),t[r].csioIntPacketsSent=parseInt(t[r].packetsSent,10)-parseInt(s.packetsSent,10)),void 0!==s.packetsReceived&&void 0!==t[r].packetsReceived&&(t[r].csioReceivedPacketRate=(parseInt(t[r].packetsReceived,10)-parseInt(s.packetsReceived,10))/(l/1e3),t[r].csioIntPacketsReceived=parseInt(t[r].packetsReceived,10)-parseInt(s.packetsReceived,10))),d.push(t[r]))}return d})(e,t,r)),i})(o,I.Transport,y,f)),I.bwe&&(H.bwe=I.bwe),void 0===o.processedStatsTupleArray&&(o.processedStatsTupleArray=[]),z.stats){var ne=(function(e){var t={},n=$t.getConferenceURL();return e.connectionState&&(t.connectionState=e.connectionState),e.fabricState&&(t.fabricState=e.fabricState),n&&(t.conferenceURL=n),t.streams={},null===e.streams?t:(e.streams.forEach(function(e){var n={cname:e.cname,ssrc:e.ssrc,msid:e.msid,remoteUserID:e.userID,usageLabel:e.usage,associatedVideoTag:e.associatedVideoTag};e.data.hasOwnProperty("csioIntFL")&&(n.fractionLoss=e.data.csioIntFL),e.data.hasOwnProperty("csioIntBRKbps")&&(n.bitrate=e.data.csioIntBRKbps),e.data.hasOwnProperty("csioMark")&&(n.quality=e.data.csioMark),e.data.hasOwnProperty("csioMediaType")&&(n.mediaType=e.data.csioMediaType),e.data.hasOwnProperty("googRtt")&&(n.rtt=e.data.googRtt),e.data.hasOwnProperty("mozRtt")&&(n.rtt=e.data.mozRtt),e.data.hasOwnProperty("roundTripTime")&&(n.rtt=e.data.roundTripTime),e.data.hasOwnProperty("jitter")&&(n.jitter=e.data.jitter),e.data.hasOwnProperty("googJitterReceived")&&(n.jitter=e.data.googJitterReceived),e.data.hasOwnProperty("audioOutputLevel")&&(n.audioOutputLevel=e.data.audioOutputLevel),e.data.hasOwnProperty("audioInputLevel")&&(n.audioInputLevel=e.data.audioInputLevel),e.data.hasOwnProperty("audioLevel")&&(n.audioLevel=e.data.audioLevel),e.data.hasOwnProperty("csioAvgRtt")&&(n.averageRTT=e.data.csioAvgRtt),e.data.hasOwnProperty("csioAvgJitter")&&(n.averageJitter=e.data.csioAvgJitter),e.data.hasOwnProperty("csioPktLossPercentage")&&(n.packetLossPercentage=e.data.csioPktLossPercentage),e.reportType&&"local"===e.reportType?e.streamType&&(n.statsType=e.streamType+"-rtp"):e.reportType&&"remote"===e.reportType&&e.streamType&&(n.statsType="remote-"+e.streamType+"-rtp"),t.streams[e.ssrc]=n}),t)})(H);z.stats(ne)}H.apiTS=f,H.batteryStatus={},H.batteryStatus.batteryLevel=(function(){if(ie)return ie.level})(),H.batteryStatus.isBatteryCharging=(function(){if(ie)return ie.charging})(),H.wifiStats=D,(function(e){if(se&&se.rtc_rtp_parameters&&se.rtc_rtp_parameters.encodings){var t=function(e){return et[e]?et[e]:(et[e]={total:0,above:0,ccDriven:0,sumMaxBitrateDiff:0,sumThroughputDiff:0,prevCsioAvgBRKbps:0},et[e])},n=0;se.rtc_rtp_parameters.encodings.forEach(function(e){e&&(n=Math.max(n,e.maxBitrate))});var r=0;if(ce){ce.encodings.forEach(function(e){r=Math.max(r,e.maxBitrate)});var i=0,o=0,a=null,s=null;e.streams.forEach(function(e){var c=e.data,u=t(c.ssrc);"video"===c.mediaType&&e.streamType===ke.outbound&&(0=Q&&(o.processedStatsTupleArray=[],ee.add(re,re.processedStatistics.length),re.msgStat=ee.getStat(),(function(e,t,n,i,o,a){var s=r();e.hasOwnProperty("token")&&e.hasOwnProperty("ucID")&&!de?(g(be.processedStats,e,a),t.latestEventSent=s,t.lastFabricState=t.pcState):(e.clockUnsynced=de,Z.cacheEvent({channel:be.processedStats,data:e}))})(re,o,0,0,0,e),o.lastProcessedStatsSentInterval=0)}}}}function d(e){var t={};if("inboundrtp"===e.type||"outboundrtp"===e.type||"inbound-rtp"===e.type||"outbound-rtp"===e.type){if(t.ssrc=e.ssrc,t.streamType="inboundrtp"===e.type||"inbound-rtp"===e.type?"inbound":"outbound","Safari"===W.name&&!e.mediaType){e.id.includes("Audio")?e.mediaType="audio":e.id.includes("Video")&&(e.mediaType="video");var n=e.id.split("_");if(n[1])try{e.ssrc=parseInt(n[1]),t.ssrc=e.ssrc}catch(e){}}t.data=e,void 0!==e.isRemote?t.reportType="true"===e.isRemote||!0===e.isRemote?"remote":"local":t.reportType="local",e.trackId&&(t.trackId=e.trackId),e.mediaType&&(t.mediaType=e.mediaType)}else"candidatepair"===e.type&&e.selected?t.Transport=e:"localcandidate"===e.type||"local-candidate"===e.type?t.localCandidate=e:"remotecandidate"===e.type||"remote-candidate"===e.type?t.remoteCandidate=e:"transport"===e.type||"googCandidatePair"===e.type?t.Transport=e:"VideoBwe"===e.type?t.bwe=e:"track"===e.type?t.trackStats=e:"candidate-pair"===e.type?t.candidatePair=e:"codec"===e.type?t.codec=e:"ssrc"===e.type&&(t.reportType="local",e.bytesSent?t.streamType="outbound":t.streamType="inbound",t.ssrc=e.ssrc,t.data=e);return t}function p(e){var t,n={};e.timestamp instanceof Date&&(n.timestamp=e.timestamp.getTime().toString()),e.type&&(n.type=e.type);var r=0;if(e.names){var i=e.names();for(r=0;r0)return!1;if(0===e.length)return!0;var t;for(t in e)if(e.hasOwnProperty(t))return!1;return!0}function f(e){return clearInterval(e),null}function m(){le.syncHandler||($e(N,L,"log",{msg:"start clockSync"}),le.offsetResults=[],zt(),le.syncHandler=!0)}function v(e){try{if(window&&window.sessionStorage){var t=JSON.parse(window.sessionStorage.getItem("csio_ucid_data"));if(t&&t[e])return t[e].ucID}}catch(e){return null}return null}function g(e,t,n){var r=t.action,i=t.remoteID,o=t.ucID;t.channel=e,te._isTokenValid(te.authToken,N,L)?(o&&!de&&i||-1!==Ve.indexOf(r))&&t.conferenceID!==A?(t.token=te.authToken,Xt(e,t,n),n&&r&&n(Te.success,r+" sent to the backend.")):(t.clockUnsynced=de,Z.cacheEvent({channel:e,data:t,callback:n})):te.sendAuthenticationRequest(N,L,function(a,s){a!==Te.success?(a===Ce.authOngoing&&(t.clockUnsynced=de,Z.cacheEvent({channel:e,data:t,callback:n})),n&&a!==Ce.authOngoing&&n(a,s)):a===Te.success&&"SDK authentication successful."===s&&((o&&!de&&i||-1!==Ve.indexOf(r))&&t.conferenceID!==A?(t.token=te.authToken,Xt(e,t,n),n&&r&&n(Te.success,r+" sent to the backend.")):(t.clockUnsynced=de,Z.cacheEvent({channel:e,data:t,callback:n})))})}function y(){he||(he=setInterval(function(){!(function(){var e={};Object.keys(ue).forEach(function(t){var n=ue[t];if(!n)return!0;var r,i,o={ucID:n.ucID,userJoinedSent:n.userJoinedSent},a=[];if(n.participants){var s,c=n.participants;for(r=0;r=p-o||!l.data.ucID)if(d=decodeURIComponent(l.data.conferenceID),ue[d]&&ue[d].ucID)l.data.ucID=ue[d].ucID;else{if(d!==a){h.push(this.eventCache[c]);continue}l.data.ucID=s}if(l.data.clockUnsynced&&(l.data.apiTS+=le.currentOffset),l.channel===be.processedStats&&l.data.clockUnsynced&&l.data.processedStatistics&&l.data.processedStatistics.length>0)for(u=0;u=0;s--)a[s]=i.charCodeAt(s);var c=function(e){for(var t="",n=new Uint8Array(e),o=n.byteLength,a=0;a0)return f(r,i,a,u[t],e.type);console.error("Authentication failed, but no error actions were defined in response."),n=setTimeout(e.sendAuthenticationRequest,5e3,r,i,a)}else n=setTimeout(e.sendAuthenticationRequest,5e3,r,i,a)})})}var s};var p=function(){null!==i&&(clearTimeout(i),i=null)},h=function(t,n,r,o){p(),i=setTimeout(function(){e.sendAuthenticationRequest(n,r,o)},t)},f=function e(t,n,r,i,a){if(0!==i.length){var s=i.shift();if(s.action===Ye.RETRY)return"jwt"===a&&h(s.params.timeout,t,n,r),e(t,n,r,i);if(s.action===Ye.GET_NEW_TOKEN)return o=!0,e(t,n,r,i);if(s.action===Ye.REPORT_ERROR){var u="Authentication Error";return s.params&&s.params.errorCode>=0&&(u=c[s.params.errorCode]),r&&r(Te.authError,u),e(t,n,r,i)}}};this.appSecretTokenGenerator=function(t){var n=null,r=null,i=!1,o=null;return t.indexOf(":")>-1&&(o=(function(e){for(var t=a(e.replace(/[ \r\n]+$/,"")),n=[],r=0;r-1){var t,n=!1,r={fileName:e.filename,line:e.lineno,col:e.colno,jsVersion:R,eventType:"error",message:e.message,pageURL:window.location.href};for(t in ue)ue.hasOwnProperty(t)&&(r.conferenceID=t,$e(N,L,"onErrorLog",r),n=!0);n||$e(N,L,"onErrorLog",r)}});var Xe="Invalid WebRTC function name",Ye={RETRY:0,GET_NEW_TOKEN:1,REPORT_ERROR:2},Ze=e(),et=Object.create(null),tt=function(e,n,i){var o=RegExp.prototype.test.bind(/^([a-z])=(.*)/),a=/^ssrc:(\d*) ([\w_]*):(.*)/,s=/^ssrc-group:SIM (\d*)/;n.split(/(\r\n|\r|\n)/).filter(o).forEach(function(n){var o=n[0],c=n.slice(2);if("a"===o){if(a.test(c)){var u=c.match(a),l=u[1];void 0===e.ssrcMap[l]&&(e.ssrcMap[l]={}),e.ssrcMap[l][u[2]]=u[3],e.ssrcMap[l].localStartTime=t(),e.ssrcMap[l].syncedStartTime=r(),e.ssrcMap[l].streamType=i}s.test(c)&&(void 0===e.ssrcMap.ssrcGroup&&(e.ssrcMap.ssrcGroup={},e.ssrcMap.ssrcGroup[i]={}),e.ssrcMap.ssrcGroup[i].simulcastGroup=c.match(/\d+/g))}})},nt=function(e,t){return e.ssrcMap[t]},rt=function(e){return e.ssrcMap},it=function(e,t,n,r,i,o){if(!i.localDescription||!i.remoteDescription)return!1;var a=i.localDescription.sdp,s=i.remoteDescription.sdp;if(!a||!s||a===r.localSDP&&s===r.remoteSDP)return!1;if(tt(r,a,ke.outbound),tt(r,s,ke.inbound),$){var c={sdp:{}};c.sdp.localSDP=a!==r.localSDP?a:-1,c.sdp.remoteSDP=s!==r.remoteSDP?s:-1,st(e,t,n,c,r.pcHash,o)}return r.localSDP=a,r.remoteSDP=s,!0},ot=function(e,t,n,i,o,a,s){if(e){var c=r(),u={version:R,apiTS:c,action:e,localID:encodeURIComponent(L),remoteID:t?encodeURIComponent(t):null,conferenceID:encodeURIComponent(n),timeShift:0,appID:N,ucID:i,pcID:a,deviceID:j,value:o};return null===n?void $e(N,L,"log",{msg:"conferenceID is null in sendEvent for "+e}):void g(be.callStatsEvent,u,s)}console.error("sendEvent: Invalid eventType ")},at=function(e,t,n,i){if(e){var o=r(),a={version:R,channel:be.preCallTest,timestamp:o,apiTS:o,action:e,eventType:e,localID:encodeURIComponent(L),remoteID:encodeURIComponent(L),conferenceID:encodeURIComponent(t),timeShift:0,appID:N,ucID:n,deviceID:j,token:te.authToken};return"preCallTestResults"===e&&(a.results=i,null==t)?void Xt(be.preCallTest,a):(e===Se&&(a.ids=i),void(n&&!de?g(be.preCallTest,a):(a.clockUnsynced=de,Z.cacheEvent({channel:be.preCallTest,data:a,callback:null}))))}console.error("sendEvent: Invalid eventType ")},st=function(e,t,n,i,o,a){var s=r(),c={version:R,appID:N,conferenceID:encodeURIComponent(t),ucID:n,apiTS:s,localID:encodeURIComponent(L),remoteID:encodeURIComponent(e),pcID:o,deviceID:j,sdpPayload:i,action:be.sdpSubmission};te.authToken&&n&&!de?(c.token=te.authToken,g(be.sdpSubmission,c,a)):(c.clockUnsynced=de,Z.cacheEvent({channel:be.sdpSubmission,data:c,callback:a}),te.authToken||te.sendAuthenticationRequest(N,L))},ct=function(e,t,n,r,i,o,a){Ct(e,function(e){t.localCandidates=e.localCandidates,t.remoteCandidates=e.remoteCandidates,t.iceCandidatePairs=e.iceCandidatePairs,t.activeIceCandidatePair=At(t.iceCandidatePairs),a.currPair=t.activeIceCandidatePair,ot(ye.fabricStateChange,i,n,r,a,o)})},ut=function(e,t,n){var r={connectedDevices:x},i=ue[e]?ue[e].ucID:null;ot(ye.connectedDevices,t,e,i,r,n)},lt=function(e){var t={ucID:ue[e].ucID,endpoint:oe,magicKey:Ze};ot(ye.refreshPresence,null,e,ue[e].ucID,t,null)},dt=function(e,t){var n={};return n.status=e,t&&(n.message=t),n},pt=function(e,t){var n,r,i=0;if(!e)return i;for(n=0,r=e.length;n0?n[0]:null;var r},gt=function(e,t){var n=null,r=null;return(n=vt(e,t))&&(r=n.pcHash),r},yt=function(e){var t=null,n=null,r=null;for(t in ue)if(ue.hasOwnProperty(t)&&(r=vt(t,e))){n={fabric:r,conferenceID:t};break}return n},bt=function(e){if(e){var t=[],n=0;if(e&&e.result)t=e.result();else if(e&&e.forEach)e.forEach(function(e){t.push(e)});else for(n in e)e.hasOwnProperty(n)&&t.push(e[n]);return t}},St=function(e){return function(t){var n={};n=(function(e){var t,n=null,r=null,i=[],o=[],a=[],s=0;if(!(n=bt(e)))return{localCandidates:i,remoteCandidates:o};for(s=0;st&&(e=e.substring(0,t)),e):null},Rt=function(e,t,n){return t||(e&&e[n]?e[n].sdp:-1)},kt=function(e){for(var t=[],n=0;n127&&r<2048?(t[t.length]=r>>6|192,t[t.length]=63&r|128):(t[t.length]=r>>12|224,t[t.length]=r>>6&63|128,t[t.length]=63&r|128)}return new Uint8Array(t).buffer},It=function(e){for(var t,n=new DataView(e),r="",i=0;i0){var n=e.filter(function(e){return"true"===e.selected||"true"===e.googActiveConnection||!0===e.selected||!0===e.googActiveConnection});n.length>0&&(t=n[0])}return t},Ot=function(e,n){if(e){var r,i=null;if(void 0!==n){var o=n.signalingState;if((i=yt(n))&&i.fabric){r=i.fabric.signalingState,i.fabric.signalingState=o;var a=i.conferenceID,c=i.fabric,u=c.remoteUserID,l=ue[a].ucID,d={changedState:"signalingState",prevState:r,newState:o};ct(n,c,a,l,u,c.pcHash,d),"closed"===o&&Et(n,ge.fabricTerminated,i.conferenceID),"have-local-offer"!==o&&"have-local-pranswer"!==o&&"stable"!==o||i.sentSenderConfigRequest||(console.log(o,".. requesting sender config.",i.sentSenderConfigRequest),i.sentSenderConfigRequest=!0,(function(e){var n=t(),r=function(){K.senderConfig=-1,K.senderExists=!1,ht()},i=function(e){var r=JSON.parse(e.response);console.log("Sender config response:",e.status,r);var i;r.recommended&&r.recommended.sender&&(i=r.recommended.sender,console.log("ConfigService, recommended SENDER info:",i)),i&&(se=i,ae=!0),K.senderConfig=t()-n,K.senderExists=!!i,ht()},o=function(e){ce=e;var t=P+N+"/configurations",n={Authorization:"Bearer "+te.authToken},r={app_id:N,local_id:L,rtc_rtp_parameters:e},o=JSON.stringify(r);Qe(t,o,i,n)},a=function(){o({encodings:[{maxBitrate:0}]})};if(Object.keys(ue).length>1)return console.warn("Multiple PCs found, skipping"),void r();if(!e.getSenders||"function"!=typeof e.getSenders)return console.warn("Get senders error"),void a();var c,u=e.getSenders();for(var l in u){var d=u[l];if("object"!=(void 0===d?"undefined":s(d))||!d.track||"object"!=s(d.track)||!d.track.kind)return console.warn("Access senders error"),void r();if("video"===d.track.kind){if(c)return console.warn("Multiple video sender, skipping"),void r();c=d}}if(!c)return console.warn("No video sender found"),void r();if(!c.getParameters||"function"!=typeof c.getParameters)return console.warn("Sender getParameters error"),void a();var p=c.getParameters();"object"!=(void 0===p?"undefined":s(p))?r():("encodings"in p||(p.encodings=[]),0===p.encodings.length&&p.encodings.push({maxBitrate:0}),o(p))})(n))}if("closed"===o&&J)try{J.callFinished()}catch(e){B("callFinished",e),J.crashDisconnect()}if("have-remote-offer"===o||"stable"===o){if(K.remoteofferSinceCreate=t()-K.create,ht(),J)try{J.callStarts()}catch(e){B("callStarts",e),J.crashDisconnect()}$e(N,L,"log",{msg:"precalltest told to stop "+H}),H+="(stopped)"}}}},Dt=function(e,n,r){if(!e.onHold){var i=n,o=r;null===e.statsInterval&&(e.statsPollingStart=t(),jt(i,o),e.statsInterval=setInterval(function(){jt(i,o)},1e3))}},xt=function(e,t,n,r,i,o){o.localCandidates=t.localCandidates,o.remoteCandidates=t.remoteCandidates,o.iceCandidatePairs=t.iceCandidatePairs,ot(ye.iceFailed,n,r,i,o,t.pcHash,t.pcCallback)},Nt=function(e,t){if(e&&void 0!==t){var n=null;(n=yt(t))&&n.fabric&&n.fabric.numNegotiationNeededCalls++}},Lt=function(e,n){if(e&&void 0!==n){var r,i,o,a,s=null,c=null;if((c=yt(n))&&c.fabric){s=c.fabric,r=c.conferenceID,i=t()-s.startTime,o=s.iceConnectionState,a=s.iceGatheringState;var u=s.remoteUserID,l=ue[r].ucID,d={changedState:"iceConnectionState",prevState:o,newState:n.iceConnectionState};if(ct(n,s,r,l,u,s.pcHash,d),a!==n.iceGatheringState){var p={changedState:"iceGatheringState",prevState:a,newState:n.iceGatheringState};ct(n,s,r,l,u,s.pcHash,p)}"complete"===n.iceGatheringState&&"checking"===n.iceConnectionState&&(function(e,n,r,i){var o=n.iceConnectionState,a=n.remoteUserID,s=ue[r],c=t(),u={prevIceConnectionState:o,currIceConnectionState:e.iceConnectionState};n.gatheringIceCandidatesDelay=i,"disconnected"===o&&n.pcState===Ae.checkingDisrupted&&(u.disruptionDelay=c-n.disruptedTS,ot(ye.iceConnectionDisruptionEnd,a,r,s.ucID,u,n.pcHash,n.pcCallback))})(n,s,r,i),"connected"===n.iceConnectionState||"completed"===n.iceConnectionState?(function(e,n,r,i){var o=n.iceConnectionState,a=n.remoteUserID,s=ue[r],c=t(),u={prevIceConnectionState:o,currIceConnectionState:e.iceConnectionState,prevPair:n.activeIceCandidatePair},l=n.pcState===Ae.disrupted,d="completed"===e.iceConnectionState;n.connectivityIceStatusDelay=i,n.established=!0,Ct(e,function(e){if(n.localCandidates=e.localCandidates,n.remoteCandidates=e.remoteCandidates,n.iceCandidatePairs=e.iceCandidatePairs,n.activeIceCandidatePair=At(n.iceCandidatePairs),u.currPair=n.activeIceCandidatePair,u.disruptionDelay=c-n.disruptedTS,l)ot(ye.iceDisruptionEnd,a,r,s.ucID,u,n.pcHash);else if(d){var t={};t.iceCandidatePairs=e.iceCandidatePairs,ot(ye.iceCompleted,a,r,s.ucID,t,n.pcHash)}}),Ft(n.pc,ye.autoFabricSetup,r),n.pcState=Ae.established,Dt(n,e,r,a)})(n,s,r,i):"failed"===n.iceConnectionState?(function(e,t,n,r){var i=t.iceConnectionState,o=t.remoteUserID,a=ue[n],s={prevIceConnectionState:i,currIceConnectionState:e.iceConnectionState,currPair:t.activeIceCandidatePair,failureDelay:r};t.pcState=Ae.failed,"checking"===i?xt(0,t,o,n,a.ucID,s):"completed"===i||"connected"===i?ot(ye.fabricDropped,o,n,a.ucID,s,t.pcHash,t.pcCallback):"disconnected"===i&&t.established?ot(ye.fabricDropped,o,n,a.ucID,s,t.pcHash,t.pcCallback):"disconnected"===i&&xt(0,t,o,n,a.ucID,s)})(n,s,r,i):"disconnected"===n.iceConnectionState?(function(e,n,r){var i=n.iceConnectionState,o=n.remoteUserID,a=ue[r],s=t(),c={prevIceConnectionState:i,currIceConnectionState:e.iceConnectionState,prevIceConnectionStateTs:n.iceConnectionStateTS,currPair:n.activeIceCandidatePair};n.startTime=s,"connected"===i||"completed"===i?(n.pcState=Ae.disrupted,n.disruptedTS=s,ot(ye.iceDisruptionStart,o,r,a.ucID,c,n.pcHash,n.pcCallback),n.pcCallback&&n.pcCallback(Te.appConnectivityError,"Connectivity check for PC object to "+o+" failed.")):"checking"===i&&(n.pcState=Ae.checkingDisrupted,n.disruptedTS=s,ot(ye.iceConnectionDisruptionStart,o,r,a.ucID,c,n.pcHash,n.pcCallback),n.pcCallback&&n.pcCallback(Te.appConnectivityError,"Connectivity check for PC object to "+o+" failed."))})(n,s,r):"closed"===n.iceConnectionState?(function(e,t,n,r){var i=t.iceConnectionState,o=t.remoteUserID,a=ue[n],s={prevIceConnectionState:i,currIceConnectionState:e.iceConnectionState};"new"===i||"checking"===i?(s.failureDelay=r,ot(ye.iceAborted,o,n,a.ucID,s,t.pcHash,t.pcCallback)):(s.currPair=t.activeIceCandidatePair,ot(ye.iceTerminated,o,n,a.ucID,s,t.pcHash,t.pcCallback))})(n,s,r,i):"new"===n.iceConnectionState&&(function(e,t,n){var r=(void 0).iceConnectionState,i=(void 0).remoteUserID,o=ue[void 0],a={prevIceConnectionState:r,currIceConnectionState:(void 0).iceConnectionState,prevPair:(void 0).activeIceCandidatePair};"new"!==r&&((void 0).established=!1,ot(ye.iceRestarted,i,void 0,o.ucID,a,(void 0).pcHash,(void 0).pcCallback))})(),s.iceConnectionState=n.iceConnectionState,s.iceGatheringState=n.iceGatheringState,s.iceConnectionStateTS=t()}}},Mt=function(e,t){var n,r,i;if(void 0!==t){if(null!==e.candidate){var o="1"===(r=e.candidate.candidate.split(" "))[1]?"rtp":"rtcp";"0"!==r[5]&&(i=-1!==r[4].indexOf(":"),n={transport:r[2],protocol:o,typePreference:r[3],address:i?"["+r[4]+"]:"+r[5]:r[4]+":"+r[5],type:r[7],media:e.candidate.sdpMid})}var a=null;a=yt(t),Ct(t,function(e){a.localCandidates=e.localCandidates,a.remoteCandidates=e.remoteCandidates,a.iceCandidatePairs=e.iceCandidatePairs,a.activeIceCandidatePair=At(a.iceCandidatePairs)}),a&&a.fabric&&(void 0!==n&&-1===a.fabric.iceCandidates.indexOf(n)&&a.fabric.iceCandidates.push(n),n&&a.fabric.fabricSetupSent&&(function(e,n){var r=yt(t);if(r&&r.fabric){var i=r.fabric,o=i.remoteUserID,a=r.conferenceID,s=ue[a].ucID;ot(ye.iceCandidateFound,o,a,s,n,i.pcHash)}})(0,n))}},jt=function(e,t){if(ue.hasOwnProperty(t)){if("closed"===e.signalingState)return Et(e,ge.fabricTerminated,t),!0;var n=vt(t,e);if(n.pcState!==Ae.established&&n.lastFabricState===n.pcState&&"connected"!==e.iceConnectionState&&"completed"!==e.iceConnectionState)return!0;var r=ue[t].ucID;try{Tt(e,n.pcCallback,l,L,r,n)}catch(e){console.log("csioGetStats: Error",e)}}else console.error("ConferenceID %o doesn't exist. Can't call getStats for it.",t)},Ft=function(e,n,r){var i=null,o=ue[r],a=null,s=t();if(void 0===o&&console.error("sendAutoFabricSetupEvent: Conference ID not found!"),null===(a=vt(r,e))&&console.error("sendAutoFabricSetupEvent: fabricData is null"),!a.fabricSetupSent){i=a.remoteUserID;var c={setupDelay:s-a.startTime,iceGatheringDelay:a.gatheringIceCandidatesDelay,iceConnectivityDelay:a.connectivityIceStatusDelay,remoteEndpointType:a.remoteEndpointType,fabricTransmissionDirection:a.fabricTransmissionDirection};de||(c.clockSyncOffset=le.currentOffset),Ct(e,function(e){c.localCandidates=e.localCandidates,c.remoteCandidates=e.remoteCandidates,c.iceCandidatePairs=e.iceCandidatePairs,a.localCandidates=e.localCandidates,a.remoteCandidates=e.remoteCandidates,a.iceCandidatePairs=e.iceCandidatePairs,a.pcState=Ae.established,a.activeIceCandidatePair=At(a.iceCandidatePairs),a.fabricSetupSent||(ot(n,i,r,o.ucID,c,a.pcHash,a.pcCallback),a.fabricSetupSent=!0)})}},Ut=function(e,t,n){var r={},i=null,o=ue[n],a=null;void 0===o&&console.error("sendFabricTransportSwitchEvent: Conference ID not found!"),null===(a=vt(n,e))&&console.error("sendFabricTransportSwitchEvent: fabricData is null"),i=a.remoteUserID,r.prevPair=a.activeIceCandidatePair,Ct(e,function(e){r.currPair=At(e.iceCandidatePairs),a.activeIceCandidatePair=r.currPair,r.switchDelay=null,r.relayType=a.transportData.relayType,ot(t,i,n,o.ucID,r,a.pcHash,a.pcCallback)})},Ht=function(e,t){if(e.trackStats){var n=e.trackStats.filter(function(e){return e.id===t});if(n.length>0)return n[0]}},Bt=function(e,t){if(e.codec){var n=e.codec.filter(function(e){return e.id===t});if(n.length>0)return n[0]}},Gt=function(e,t){if(e.candidatePair){var n=e.candidatePair.filter(function(e){return e.id===t});if(n.length>0)return n[0]}},Jt=function(e){for(var t,n,r=e.Transport.length,i=0;ile.maxAllowedLatency)le.offsetResults=[],$e(N,L,"log",{msg:"clockSync restarting"});else{var i=e.now+r,o=i-n;le.offsetResults.push(o),$e(N,L,"log",{msg:"clockSync Info serverTime "+i+" responseRecvTs "+n+" requestExecutionTime "+t})}if(le.offsetResults.length>=le.syncAttempts){var a=le.offsetResults.reduce(function(e,t){return e+t});le.currentOffset=a/le.offsetResults.length,$e(N,L,"log",{msg:"clockSync Done "+le.currentOffset+"Length "+le.offsetResults.length}),le.offsetResults=[],de=!1,Z.sendCachedUserJoinedEvents(te),Z.sendCachedEvents(te,de)}else de&&setTimeout(function(){zt()},100)}},$t=new(function(){var e=this,n=null,r=!1,i=!1,o={};this._wsConnectionState="closed";var a=function(e,t){try{if(window&&window.sessionStorage){var n=JSON.parse(window.sessionStorage.getItem("csio_ucid_data"));n||(n={}),n[e]||(n[e]={}),n[e].ucID=t,window.sessionStorage.setItem("csio_ucid_data",JSON.stringify(n))}}catch(e){return}o[e]="https://dashboard.callstats.io/apps/"+N+"/conferences/"+encodeURIComponent(e)+"/"+t+"/general"};this.getConferenceURL=function(){return o},this._setupWebSocketConnection=function(o){return"initiated"===e._wsConnectionState||"connected"===e._wsConnectionState?($e(N,L,"log",{msg:"### _setupWebSocketConnection is called when already connected!"}),console.log("### _setupWebSocketConnection is called when already connected!"),void(o&&o(Te.success,"WebSocket establishment successful."))):(e._wsConnectionState="initiated",(n=new WebSocket(k,"echo-protocol")).onopen=function(){console.log("**** Successfully connected to the backend ",i,L),$e(N,L,"log",{msg:"Successfully connected to the backend"}),e._wsConnectionState="connected",r?i=!0:r=!0,Z.sendCachedUserJoinedEvents(te),i&&((function(){var e;for(e in ue)if(ue.hasOwnProperty(e)){if(e===A)return;lt(e),ue[e].refreshPresence=(function(e){return setInterval(function(){lt(e)},1e4)})(e)}b()})(),Z.sendCachedEvents(te,de)),i=!1},n.onclose=function(){e._wsConnectionState="closed",console.log("**** Connection to the server closed."),$e(N,L,"log",{msg:"Connection to the server closed."}),(function(){var e;for(e in ue)ue.hasOwnProperty(e)&&(ue[e].refreshPresence=f(ue[e].refreshPresence),delete ue[e].refreshPresence);y()})(),n&&(n=null),o&&o(Te.httpError,"Connection to the server closed.")},n.onerror=function(t){e._wsConnectionState="closed",$e(N,L,"log",{msg:"WebSocket establishment failed."}),console.log("WebSocket establishment failed.",t),o&&o(Te.wsChannelFailure,"WebSocket establishment failed.",t)},void(n.onmessage=function(e){var n,r=e.data,i=t(),s=JSON.parse(r);if("Error"===s.status)"Invalid client token."===s.reason&&(te.tokenData=null,te.authToken=null,te.sendAuthenticationRequest(N,L)),o&&o(Te.csProtoError,r.reason);else if("200 OK"===s.status)if("feedback"===s.event)Ke("feedback");else if(s.event===ye.userJoined||s.event===ye.refreshPresence){var c=!1,u=null;n=decodeURIComponent(s.conferenceID),ue.hasOwnProperty(n)&&(ue[n].ucID!==s.ucID&&(c=!0,a(n,s.ucID),u=s.conferenceCreationTS?s.conferenceCreationTS:s.conferenceDuration),s.event!==ye.userJoined||ue[n].refreshPresence||(ue[n].refreshPresence=(l=n,setInterval(function(){lt(l)},1e4)),b()),ue[n].ucID=s.ucID),Z.sendCachedFeedback(),Z.sendCachedEvents(te,de,c,u,n,s.ucID)}else s.event===be.clockSync?Qt(s,i):s.event===ge.fabricSetupFailed&&(n=decodeURIComponent(s.conferenceID),s.ucID&&(a(n,s.ucID),Z.sendCachedFeedback(),Z.sendCachedEvents(te,de,!0,0,n,s.ucID)));var l}))},this._isChannelReady=function(){return!(!n||1!==n.readyState)},this.getWSConnectionState=function(){return n?n.readyState:-1},this.send=function(e){n.send(JSON.stringify(e))}}),Xt=function(e,t,n){if($t._isChannelReady())try{$t.send(t)}catch(r){r&&"InvalidStateError"===r.name&&Z.cacheEvent({channel:e,data:t,callback:n})}else Z.cacheEvent({channel:e,data:t,callback:n}),$t._setupWebSocketConnection()};return navigator&&"function"==typeof navigator.getBattery&&navigator.getBattery().then(function(e){ie=e}),(function(){if(null===(j=qe("endpointID"))){var e=t();!(function(e,t){var n={name:"SHA-256"};if(window.crypto){var r=window.crypto.subtle||window.crypto.webkitSubtle;if(!r)return void pt(e,t);r.digest(n,kt(e)).then(function(e){t(It(e))}).catch(function(){pt(e,t)})}else if(window.msCrypto){if(!window.msCrypto.subtle)return void pt(e,t);var i=window.msCrypto.subtle.digest(n,kt(e));i.oncomplete=function(e){e.target&&t(It(e.target.result))},i.onerror=function(){pt(e,t)}}else pt(e,t)})((Math.random()*e).toString(),function(e){We("endpointID",j=e)})}})(),Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(e){if(null===this)throw new TypeError("Array.prototype.find called on null or undefined");if("function"!=typeof e)throw new TypeError("predicate must be a function");for(var t,n=Object(this),r=n.length>>>0,i=arguments[1],o=0;o0&&(t=!0),t})(a)){var g=(b=Ze,{magicKey:b,statsSubmissionInterval:Q,endpoint:oe,localUserIDObject:M});ot(ye.userJoined,null,a,null,g,null,null),v=!0,f={authStatus:!!te.tokenData,clockSync:!de,msg:"userJoined sent",pageURL:window.location.href,eventType:"warn",conferenceID:a,version:R},$e(N,L,"stateMachine",f)}var b;if(null,k=v,void 0===ue[w=a]&&(ue[w]={},ue[w].participants=null,k&&(ue[w].userJoinedSent=!0)),!vt(a,n)){n&&new C(n,Mt,Lt,Ot,Nt),l=e();var S={pc:n,remoteUserID:d,fabricUsage:i,magicKey:Ze,startTime:m,pcCallback:u,pcState:Ae.initializing,fabricSetupSent:!1,iceCandidates:[],iceConnectionState:n.iceConnectionState,latestEventSent:m,pcHash:l,localSDP:-1,remoteSDP:-1,statsInterval:null,intervalAdaptionPhase:!0,gatheringIceCandidatesDelay:0,connectivityIceStatusDelay:0,numNegotiationNeededCalls:0,currentActivePhaseIndex:0,onHold:!1,ssrcMap:{},conferenceID:a,remoteEndpointType:p,fabricTransmissionDirection:h};ae&&(ot(be.senderConfiguration,r,a,null,se,S.pcHash),ae=!1),"function"!=typeof n||n.callstatsID||(n.callstatsID=l),"connected"!==n.iceConnectionState&&"completed"!==n.iceConnectionState||(S.pcState=Ae.established),(function(e,t){var n=ue[e],r=[];if(void 0===n)r.push(t),ue[e]={},ue[e].participants=r;else{var i=n.participants;null===i?(r.push(t),n.participants=r):i.push(t)}})(a,S),S.pcState===Ae.established&&(Ft(n,ye.autoFabricSetup,a),Dt(S,n,a));var _=function(e){var t=0;return Object.keys(e).forEach(function(e){e&&e.mediaType&&(t+="audio"===e.mediaType?1:0)}),00){var O=ue[a].ucID;at(Se,a,O,I)}}catch(e){B("send associate",e)}return dt(_e.success)},sendFabricEvent:Et,sendUserFeedback:function(e,t,n){var r;if(!t||!e)return console.error("sendUserFeedback: Arguments missing/Invalid"),dt(_e.error,"sendUserFeedback: Arguments missing/Invalid");if("object"!=(void 0===t?"undefined":s(t)))return console.error("sendUserFeedback: Invalid feedback object."),dt(_e.error,"sendUserFeedback: Invalid feedback object.");if(0===Object.keys(t).length)return console.error("sendUserFeedback: Feedback data object must not be empty."),dt(_e.error,"sendUserFeedback: Feedback data object must not be empty.");var i=ue[e],o=null;void 0!==i&&void 0!==i.ucID&&(o=i.ucID);var a=null;if(a=null===Ze?pe:Ze,null===o&&(o=v(e)),null===o)return console.error("sendUserFeedback: ucID unavailable"),dt(_e.error,"sendUserFeedback: ucID unavailable");r="object"==s(t.userID)?t.userID.aliasName:t.userID,t&&t.overall<=0&&(t.overall=null);var c={conferenceID:encodeURIComponent(e),magicKey:a,appID:N,version:R,ucID:o,remoteID:encodeURIComponent(r),userID:encodeURIComponent(r),userQoe:{overall:t.overall}};return t.video&&(c.userQoe.video=t.video),t.audio&&(c.userQoe.audio=t.audio),t.screen&&(c.userQoe.screen=t.screen),t.comment&&(c.userQoe.comment=t.comment),te.authToken&&!de?(c.token=te.authToken,We("feedback",JSON.stringify(c)),g(be.userFeedback,c,n)):(c.clockUnsynced=de,Z.cacheEvent({channel:be.userFeedback,data:c,callback:n}),te.sendAuthenticationRequest(N,r)),dt(_e.success)},associateMstWithUserID:function(e,t,n,r,a,c){var u=null;if(u="object"==(void 0===t?"undefined":s(t))?t.aliasName:t,n||(n=A),!(e&&r&&a&&u))return console.error("associateMstWithUserID: Arguments missing"),dt(_e.error,"associateMstWithUserID: Arguments missing");if(!o(e))return console.error("associateMstWithUserID: Invalid PeerConnection object passed"),dt(_e.error,"associateMstWithUserID: Invalid PeerConnection object passed");if(void 0===ue[n])return console.error("associateMstWithUserID: conferenceID doesn't exist"),dt(_e.error,"associateMstWithUserID: conferenceID doesn't exist");var l=null;if(null===(l=vt(n,e)))return console.error("associateMstWithUserID: Unknown pcObject passed"),dt(_e.error,"associateMstWithUserID: Unknown pcObject passed");var d=nt(l,r);return void 0===d&&(l.ssrcMap[r]={},d=nt(l,r)),d.remoteUserID=u,d.ssrc=r,d.associatedVideoTag=c,d.usageLabel=a,c&&u!==L&&(function(e,t,n,r){var o,a=document.getElementById(t);a&&(a.oncanplay=function(){o=ue[e].ucID;var t=vt(e,n);t&&(function(e,t,r,o,a){var s=gt(r,n),c=null;s&&(c=i(),ot(ye.mediaPlaybackStartEvent,t,r,e,{ssrc:a,highResTs:c},s,null))})(o,t.remoteUserID,e,0,r)},a.onsuspend=function(){o=ue[e].ucID;var t=vt(e,n);t&&(function(e,t,r,o,a){var s=gt(r,n),c=null;s&&(c=i(),ot(ye.mediaSuspendedEvent,t,r,e,{ssrc:a,highResTs:c},s,null))})(o,t.remoteUserID,e,0,r)})})(n,c,e,r),dt(_e.success)},attachWifiStatsHandler:function(e){return e?"function"!=typeof e?(console.error("attachWifiStatsHandler: Arguments Invalid"),dt(_e.error,"attachWifiStatsHandler: Arguments Invalid")):(S=e,void(function e(){S&&S().then(function(t){D=JSON.parse(t),setTimeout(function(){e()},1e4)}).catch(function(){setTimeout(function(){e()},1e4)})})()):(console.error("attachWifiStatsHandler: Arguments missing"),dt(_e.error,"attachWifiStatsHandler: Arguments missing"))},csError:Te,fabricUsage:Re,qualityRating:{excellent:5,good:4,fair:3,poor:2,bad:1},webRTCFunctions:Oe,reportError:function(n,r,i,a,c,u){if(r||(r=A),void 0===n||!i)return console.error("reportError: Arguments missing/invalid"),dt(_e.error,"reportError: Arguments missing/invalid");if(!N||!L)return console.error("reportError: SDK is not initialized"),dt(_e.error,"reportError: SDK is not initialized");if(!Oe.hasOwnProperty(i))return console.error("reportError: Invalid webRTC functionName value: %o",i),dt(_e.error,"reportError: Invalid webRTC functionName value: "+i);void 0===a&&console.warn("reportError: Missing dom error parameter");var l,d,p,h=null,f=t(),m=ue[r],g=null,y=f,b=null,S=null,_=null,T=null;if(_=v(r),d=(function(e){var t;return Oe.hasOwnProperty(e)?e===Oe.createOffer||e===Oe.createAnswer||e===Oe.setRemoteDescription?t=xe:e===Oe.setLocalDescription?t=Ne:e===Oe.addIceCandidate?t=Ne:e===Oe.getUserMedia?t=De:e===Oe.iceConnectionFailure?t=Le:e===Oe.signalingError?t=Me:(e===Oe.applicationLog||Oe.applicationError)&&(t=je):t=Xe,t})(i),i!==Oe.getUserMedia&&(T=e()),a)if(window.DOMException&&a instanceof window.DOMException)a={message:a.message,name:a.name};else if("object"==(void 0===a?"undefined":s(a))){var C={};a.message&&(C.message=a.message),a.name&&(C.name=a.name),a.constraintName&&(C.constraintName=a.constraintName),a.stack&&(C.stack=a.stack),a=C}if((w=a)&&"string"==typeof w?w=wt(w,2e4):w&&"object"==(void 0===w?"undefined":s(w))&&w.message&&(w.message=wt(w.message,2e4)),a=w,o(n)){if(m&&(_=m.ucID),(g=vt(r,n))?(l=g.remoteUserID,y=g.startTime,b=g.pcState,S=g.pc.iceConnectionState,T=g.pcHash,p=g.pcCallback):l=L,h={failureDelay:f-y,reason:d,domError:a,fabricState:b,iceConnectionState:S,function:i,magicKey:Ze,endpoint:oe},i===Oe.applicationLog||i===Oe.applicationError?ot(ge.applicationErrorLog,l,r,_,h,T,p):(ot(ge.fabricSetupFailed,l,r,_,h,T,p),K.errorSinceCreate=t()-K.create,ht()),c||u||n&&n.localDescription||n&&n.remoteDescription){var E={sdp:{}};E.sdp.localSDP=Rt(n,c,"localDescription"),E.sdp.remoteSDP=Rt(n,u,"remoteDescription"),st(l,r,_,E,T)}}else null===Ze&&(Ze=e()),l=L,h={failureDelay:0,reason:d,domError:a,function:i,magicKey:Ze,endpoint:oe},i===Oe.applicationLog||i===Oe.applicationError?ot(ge.applicationErrorLog,l,r,_,h,null,null):ot(ge.fabricSetupFailed,l,r,_,h,T,null);var w;return dt(_e.success)},reportUserIDChange:function(e,t,n,r){if(!(e&&n&&r&&t))return console.error("reportUserIDChange: Arguments missing/Invalid"),dt(_e.error,"reportUserIDChange: Arguments missing/Invalid");if(!N||!L)return console.error("reportUserIDChange: SDK not initialized."),dt(_e.error,"reportUserIDChange: SDK not initialized");if(""===n||""===t)return console.error("reportUserIDChange: id or conferenceID MUST not be empty"),dt(_e.error,"reportUserIDChange: id or conferenceID MUST not be empty");if(!o(e))return console.error("reportUserIDChange: Invalid PeerConnection object passed"),dt(_e.error,"reportUserIDChange: Invalid PeerConnection object passed");var i=null,a=ue[t];if(void 0===a)return console.error("reportUserIDChange: Conference ID not found!"),dt(_e.error,"reportUserIDChange: Conference ID not found!");var s=null;if(void 0===a.ucID)return console.error("reportUserIDChange: addNewFabric was not called"),$e(N,L,"log",{msg:"### reportUserIDChange: addNewFabric was not called"}),dt(_e.error,"reportUserIDChange: addNewFabric was not called");if(s=a.ucID,null===(i=vt(t,e)))return console.error("reportUserIDChange: Invalid pcObject passed as argument"),dt(_e.error,"reportUserIDChange: Invalid pcObject passed as argument");var c={id:n,idType:r};return ot(ge.userIDChangedEvent,void 0,t,s,c,null,i.pcCallback),dt(_e.success)},userIDType:{local:"local",remote:"remote"},setProxyConfig:function(e){return window&&!window.csioproxy?(console.error("setCallstatsURLs: cannot be called if window.csioproxy is false"),dt(_e.error,"setCallstatsURLs: cannot be called if window.csioproxy is false")):e?(e.collectorURL&&(E=e.collectorURL),e.authServiceURL&&(w=e.authServiceURL),e.csioInternalAPIURL&&(I=e.csioInternalAPIURL),e.wsURL&&(k=e.wsURL),Kt(),m(),y(),dt(_e.success)):(console.error("setCallstatsURLs: Arguments missing/Invalid"),dt(_e.error,"setCallstatsURLs: Arguments missing/Invalid"))},callStatsAPIReturnStatus:_e,setIdentifiers:function(e,t){return e?(e.conferenceID&&((function(e){if(ue[A]){var t=ue[A].participants;if(t)for(var n=0;nr&&(o=u[c].name.substr(u[c].name.lastIndexOf("/")+1),r=u[c].duration),u[c].duration-1&&(t=u[c].duration);return{support:"full",callstats:t,min:{name:i,time:n},max:{name:o,time:r},total:s}}return"Firefox"===e.name?{support:"limited",total:s}:void 0},r.prototype.detectBrowserInfo=function(){var e="Chrome",t=null,n=null,r=null,i="Chrome";if(!window.navigator.userAgent||window.csioReactNative)return console.log("Invalid userAgent"),window&&window.csioGetOsName&&(t=window.csioGetOsName()),window&&window.csioGetOsVer&&(n=window.csioGetOsVer()),window&&window.csioReactNative&&(r="react-native"),{name:e,codebase:i,os:t,osVersion:n,userAgent:r};var o,a=(r=navigator.userAgent).toLowerCase(),s=navigator.appVersion,c=""+parseFloat(navigator.appVersion);-1!==(o=a.indexOf("opera"))?(e="Opera",c=a.substring(o+6),-1!==(o=a.indexOf("Version"))&&(c=a.substring(o+8)),i="Chrome"):-1!==(o=a.indexOf("opr"))?(e="Opera",c=a.substring(o+4),-1!==(o=a.indexOf("Version"))&&(c=a.substring(o+8)),i="Chrome"):-1!==(o=a.indexOf("msie"))?(e="Microsoft Internet Explorer",c=a.substring(o+5),i="Chrome"):-1!==(o=a.indexOf("edge"))?(e="Edge",c=a.substring(o+5),i="Edge"):-1!==(o=a.indexOf("chrome"))?(e="Chrome",c=a.substring(o+7),i="Chrome"):-1!==(o=a.indexOf("safari"))?(e="Safari",c=a.substring(o+7),-1!==(o=a.indexOf("version"))&&(c=a.substring(o+8)),i="Chrome"):-1!==(o=a.indexOf("firefox"))?(e="Firefox",c=a.substring(o+8),i="Firefox"):-1!==(o=a.indexOf("trident"))&&(e="Microsoft Internet Explorer",o=a.indexOf("rv"),c=a.substring(o+3,o+7),i="Chrome");var u,l,d=[{s:"Windows 3.11",r:/win16/},{s:"Windows 95",r:/(windows 95|win95|windows_95)/},{s:"Windows ME",r:/(win 9x 4.90|windows me)/},{s:"Windows 98",r:/(windows 98|win98)/},{s:"Windows CE",r:/windows ce/},{s:"Windows 2000",r:/(windows nt 5.0|windows 2000)/},{s:"Windows XP",r:/(windows nt 5.1|windows xp)/},{s:"Windows Server 2003",r:/windows nt 5.2/},{s:"Windows Vista",r:/windows nt 6.0/},{s:"Windows 7",r:/(windows 7|windows nt 6.1)/},{s:"Windows 8.1",r:/(windows 8.1|windows nt 6.3)/},{s:"Windows 8",r:/(windows 8|windows nt 6.2)/},{s:"Windows 10",r:/(windows 10|windows nt 10.0)/},{s:"Windows NT 4.0",r:/(windows nt 4.0|winnt4.0|winnt|windows nt)/},{s:"Windows ME",r:/windows me/},{s:"Android",r:/android/},{s:"Open BSD",r:/openbsd/},{s:"Sun OS",r:/sunos/},{s:"Linux",r:/(linux|x11)/},{s:"iOS",r:/(iphone|ipad|ipod)/},{s:"Mac OS X",r:/mac os x/},{s:"Mac OS",r:/(macppc|macintel|mac_powerpc|macintosh)/},{s:"QNX",r:/qnx/},{s:"UNIX",r:/unix/},{s:"BeOS",r:/beos/},{s:"OS/2",r:/os\/2/},{s:"Search Bot",r:/(nuhk|googlebot|yammybot|openbot|slurp|msnbot|ask jeeves\/teoma|ia_archiver)/}];for(u in d)if((l=d[u]).r.test(a)){t=l.s;break}switch(t&&/Windows/.test(t)&&(n=/Windows (.*)/.exec(t)[1],t="Windows"),t){case"Mac OS X":n=/mac os x (10[\.\_\d]+)/.exec(a)[1];break;case"Android":n=/android ([\.\_\d]+)/.exec(a)[1];break;case"iOS":n=(n=/os (\d+)_(\d+)_?(\d+)?/.exec(s))[1]+"."+n[2]+"."+(0|n[3])}return{name:e,ver:c.toString(),os:t,osVersion:n,codebase:i,userAgent:r}},e.exports=r},function(e,t,n){function r(){this.invoker=null,this.receiver=null}function i(){this.listeners=[]}function o(e){this.stats=null,this.lastResponsed=(new Date).getTime(),this.onDataSent=e}function a(e){this.ssrc=null,this.direction=void 0,this.mediaType=null,this.resolution=null,this.frameRateReceived=null,this.frameWidth=null,this.rtt=null,this.droppedFramesNum=null,this.packetLosts=null,this.jitter=null,this.bytesReceived=-1,this.packetsReceived=-1,this.packetsDiscarded=-1,this.bytesSent=-1,this.packetsSent=-1,this.stream=e}function s(e,t){var n="number"==typeof e?e:parseInt(e),r="number"==typeof t?t:parseInt(t);return n===r?0:n0&&n>0&&(i=r.concat("x",n,"@",t)),i},getFrameWidth:function(e){var t;return void 0!==e.data.googFrameWidthReceived?t=e.data.googFrameWidthReceived:void 0!==e.data.googFrameWidthSent?t=e.data.googFrameWidthSent:void 0!==e.data.frameWidth&&(t=e.data.frameWidth),t},validateRTT:function(e){return isNaN(e)||e<0?null:e},getLatencyData:function(e){return void 0===e?null:void 0!==e.data.googRtt?this.validateRTT(parseInt(e.data.googRtt,10)):void 0!==e.data.roundTripTime?this.validateRTT(parseInt(e.data.roundTripTime,10)):void 0!==e.data.mozRtt?this.validateRTT(parseInt(e.data.mozRtt,10)):null},getMediaType:function(e){var t=this.mediaType.unknown;if(void 0!==e)return e.data&&void 0!==e.data.mediaType?e.data.mediaType:(e.data.mediaType?t=e.data.mediaType:void 0!==e.data.googFrameRateReceived||void 0!==e.data.googFrameRateSent?t=this.mediaType.video:void 0!==e.data.audioInputLevel||void 0!==e.data.audioOutputLevel||void 0!==e.data.audioLevel?t=this.mediaType.audio:void 0!==e.data.framerateMean&&(t=this.mediaType.video),t)},checkForNan:function(e){return isNaN(e)?null:e},checkForNegativeValue:function(e){if(null!==(e=this.checkForNan(e)))return e<0?null:e},toString:function(){return"ToString function is not defined. for this object"}},m.prototype=Object.create(O.prototype),m.prototype.constructor=m,m.prototype.onDataSent=function(){this.prev=this.actual},m.prototype.getActual=function(){return this.actual},m.prototype.getFirst=function(){return this.first},m.prototype.getDelta=function(){return this.delta=this.actual-this.prev,this.delta},m.prototype.getPrevious=function(){return this.prev},m.prototype.add=function(e){var t=this.extractorFnc(e);if(void 0!==t&&null!==t){if(0===this.first)return this.first=t,void(this.actual=t);this.actual=t,this.notify({last:this.prev,delta:this.delta})}},m.prototype.remove=function(){},v.prototype=Object.create(O.prototype),v.prototype.constructor=v,v.prototype.add=function(e){void 0!==e&&null!==e&&(this.prev=this.extractorFnc(e))},v.prototype.remove=function(){},v.prototype.getPrevious=function(){return this.prev},g.prototype={constructor:g,getIntervalLoss:function(){return this.intervalLoss},getIntervalPacketsRecv:function(){return this.intervalPacketsRecv},getValue:function(){return void 0===this.intervalPacketsRecv||void 0===this.intervalLoss?null:null===this.intervalPacketsRecv||null===this.intervalLoss?null:0===this.intervalLoss&&0===this.intervalPacketsRecv?0:this.intervalLoss/(this.intervalPacketsRecv+this.intervalLoss)}},S.VideoThroughputThresholds={green:1024,red:256},S.AudioThroughputThresholds={green:30,red:8},S.FrameRateRatioTresholds={green:.8,red:.3},S.VideoRTTThresholds={green:400,red:1e3},S.VideoFractionLostTreshdolds={green:10,red:50},S.AudioFractionLostTresholds={green:15,red:30},S.AudioEModelTresholds={green:240,red:400},S.avQualityRating={excellent:3,fair:2,bad:1},S.avQualityRatingString={excellent:"excellent",fair:"fair",bad:"bad"},S.prototype={constructor:S,doStart:function(){return!0},getStartTime:function(){return this.firstAdded},hasTraffic:function(){var e=this.getLastMeasurement();return this.direction===a.Direction.inbound?00&&(e.data.csioAvgBRKbps=8*this.bytesReceivedTracker.getActual()/this.getTotalTimeInMs()),e.data.csioIntFL=t.getValue(),e.data.csioPercentileFl=this.getIntervalFractionLost95(),e.data.csioeM=(null!==this.getRTT95()?this.getRTT95():0)+40,e.data.csioIntPktLoss=this.packetLostTracker.getDelta(),void 0!==e.data.csioAvgBRKbps&&null!==e.data.csioAvgBRKbps?e.data.csioAvgPacketSize=this.bytesReceivedTracker.getDelta()/this.packetsReceivedTracker.getDelta():e.data.csioAvgPacketSize=null,e.data.csioPktLossPercentage=this.packetLostTracker.getDelta()/this.packetsReceivedTracker.getDelta()*100},T.prototype=Object.create(_.prototype),T.prototype.constructor=T,T.prototype.setup=function(e){_.prototype.setup.call(this,e),e.data.csioMediaType=a.MediaTypes.audio,this.quality.eModel=this.getQualityReverseEvaluation(e.data.csioeM,S.AudioEModelTresholds),this.quality.bandwidth=this.getQualityEvaluation(e.data.csioIntBRKbps,S.AudioThroughputThresholds),e.data.csioMark=this.getQuality()},T.prototype.getQuality=function(){var e=0,t=.5*this.quality.bandwidth+.5*this.quality.eModel;return(t=Math.floor(t))===S.avQualityRating.excellent?e=S.avQualityRatingString.excellent:t===S.avQualityRating.fair?e=S.avQualityRatingString.fair:t===S.avQualityRating.bad&&(e=S.avQualityRatingString.bad),e},C.prototype=Object.create(_.prototype),C.prototype.constructor=C,C.prototype.setFrameRatioQuality=function(){var e=this.frameRateReceivedTracker.getActual(),t=this.frameRateReceivedTracker.getPrevious(),n=0;this.quality.frameRate=0,void 0!==e&&void 0!==t&&null!==e&&null!==t&&0!==t&&(n=e/t,this.quality.frameRate=this.getQualityEvaluation(n,S.FrameRateRatioTresholds))},C.prototype.setFrameRateReceived10=function(e){return null===e?void(this.frameRateReceived10=null):void(this.frameRateReceived10=e.actual)},C.prototype.setup=function(e){_.prototype.setup.call(this,e),e.data.csioMediaType=a.MediaTypes.video,this.setFrameRatioQuality(),this.quality.eModel=this.getQualityReverseEvaluation(e.data.csioeM,S.AudioEModelTresholds),this.quality.bandwidth=this.getQualityEvaluation(e.data.csioIntBRKbps,S.VideoThroughputThresholds),e.data.csioMark=this.getQuality()},C.prototype.getQuality=function(){var e=0,t=.33*this.quality.bandwidth+.33*this.quality.eModel+.33*this.quality.frameRate;return(t=Math.floor(t))===S.avQualityRating.excellent?e=S.avQualityRatingString.excellent:t===S.avQualityRating.fair?e=S.avQualityRatingString.fair:t===S.avQualityRating.bad&&(e=S.avQualityRatingString.bad),e},E.prototype=Object.create(S.prototype),E.prototype.constructor=E,E.prototype.setup=function(e){S.prototype.setup.call(this,e);var t=new g(this.packetLostTracker.getDelta(),this.packetsSentTracker.getDelta());this.addIntervalFractionLost(t),this.setIntBRAndPR(this.bytesSentTracker,this.packetsSentTracker,e),this.getTotalTimeInMs()>0&&(e.data.csioAvgBRKbps=8*this.bytesSentTracker.getActual()/this.getTotalTimeInMs()),e.data.csioIntFL=t.getValue(),e.data.csioIntPktRcv=this.packetsSentTracker.getDelta(),e.data.csioPercentileFl=this.getIntervalFractionLost95(),e.data.csioeM=(null!==this.getRTT95()?this.getRTT95():0)+40,e.data.csioIntPktLoss=this.packetLostTracker.getDelta(),void 0!==e.data.csioAvgBRKbps&&null!==e.data.csioAvgBRKbps?e.data.csioAvgPacketSize=this.bytesSentTracker.getDelta()/this.packetsSentTracker.getDelta():e.data.csioAvgPacketSize=null,e.data.csioPktLossPercentage=this.packetLostTracker.getDelta()/this.packetsSentTracker.getDelta()*100},w.verificationElapsedThreshold=1e4,w.initialElapsedThreshold=15e3,w.minStableKBpsSlack=50,w.prototype=Object.create(E.prototype),w.prototype.constructor=w,w.prototype.setFrameRatioQuality=function(){var e=this.frameRateReceivedTracker.getActual(),t=this.frameRateReceivedTracker.getPrevious(),n=0;this.quality.frameRate=0,void 0!==e&&void 0!==t&&null!==e&&null!==t&&0!==t&&(n=e/t,this.quality.frameRate=this.getQualityEvaluation(n,S.FrameRateRatioTresholds))},w.prototype.setFrameRateReceived10=function(e){return null===e?void(this.frameRateReceived10=null):void(this.frameRateReceived10=e.actual)},w.prototype.getSendingKBitrateObservations=function(){var e=this.sendingThroughputObservations;return e.ready?{ssrc:e.ssrc,maxsendingKBitrate:e.max,timeToMaxSendingKBitrate:e.maxTs-e.started,stablesendingKBitrate:e.stable,timeToStableSendingKBitrate:e.stableTs-e.started}:null},w.prototype.checkSendingKBitrateObservations=function(e){var t=this.sendingThroughputObservations,n=(new Date).getTime();if(!0!==t.ready){if(0===t.started)return t.ssrc=this.getSSRC(),void(t.started=n);var r=e.data.csioIntBRKbps,i=Math.min(w.minStableKBpsSlack,.05*r);t.maxS.AudioEModelTresholds.green&&eS.AudioEModelTresholds.red&&(this.quality.eModel=S.avQualityRating.bad)},R.prototype.setThroughputQuality=function(e){null!==e&&void 0!==e&&(e>S.AudioThroughputThresholds.green?this.quality.bandwidth=S.avQualityRating.excellent:e>S.AudioThroughputThresholds.red&&e ("+r+") values: ["+t.value.toString()+"]"),null!==t.left&&e(t.left,n+1,"Left"),null!==t.right&&e(t.right,n+1,"Right")}})(this.root,0,"Root"))},r.prototype.insertByTop=function(e){this.inserting(e,this.top)},r.prototype.insertByBottom=function(e){this.inserting(e,this.bottom)},r.prototype.insert=function(e){this.inserting(e,this.root)},r.prototype.inserting=function(e,t){var r=function(){var t=new n(e);return(null===this.top||this.comparator(this.top.value[0],t.value[0])<0)&&(this.top=t),(null===this.bottom||this.comparator(t.value[0],this.bottom.value[0])<0)&&(this.bottom=t),t};if(null===this.root)return this.root=r.call(this),void(this.node_counter=1);for(var i=null,o=t,a=0,s=function(t){return t===e};null!==o;){if(0===(a=this.comparator(e,o.value[0])))return void(o.value.find(s)||(o.value.push(e),++this.duplicate_counter));i=o,o=a<0?o.left:o.right}++this.node_counter,a<0?i.left=r.call(this):i.right=r.call(this)},r.prototype.search=function(e){for(var t,n=this.root,r=null,i=function(t){return t===e};null!==n;){if(0===(t=this.comparator(e,n.value[0])))return n.value.find(i)?(n.parent=r,n):null;r=n,n=t<0?n.left:n.right}return null},r.prototype.getRightist=function(e){for(var t=null;null!==e.right;)t=e,e=e.right;return e.parent=t,e},r.prototype.getLeftist=function(e){for(var t=null;null!==e.left;)t=e,e=e.left;return e.parent=t,e},r.prototype.deleteBottomValue=function(){for(;null===this.bottom;)return null;var e=this.bottom.value[0];return this.delete(e),e},r.prototype.deleteTopValue=function(){for(;null===this.top;)return null;var e=this.top.value[0];return this.delete(e),e},r.prototype.delete=function(e){var t=null,n=function(e,t,n){null!==t?t.left===e?t.left=n:t.right=n:this.root=n};if(null===(t=this.search(e)))return!1;if(10)&&e)){this.turnTestCounter=0,this.resultsHandler=new s.ResultsHandler;var n={type:"browser",os:this.browserInfo.os,osVersion:this.browserInfo.osVersion,buildName:this.browserInfo.browserName,buildVersion:this.browserInfo.browserVersion};this.resultsHandler.add("endpointInfo",n),this.onlineCheck.start(),this.active=!0,this._start()}}},{key:"_start",value:function(){var e=this;this.active&&this.turnConnection.connect(this.iceServers).then(function(){e.active&&e.startTurnTests().then(function(){e.stop()},function(t){e.stop()})},function(t){e.resultsHandler.failure(t),e.resultsHandler.getFailureNumber()>=10?e.stop():(e.turnConnection.disconnect(),setTimeout(function(){e._start()},0))})}},{key:"stop",value:function(){var e=this;if(this.browserInfo.browserName!==u.Constants.browserName.msie&&this.active){this.active=!1,this.activeTurnTest&&this.activeTurnTest.forceStop();var t=this.onlineCheck.stop();this.resultsHandler.add("onlineStatus",t),this.turnConnection.getIceResults().then(function(t){e.resultsHandler.add("ice",t),e.turnConnection.disconnect(),e.sendResults()},function(t){e.resultsHandler.failure(t),e.turnConnection.disconnect(),e.sendResults()})}}},{key:"sendResults",value:function(){var e=this.resultsHandler.getResults();this.callback&&this.callback(e),this.resultsHandler=null}},{key:"callStarts",value:function(){this.callsInProgress+=1,this.stop()}},{key:"callFinished",value:function(){this.callsInProgress-=1}},{key:"getId",value:function(){return this.resultsHandler?this.resultsHandler.getId():null}},{key:"crashDisconnect",value:function(){try{this.turnConnection.disconnect()}catch(e){}}},{key:"startTurnTests",value:function(){var e=this;if(this.turnTestCounter>=this.turnTests.length)return new l(function(e,t){e()});var t=this.turnTests[this.turnTestCounter],n=null;switch(t){case d.RTT:n=new o.RttTest(this.turnConnection);break;case d.THROUGHPUT:n=new a.ThroughputTest(this.turnConnection,this.rtt);break;default:return new l(function(e,n){n(new Error("Unknown test: "+t))})}return this.activeTurnTest=n,this.active?n.start().then(function(){return e.handleTestResults(t,n.getResults()),e.turnTestCounter+=1,e.activeTurnTest=null,e.startTurnTests()},function(r){return e.handleTestResults(t,n.getResults(),r),e.turnTestCounter+=1,e.activeTurnTest=null,e.startTurnTests()}):new l(function(e,t){t(new Error("Test trying to start while testing is not active"))})}},{key:"handleTestResults",value:function(e,t){null==(arguments.length>2&&void 0!==arguments[2]?arguments[2]:null)&&e==d.RTT&&(this.rtt=t.median),this.resultsHandler&&this.resultsHandler.add(e,t)}}]),e})();t.PreCallTest=p},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TurnConnection=void 0;var r=(function(){function e(e,t){for(var n=0;n>24;if("rtp"===this.protocol&&n>=0&&n<=2)switch(n){case 0:t="TLS";break;case 1:t="TCP";break;case 2:t="UDP"}return t}},{key:"getString",value:function(){return this.iceCandidateStr}},{key:"getType",value:function(){return this.type}},{key:"isHost",value:function(){return"host"===this.type.toLowerCase()}},{key:"isServerReflexive",value:function(){return"srflx"===this.type.toLowerCase()}},{key:"isPeerReflexive",value:function(){return"prflx"===this.type.toLowerCase()}},{key:"isRelay",value:function(){return"relay"===this.type.toLowerCase()||"relayed"===this.type.toLowerCase()}},{key:"getTypeTransport",value:function(){return this.typeTransport}},{key:"isTypeTransportUdp",value:function(){return"UDP"===this.typeTransport}},{key:"isTypeTransportTcp",value:function(){return"TCP"===this.typeTransport}},{key:"isTypeTransportTls",value:function(){return"TLS"===this.typeTransport}},{key:"getTransport",value:function(){return this.transport}},{key:"isUdp",value:function(){return"udp"===this.transport.toLowerCase()}},{key:"isTcp",value:function(){return"tcp"===this.transport.toLowerCase()}},{key:"getProtocol",value:function(){return this.protocol}},{key:"isRtp",value:function(){return"rtp"===this.protocol}},{key:"isRtcp",value:function(){return"rtcp"===this.protocol}},{key:"isIpv6",value:function(){return this.ipv6}},{key:"getIpAddress",value:function(){return this.ipAddress}},{key:"getPort",value:function(){return this.port}}]),e})();t.ParsedIceCandidate=i},function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.GetStatsHandler=void 0;var r=(function(){function e(e,t){for(var n=0;n>24;c.mozLocalTransport=this.formatRelayType(u)}c.mozLocalTransport=c.mozLocalTransport.toLowerCase()}t.push(c)}else s.remoteCandidate&&n.push(s.remoteCandidate)}if(i)for(var l=0;l=this.intervalLength){var r=n-this.intervalStart,i=this.averageThroughput(this.intervalBytes,r),a=null;try{a=n-JSON.parse(this.lastMessage).timestamp}catch(e){}this.intervals.push({startTimestamp:this.intervalStart,endTimestamp:n,bytesReceived:this.intervalBytes,average:i,rtt:a}),this.intervalStart=n,this.intervalBytes=0}this.results.startTimestamp&&n-this.results.startTimestamp>this.duration/2&&(this.secondHalfStart||(this.secondHalfStart=n),this.secondHalfBytes+=e.length)}}},{key:"handleError",value:function(e){this.stop(),this.failed(e)}},{key:"averageThroughput",value:function(e,t){return e/(t/1e3)*8/1024}},{key:"bufferListener",value:function(){this.sendChannel.removeEventListener("bufferedamountlow",this.bufferListener.bind(this)),this.fillBuffer()}},{key:"fillBuffer",value:function(){for(0==this.sendChannel.bufferedAmount&&(this.bufferEmpty+=1);this.isActive();){if(this.sendChannel.bufferedAmount>this.bufferFullThreshold)return void(this.usePolling?setTimeout(this.fillBuffer.bind(this),250):this.sendChannel.addEventListener("bufferedamountlow",this.bufferListener.bind(this)));var e=this.messageMaker.make(this.sentBytes);this.sentBytes+=e.length,this.send(e)}this.sendChannel.removeEventListener("bufferedamountlow",this.bufferListener.bind(this))}},{key:"startSend",value:function(){this.isActive()&&(this.bufferFullThreshold=1e3*this.chunkSize,this.sendChannel=this.connection.sendChannel,this.usePolling=!0,"number"==typeof this.sendChannel.bufferedAmountLowThreshold&&(this.usePolling=!1,this.sendChannel.bufferedAmountLowThreshold=this.bufferFullThreshold/10),setTimeout(this.fillBuffer.bind(this),0))}},{key:"fillResults",value:function(){this.results.endTimestamp=o.getCurrent(),this.results.maxDuration=this.duration,this.results.forceStopped=this.forceStopped,this.results.bufferEmpty=this.bufferEmpty,this.results.intervals=this.intervals,this.results.bytesPrepared=this.sentBytes,this.results.bytesReceived=this.receivedBytes;var e=0,t=0;this.secondHalfStart&&(e=this.results.endTimestamp-this.secondHalfStart,t=this.averageThroughput(this.secondHalfBytes,e));var n=this.results.endTimestamp-this.results.startTimestamp,r=this.averageThroughput(this.receivedBytes,n);r>t&&(t=r),this.results.average=t;var i=null;try{i=JSON.parse(this.lastMessage)}catch(e){return}if(i){var a=i.sentBytes+this.lastMessage.length;this.results.bytesSent=a,this.results.fractionLostBytes=1-this.receivedBytes/a}else this.results.bytesSent=-1,this.results.fractionLostBytes=-1}},{key:"stop",value:function(){this.isActive()&&(clearTimeout(this.sendTimer),this.sendTimer=null,(function e(t,n,r){null===t&&(t=Function.prototype);var i=Object.getOwnPropertyDescriptor(t,n);if(void 0===i){var o=Object.getPrototypeOf(t);return null===o?void 0:e(o,n,r)}if("value"in i)return i.value;var a=i.get;return void 0!==a?a.call(r):void 0})(t.prototype.__proto__||Object.getPrototypeOf(t.prototype),"stop",this).call(this),this.fillResults())}}]),t})();t.ThroughputTest=c},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e};Object.defineProperty(t,"__esModule",{value:!0}),t.ResultsHandler=void 0;var i="function"==typeof Symbol&&"symbol"==r("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return void 0===e?"undefined":r(e)}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":void 0===e?"undefined":r(e)},o=(function(){function e(e,t){for(var n=0;n0&&(P=A-t(m,"bytesSent")))),P=Math.max(0,P);var O=u.timestamp-m.timestamp,D=0,x=0;O>0&&(D=Math.round(8*I/O),x=Math.round(8*P/O)),g.addBitrate({download:D,upload:x});var N={height:null,width:null};try{var L=void 0,M=void 0;(L=t(u,"googFrameHeightReceived"))&&(M=t(u,"googFrameWidthReceived"))?(N.height=L,N.width=M):(L=t(u,"googFrameHeightSent"))&&(M=t(u,"googFrameWidthSent"))&&(N.height=L,N.width=M)}catch(e){}var j=void 0;try{j=t(u,"googFrameRateReceived")||t(u,"googFrameRateSent")||0}catch(e){try{j=this.getNonNegativeStat(u,"framerateMean")}catch(e){}}g.setFramerate(Math.round(j||0)),N.height&&N.width?g.setResolution(N):g.setResolution(null)}}}}var F={download:0,upload:0},U={download:0,upload:0},H=0,B=0,G={},J={},V=0,W=0,q=0,K=0,z=!0,Q=!1,$=void 0;try{for(var X,Y=this.ssrc2stats["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(z=(X=Y.next()).done);z=!0){var Z=X.value,ee=c(Z,2),te=ee[0],ne=ee[1],re=ne.loss,ie=re.isDownloadStream?"download":"upload";F[ie]+=re.packetsTotal,U[ie]+=re.packetsLost,H+=ne.bitrate.download,B+=ne.bitrate.upload;var oe=this.peerconnection.getTrackBySSRC(te);if(oe){oe.isAudioTrack()?(V+=ne.bitrate.download,W+=ne.bitrate.upload):(q+=ne.bitrate.download,K+=ne.bitrate.upload);var ae=oe.getParticipantId();if(ae){var se=ne.resolution;if(se.width&&se.height&&-1!==se.width&&-1!==se.height){var ce=G[ae]||{};ce[te]=se,G[ae]=ce}if(0!==ne.framerate){var ue=J[ae]||{};ue[te]=ne.framerate,J[ae]=ue}}else l.error("No participant ID returned by "+oe)}else this.peerconnection.isP2P&&l.error("JitsiTrack not found for SSRC "+te+" in "+this.peerconnection);ne.resetBitrate()}}catch(e){Q=!0,$=e}finally{try{!z&&Y.return&&Y.return()}finally{if(Q)throw $}}this.eventEmitter.emit(s.c,this.peerconnection,n),this.conferenceStats.bitrate={upload:B,download:H},this.conferenceStats.bitrate.audio={upload:W,download:V},this.conferenceStats.bitrate.video={upload:K,download:q},this.conferenceStats.packetLoss={total:r(U.download+U.upload,F.download+F.upload),download:r(U.download,F.download),upload:r(U.upload,F.upload)},this.eventEmitter.emit(s.d,this.peerconnection,{bandwidth:this.conferenceStats.bandwidth,bitrate:this.conferenceStats.bitrate,packetLoss:this.conferenceStats.packetLoss,resolution:G,framerate:J,transport:this.conferenceStats.transport}),this.conferenceStats.transport=[]}},o.prototype.processAudioLevelReport=function(){if(this.baselineAudioLevelsReport){var e=this._getStatValue;for(var t in this.currentAudioLevelsReport)if(this.currentAudioLevelsReport.hasOwnProperty(t)){var n=this.currentAudioLevelsReport[t];if("ssrc"===n.type||"track"===n.type){var r=this.baselineAudioLevelsReport[t],i=this.getNonNegativeStat(n,"ssrc");if(!i&&Array.isArray(n.ssrcIds)&&(i=Number(n.ssrcIds[0])),r)if(i){var o=void 0;try{o=e(n,"audioInputLevel")||e(n,"audioOutputLevel")}catch(e){return l.warn("Audio Levels are not available in the statistics."),void clearInterval(this.audioLevelsIntervalId)}if(o){var c=void 0;c="ssrc"===n.type?!e(n,"packetsReceived"):!n.remoteSource,a.b.isEdge()?o=o<0?Math.pow(10,o/20):0:o/=32767,this.eventEmitter.emit(s.a,this.peerconnection,i,o,c)}}else Date.now()-n.timestamp<3e3&&l.warn("No ssrc: ");else l.warn(i+" not enough data")}}}}}).call(t,"modules/statistics/RTPStatsCollector.js")},function(e,t,n){var r,i;"function"==typeof Symbol&&"function"==typeof Symbol&&Symbol.iterator,this||window,void 0===(i="function"==typeof(r=function(){function e(){if(0===i.length)return null;var n,l,d,p,h,f=[],m=e.skipStackDepth||1;for(n=0;n0?e(t.slice(t.indexOf(r[0])+r[0].length),n-1):r[1])),i})(d,m),f))&&r&&p===r&&(h=(function(e){var t,n,r=null;for(t=0,n=(e=e||i).length;t0&&i.a.sendLog(JSON.stringify(s))}else o.warn("Received versions not from the focus user: "+e,n);else o.warn("Ignored presence versions node - invalid xmlns",e)},r.prototype.getComponentVersion=function(e){return this.versions[e]}}).call(t,"modules/version/ComponentsVersions.js")},function(e,t,n){"use strict";(function(e){var r=n(31),i=n(7),o=n(0),a=(n.n(o),(function(){function e(e,t){for(var n=0;n0&&(h=o.startBitrate),t.on(i.CONNECTION_INTERRUPTED,function(){a._updateLocalConnectionQuality(0),a.eventEmitter.emit(r.LOCAL_STATS_UPDATED,a._localStats),a._broadcastLocalStats()}),t.room.addListener(s.ICE_CONNECTION_STATE_CHANGED,function(e,t){e.isP2P||"connected"!==t||(a._timeIceConnected=window.performance.now())}),t.on(i.ENDPOINT_MESSAGE_RECEIVED,function(e,t){t.type===d&&a._updateRemoteStats(e.getId(),t.values)}),t.statistics.addConnectionStatsListener(this._updateLocalStats.bind(this)),t.on(i.TRACK_MUTE_CHANGED,function(e){e.isVideoTrack()&&(e.isMuted()?a._timeVideoUnmuted=-1:a._maybeUpdateUnmuteTime())}),t.on(i.TRACK_ADDED,function(e){e.isVideoTrack()&&!e.isMuted()&&a._maybeUpdateUnmuteTime()})}return a(e,[{key:"_maybeUpdateUnmuteTime",value:function(){this._timeVideoUnmuted<0&&(this._timeVideoUnmuted=window.performance.now())}},{key:"_calculateConnectionQuality",value:function(e,t,n){var r=u[n],i=100,o=void 0,a=void 0,s=void 0,d=void 0;if(this._localStats.packetLoss&&(s=this._localStats.packetLoss.upload,t&&(s*=.5)),t||!r||e===c.DESKTOP||this._timeIceConnected<0||this._timeVideoUnmuted<0)void 0===s?(l.error("Cannot calculate connection quality, unknown packet loss."),i=100):i=s<=2?100:s<=4?70:s<=6?50:s<=8?30:s<=12?10:0;else{a=window.performance.now()-Math.max(this._timeVideoUnmuted,this._timeIceConnected);var f=this._conference.getActivePeerConnection();d=(function(e,t,n){if(n<5e3)return 1;var r=0,i=Math.min(t.height,t.width);if(e){var o=p.find(function(e){return e.height<=i});if(o)for(i=o.height;i>=180&&"break"!==(function(){var e=i;if(!(o=p.find(function(t){return t.height===e})))return"break";r+=o.target})();i/=2);}else{var a=t.width*t.height;r=a<=76800?600:a<=307200?1700:a<=518400?2e3:2500}return Math.min(r,(s=Math.max(0,n-1e3))>6e4?Number.MAX_SAFE_INTEGER:h*Math.pow(1.08,s/1e3));var s})(o=Boolean(f&&f.isSimulcastOn()),r,a),d*=.9,i=100*this._localStats.bitrate.upload/d,s&&s>=10&&(i=Math.min(i,30))}if(this._lastConnectionQualityUpdate>0){var m=this._localStats.connectionQuality,v=(window.performance.now()-this._lastConnectionQualityUpdate)/1e3;i=Math.min(i,m+2*v)}var g=Math.min(100,i);return console.debug("calculated connection quality",JSON.stringify({connectionQuality:g,isMuted:t,isSimulcastOn:o,lastUpdate:this._lastConnectionQualityUpdate,millisSinceStart:a,packetLoss:s,resolution:r,target:d,upload:this._localStats.bitrate&&this._localStats.bitrate.upload})),g}},{key:"_updateLocalConnectionQuality",value:function(e){this._localStats.connectionQuality=e,this._lastConnectionQualityUpdate=window.performance.now()}},{key:"_broadcastLocalStats",value:function(){var e={bitrate:this._localStats.bitrate,packetLoss:this._localStats.packetLoss,connectionQuality:this._localStats.connectionQuality,jvbRTT:this._localStats.jvbRTT};try{this._conference.broadcastEndpointMessage({type:d,values:e})}catch(e){}}},{key:"_updateLocalStats",value:function(e,t){if(!e.isP2P){var n=t.transport&&t.transport.length&&t.transport[0].rtt;this._localStats.jvbRTT=n||void 0}if(e===this._conference.getActivePeerConnection()){var i=void 0,o=!this._conference.isConnectionInterrupted(),a=this._conference.getLocalVideoTrack(),s=a?a.videoType:void 0,c=!a||a.isMuted(),u=a?a.resolution:null;c||this._maybeUpdateUnmuteTime();for(i in t)t.hasOwnProperty(i)&&(this._localStats[i]=t[i]);o&&this._updateLocalConnectionQuality(this._calculateConnectionQuality(s,c,u)),this.eventEmitter.emit(r.LOCAL_STATS_UPDATED,this._localStats),this._broadcastLocalStats()}}},{key:"_updateRemoteStats",value:function(e,t){this._remoteStats[e]={bitrate:t.bitrate,packetLoss:t.packetLoss,connectionQuality:t.connectionQuality,jvbRTT:t.jvbRTT},this.eventEmitter.emit(r.REMOTE_STATS_UPDATED,e,this._remoteStats[e])}},{key:"getStats",value:function(){return this._localStats}}]),e})();t.a=f}).call(t,"modules/connectivity/ConnectionQuality.js")},function(e,t,n){"use strict";t.a=function(e){var t=this,n=e.id,o=e.password,a=e.onLoginSuccessful,s=e.roomPassword,c=!1,u=void 0,l=new i.a(this.connection.options),d=new Promise(function(e,i){u=i,l.addListener(r.CONNECTION_DISCONNECTED,function(){l=void 0}),l.addListener(r.CONNECTION_ESTABLISHED,function(){c||(a&&a(),l.createRoom(t.options.name,t.options.config).moderator.authenticate().then(function(){l&&l.disconnect(),c||(t.join(s),e())}).catch(function(e){var t=e.error,n=e.message;l.disconnect(),i({authenticationError:t,message:n})}))}),l.addListener(r.CONNECTION_FAILED,function(e,t,n){i({connectionError:e,credentials:n,message:t}),l=void 0}),c||l.connect(n,o)});return d.cancel=function(){c=!0,u({}),l&&l.disconnect()},d};var r=n(26),i=n(46)},function(e,t,n){var r,i,o;a=function(e){"use strict";e.Strophe.addConnectionPlugin("disco",{_connection:null,_identities:[],_features:[],_items:[],init:function(t){this._connection=t,this._identities=[],this._features=[],this._items=[],t.addHandler(this._onDiscoInfo.bind(this),e.Strophe.NS.DISCO_INFO,"iq","get",null,null),t.addHandler(this._onDiscoItems.bind(this),e.Strophe.NS.DISCO_ITEMS,"iq","get",null,null)},addIdentity:function(e,t,n,r){for(var i=0;ix[xmlns="http://jabber.org/protocol/muc#user"]>status[code="201"]').length&&n.createNonAnonymousRoom(),n.onPresence(e),!0):void 0}},{key:"onPresenceUnavailable",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];if(n)return n.onPresenceUnavailable(e,t),!0}},{key:"onPresenceError",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];if(n)return n.onPresenceError(e,t),!0}},{key:"onMessage",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];if(n)return n.onMessage(e,t),!0}},{key:"onMute",value:function(e){var t=e.getAttribute("from"),n=this.rooms[i.Strophe.getBareJidFromJid(t)];if(n)return n.onMute(e),!0}}]),t})();t.a=function(e){i.Strophe.addConnectionPlugin("emuc",new d(e))}}).call(t,"modules/xmpp/strophe.emuc.js")},function(e,t,n){"use strict";(function(e){function r(e,t){for(var n=[],r=0;r0&&void 0!==arguments[0]?arguments[0]:{};this.presMap.to=this.myroomjid,this.presMap.xns="http://jabber.org/protocol/muc",this.presMap.nodes=[],this.presMap.nodes.push({tagName:"user-agent",value:navigator.userAgent,attributes:{xmlns:"http://jitsi.org/jitmeet/user-agent"}}),e.enableStatsID&&this.presMap.nodes.push({tagName:"stats-id",value:l.a.callStatsUserName}),this.addVideoInfoToPresence(!1),e.deploymentInfo&&e.deploymentInfo.userRegion&&this.presMap.nodes.push({tagName:"region",attributes:{id:e.deploymentInfo.userRegion,xmlns:"http://jitsi.org/jitsi-meet"}})}},{key:"updateDeviceAvailability",value:function(e){this.presMap.nodes.push({tagName:"devices",children:[{tagName:"audio",value:e.audio},{tagName:"video",value:e.video}]})}},{key:"join",value:function(e){var t=this;this.password=e,this.moderator.allocateConferenceFocus(function(){return t.sendPresence(!0)})}},{key:"sendPresence",value:function(e){var t=this.presMap.to;if(t&&(this.joined||e)){var n=Object(o.$pres)({to:t});e&&(n.c("x",{xmlns:this.presMap.xns}),this.password&&n.c("password").t(this.password).up(),n.up()),b.json2packet(this.presMap.nodes,n),this.connection.send(n),e&&this.connection.flush()}}},{key:"doLeave",value:function(){y.log("do leave",this.myroomjid);var e=Object(o.$pres)({to:this.myroomjid,type:"unavailable"});this.presMap.length=0,this.connection.flush(),this.connection.send(e),this.connection.flush()}},{key:"discoRoomInfo",value:function(){var e=this,t=Object(o.$iq)({type:"get",to:this.roomjid}).c("query",{xmlns:o.Strophe.NS.DISCO_INFO});this.connection.sendIQ(t,function(t){var n=1===$(t).find('>query>feature[var="muc_passwordprotected"]').length;n!==e.locked&&(e.eventEmitter.emit(h.a.MUC_LOCK_CHANGED,n),e.locked=n)},function(e){s.a.callErrorHandler(e),y.error("Error getting room info: ",e)})}},{key:"createNonAnonymousRoom",value:function(){var e=Object(o.$iq)({type:"get",to:this.roomjid}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"}).c("x",{xmlns:"jabber:x:data",type:"submit"}),t=this;this.connection.sendIQ(e,function(e){if(!$(e).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_whois"]').length){var n="non-anonymous rooms not supported";return s.a.callErrorHandler(new Error(n)),void y.error(n)}var r=Object(o.$iq)({to:t.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"});r.c("x",{xmlns:"jabber:x:data",type:"submit"}),r.c("field",{var:"FORM_TYPE"}).c("value").t("http://jabber.org/protocol/muc#roomconfig").up().up(),r.c("field",{var:"muc#roomconfig_whois"}).c("value").t("anyone").up().up(),t.connection.sendIQ(r)},function(e){s.a.callErrorHandler(e),y.error("Error getting room configuration form: ",e)})}},{key:"onPresence",value:function(e){var t=e.getAttribute("from"),n={};n.show=$(e).find(">show").text();var r=$(e).find(">status");r.length&&(n.status=r.text());var i=!1,a=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>item');n.affiliation=a.attr("affiliation"),n.role=a.attr("role");var s=a.attr("jid");n.jid=s,n.isFocus=s&&0===s.indexOf(this.moderator.getFocusUserJid()+"/"),n.isHiddenDomain=s&&s.indexOf("@")>0&&this.options.hiddenDomain===s.substring(s.indexOf("@")+1,s.indexOf("/")),$(e).find(">x").remove();var c=[];b.packet2JSON(e,c),this.lastPresences[t]=c;for(var u=null,l=0;l0&&this.eventEmitter.emit(h.a.DISPLAY_NAME_CHANGED,t,S)}break;case"bridgeNotAvailable":n.isFocus&&!this.noBridgeAvailable&&(this.noBridgeAvailable=!0,this.eventEmitter.emit(h.a.BRIDGE_DOWN));break;case"jibri-recording-status":u=g;break;case"transcription-status":var _=g.attributes;if(!_)break;var T=_.status;T&&T!==this.transcriptionStatus&&(this.transcriptionStatus=T,this.eventEmitter.emit(h.a.TRANSCRIPTION_STATUS_CHANGED,T));break;case"call-control":var C=g.attributes;if(!C)break;this.phoneNumber=C.phone||null,this.phonePin=C.pin||null,this.eventEmitter.emit(h.a.PHONE_NUMBER_CHANGED);break;default:this.processNode(g,t)}}i&&this.eventEmitter.emit(h.a.PRESENCE_STATUS,t,n.status),u&&(this.lastJibri=u,this.recording&&this.recording.handleJibriPresence(u))}},{key:"_initFocus",value:function(e,t){this.focusMucJid=e,this.recording||(this.recording=new m.a(this.options.recordingType,this.eventEmitter,this.connection,this.focusMucJid,this.options.jirecon,this.roomjid),this.lastJibri&&this.recording.handleJibriPresence(this.lastJibri)),y.info("Ignore focus: "+e+", real JID: "+t)}},{key:"setParticipantPropertyListener",value:function(e){this.participantPropertyListener=e}},{key:"processNode",value:function(e,t){try{var n=this.presHandlers[e.tagName];e.tagName.startsWith("jitsi_participant_")&&(n=[this.participantPropertyListener]),n&&n.forEach(function(n){n(e,o.Strophe.getResourceFromJid(t),t)})}catch(t){s.a.callErrorHandler(t),y.error("Error processing:"+e.tagName+" node.",t)}}},{key:"sendMessage",value:function(e,t){var n=Object(o.$msg)({to:this.roomjid,type:"groupchat"});n.c("body",e).up(),t&&n.c("nick",{xmlns:"http://jabber.org/protocol/nick"}).t(t).up().up(),this.connection.send(n),this.eventEmitter.emit(h.a.SENDING_CHAT_MESSAGE,e)}},{key:"sendPrivateMessage",value:function(e,t,n){var r=Object(o.$msg)({to:this.roomjid+"/"+e,type:"chat"});r.c("body",t).up(),n&&r.c("nick",{xmlns:"http://jabber.org/protocol/nick"}).t(n).up().up(),this.connection.send(r),this.eventEmitter.emit(h.a.SENDING_PRIVATE_CHAT_MESSAGE,t)}},{key:"setSubject",value:function(e){var t=Object(o.$msg)({to:this.roomjid,type:"groupchat"});t.c("subject",e),this.connection.send(t)}},{key:"onParticipantLeft",value:function(e,t){delete this.lastPresences[e],t||(this.eventEmitter.emit(h.a.MUC_MEMBER_LEFT,e),this.moderator.onMucMemberLeft(e))}},{key:"onPresenceUnavailable",value:function(e,t){var n=this;if($(e).find('>ignore[xmlns="http://jitsi.org/jitmeet/"]').length)return!0;if($(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>destroy').length){var r=void 0,i=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>destroy>reason');return i.length&&(r=i.text()),this.eventEmitter.emit(h.a.MUC_DESTROYED,r),this.connection.emuc.doLeave(this.roomjid),!0}var o=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="110"]').length,a=$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="307"]').length,s=Object.keys(this.members);o?s.length>0&&(s.forEach(function(e){var t=n.members[e];delete n.members[e],n.onParticipantLeft(e,t.isFocus)}),this.connection.emuc.doLeave(this.roomjid),a||this.eventEmitter.emit(h.a.MUC_LEFT)):(delete this.members[t],this.onParticipantLeft(t,!1)),a&&this.myroomjid===t&&this.eventEmitter.emit(h.a.KICKED)}},{key:"onMessage",value:function(e,t){var n=$(e).find('>nick[xmlns="http://jabber.org/protocol/nick"]').text()||o.Strophe.getResourceFromJid(t),r=$(e).find(">body").text(),i=e.getAttribute("type");if("error"===i)return this.eventEmitter.emit(h.a.CHAT_ERROR_RECEIVED,$(e).find(">text").text(),r),!0;var a=$(e).find(">subject");if(a.length){var s=a.text();(s||""===s)&&(this.eventEmitter.emit(h.a.SUBJECT_CHANGED,s),y.log("Subject is changed to "+s))}var c=$(e).find(">delay").attr("stamp");if(!c&&(c=$(e).find('>[xmlns="jabber:x:delay"]').attr("stamp"))){var u=c.match(/(\d{4})(\d{2})(\d{2}T\d{2}:\d{2}:\d{2})/);c=u[1]+"-"+u[2]+"-"+u[3]+"Z"}t===this.roomjid&&$(e).find('>x[xmlns="http://jabber.org/protocol/muc#user"]>status[code="104"]').length&&this.discoRoomInfo();var l=(function(e){try{var t=JSON.parse(e);if(t&&"object"===(void 0===t?"undefined":g(t))){var n=t[S[0]],r=t[S[1]];if(("string"==typeof n||n instanceof String)&&r)return t;y.debug("parsing valid json but does not have correct structure","topic: ",n,"payload: ",r)}}catch(e){return!1}return!1})(r);l?this.eventEmitter.emit(h.a.JSON_MESSAGE_RECEIVED,t,l):r&&("chat"===i&&(y.log("privatechat",n,r),this.eventEmitter.emit(h.a.PRIVATE_MESSAGE_RECEIVED,t,n,r,this.myroomjid,c)),"groupchat"===i&&(y.log("chat",n,r),this.eventEmitter.emit(h.a.MESSAGE_RECEIVED,t,n,r,this.myroomjid,c)))}},{key:"onPresenceError",value:function(e,t){$(e).find('>error[type="auth"]>not-authorized[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length?(y.log("on password required",t),this.eventEmitter.emit(h.a.PASSWORD_REQUIRED)):$(e).find('>error[type="cancel"]>not-allowed[xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"]').length?o.Strophe.getDomainFromJid(e.getAttribute("to"))===this.xmpp.options.hosts.anonymousdomain?this.eventEmitter.emit(h.a.ROOM_JOIN_ERROR):(y.warn("onPresError ",e),this.eventEmitter.emit(h.a.ROOM_CONNECT_NOT_ALLOWED_ERROR)):$(e).find(">error>service-unavailable").length?(y.warn("Maximum users limit for the room has been reached",e),this.eventEmitter.emit(h.a.ROOM_MAX_USERS_ERROR)):(y.warn("onPresError ",e),this.eventEmitter.emit(h.a.ROOM_CONNECT_ERROR))}},{key:"kick",value:function(e){var t=Object(o.$iq)({to:this.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#admin"}).c("item",{nick:o.Strophe.getResourceFromJid(e),role:"none"}).c("reason").t("You have been kicked.").up().up().up();this.connection.sendIQ(t,function(t){return y.log("Kick participant with jid: ",e,t)},function(e){return y.log("Kick participant error: ",e)})}},{key:"lockRoom",value:function(e,t,n,r){var i=this;this.connection.sendIQ(Object(o.$iq)({to:this.roomjid,type:"get"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"}),function(a){if($(a).find('>query>x[xmlns="jabber:x:data"]>field[var="muc#roomconfig_roomsecret"]').length){var s=Object(o.$iq)({to:i.roomjid,type:"set"}).c("query",{xmlns:"http://jabber.org/protocol/muc#owner"});s.c("x",{xmlns:"jabber:x:data",type:"submit"}),s.c("field",{var:"FORM_TYPE"}).c("value").t("http://jabber.org/protocol/muc#roomconfig").up().up(),s.c("field",{var:"muc#roomconfig_roomsecret"}).c("value").t(e).up().up(),s.c("field",{var:"muc#roomconfig_whois"}).c("value").t("anyone").up().up(),i.connection.sendIQ(s,t,n)}else r()},n)}},{key:"addToPresence",value:function(e,t){t.tagName=e,this.removeFromPresence(e),this.presMap.nodes.push(t)}},{key:"removeFromPresence",value:function(e){var t=this.presMap.nodes.filter(function(t){return e!==t.tagName});this.presMap.nodes=t}},{key:"addPresenceListener",value:function(e,t){if("function"!=typeof t)throw new Error('"handler" is not a function');var n=this.presHandlers[e];n||(this.presHandlers[e]=n=[]),-1===n.indexOf(t)?n.push(t):y.warn("Trying to add the same handler more than once for: "+e)}},{key:"removePresenceListener",value:function(e,t){var n=this.presHandlers[e],r=n?n.indexOf(t):-1;-1!==r?n.splice(r,1):y.warn("Handler for: "+e+" was not registered")}},{key:"isFocus",value:function(e){var t=this.members[e];return t?t.isFocus:null}},{key:"isModerator",value:function(){return"moderator"===this.role}},{key:"getMemberRole",value:function(e){return this.members[e]?this.members[e].role:null}},{key:"setVideoMute",value:function(e,t){this.sendVideoInfoPresence(e),t&&t(e)}},{key:"setAudioMute",value:function(e,t){return this.sendAudioInfoPresence(e,t)}},{key:"addAudioInfoToPresence",value:function(e){this.removeFromPresence("audiomuted"),this.addToPresence("audiomuted",{attributes:{xmlns:"http://jitsi.org/jitmeet/audio"},value:e.toString()})}},{key:"sendAudioInfoPresence",value:function(e,t){this.addAudioInfoToPresence(e),this.connection&&this.sendPresence(),t&&t()}},{key:"addVideoInfoToPresence",value:function(e){this.removeFromPresence("videomuted"),this.addToPresence("videomuted",{attributes:{xmlns:"http://jitsi.org/jitmeet/video"},value:e.toString()})}},{key:"sendVideoInfoPresence",value:function(e){this.addVideoInfoToPresence(e),this.connection&&this.sendPresence()}},{key:"getMediaPresenceInfo",value:function(e,t){var n=this.lastPresences[this.roomjid+"/"+e];if(!n)return null;var i={muted:!1,videoType:void 0},o=null;if(t===d.a)o=r(n,"audiomuted");else{if(t!==d.b)return y.error("Unsupported media type: "+t),null;o=r(n,"videomuted");var a=r(n,"videoType");a.length>0&&(i.videoType=a[0].value)}return i.muted=o.length>0&&"true"===o[0].value,i}},{key:"isRecordingSupported",value:function(){return!!this.recording&&this.recording.isSupported()}},{key:"getRecordingState",value:function(){return this.recording?this.recording.getState():void 0}},{key:"getRecordingURL",value:function(){return this.recording?this.recording.getURL():null}},{key:"toggleRecording",value:function(e,t){return this.recording?this.recording.toggleRecording(e,t):t("error",new Error("The conference is not created yet!"))}},{key:"isSIPCallingSupported",value:function(){return!!this.moderator&&this.moderator.isSipGatewayEnabled()}},{key:"dial",value:function(e){return this.connection.rayo.dial(e,"fromnumber",o.Strophe.getBareJidFromJid(this.myroomjid),this.password,this.focusMucJid)}},{key:"hangup",value:function(){return this.connection.rayo.hangup()}},{key:"getPhoneNumber",value:function(){return this.phoneNumber}},{key:"getPhonePin",value:function(){return this.phonePin}},{key:"muteParticipant",value:function(e,t){y.info("set mute",t);var n=Object(o.$iq)({to:this.focusMucJid,type:"set"}).c("mute",{xmlns:"http://jitsi.org/jitmeet/audio",jid:e}).t(t.toString()).up();this.connection.sendIQ(n,function(e){return y.log("set mute",e)},function(e){return y.log("set mute error",e)})}},{key:"onMute",value:function(e){if(e.getAttribute("from")===this.focusMucJid){var t=$(e).find("mute");t.length&&"true"===t.text()?this.eventEmitter.emit(h.a.AUDIO_MUTED_BY_FOCUS):y.warn("Ignoring a mute request which does not explicitly specify a positive mute command.")}else y.warn("Ignored mute from non focus peer")}},{key:"leave",value:function(){var e=this;return new Promise(function(t,n){function r(){var e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];o.removeListener(h.a.MUC_LEFT,r),clearTimeout(i),e?n(new Error("The timeout for the confirmation about leaving the room expired.")):t()}var i=setTimeout(function(){return r(!0)},5e3),o=e.eventEmitter;o.on(h.a.MUC_LEFT,r),e.doLeave()})}}]),t})();t.a=_}).call(t,"modules/xmpp/ChatRoom.js")},function(e,t,n){"use strict";(function(e){function r(e){var t=1;return function(n){if(!n){var r=Math.pow(2,t-1);return t+=1,r*e}t=1}}function i(e,t,n,i){function o(e){if(e.data&&e.data.sessionId){if(e.origin!==window.location.origin)return void c.warn("Ignoring sessionId from different origin: "+e.origin);s.a.sessionId=e.data.sessionId}}this.roomName=e,this.xmppService=t,this.getNextTimeout=r(1e3),this.getNextErrorTimeout=r(1e3),this.externalAuthEnabled=!1,this.options=i,this.sipGatewayEnabled=this.options.connection.hosts&&void 0!==this.options.connection.hosts.call_control,this.eventEmitter=n,this.connection=this.xmppService.connection,window.addEventListener?window.addEventListener("message",o,!1):window.attachEvent("onmessage",o)}t.a=i;var o=n(2),a=(n.n(o),n(1)),s=n(23),c=n(0).getLogger(e),u=n(8),l=n(49),d=n(3);i.prototype.isExternalAuthEnabled=function(){return this.externalAuthEnabled},i.prototype.isSipGatewayEnabled=function(){return this.sipGatewayEnabled},i.prototype.onMucMemberLeft=function(e){c.info("Someone left is it focus ? "+e),"focus"===o.Strophe.getResourceFromJid(e)&&(c.info("Focus has left the room - leaving conference"),this.eventEmitter.emit(u.FOCUS_LEFT))},i.prototype.setFocusUserJid=function(e){this.focusUserJid||(this.focusUserJid=e,c.info("Focus jid set to: "+this.focusUserJid))},i.prototype.getFocusUserJid=function(){return this.focusUserJid},i.prototype.getFocusComponent=function(){var e=this.options.connection.hosts.focus;return e||(e="focus."+this.options.connection.hosts.domain),e},i.prototype.createConferenceIq=function(){var e=Object(o.$iq)({to:this.getFocusComponent(),type:"set"}),t=s.a.sessionId,n=s.a.machineId;c.info("Session ID: "+t+" machine UID: "+n),e.c("conference",{xmlns:"http://jitsi.org/protocol/focus",room:this.roomName,"machine-uid":n}),t&&e.attrs({"session-id":t}),void 0!==this.options.connection.enforcedBridge&&e.c("property",{name:"enforcedBridge",value:this.options.connection.enforcedBridge}).up(),void 0!==this.options.connection.hosts&&void 0!==this.options.connection.hosts.call_control&&e.c("property",{name:"call_control",value:this.options.connection.hosts.call_control}).up(),void 0!==this.options.conference.channelLastN&&e.c("property",{name:"channelLastN",value:this.options.conference.channelLastN}).up(),e.c("property",{name:"disableRtx",value:Boolean(this.options.conference.disableRtx)}).up(),e.c("property",{name:"enableLipSync",value:!1!==this.options.connection.enableLipSync}).up(),void 0!==this.options.conference.audioPacketDelay&&e.c("property",{name:"audioPacketDelay",value:this.options.conference.audioPacketDelay}).up(),this.options.conference.startBitrate&&e.c("property",{name:"startBitrate",value:this.options.conference.startBitrate}).up(),this.options.conference.minBitrate&&e.c("property",{name:"minBitrate",value:this.options.conference.minBitrate}).up();var r=void 0;switch(this.options.conference.openBridgeChannel){case"datachannel":case!0:case void 0:r=!0;break;case"websocket":r=!1}return r&&!a.b.supportsDataChannels()&&(r=!1),e.c("property",{name:"openSctp",value:r}).up(),void 0!==this.options.conference.startAudioMuted&&e.c("property",{name:"startAudioMuted",value:this.options.conference.startAudioMuted}).up(),void 0!==this.options.conference.startVideoMuted&&e.c("property",{name:"startVideoMuted",value:this.options.conference.startVideoMuted}).up(),void 0!==this.options.conference.stereo&&e.c("property",{name:"stereo",value:this.options.conference.stereo}).up(),void 0!==this.options.conference.useRoomAsSharedDocumentName&&e.c("property",{name:"useRoomAsSharedDocumentName",value:this.options.conference.useRoomAsSharedDocumentName}).up(),e.up(),e},i.prototype.parseSessionId=function(e){var t=$(e).find("conference").attr("session-id");t&&(c.info("Received sessionId: "+t),s.a.sessionId=t)},i.prototype.parseConfigOptions=function(e){this.setFocusUserJid($(e).find("conference").attr("focusjid"));var t=$(e).find(">conference>property[name='authentication'][value='true']").length>0;c.info("Authentication enabled: "+t),this.externalAuthEnabled=$(e).find(">conference>property[name='externalAuth'][value='true']").length>0,c.info("External authentication enabled: "+this.externalAuthEnabled),this.externalAuthEnabled||this.parseSessionId(e);var n=$(e).find(">conference").attr("identity");this.eventEmitter.emit(l.IDENTITY_UPDATED,t,n),$(e).find(">conference>property[name='sipGatewayEnabled'][value='true']").length&&(this.sipGatewayEnabled=!0),c.info("Sip gateway enabled: "+this.sipGatewayEnabled)},i.prototype.allocateConferenceFocus=function(e){var t=this;this.setFocusUserJid(this.options.connection.focusUserJid),this.connection.sendIQ(this.createConferenceIq(),function(n){return t._allocateConferenceFocusSuccess(n,e)},function(n){return t._allocateConferenceFocusError(n,e)}),this.connection.flush()},i.prototype._allocateConferenceFocusError=function(e,t){var n=this,r=$(e).find(">error>session-invalid").length||$(e).find(">error>not-acceptable").length;if(r&&(c.info("Session expired! - removing"),s.a.sessionId=void 0),$(e).find(">error>graceful-shutdown").length)this.eventEmitter.emit(u.GRACEFUL_SHUTDOWN);else{var i=$(e).find(">error>reservation-error");if(i.length){var a=i.attr("error-code"),l=$(e).find(">error>text"),p=void 0;return l&&(p=l.text()),void this.eventEmitter.emit(u.RESERVATION_ERROR,a,p)}if($(e).find(">error>not-authorized").length)return c.warn("Unauthorized to start the conference",e),o.Strophe.getDomainFromJid(e.getAttribute("to"))!==this.options.connection.hosts.anonymousdomain&&(this.externalAuthEnabled=!0),void this.eventEmitter.emit(u.AUTHENTICATION_REQUIRED);var h=this.getNextErrorTimeout(),f="Focus error, retry after "+h;d.callErrorHandler(new Error(f)),c.error(f,e);var m=this.getFocusComponent(),v=h/1e3;r||this.eventEmitter.emit(u.FOCUS_DISCONNECTED,m,v),this.getNextTimeout(!0),window.setTimeout(function(){return n.allocateConferenceFocus(t)},h)}},i.prototype._allocateConferenceFocusSuccess=function(e,t){var n=this;if(this.parseConfigOptions(e),this.getNextErrorTimeout(!0),"true"===$(e).find("conference").attr("ready"))this.getNextTimeout(!0),t();else{var r=this.getNextTimeout();c.info("Waiting for the focus... "+r),window.setTimeout(function(){return n.allocateConferenceFocus(t)},r)}},i.prototype.authenticate=function(){var e=this;return new Promise(function(t,n){e.connection.sendIQ(e.createConferenceIq(),function(n){e.parseSessionId(n),t()},function(e){return n({error:$(e).find("iq>error :first").prop("tagName"),message:$(e).find("iq>error>text").text()})})})},i.prototype.getLoginUrl=function(e,t){this._getLoginUrl(!1,e,t)},i.prototype._getLoginUrl=function(e,t,n){function r(e,t){d.callErrorHandler(new Error(e)),c.error(e,t),n(t)}var i=Object(o.$iq)({to:this.getFocusComponent(),type:"get"}),a={xmlns:"http://jitsi.org/protocol/focus",room:this.roomName,"machine-uid":s.a.machineId},u="auth url";e&&(a.popup=!0,u="POPUP "+u),i.c("login-url",a),this.connection.sendIQ(i,function(e){var n=$(e).find("login-url").attr("url");(n=decodeURIComponent(n))?(c.info("Got "+u+": "+n),t(n)):r("Failed to get "+u+" from the focus",e)},r.bind(void 0,"Get "+u+" error"))},i.prototype.getPopupLoginUrl=function(e,t){this._getLoginUrl(!0,e,t)},i.prototype.logout=function(e){var t=Object(o.$iq)({to:this.getFocusComponent(),type:"set"}),n=s.a.sessionId;n?(t.c("logout",{xmlns:"http://jitsi.org/protocol/focus","session-id":n}),this.connection.sendIQ(t,function(t){var n=$(t).find("logout").attr("logout-url");n&&(n=decodeURIComponent(n)),c.info("Log out OK, url: "+n,t),s.a.sessionId=void 0,e(n)},function(e){d.callErrorHandler(new Error("Logout error")),c.error("Logout error",e)})):e()}}).call(t,"modules/xmpp/moderator.js")},function(e,t,n){"use strict";(function(e){t.a=function(e,t,n){o.Strophe.addConnectionPlugin("jingle",new g(e,t,n))};var r=n(6),i=n(0),o=(n.n(i),n(2)),a=(n.n(o),n(8)),s=n.n(a),c=n(3),u=n.n(c),l=n(19),d=n.n(l),p=n(5),h=n(106),f=n(22),m=(function(){function e(e,t){for(var n=0;nstartmuted");if(f&&f.length>0){var m=f.attr("audio"),g=f.attr("video");this.eventEmitter.emit(s.a.START_MUTED_FROM_FOCUS,"true"===m,"true"===g)}v.info("Marking session from "+i+" as "+(d?"":"*not*")+" P2P"),c=new h.a($(e).find("jingle").attr("sid"),$(e).attr("to"),i,this.connection,this.mediaConstraints,d?this.p2pIceConfig:this.jvbIceConfig,d,!1,this.xmpp.options),this.sessions[c.sid]=c,this.eventEmitter.emit(s.a.CALL_INCOMING,c,$(e).find(">jingle"),l);break;case"session-accept":this.eventEmitter.emit(s.a.CALL_ACCEPTED,c,$(e).find(">jingle"));break;case"content-modify":c.modifyContents($(e).find(">jingle"));break;case"transport-info":this.eventEmitter.emit(s.a.TRANSPORT_INFO,c,$(e).find(">jingle"));break;case"session-terminate":v.log("terminating...",c.sid);var y=null,b=null;$(e).find(">jingle>reason").length&&(y=$(e).find(">jingle>reason>:first")[0].tagName,b=$(e).find(">jingle>reason>text").text()),this.terminate(c.sid,y,b),this.eventEmitter.emit(s.a.CALL_ENDED,c,y,b);break;case"transport-replace":v.info("(TIME) Start transport replace",l),p.a.sendAnalytics(Object(r.z)(r.e,{p2p:d,value:l})),c.replaceTransport($(e).find(">jingle"),function(){var e=window.performance.now();v.info("(TIME) Transport replace success!",e),p.a.sendAnalytics(Object(r.z)(r.f,{p2p:d,value:e}))},function(e){u.a.callErrorHandler(e),v.error("Transport replace failed",e),c.sendTransportReject()});break;case"addsource":case"source-add":c.addRemoteStream($(e).find(">jingle>content"));break;case"removesource":case"source-remove":c.removeRemoteStream($(e).find(">jingle>content"));break;default:v.warn("jingle action not implemented",n),a.attrs({type:"error"}),a.c("error",{type:"cancel"}).c("bad-request",{xmlns:"urn:ietf:params:xml:ns:xmpp-stanzas"}).up()}return this.connection.send(a),!0}},{key:"newP2PJingleSession",value:function(e,t){var n=new h.a(d.a.randomHexString(12),e,t,this.connection,this.mediaConstraints,this.p2pIceConfig,!0,!0,this.xmpp.options);return this.sessions[n.sid]=n,n}},{key:"terminate",value:function(e,t,n){this.sessions.hasOwnProperty(e)&&("ended"!==this.sessions[e].state&&this.sessions[e].onTerminated(t,n),delete this.sessions[e])}},{key:"getStunAndTurnCredentials",value:function(){var e=this;this.connection.sendIQ(Object(o.$iq)({type:"get",to:this.connection.domain}).c("services",{xmlns:"urn:xmpp:extdisco:1"}).c("service",{host:"turn."+this.connection.domain}),function(t){var n=[];$(t).find(">services>service").each(function(e,t){var r={},i=(t=$(t)).attr("type");switch(i){case"stun":r.url="stun:"+t.attr("host"),t.attr("port")&&(r.url+=":"+t.attr("port")),n.push(r);break;case"turn":case"turns":r.url=i+":";var o=t.attr("username");if(o){var a=navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);a&&parseInt(a[2],10)<28?r.url+=o+"@":r.username=o}r.url+=t.attr("host");var s=t.attr("port");s&&"3478"!==s&&(r.url+=":"+t.attr("port"));var c=t.attr("transport");c&&"udp"!==c&&(r.url+="?transport="+c),r.credential=t.attr("password")||r.credential,n.push(r)}});var r=e.xmpp.options;r.useStunTurn&&(e.jvbIceConfig.iceServers=n),r.p2p&&r.p2p.useStunTurn&&(e.p2pIceConfig.iceServers=n)},function(e){v.warn("getting turn credentials failed",e),v.warn("is mod_turncredentials or similar installed?")})}},{key:"getLog",value:function(){var e=this,t={};return Object.keys(this.sessions).forEach(function(n){var r=e.sessions[n].peerconnection;r&&r.updateLog&&(t["jingle_"+n]={updateLog:r.updateLog,stats:r.stats,url:window.location.href})}),t}}]),t})()}).call(t,"modules/xmpp/strophe.jingle.js")},function(e,t,n){"use strict";(function(e){var r=n(6),i=n(107),o=n.n(i),a=n(0),s=(n.n(a),n(2)),c=(n.n(s),n(108)),u=(n.n(c),n(109)),l=n(52),d=n(53),p=n(110),h=n(14),f=n(111),m=n(5),v=n(8),g=n.n(v),y=n(3),b=n.n(y),S=(function(){function e(e,t){for(var n=0;ncontent[name="video"]');if(t.length){var n=t[0].getAttribute("senders");if("both"===n||"initiator"===n||"responder"===n||"none"===n)return n}return null}}]),S(t,[{key:"_assertNotEnded",value:function(e){return this.state!==l.b||(_.log("The session has ended - cancelling action: "+e),!1)}},{key:"doInitialize",value:function(){var e=this;this.lasticecandidate=!1,this.isReconnect=!1,this.wasstable=!1;var t={disableRtx:this.room.options.disableRtx};if(this.isP2P){t.disableSimulcast=!0,t.disableH264=this.room.options.p2p&&this.room.options.p2p.disableH264,t.preferH264=this.room.options.p2p&&this.room.options.p2p.preferH264;var n=this._abtestSuspendVideoEnabled();void 0!==n&&(t.abtestSuspendVideo=n)}else t.disableSimulcast=this.room.options.disableSimulcast||this.room.options.preferH264&&!this.room.options.disableH264,t.preferH264=this.room.options.preferH264,t.enableFirefoxSimulcast=this.room.options.testing&&this.room.options.testing.enableFirefoxSimulcast;this.peerconnection=this.rtc.createPeerConnection(this.signalingLayer,this.iceConfig,this.isP2P,t),this.peerconnection.onicecandidate=function(t){if(t){var n=t.candidate,i=window.performance.now();if(n){null===e._gatheringStartedTimestamp&&(e._gatheringStartedTimestamp=i);var o=n.protocol;if("string"==typeof o)if("tcp"===(o=o.toLowerCase())||"ssltcp"===o){if(e.webrtcIceTcpDisable)return}else if("udp"===o&&e.webrtcIceUdpDisable)return}else e._gatheringReported||(m.a.sendAnalytics(r.m,{phase:"gathering",value:i-e._gatheringStartedTimestamp,p2p:e.isP2P,initiator:e.isInitiator}),e._gatheringReported=!0);e.sendIceCandidate(n)}},this.peerconnection.onsignalingstatechange=function(){e.peerconnection&&("stable"===e.peerconnection.signalingState?e.wasstable=!0:"closed"!==e.peerconnection.signalingState&&"closed"!==e.peerconnection.connectionState||e.closed||e.room.eventEmitter.emit(g.a.SUSPEND_DETECTED,e))},this.peerconnection.oniceconnectionstatechange=function(){if(e.peerconnection&&e._assertNotEnded("oniceconnectionstatechange")){var t=window.performance.now();switch(e.isP2P||(e.room.connectionTimes["ice.state."+e.peerconnection.iceConnectionState]=t),_.log("(TIME) ICE "+e.peerconnection.iceConnectionState+" P2P? "+e.isP2P+":\t",t),m.a.sendAnalytics(r.o,{p2p:e.isP2P,state:e.peerconnection.iceConnectionState,signaling_state:e.peerconnection.signalingState,reconnect:e.isReconnect,value:t}),e.room.eventEmitter.emit(g.a.ICE_CONNECTION_STATE_CHANGED,e,e.peerconnection.iceConnectionState),e.peerconnection.iceConnectionState){case"checking":e._iceCheckingStartedTimestamp=t;break;case"connected":if("stable"===e.peerconnection.signalingState&&e.isReconnect&&e.room.eventEmitter.emit(g.a.CONNECTION_RESTORED,e),!e.wasConnected&&e.wasstable){m.a.sendAnalytics(r.m,{phase:"checking",value:t-e._iceCheckingStartedTimestamp,p2p:e.isP2P,initiator:e.isInitiator});var n=Math.min(e._iceCheckingStartedTimestamp,e._gatheringStartedTimestamp);e.establishmentDuration=t-n,m.a.sendAnalytics(r.m,{phase:"establishment",value:e.establishmentDuration,p2p:e.isP2P,initiator:e.isInitiator}),e.wasConnected=!0,e.room.eventEmitter.emit(g.a.CONNECTION_ESTABLISHED,e)}e.isReconnect=!1;break;case"disconnected":if(e.closed)break;e.isReconnect=!0,e.wasstable&&e.room.eventEmitter.emit(g.a.CONNECTION_INTERRUPTED,e);break;case"failed":e.room.eventEmitter.emit(g.a.CONNECTION_ICE_FAILED,e),e.room.eventEmitter.emit(g.a.CONFERENCE_SETUP_FAILED,e,new Error("ICE fail"))}}},this.peerconnection.onnegotiationneeded=function(){e.room.eventEmitter.emit(g.a.PEERCONNECTION_READY,e)},this.signalingLayer.setChatRoom(this.room)}},{key:"sendIceCandidate",value:function(e){var t=this,n=new d.a(this.peerconnection.localDescription.sdp);if(e&&!this.lasticecandidate){var r=h.a.iceparams(n.media[e.sdpMLineIndex],n.session),i=h.a.candidateToJingle(e.candidate);if(!r||!i){var o="failed to get ice && jcand";return b.a.callErrorHandler(new Error(o)),void _.error(o)}r.xmlns="urn:xmpp:jingle:transports:ice-udp:1",this.usedrip?(0===this.dripContainer.length&&setTimeout(function(){0!==t.dripContainer.length&&(t.sendIceCandidates(t.dripContainer),t.dripContainer=[])},20),this.dripContainer.push(e)):this.sendIceCandidates([e])}else _.log("sendIceCandidate: last candidate."),this.lasticecandidate=!0}},{key:"sendIceCandidates",value:function(e){var t=this;if(this._assertNotEnded("sendIceCandidates")){_.log("sendIceCandidates",e);for(var n=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-info",initiator:this.initiator,sid:this.sid}),r=new d.a(this.peerconnection.localDescription.sdp),i=0;i0){var s=h.a.iceparams(r.media[i],r.session);s.xmlns="urn:xmpp:jingle:transports:ice-udp:1",n.c("content",{creator:t.initiator===t.localJid?"initiator":"responder",name:o[0].sdpMid?o[0].sdpMid:a.media}).c("transport",s);for(var c=0;ccontent>transport>candidate").each(function(e,t){var r=h.a.candidateFromJingle(t);r=r.replace("\r\n","").replace("a=","");var i=new RTCIceCandidate({sdpMLineIndex:0,sdpMid:"",candidate:r});n.push(i)}),n.length?(_.debug("Queued add ("+n.length+") ICE candidates task..."),this.modificationQueue.push(function(e){var r=!0,i=!1,o=void 0;try{for(var a,s=n["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done);r=!0){var c=a.value;t.peerconnection.addIceCandidate(c,function(){_.debug("addIceCandidate ok!")},function(e){_.error("addIceCandidate failed!",e)})}}catch(e){i=!0,o=e}finally{try{!r&&s.return&&s.return()}finally{if(i)throw o}}e()})):_.error("No ICE candidates to add ?",e[0]&&e[0].outerHTML)}else _.warn("Ignored add ICE candidate when in closed state")}},{key:"readSsrcInfo",value:function(e){var t=this;$(e).find('>description>source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(e,n){var r=Number(n.getAttribute("ssrc"));t.isP2P?t.signalingLayer.setSSRCOwner(r,s.Strophe.getResourceFromJid(t.remoteJid)):$(n).find('>ssrc-info[xmlns="http://jitsi.org/jitmeet"]').each(function(e,n){var i=n.getAttribute("owner");i&&i.length&&(isNaN(r)||r<0?_.warn("Invalid SSRC "+r+" value received for "+i):t.signalingLayer.setSSRCOwner(r,s.Strophe.getResourceFromJid(i)))})})}},{key:"generateRecvonlySsrc",value:function(){this.peerconnection?this.peerconnection.generateRecvonlySsrc():_.error("Unable to generate recvonly SSRC - no peerconnection")}},{key:"acceptOffer",value:function(e,t,n,r){var i=this;this.setOfferAnswerCycle(e,function(){i.sendSessionAccept(t,n)},n,r)}},{key:"invite",value:function(e){var t=this;if(!this.isInitiator)throw new Error("Trying to invite from the responder session");this.modificationQueue.push(function(n){var r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done);r=!0){var c=a.value;t.peerconnection.addTrack(c)}}catch(e){i=!0,o=e}finally{try{!r&&s.return&&s.return()}finally{if(i)throw o}}t.peerconnection.createOffer(function(e){t.peerconnection.setLocalDescription(e,function(){t.sendSessionInitiate(t.peerconnection.localDescription.sdp),n()},function(t){_.error("Failed to set local SDP",t,e),n(t)})},function(e){_.error("Failed to create an offer",e,t.mediaConstraints),n(e)},t.mediaConstraints)},function(e){e?_.error("invite error",e):_.debug("invite executed - OK")})}},{key:"sendSessionInitiate",value:function(e){var t=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-initiate",initiator:this.initiator,sid:this.sid});new d.a(e).toJingle(t,this.initiator===this.me?"initiator":"responder"),t=t.tree(),_.info("Session-initiate: ",t),this.connection.sendIQ(t,function(){_.info('Got RESULT for "session-initiate"')},function(e){_.error('"session-initiate" error',e)},1e4)}},{key:"setAnswer",value:function(e){if(!this.isInitiator)throw new Error("Trying to set an answer on the responder session");this.setOfferAnswerCycle(e,function(){_.info("setAnswer - succeeded")},function(e){_.error("setAnswer failed: ",e)})}},{key:"setOfferAnswerCycle",value:function(e,n,r,i){var o=this;this.modificationQueue.push(function(n){if(i){var r=!0,a=!1,s=void 0;try{for(var c,u=i["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(c=u.next()).done);r=!0){var p=c.value;o.peerconnection.addTrack(p)}}catch(e){a=!0,s=e}finally{try{!r&&u.return&&u.return()}finally{if(a)throw s}}}var h=o._processNewJingleOfferIq(e),f=o.peerconnection.localDescription.sdp;o._renegotiate(h.raw).then(function(){if(o.state===l.c&&(o.state=l.a,o.isP2P&&!o._localVideoActive&&o.sendContentModify(o._localVideoActive)),f){var e=new d.a(o.peerconnection.localDescription.sdp);o.notifyMySSRCUpdate(new d.a(f),e)}n()},function(e){_.error("Error renegotiating after setting new remote "+(o.isInitiator?"answer: ":"offer: ")+e,h),t.onJingleFatalError(o,e),n(e)})},function(e){e?r(e):n()})}},{key:"replaceTransport",value:function(e,t,n){var r=this;this.room.eventEmitter.emit(g.a.ICE_RESTARTING,this);var i=e.clone();e.find(">content[name='data']").remove(),this.setOfferAnswerCycle(e,function(){r.setOfferAnswerCycle(i,function(){var e=new d.a(r.peerconnection.localDescription.sdp);r.sendTransportAccept(e,t,n)},n)},n)}},{key:"sendSessionAccept",value:function(e,t){var n=this,r=new d.a(this.peerconnection.localDescription.sdp),i=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-accept",initiator:this.initiator,responder:this.responder,sid:this.sid});this.webrtcIceTcpDisable&&(r.removeTcpCandidates=!0),this.webrtcIceUdpDisable&&(r.removeUdpCandidates=!0),this.failICE&&(r.failICE=!0),r.toJingle(i,this.initiator===this.localJid?"initiator":"responder",null),i=i.tree(),_.info("Sending session-accept",i),this.connection.sendIQ(i,e,this.newJingleErrorHandler(i,function(e){t(e),n.room.eventEmitter.emit(g.a.SESSION_ACCEPT_TIMEOUT,n)}),1e4)}},{key:"sendContentModify",value:function(e){var t=e?"both":"none",n=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"content-modify",initiator:this.initiator,sid:this.sid}).c("content",{name:"video",senders:t});_.info("Sending content-modify, video senders: "+t),this.connection.sendIQ(n,null,this.newJingleErrorHandler(n),1e4)}},{key:"sendTransportAccept",value:function(e,t,n){var r=this,i=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-accept",initiator:this.initiator,sid:this.sid});e.media.forEach(function(t,n){var o=h.a.parseMLine(t.split("\r\n")[0]);i.c("content",{creator:r.initiator===r.localJid?"initiator":"responder",name:o.media}),e.transportToJingle(n,i),i.up()}),i=i.tree(),_.info("Sending transport-accept: ",i),this.connection.sendIQ(i,t,this.newJingleErrorHandler(i,n),1e4)}},{key:"sendTransportReject",value:function(e,t){var n=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"transport-reject",initiator:this.initiator,sid:this.sid});n=n.tree(),_.info("Sending 'transport-reject",n),this.connection.sendIQ(n,e,this.newJingleErrorHandler(n,t),1e4)}},{key:"terminate",value:function(e,t,n){if(this.state!==l.b){if(!n||Boolean(n.sendSessionTerminate)){var r=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"session-terminate",initiator:this.initiator,sid:this.sid}).c("reason").c(n&&n.reason||"success");n&&n.reasonDescription&&r.up().c("text").t(n.reasonDescription),r=r.tree(),_.info("Sending session-terminate",r),this.connection.sendIQ(r,e,this.newJingleErrorHandler(r,t),1e4)}else _.info("Skipped sending session-terminate for "+this);this.connection.jingle.terminate(this.sid)}}},{key:"onTerminated",value:function(e,t){this.state=l.b,this.establishmentDuration=void 0,_.info("Session terminated "+this,e,t),this.close()}},{key:"_parseSsrcInfoFromSourceAdd",value:function(e,t){var n=[];return $(e).each(function(e,r){var i=$(r).attr("name"),o="";$(r).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(o+="a=ssrc-group:"+e+" "+t.join(" ")+"\r\n")}),$(r).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=$(this).attr("ssrc");t.containsSSRC(e)?_.warn("Source-add request for existing SSRC: "+e):$(this).find(">parameter").each(function(){o+="a=ssrc:"+e+" "+$(this).attr("name"),$(this).attr("value")&&$(this).attr("value").length&&(o+=":"+$(this).attr("value")),o+="\r\n"})}),t.media.forEach(function(e,t){h.a.findLine(e,"a=mid:"+i)&&(n[t]||(n[t]=""),n[t]+=o)})}),n}},{key:"addRemoteStream",value:function(e){this._addOrRemoveRemoteStream(!0,e)}},{key:"removeRemoteStream",value:function(e){this._addOrRemoveRemoteStream(!1,e)}},{key:"_addOrRemoveRemoteStream",value:function(e,t){var n=this,r=e?"addRemoteStream":"removeRemoteStream";e&&this.readSsrcInfo(t),this.modificationQueue.push(function(i){if(!n.peerconnection.localDescription||!n.peerconnection.localDescription.sdp){var o=r+" - localDescription not ready yet";return _.error(o),void i(o)}_.log("Processing "+r),_.log("ICE connection state: ",n.peerconnection.iceConnectionState);var a=new d.a(n.peerconnection.localDescription.sdp),s=new d.a(n.peerconnection.remoteDescription.sdp),c=e?n._parseSsrcInfoFromSourceAdd(t,s):n._parseSsrcInfoFromSourceRemove(t,s),u=e?n._processRemoteAddSource(c):n._processRemoteRemoveSource(c);n._renegotiate(u.raw).then(function(){var e=new d.a(n.peerconnection.localDescription.sdp);_.log(r+" - OK, SDPs: ",a,e),n.notifyMySSRCUpdate(a,e),i()},function(e){_.error(r+" failed:",e),i(e)})})}},{key:"_processQueueTasks",value:function(e,t){e(t)}},{key:"_processNewJingleOfferIq",value:function(e){var t=new d.a("");return this.webrtcIceTcpDisable&&(t.removeTcpCandidates=!0),this.webrtcIceUdpDisable&&(t.removeUdpCandidates=!0),this.failICE&&(t.failICE=!0),t.fromJingle(e),this.readSsrcInfo($(e).find(">content")),t}},{key:"_processRemoteRemoveSource",value:function(e){var t=new d.a(this.peerconnection.remoteDescription.sdp);return e.forEach(function(e,n){(e=e.split("\r\n")).pop(),e.forEach(function(e){t.media[n]=t.media[n].replace(e+"\r\n","")})}),t.raw=t.session+t.media.join(""),t}},{key:"_processRemoteAddSource",value:function(e){var t=new d.a(this.peerconnection.remoteDescription.sdp);return e.forEach(function(e,n){t.media[n]+=e}),t.raw=t.session+t.media.join(""),t}},{key:"_renegotiate",value:function(e){var t=this,n=e||this.peerconnection.remoteDescription.sdp;if(!n)return Promise.reject("Can not renegotiate without remote description,- current state: "+this.state);var r=new RTCSessionDescription({type:this.isInitiator?"answer":"offer",sdp:n});return new Promise(function(e,n){"closed"!==t.peerconnection.signalingState?t.isInitiator?t._initiatorRenegotiate(r,e,n):t._responderRenegotiate(r,e,n):n("Attempted to renegotiate in state closed")})}},{key:"_responderRenegotiate",value:function(e,t,n){var r=this;_.debug("Renegotiate: setting remote description"),this.peerconnection.setRemoteDescription(e,function(){_.debug("Renegotiate: creating answer"),r.peerconnection.createAnswer(function(e){_.debug("Renegotiate: setting local description"),r.peerconnection.setLocalDescription(e,function(){t()},function(e){n("setLocalDescription failed: "+e)})},function(e){return n("createAnswer failed: "+e)},r.mediaConstraints)},function(e){return n("setRemoteDescription failed: "+e)})}},{key:"_initiatorRenegotiate",value:function(e,t,n){var r=this;"have-local-offer"===this.peerconnection.signalingState?(_.debug("Renegotiate: setting remote description"),this.peerconnection.setRemoteDescription(e,function(){r._initiatorRenegotiate(e,t,n)},function(e){return n("setRemoteDescription failed: "+e)})):(_.debug("Renegotiate: creating offer"),this.peerconnection.createOffer(function(i){_.debug("Renegotiate: setting local description"),r.peerconnection.setLocalDescription(i,function(){_.debug("Renegotiate: setting remote description"),r.peerconnection.setRemoteDescription(e,function(){t()},function(e){return n("setRemoteDescription failed: "+e)})},function(e){n("setLocalDescription failed: ",e)})},function(e){return n("createOffer failed: "+e)},this.mediaConstraints))}},{key:"replaceTrack",value:function(e,t){var n=this;this.modificationQueue.push(function(r){if("closed"===n.peerconnection.signalingState||"closed"===n.peerconnection.connectionState||n.closed)r();else{var i=n.peerconnection.localDescription.sdp;!e&&t&&t.isVideoTrack()?n.peerconnection.clearRecvonlySsrc():e&&e.isVideoTrack()&&!t&&(n.peerconnection.clearRecvonlySsrc(),n.peerconnection.generateRecvonlySsrc()),e&&n.peerconnection.removeTrack(e),t&&n.peerconnection.addTrack(t),(e||t)&&n.state===l.a?n._renegotiate().then(function(){var e=new d.a(n.peerconnection.localDescription.sdp);n.notifyMySSRCUpdate(new d.a(i),e),r()},r):r()}},function(e){e?_.error("Replace track error:",e):_.info("Replace track done!")})}},{key:"_parseSsrcInfoFromSourceRemove",value:function(e,t){var n=[];return $(e).each(function(e,r){var i=$(r).attr("name"),o="";$(r).find('ssrc-group[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=this.getAttribute("semantics"),t=$(this).find(">source").map(function(){return this.getAttribute("ssrc")}).get();t.length&&(o+="a=ssrc-group:"+e+" "+t.join(" ")+"\r\n")});var a=[];$(r).find('source[xmlns="urn:xmpp:jingle:apps:rtp:ssma:0"]').each(function(){var e=$(this).attr("ssrc");a.push(e)}),t.media.forEach(function(e,t){h.a.findLine(e,"a=mid:"+i)&&(n[t]||(n[t]=""),a.forEach(function(r){var i=h.a.findLines(e,"a=ssrc:"+r);i.length&&(n[t]+=i.join("\r\n")+"\r\n")}),n[t]+=o)})}),n}},{key:"_verifyNoSSRCChanged",value:function(e,t){var n=new d.a(this.peerconnection.localDescription.sdp),r=new p.a(t,n),i=r.getNewMedia();if(Object.keys(i).length)return _.error(this+" - some SSRC were added on "+e,i),!1;var o=(r=new p.a(n,t)).getNewMedia();return!Object.keys(o).length||(_.error(this+" - some SSRCs were removed on "+e,o),!1)}},{key:"addTrackAsUnmute",value:function(e){return this._addRemoveTrackAsMuteUnmute(!1,e)}},{key:"removeTrackAsMute",value:function(e){return this._addRemoveTrackAsMuteUnmute(!0,e)}},{key:"_addRemoveTrackAsMuteUnmute",value:function(e,t){var n=this;if(!t)return Promise.reject('invalid "track" argument value');var r=e?"removeTrackMute":"addTrackUnmute",i=(function(e){function t(t){return e.apply(this,arguments)}return t.toString=function(){return e.toString()},t})(function(i){var o=n.peerconnection;if(o){var a=o.localDescription.sdp;(e?o.removeTrackMute.bind(o,t):o.addTrackUnmute.bind(o,t))()?a&&o.remoteDescription.sdp?n._renegotiate().then(function(){n._verifyNoSSRCChanged(r,new d.a(a)),i()},i):i():i(r+" failed!")}else i("Error: tried "+r+" track with no active peerconnection")});return new Promise(function(e,t){n.modificationQueue.push(i,function(n){n?t(n):e()})})}},{key:"setMediaTransferActive",value:function(e,t){var n=this;if(!this.peerconnection)return Promise.reject('Can not modify transfer active state, before "initialize" is called');var r=e?"audio active":"audio inactive",i=t?"video active":"video inactive";_.info("Queued make "+i+", "+r+" task...");var o=function(r){var i=n.state===l.a,o=n.peerconnection.setAudioTransferActive(e);n._localVideoActive!==t&&(n._localVideoActive=t,n.isP2P&&i&&n.sendContentModify(t));var a=n.peerconnection.setVideoTransferActive(n._localVideoActive&&n._remoteVideoActive);i&&(o||a)?n._renegotiate().then(r,r):r()};return new Promise(function(e,t){n.modificationQueue.push(o,function(n){n?t(n):e()})})}},{key:"modifyContents",value:function(e){var n=this,r=t.parseVideoSenders(e);null!==r?(_.debug(this+' queued "content-modify" task(video senders="'+r+'")'),this.modificationQueue.push(function(e){n._assertNotEnded("content-modify")&&n._modifyRemoteVideoActive(r)?n._renegotiate().then(e,e):e()},function(e){e&&_.error('"content-modify" failed',e)})):_.error(this+' - failed to parse video "senders" attribute in"content-modify" action')}},{key:"_modifyRemoteVideoActive",value:function(e){var t="both"===e||"initiator"===e&&this.isInitiator||"responder"===e&&!this.isInitiator;return t!==this._remoteVideoActive&&(_.debug(this+" new remote video active: "+t),this._remoteVideoActive=t),this.peerconnection.setVideoTransferActive(this._localVideoActive&&this._remoteVideoActive)}},{key:"notifyMySSRCUpdate",value:function(e,t){if(this.state===l.a){var n=new p.a(t,e),r=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"source-remove",initiator:this.initiator,sid:this.sid});n.toJingle(r)?(_.info("Sending source-remove",r.tree()),this.connection.sendIQ(r,null,this.newJingleErrorHandler(r),1e4)):_.log("removal not necessary"),n=new p.a(e,t);var i=Object(s.$iq)({to:this.remoteJid,type:"set"}).c("jingle",{xmlns:"urn:xmpp:jingle:1",action:"source-add",initiator:this.initiator,sid:this.sid});n.toJingle(i)?(_.info("Sending source-add",i.tree()),this.connection.sendIQ(i,null,this.newJingleErrorHandler(i),1e4)):_.log("addition not necessary")}else _.warn("Skipping SSRC update in '"+this.state+" ' state.")}},{key:"newJingleErrorHandler",value:function(e,t){var n=this;return function(e){var r={},i=$(e).find("error");if(i.length){r.code=i.attr("code");var o=$(e).find("error :first");o.length&&(r.reason=o[0].tagName);var a=i.find(">text");a.length&&(r.msg=a.text())}e||(r.reason="timeout"),r.session=n.toString(),t?t(r):n.state===l.b&&"item-not-found"===r.reason?_.debug("Jingle error: "+JSON.stringify(r)):b.a.callErrorHandler(new Error("Jingle error: "+JSON.stringify(r)))}}},{key:"getIceConnectionState",value:function(){return this.peerconnection.iceConnectionState}},{key:"close",value:function(){this.closed=!0,this.signalingLayer.setChatRoom(null),this.peerconnection&&(this.peerconnection.signalingState&&"closed"!==this.peerconnection.signalingState||this.peerconnection.connectionState&&"closed"!==this.peerconnection.connectionState)&&this.peerconnection.close()}},{key:"toString",value:function(){return"JingleSessionPC[p2p="+this.isP2P+",initiator="+this.isInitiator+",sid="+this.sid+"]"}},{key:"_abtestSuspendVideoEnabled",value:function(){if(this.room.options.abTesting&&this.room.options.abTesting.enableSuspendVideoTest){var e=this._getInitiatorJid();return Object(c.integerHash)(e)%2==0}}}],[{key:"onJingleFatalError",value:function(e,t){this.room&&(this.room.eventEmitter.emit(g.a.CONFERENCE_SETUP_FAILED,e,t),this.room.eventEmitter.emit(g.a.JINGLE_FATAL_ERROR,e,t))}}]),t})();t.a=T}).call(t,"modules/xmpp/JingleSessionPC.js")},function(e,t,n){(function(n,r){var i;!(function(){function o(e){var t=!1;return function(){if(t)throw new Error("Callback was already called.");t=!0,e.apply(a,arguments)}}var a,s,c={};null!=(a=this)&&(s=a.async),c.noConflict=function(){return a.async=s,c};var u=Object.prototype.toString,l=Array.isArray||function(e){return"[object Array]"===u.call(e)},d=function(e,t){if(e.forEach)return e.forEach(t);for(var n=0;n=e.length&&n()}if(n=n||function(){},!e.length)return n();var i=0;d(e,function(e){t(e,o(r))})},c.forEach=c.each,c.eachSeries=function(e,t,n){if(n=n||function(){},!e.length)return n();var r=0;!(function i(){t(e[r],function(t){t?(n(t),n=function(){}):(r+=1)>=e.length?n():i()})})()},c.forEachSeries=c.eachSeries,c.eachLimit=function(e,t,n,r){f(t).apply(null,[e,n,r])},c.forEachLimit=c.eachLimit;var f=function(e){return function(t,n,r){if(r=r||function(){},!t.length||e<=0)return r();var i=0,o=0,a=0;!(function s(){if(i>=t.length)return r();for(;a=t.length?r():s())})})()}},m=function(e){return function(){var t=Array.prototype.slice.call(arguments);return e.apply(null,[c.each].concat(t))}},v=function(e){return function(){var t=Array.prototype.slice.call(arguments);return e.apply(null,[c.eachSeries].concat(t))}},g=function(e,t,n,r){if(t=p(t,function(e,t){return{index:t,value:e}}),r){var i=[];e(t,function(e,t){n(e.value,function(n,r){i[e.index]=r,t(n)})},function(e){r(e,i)})}else e(t,function(e,t){n(e.value,function(e){t(e)})})};c.map=m(g),c.mapSeries=v(g),c.mapLimit=function(e,t,n,r){return y(t)(e,n,r)};var y=function(e){return t=e,n=g,function(){var e=Array.prototype.slice.call(arguments);return n.apply(null,[f(t)].concat(e))};var t,n};c.reduce=function(e,t,n,r){c.eachSeries(e,function(e,r){n(t,e,function(e,n){t=n,r(e)})},function(e){r(e,t)})},c.inject=c.reduce,c.foldl=c.reduce,c.reduceRight=function(e,t,n,r){var i=p(e,function(e){return e}).reverse();c.reduce(i,t,n,r)},c.foldr=c.reduceRight;var b=function(e,t,n,r){var i=[];e(t=p(t,function(e,t){return{index:t,value:e}}),function(e,t){n(e.value,function(n){n&&i.push(e),t()})},function(e){r(p(i.sort(function(e,t){return e.index-t.index}),function(e){return e.value}))})};c.filter=m(b),c.filterSeries=v(b),c.select=c.filter,c.selectSeries=c.filterSeries;var S=function(e,t,n,r){var i=[];e(t=p(t,function(e,t){return{index:t,value:e}}),function(e,t){n(e.value,function(n){n||i.push(e),t()})},function(e){r(p(i.sort(function(e,t){return e.index-t.index}),function(e){return e.value}))})};c.reject=m(S),c.rejectSeries=v(S);var _=function(e,t,n,r){e(t,function(e,t){n(e,function(n){n?(r(e),r=function(){}):t()})},function(e){r()})};c.detect=m(_),c.detectSeries=v(_),c.some=function(e,t,n){c.each(e,function(e,r){t(e,function(e){e&&(n(!0),n=function(){}),r()})},function(e){n(!1)})},c.any=c.some,c.every=function(e,t,n){c.each(e,function(e,r){t(e,function(e){e||(n(!1),n=function(){}),r()})},function(e){n(!0)})},c.all=c.every,c.sortBy=function(e,t,n){c.map(e,function(e,n){t(e,function(t,r){t?n(t):n(null,{value:e,criteria:r})})},function(e,t){if(e)return n(e);n(null,p(t.sort(function(e,t){var n=e.criteria,r=t.criteria;return nr?1:0}),function(e){return e.value}))})},c.auto=function(e,t){t=t||function(){};var n=h(e),r=n.length;if(!r)return t();var i={},o=[],a=function(e){o.unshift(e)},s=function(){r--,d(o.slice(0),function(e){e()})};a(function(){if(!r){var e=t;t=function(){},e(null,i)}}),d(n,function(n){var r=l(e[n])?e[n]:[e[n]],u=function(e){var r=Array.prototype.slice.call(arguments,1);if(r.length<=1&&(r=r[0]),e){var o={};d(h(i),function(e){o[e]=i[e]}),o[n]=r,t(e,o),t=function(){}}else i[n]=r,c.setImmediate(s)},p=r.slice(0,Math.abs(r.length-1))||[],f=function(){return t=function(e,t){return e&&i.hasOwnProperty(t)},r=!0,((e=p).reduce?e.reduce(t,r):(d(e,function(e,n,i){r=t(r,e)}),r))&&!i.hasOwnProperty(n);var e,t,r};f()?r[r.length-1](u,i):a(function e(){f()&&((function(e){for(var t=0;t>>1);n(t,e[o])>=0?r=o:i=o-1}return r})(e.tasks,o,n)+1,0,o),e.saturated&&e.tasks.length===e.concurrency&&e.saturated(),c.setImmediate(e.process)})})(r,e,t,i)},delete r.unshift,r},c.cargo=function(e,t){var n=!1,r=[],i={tasks:r,payload:t,saturated:null,empty:null,drain:null,drained:!0,push:function(e,n){l(e)||(e=[e]),d(e,function(e){r.push({data:e,callback:"function"==typeof n?n:null}),i.drained=!1,i.saturated&&r.length===t&&i.saturated()}),c.setImmediate(i.process)},process:function o(){if(!n){if(0===r.length)return i.drain&&!i.drained&&i.drain(),void(i.drained=!0);var a="number"==typeof t?r.splice(0,t):r.splice(0,r.length),s=p(a,function(e){return e.data});i.empty&&i.empty(),n=!0,e(s,function(){n=!1;var e=arguments;d(a,function(t){t.callback&&t.callback.apply(null,e)}),o()})}},length:function(){return r.length},running:function(){return n}};return i};var E=function(e){return function(t){var n=Array.prototype.slice.call(arguments,1);t.apply(null,n.concat([function(t){var n=Array.prototype.slice.call(arguments,1);"undefined"!=typeof console&&(t?console.error&&console.error(t):console[e]&&d(n,function(t){console[e](t)}))}]))}};c.log=E("log"),c.dir=E("dir"),c.memoize=function(e,t){var n={},r={};t=t||function(e){return e};var i=function(){var i=Array.prototype.slice.call(arguments),o=i.pop(),a=t.apply(null,i);a in n?c.nextTick(function(){o.apply(null,n[a])}):a in r?r[a].push(o):(r[a]=[o],e.apply(null,i.concat([function(){n[a]=arguments;var e=r[a];delete r[a];for(var t=0,i=e.length;t2?n.apply(this,Array.prototype.slice.call(arguments,2)):n};c.applyEach=m(w),c.applyEachSeries=v(w),c.forever=function(e,t){!(function n(r){if(r){if(t)return t(r);throw r}e(n)})()},void 0!==e&&e.exports?e.exports=c:void 0===(i=function(){return c}.apply(t,[]))||(e.exports=i)})()}).call(t,n(34),n(40).setImmediate)},function(e,t){e.exports={integerHash:function(e){if(!e)return 0;var t=0,n=void 0;for(n=0;n1&&void 0!==arguments[1]?arguments[1]:1e4;if(this.intervalId){var r="Ping task scheduled already";return a.a.callErrorHandler(new Error(r)),void u.error(r)}this.intervalId=window.setInterval(function(){t.ping(e,function(){t.failedPings=0},function(e){t.failedPings+=1;var n="Ping "+(e?"error":"timeout");t.failedPings>=3?(a.a.callErrorHandler(new Error(n)),u.error(n,e)):u.warn(n,e)},15e3)},n),u.info("XMPP pings will be sent every "+n+" ms")}},{key:"stopInterval",value:function(){this.intervalId&&(window.clearInterval(this.intervalId),this.intervalId=null,this.failedPings=0,u.info("Ping interval cleared"))}},{key:"_addPingExecutionTimestamp",value:function(){this.pingExecIntervals.push((new Date).getTime()),this.pingExecIntervals.length>l&&this.pingExecIntervals.shift()}},{key:"getPingSuspendTime",value:function(){var e=this.pingExecIntervals.slice();e.push((new Date).getTime());var t=0,n=e[0];return e.forEach(function(e){var r=e-n;r>t&&(t=r),n=e}),t-=1e4,Math.max(t,0)}}]),t})();t.a=function(e){i.Strophe.addConnectionPlugin("ping",new d(e))}}).call(t,"modules/xmpp/strophe.ping.js")},function(e,t,n){"use strict";(function(e){var r=n(0),i=(n.n(r),n(2)),o=(n.n(i),n(22)),a=(function(){function e(e,t){for(var n=0;nt[r]?1:e[r]0&&void 0!==arguments[0]?arguments[0]:{},n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"http://jitsi.org/jitsimeet";!(function(e,n){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this);var r=(function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t})(this,(t.__proto__||Object.getPrototypeOf(t)).call(this));if(r.node=n,r.disco=e.disco,!r.disco)throw new Error("Missing strophe-plugins (disco plugin is required)!");r.versionToCapabilities=Object.create(null),r.jidToVersion=Object.create(null),r.version="",r.rooms=new Set;var o=e.emuc;return o.addListener(a.a.EMUC_ROOM_ADDED,function(e){return r._addChatRoom(e)}),o.addListener(a.a.EMUC_ROOM_REMOVED,function(e){return r._removeChatRoom(e)}),Object.keys(o.rooms).forEach(function(e){r._addChatRoom(o.rooms[e])}),i.Strophe.addNamespace("CAPS","http://jabber.org/protocol/caps"),r.disco.addFeature(i.Strophe.NS.CAPS),e.addHandler(r._handleCaps.bind(r),i.Strophe.NS.CAPS),r._onMucMemberLeft=r._removeJidToVersionEntry.bind(r),r}return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)})(t,s.a),c(t,[{key:"addFeature",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.disco.addFeature(e),this._generateVersion(),t&&this.submit()}},{key:"removeFeature",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];this.disco.removeFeature(e),this._generateVersion(),t&&this.submit()}},{key:"submit",value:function(){this.rooms.forEach(function(e){return e.sendPresence()})}},{key:"getFeatures",value:function(e){var t=this,n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:5e3,r=e in this.jidToVersion?this.jidToVersion[e]:null;if(!(r&&r.version in this.versionToCapabilities)){var i=r?r.node+"#"+r.version:null;return new Promise(function(o,a){return t.disco.info(e,i,function(e){var n=new Set;$(e).find(">query>feature").each(function(e,t){return n.add(t.getAttribute("var"))}),r&&(t.versionToCapabilities[r.version]=n),o(n)},a,n)})}return Promise.resolve(this.versionToCapabilities[r.version])}},{key:"_addChatRoom",value:function(e){this.rooms.add(e),e.addListener(a.a.MUC_MEMBER_LEFT,this._onMucMemberLeft),this._fixChatRoomPresenceMap(e)}},{key:"_removeChatRoom",value:function(e){this.rooms.delete(e),e.removeListener(a.a.MUC_MEMBER_LEFT,this._onMucMemberLeft)}},{key:"_fixChatRoomPresenceMap",value:function(e){e.addToPresence("c",{attributes:{xmlns:i.Strophe.NS.CAPS,hash:"sha-1",node:this.node,ver:this.version}})}},{key:"_notifyVersionChanged",value:function(){var e=this;this.rooms.forEach(function(t){return e._fixChatRoomPresenceMap(t)}),this.submit()}},{key:"_generateVersion",value:function(){var e=this.disco._identities.sort(r).reduce(function(e,t){return u.reduce(function(e,n,r){return e+(0===r?"":"/")+t[n]},"")+"<"},""),t=this.disco._features.sort().reduce(function(e,t){return e+t+"<"},"");this.version=Object(i.b64_sha1)(e+t),this._notifyVersionChanged()}},{key:"_handleCaps",value:function(e){var t=e.getAttribute("from"),n=e.querySelector("c"),r=n.getAttribute("ver"),i=n.getAttribute("node"),o=this.jidToVersion[t];return this.jidToVersion[t]={version:r,node:i},o&&o.version!==r&&this.eventEmitter.emit(a.a.PARTCIPANT_FEATURES_CHANGED,t),!0}},{key:"_removeJidToVersionEntry",value:function(e){e in this.jidToVersion&&delete this.jidToVersion[e]}}]),t})();t.a=d},function(e,t,n){"use strict";(function(e){function r(e){this.conference=e,e.on(p.TRACK_MUTE_CHANGED,function(t){if(t.isLocal()&&e.statistics){var n=t.isP2P?e.p2pJingleSession:e.jvbJingleSession,r=n&&n.peerconnection||null;e.statistics.sendMuteEvent(r,t.isMuted(),t.getType())}})}t.a=r;var i=n(2),o=(n.n(i),n(6)),a=n(49),s=n.n(a),c=n(119),u=n.n(c),l=n(0),d=(n.n(l),n(32)),p=n(7),h=n(4),f=n(9),m=n.n(f),v=n(13),g=n.n(v),y=n(5),b=n(8),S=n.n(b),_=Object(l.getLogger)(e);r.prototype.setupChatRoomListeners=function(){var e=this,t=this.conference,n=t.room;this.chatRoomForwarder=new u.a(n,this.conference.eventEmitter),n.addListener(S.a.ICE_RESTARTING,function(e){e.isP2P||t.rtc.closeBridgeChannel()}),n.addListener(S.a.AUDIO_MUTED_BY_FOCUS,function(){y.a.sendAnalytics(Object(o.C)()),t.rtc.setAudioMute(!0).then(function(){t.isMutedByFocus=!0},function(){return _.warn("Error while audio muting due to focus request")})}),this.chatRoomForwarder.forward(S.a.SUBJECT_CHANGED,p.SUBJECT_CHANGED),this.chatRoomForwarder.forward(S.a.MUC_JOINED,p.CONFERENCE_JOINED),n.addListener(S.a.MUC_JOINED,function(){e.conference.isJvbConnectionInterrupted=!1,Object.keys(n.connectionTimes).forEach(function(e){var t=Object(o.w)("conference_"+e,{value:n.connectionTimes[e]});y.a.sendAnalytics(t)}),Object.keys(n.xmpp.connectionTimes).forEach(function(e){var t=Object(o.w)("xmpp_"+e,{value:n.xmpp.connectionTimes[e]});y.a.sendAnalytics(t)})}),this.chatRoomForwarder.forward(S.a.ROOM_JOIN_ERROR,p.CONFERENCE_FAILED,d.CONNECTION_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_CONNECT_ERROR,p.CONFERENCE_FAILED,d.CONNECTION_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_CONNECT_NOT_ALLOWED_ERROR,p.CONFERENCE_FAILED,d.NOT_ALLOWED_ERROR),this.chatRoomForwarder.forward(S.a.ROOM_MAX_USERS_ERROR,p.CONFERENCE_FAILED,d.CONFERENCE_MAX_USERS),this.chatRoomForwarder.forward(S.a.PASSWORD_REQUIRED,p.CONFERENCE_FAILED,d.PASSWORD_REQUIRED),this.chatRoomForwarder.forward(S.a.AUTHENTICATION_REQUIRED,p.CONFERENCE_FAILED,d.AUTHENTICATION_REQUIRED),this.chatRoomForwarder.forward(S.a.BRIDGE_DOWN,p.CONFERENCE_FAILED,d.VIDEOBRIDGE_NOT_AVAILABLE),n.addListener(S.a.BRIDGE_DOWN,function(){return y.a.sendAnalytics(Object(o.u)())}),this.chatRoomForwarder.forward(S.a.RESERVATION_ERROR,p.CONFERENCE_FAILED,d.RESERVATION_ERROR),this.chatRoomForwarder.forward(S.a.GRACEFUL_SHUTDOWN,p.CONFERENCE_FAILED,d.GRACEFUL_SHUTDOWN),n.addListener(S.a.JINGLE_FATAL_ERROR,function(e,n){e.isP2P||t.eventEmitter.emit(p.CONFERENCE_FAILED,d.JINGLE_FATAL_ERROR,n)}),n.addListener(S.a.CONNECTION_ICE_FAILED,function(e){t._onIceConnectionFailed(e)}),this.chatRoomForwarder.forward(S.a.MUC_DESTROYED,p.CONFERENCE_FAILED,d.CONFERENCE_DESTROYED),this.chatRoomForwarder.forward(S.a.CHAT_ERROR_RECEIVED,p.CONFERENCE_ERROR,d.CHAT_ERROR),this.chatRoomForwarder.forward(S.a.FOCUS_DISCONNECTED,p.CONFERENCE_FAILED,d.FOCUS_DISCONNECTED),n.addListener(S.a.FOCUS_LEFT,function(){y.a.sendAnalytics(Object(o.x)()),t.eventEmitter.emit(p.CONFERENCE_FAILED,d.FOCUS_LEFT)}),n.addListener(S.a.SESSION_ACCEPT_TIMEOUT,function(e){y.a.sendAnalyticsAndLog(Object(o.z)(o.b,{p2p:e.isP2P}))}),this.chatRoomForwarder.forward(S.a.RECORDER_STATE_CHANGED,p.RECORDER_STATE_CHANGED),this.chatRoomForwarder.forward(S.a.TRANSCRIPTION_STATUS_CHANGED,p.TRANSCRIPTION_STATUS_CHANGED),this.chatRoomForwarder.forward(S.a.VIDEO_SIP_GW_AVAILABILITY_CHANGED,p.VIDEO_SIP_GW_AVAILABILITY_CHANGED),this.chatRoomForwarder.forward(S.a.VIDEO_SIP_GW_SESSION_STATE_CHANGED,p.VIDEO_SIP_GW_SESSION_STATE_CHANGED),this.chatRoomForwarder.forward(S.a.PHONE_NUMBER_CHANGED,p.PHONE_NUMBER_CHANGED),n.addListener(S.a.CONFERENCE_SETUP_FAILED,function(e,n){e.isP2P||t.eventEmitter.emit(p.CONFERENCE_FAILED,d.SETUP_FAILED,n)}),n.setParticipantPropertyListener(function(e,n){var r=t.getParticipantById(n);r&&r.setProperty(e.tagName.substring("jitsi_participant_".length),e.value)}),this.chatRoomForwarder.forward(S.a.KICKED,p.KICKED),n.addListener(S.a.KICKED,function(){t.room=null,t.leave()}),n.addListener(S.a.SUSPEND_DETECTED,t.onSuspendDetected.bind(t)),this.chatRoomForwarder.forward(S.a.MUC_LOCK_CHANGED,p.LOCK_STATE_CHANGED),n.addListener(S.a.MUC_MEMBER_JOINED,t.onMemberJoined.bind(t)),n.addListener(S.a.MUC_MEMBER_LEFT,t.onMemberLeft.bind(t)),this.chatRoomForwarder.forward(S.a.MUC_LEFT,p.CONFERENCE_LEFT),n.addListener(S.a.DISPLAY_NAME_CHANGED,t.onDisplayNameChanged.bind(t)),n.addListener(S.a.LOCAL_ROLE_CHANGED,function(e){t.onLocalRoleChanged(e),t.statistics&&t.isModerator()&&t.on(p.RECORDER_STATE_CHANGED,function(e,t){var n={id:"recorder_status",status:e};t&&(n.error=t),y.a.sendLog(JSON.stringify(n))})}),n.addListener(S.a.MUC_ROLE_CHANGED,t.onUserRoleChanged.bind(t)),n.addListener(s.a.IDENTITY_UPDATED,function(e,n){t.authEnabled=e,t.authIdentity=n,t.eventEmitter.emit(p.AUTH_STATUS_CHANGED,e,n)}),n.addListener(S.a.MESSAGE_RECEIVED,function(e,n,r,o,a){var s=i.Strophe.getResourceFromJid(e);t.eventEmitter.emit(p.MESSAGE_RECEIVED,s,r,a)}),n.addListener(S.a.PRIVATE_MESSAGE_RECEIVED,function(e,n,r,o,a){var s=i.Strophe.getResourceFromJid(e);t.eventEmitter.emit(p.PRIVATE_MESSAGE_RECEIVED,s,r,a)}),n.addListener(S.a.PRESENCE_STATUS,function(e,n){var r=i.Strophe.getResourceFromJid(e),o=t.getParticipantById(r);o&&o._status!==n&&(o._status=n,t.eventEmitter.emit(p.USER_STATUS_CHANGED,r,n))}),n.addListener(S.a.JSON_MESSAGE_RECEIVED,function(e,n){var r=i.Strophe.getResourceFromJid(e),o=t.getParticipantById(r);o?t.eventEmitter.emit(p.ENDPOINT_MESSAGE_RECEIVED,o,n):_.warn("Ignored XMPPEvents.JSON_MESSAGE_RECEIVED for not existing participant: "+e,n)}),n.addPresenceListener("startmuted",function(e,n){var r=!1;if(t.myUserId()===n&&t.isModerator())r=!0;else{var i=t.getParticipantById(n);i&&i.isModerator()&&(r=!0)}if(r){var o="true"===e.attributes.audio,a="true"===e.attributes.video,s=!1;o!==t.startMutedPolicy.audio&&(t.startMutedPolicy.audio=o,s=!0),a!==t.startMutedPolicy.video&&(t.startMutedPolicy.video=a,s=!0),s&&t.eventEmitter.emit(p.START_MUTED_POLICY_CHANGED,t.startMutedPolicy)}}),n.addPresenceListener("devices",function(e,n){var r=!1,i=!1;e.children.forEach(function(e){"audio"===e.tagName&&(r="true"===e.value),"video"===e.tagName&&(i="true"===e.value)});var o=void 0;if(t.myUserId()===n)o=t.availableDevices;else{var a=t.getParticipantById(n);if(!a)return;o=a._availableDevices}var s=!1;o.audio!==r&&(s=!0,o.audio=r),o.video!==i&&(s=!0,o.video=i),s&&t.eventEmitter.emit(p.AVAILABLE_DEVICES_CHANGED,n,o)}),t.statistics&&(n.addListener(S.a.CONNECTION_ICE_FAILED,function(e){t.statistics.sendIceConnectionFailedEvent(e.peerconnection)}),n.addListener(S.a.ADD_ICE_CANDIDATE_FAILED,function(e,n){t.statistics.sendAddIceCandidateFailed(e,n)}))},r.prototype.setupRTCListeners=function(){var e=this.conference,t=e.rtc;t.addListener(m.a.REMOTE_TRACK_ADDED,e.onRemoteTrackAdded.bind(e)),t.addListener(m.a.REMOTE_TRACK_REMOVED,e.onRemoteTrackRemoved.bind(e)),t.addListener(m.a.DOMINANT_SPEAKER_CHANGED,function(t){e.lastDominantSpeaker!==t&&e.room&&(e.lastDominantSpeaker=t,e.eventEmitter.emit(p.DOMINANT_SPEAKER_CHANGED,t)),e.statistics&&e.myUserId()===t&&e.statistics.sendDominantSpeakerEvent()}),t.addListener(m.a.DATA_CHANNEL_OPEN,function(){var t=window.performance.now(),n="data.channel.opened";_.log("(TIME) "+n,t),e.room.connectionTimes[n]=t,y.a.sendAnalytics(Object(o.w)(n,{value:t})),e.eventEmitter.emit(p.DATA_CHANNEL_OPENED)}),t.addListener(m.a.AVAILABLE_DEVICES_CHANGED,function(t){return e.room.updateDeviceAvailability(t)}),t.addListener(m.a.ENDPOINT_MESSAGE_RECEIVED,function(t,n){var r=e.getParticipantById(t);r?e.eventEmitter.emit(p.ENDPOINT_MESSAGE_RECEIVED,r,n):_.warn("Ignored ENDPOINT_MESSAGE_RECEIVED for not existing participant: "+t,n)}),t.addListener(m.a.LOCAL_UFRAG_CHANGED,function(e,t){e.isP2P||y.a.sendLog(JSON.stringify({id:"local_ufrag",value:t}))}),t.addListener(m.a.REMOTE_UFRAG_CHANGED,function(e,t){e.isP2P||y.a.sendLog(JSON.stringify({id:"remote_ufrag",value:t}))}),t.addListener(m.a.CREATE_ANSWER_FAILED,function(t,n){e.statistics.sendCreateAnswerFailed(t,n)}),t.addListener(m.a.CREATE_OFFER_FAILED,function(t,n){e.statistics.sendCreateOfferFailed(t,n)}),t.addListener(m.a.SET_LOCAL_DESCRIPTION_FAILED,function(t,n){e.statistics.sendSetLocalDescFailed(t,n)}),t.addListener(m.a.SET_REMOTE_DESCRIPTION_FAILED,function(t,n){e.statistics.sendSetRemoteDescFailed(t,n)}),t.addListener(m.a.LOCAL_TRACK_SSRC_UPDATED,function(t,n){t.isVideoTrack()&&t.videoType===g.a.DESKTOP&&e.statistics.sendScreenSharingEvent(!0,n)})},r.prototype.setupXMPPListeners=function(){var e=this.conference;e.xmpp.caps.addListener(S.a.PARTCIPANT_FEATURES_CHANGED,function(t){var n=e.getParticipantById(i.Strophe.getResourceFromJid(t));n&&e.eventEmitter.emit(p.PARTCIPANT_FEATURES_CHANGED,n)}),e.xmpp.addListener(S.a.CALL_INCOMING,e.onIncomingCall.bind(e)),e.xmpp.addListener(S.a.CALL_ACCEPTED,e.onCallAccepted.bind(e)),e.xmpp.addListener(S.a.TRANSPORT_INFO,e.onTransportInfo.bind(e)),e.xmpp.addListener(S.a.CALL_ENDED,e.onCallEnded.bind(e)),e.xmpp.addListener(S.a.START_MUTED_FROM_FOCUS,function(t,n){e.options.config.ignoreStartMuted||(e.startAudioMuted=t,e.startVideoMuted=n,e.getLocalTracks().forEach(function(t){switch(t.getType()){case h.a:e.startAudioMuted&&t.mute();break;case h.b:e.startVideoMuted&&t.mute()}}),e.eventEmitter.emit(p.STARTED_MUTED))})},r.prototype.setupStatisticsListeners=function(){var e=this.conference;e.statistics&&(e.statistics.addAudioLevelListener(function(t,n,r,i){e.rtc.setAudioLevel(t,n,r,i)}),e.statistics.addBeforeDisposedListener(function(){e.eventEmitter.emit(p.BEFORE_STATISTICS_DISPOSED)}),e.statistics.addByteSentStatsListener(function(t,n){e.getLocalTracks(h.a).forEach(function(e){var r=t.getLocalSSRC(e);r&&n.hasOwnProperty(r)&&e._onByteSentStatsReceived(t,n[r])})}))}}).call(t,"JitsiConferenceEventManager.js")},function(e,t){function n(e,t){if(!e||!t||"function"!=typeof e.addListener||"function"!=typeof t.emit)throw new Error("Invalid arguments passed to EventEmitterForwarder");this.src=e,this.dest=t}n.prototype.forward=function(){for(var e=arguments.length,t=Array(e),n=0;n0&&void 0!==arguments[0]?arguments[0]:5e3;return this._conference.xmpp.caps.getFeatures(this._jid,e)}}]),e})();t.a=c},function(e,t,n){"use strict";(function(e){var r=n(0),i=(n.n(r),n(7)),o=(function(){function e(e,t){for(var n=0;n "+t),this._conference.eventEmitter.emit(i.JVB121_STATUS,e,t))}}]),e})();t.a=s}).call(t,"modules/event/Jvb121EventGenerator.js")},function(e,t,n){"use strict";var r=n(7),i=(function(){function e(e,t){for(var n=0;n0&&(this._hasSentData=!0);var r=e.getConnectionState();this._testDataSent&&"connected"===r&&(setTimeout(function(){n._hasSentData||(_.warn(n+" 'bytes sent' <= 0: "+n._bytesSent),n._fireNoDataFromSourceEvent())},3e3),this._testDataSent=!1)}},{key:"getCameraFacingMode",value:function(){if(this.isVideoTrack()&&this.videoType===v.a.CAMERA){var e=void 0;try{e=this.track.getSettings()}catch(e){}return e&&"facingMode"in e?e.facingMode:void 0!==this._facingMode?this._facingMode:d.a.USER}}},{key:"_stopStream",value:function(){this._stopStreamInProgress=!0;try{u.a.stopMediaStream(this.stream)}finally{this._stopStreamInProgress=!1}}},{key:"_switchCamera",value:function(){this.isVideoTrack()&&this.videoType===v.a.CAMERA&&"function"==typeof this.track._switchCamera&&(this.track._switchCamera(),this._facingMode=this._facingMode===d.a.ENVIRONMENT?d.a.USER:d.a.ENVIRONMENT)}},{key:"_checkForCameraIssues",value:function(){return!(!this.isVideoTrack()||this._stopStreamInProgress||this.videoType===v.a.DESKTOP||this._isReceivingData())}},{key:"_isReceivingData",value:function(){return!!this.stream&&this.stream.getTracks().some(function(e){return!("readyState"in e&&"live"!==e.readyState||"muted"in e&&!0===e.muted)})}},{key:"toString",value:function(){return"LocalTrack["+this.rtcId+","+this.getType()+"]"}}]),t})();t.a=T}).call(t,"modules/RTC/JitsiLocalTrack.js")},function(e,t,n){"use strict";(function(e){var r=n(0),i=(n.n(r),n(127)),o=n.n(i),a=n(130),s=n(133),c=n(134),u=n(19),l=n.n(u),d=n(14),p=function(e,t){if(Array.isArray(e))return e;if(("function"==typeof Symbol?Symbol.iterator:"@@iterator")in Object(e))return(function(e,t){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){i=!0,o=e}finally{try{!r&&s.return&&s.return()}finally{if(i)throw o}}return n})(e,t);throw new TypeError("Invalid attempt to destructure non-iterable instance")},h=(function(){function e(e,t){for(var n=0;n1&&(k.encoding=R.numChannels),a.rtp.push(k),R.parameters){var I={config:"",payload:R.preferredPayloadType},P=!0,A=!1,O=void 0;try{for(var D,x=Object.keys(R.parameters)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(P=(D=x.next()).done);P=!0){var N=D.value;I.config&&(I.config+=";"),I.config+=N+"="+R.parameters[N]}}catch(e){A=!0,O=e}finally{try{!P&&x.return&&x.return()}finally{if(A)throw O}}I.config&&a.fmtp.push(I)}var L=!0,M=!1,j=void 0;try{for(var F,U=(R.rtcpFeedback||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(L=(F=U.next()).done);L=!0){var H=F.value;a.rtcpFb.push({payload:R.preferredPayloadType,subtype:H.parameter||void 0,type:H.type})}}catch(e){M=!0,j=e}finally{try{!L&&U.return&&U.return()}finally{if(M)throw j}}}}}catch(e){T=!0,C=e}finally{try{!_&&w.return&&w.return()}finally{if(T)throw C}}0===S.length?(a.payloads="9",a.port=0,a.direction="inactive"):a.payloads=S.join(" "),a.ssrcs=[],a.ssrcGroups=[];var B=!0,G=!1,J=void 0;try{for(var V,W=u.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(B=(V=W.next()).done);B=!0){var q=V.value,K=q.rtpSender,z=q.stream.id,Q=K.track;if("ended"!==Q.readyState&&Q.kind===o){q.ssrc||(q.ssrc=d.a.generateSsrc());var $=l&&"video"===Q.kind;$&&!q.rtxSsrc&&(q.rtxSsrc=q.ssrc+1),a.ssrcs.push({attribute:"cname",id:q.ssrc,value:g}),a.ssrcs.push({attribute:"msid",id:q.ssrc,value:z+" "+Q.id}),a.ssrcs.push({attribute:"mslabel",id:q.ssrc,value:z}),a.ssrcs.push({attribute:"label",id:q.ssrc,value:Q.id}),$&&(a.ssrcs.push({attribute:"cname",id:q.rtxSsrc,value:g}),a.ssrcs.push({attribute:"msid",id:q.rtxSsrc,value:z+" "+Q.id}),a.ssrcs.push({attribute:"mslabel",id:q.rtxSsrc,value:z}),a.ssrcs.push({attribute:"label",id:q.rtxSsrc,value:Q.id}),a.ssrcGroups.push({semantics:"FID",ssrcs:q.ssrc+" "+q.rtxSsrc}))}}}catch(e){G=!0,J=e}finally{try{!B&&W.return&&W.return()}finally{if(G)throw J}}a.ext=[];var X=!0,Y=!1,Z=void 0;try{for(var ee,te=c.headerExtensions["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(X=(ee=te.next()).done);X=!0){var ne=ee.value;ne.kind&&ne.kind!==o||a.ext.push({value:ne.preferredId,uri:ne.uri})}}catch(e){Y=!0,Z=e}finally{try{!X&&te.return&&te.return()}finally{if(Y)throw Z}}a.rtcpMux="rtcp-mux",a.rtcpRsize="rtcp-rsize"}n.media.push(a)}var n={},r=this._iceGatherer.getLocalParameters(),i=this._iceGatherer.getLocalCandidates(),o=this._dtlsTransport.getLocalParameters(),s=this._dtlsTransport.getRemoteParameters(),c=this._localCapabilities,u=this._localTrackInfos;"offer"===e&&this._sdpGlobalFields.version++,n.version=0,n.origin={address:"127.0.0.1",ipVer:4,netType:"IN",sessionId:this._sdpGlobalFields.id,sessionVersion:this._sdpGlobalFields.version,username:"jitsi-ortc-webrtc-shim"},n.name="-",n.timing={start:0,stop:0},n.msidSemantic={semantic:"WMS",token:"*"},n.groups=[{mids:Array.from(this._mids.keys()).join(" "),type:"BUNDLE"}],n.media=[],n.fingerprint={hash:o.fingerprints[0].value,type:o.fingerprints[0].algorithm};var l=!1,h=!0,m=!1,v=void 0;try{for(var y,b=c.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(h=(y=b.next()).done);h=!0){var S=y.value;if("video"===S.kind&&"rtx"===S.name){l=!0;break}}}catch(e){m=!0,v=e}finally{try{!h&&b.return&&b.return()}finally{if(m)throw v}}var _=!0,T=!1,C=void 0;try{for(var E,w=this._mids["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(_=(E=w.next()).done);_=!0){var R=E.value,k=p(R,2),I=k[0],P=k[1];t.call(this,I,P)}}catch(e){T=!0,C=e}finally{try{!_&&w.return&&w.return()}finally{if(T)throw C}}var A=new a.a({type:e,_sdpObject:n});return f.debug("_createLocalDescription():",A),A}},{key:"_createOffer",value:function(e){return this._closed?Promise.reject(new c.a("RTCPeerConnection closed")):this.signalingState!==m.stable?Promise.reject(new c.a('invalid signalingState "'+this.signalingState+'"')):Promise.reject(new Error("createoOffer() not yet supported"))}},{key:"_emitAddStream",value:function(e){if(!this._closed){f.debug('emitting "addstream"');var t=new o.a.Event("addstream");t.stream=e,this.dispatchEvent(t)}}},{key:"_emitBufferedIceCandidates",value:function(){if(!this._closed){var e=!0,t=!1,n=void 0;try{for(var r,i=this._bufferedIceCandidates["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(e=(r=i.next()).done);e=!0){var a=r.value;if(a){a.sdpMIndex=this._mids.keys().next().value,f.debug('emitting buffered "icecandidate", candidate:',a);var s=new o.a.Event("icecandidate");s.candidate=a,this.dispatchEvent(s)}}}catch(e){t=!0,n=e}finally{try{!e&&i.return&&i.return()}finally{if(t)throw n}}this._bufferedIceCandidates=[]}}},{key:"_emitConnectionStateChange",value:function(){if(!this._closed||"closed"===this.connectionState){f.debug('emitting "connectionstatechange", connectionState:',this.connectionState);var e=new o.a.Event("connectionstatechange");this.dispatchEvent(e)}}},{key:"_emitIceCandidate",value:function(e){if(!this._closed){var t=null;if(e){var n=this._mids.keys().next().value,r="candidate:"+e.foundation+" 1 "+e.protocol+" "+e.priority+" "+e.ip+" "+e.port+" typ "+e.type;e.relatedAddress&&(r+=" raddr "+e.relatedAddress),e.relatedPort&&(r+=" rport "+e.relatedPort),"tcp"===e.protocol&&(r+=" tcptype "+e.tcpType),t={candidate:r,component:1,foundation:e.foundation,ip:e.ip,port:e.port,priority:e.priority,protocol:e.protocol,type:e.type,sdpMIndex:n,sdpMLineIndex:0},"tcp"===e.protocol&&(t.tcptype=e.tcpType),e.relatedAddress&&(t.relatedAddress=e.relatedAddress),e.relatedPort&&(t.relatedPort=e.relatedPort)}if(this._localDescription){f.debug('emitting "icecandidate", candidate:',t);var i=new o.a.Event("icecandidate");i.candidate=t,this.dispatchEvent(i)}else f.debug("buffering gathered ICE candidate:",t),this._bufferedIceCandidates.push(t)}}},{key:"_emitIceConnectionStateChange",value:function(){if(!this._closed||"closed"===this.iceConnectionState){f.debug('emitting "iceconnectionstatechange", iceConnectionState:',this.iceConnectionState);var e=new o.a.Event("iceconnectionstatechange");this.dispatchEvent(e)}}},{key:"_emitNegotiationNeeded",value:function(){if(this.signalingState===m.stable){f.debug('emitting "negotiationneeded"');var e=new o.a.Event("negotiationneeded");this.dispatchEvent(e)}}},{key:"_emitRemoveStream",value:function(e){if(!this._closed){f.debug('emitting "removestream"');var t=new o.a.Event("removestream");t.stream=e,this.dispatchEvent(t)}}},{key:"_getParametersForRtpReceiver",value:function(e,t){var n=t.ssrc,r=t.rtxSsrc,i=t.cname,o=this._localCapabilities,a={codecs:[],degradationPreference:"balanced",encodings:[],headerExtensions:[],muxId:"",rtcp:{cname:i,compound:!0,mux:!0,reducedSize:!0}},s=[],c=void 0,u=!0,l=!1,d=void 0;try{for(var p,h=o.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=h.next()).done);u=!0){var f=p.value;if(f.kind===e&&"rtx"!==f.name){c=f.preferredPayloadType,s.push({clockRate:f.clockRate,maxptime:f.maxptime,mimeType:f.mimeType,name:f.name,numChannels:f.numChannels,parameters:f.parameters,payloadType:f.preferredPayloadType,ptime:f.ptime,rtcpFeedback:f.rtcpFeedback});break}}}catch(e){l=!0,d=e}finally{try{!u&&h.return&&h.return()}finally{if(l)throw d}}if(r){var m=!0,v=!1,g=void 0;try{for(var y,b=o.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(m=(y=b.next()).done);m=!0){var S=y.value;if(S.kind===e&&"rtx"===S.name){s.push({clockRate:S.clockRate,mimeType:S.mimeType,name:"rtx",parameters:S.parameters,payloadType:S.preferredPayloadType,rtcpFeedback:S.rtcpFeedback});break}}}catch(e){v=!0,g=e}finally{try{!m&&b.return&&b.return()}finally{if(v)throw g}}}a.codecs=s;var _={active:!0,codecPayloadType:c,ssrc:n};r&&(_.rtx={ssrc:r}),a.encodings.push(_);var T=!0,C=!1,E=void 0;try{for(var w,R=o.headerExtensions["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(T=(w=R.next()).done);T=!0){var k=w.value;k.kind===e&&a.headerExtensions.push({encrypt:k.preferredEncrypt,id:k.preferredId,uri:k.uri})}}catch(e){C=!0,E=e}finally{try{!T&&R.return&&R.return()}finally{if(C)throw E}}return a}},{key:"_getParametersForRtpSender",value:function(e,t){var n=t.ssrc,r=t.rtxSsrc,i=g,o=this._localCapabilities,a={codecs:[],degradationPreference:"balanced",encodings:[],headerExtensions:[],muxId:"",rtcp:{cname:i,compound:!0,mux:!0,reducedSize:!0}},s=[],c=void 0,u=!0,l=!1,d=void 0;try{for(var p,h=o.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=h.next()).done);u=!0){var f=p.value;if(f.kind===e&&"rtx"!==f.name){c=f.preferredPayloadType,s.push({clockRate:f.clockRate,maxptime:f.maxptime,mimeType:f.mimeType,name:f.name,numChannels:f.numChannels,parameters:f.parameters,payloadType:f.preferredPayloadType,ptime:f.ptime,rtcpFeedback:f.rtcpFeedback});break}}}catch(e){l=!0,d=e}finally{try{!u&&h.return&&h.return()}finally{if(l)throw d}}if(r){var m=!0,v=!1,y=void 0;try{for(var b,S=o.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(m=(b=S.next()).done);m=!0){var _=b.value;if(_.kind===e&&"rtx"===_.name){s.push({clockRate:_.clockRate,mimeType:_.mimeType,name:"rtx",parameters:_.parameters,payloadType:_.preferredPayloadType,rtcpFeedback:_.rtcpFeedback});break}}}catch(e){v=!0,y=e}finally{try{!m&&S.return&&S.return()}finally{if(v)throw y}}}a.codecs=s;var T={active:!0,codecPayloadType:c,ssrc:n};r&&(T.rtx={ssrc:r}),a.encodings.push(T);var C=!0,E=!1,w=void 0;try{for(var R,k=o.headerExtensions["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(C=(R=k.next()).done);C=!0){var I=R.value;I.kind===e&&a.headerExtensions.push({encrypt:I.preferredEncrypt,id:I.preferredId,uri:I.uri})}}catch(e){E=!0,w=e}finally{try{!C&&k.return&&k.return()}finally{if(E)throw w}}return a}},{key:"_getStats",value:function(e){if(this._closed)return Promise.reject(new c.a("RTCPeerConnection closed"));var t=this._iceGatherer,n=this._iceTransport,r=[],i=[],o=[],a=!0,s=!1,u=void 0;try{for(var l,d=this._localTrackInfos.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(a=(l=d.next()).done);a=!0){var p=l.value,h=p.rtpSender;p.sending&&r.push(h)}}catch(e){s=!0,u=e}finally{try{!a&&d.return&&d.return()}finally{if(s)throw u}}var f=!0,m=!1,v=void 0;try{for(var g,y=this._remoteTrackInfos.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(f=(g=y.next()).done);f=!0){var b=g.value.rtpReceiver;i.push(b)}}catch(e){m=!0,v=e}finally{try{!f&&y.return&&y.return()}finally{if(m)throw v}}t&&o.push(t.getStats().catch(function(){return null})),n&&(o.push(n.getStats().catch(function(){return null})),"function"==typeof n.msGetStats&&o.push(n.msGetStats().catch(function(){return null})));var S=!0,_=!1,T=void 0;try{for(var C,E=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(S=(C=E.next()).done);S=!0)!(function(e){var t="audio"===e.track.kind;o.push(e.getStats().then(function(e){if(!t){var n=!0,r=!1,i=void 0;try{for(var o,a=Object.keys(e)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=e[o.value];"track"===s.type&&delete s.audioLevel}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}}return e}).catch(function(){return null}))})(h=C.value)}catch(e){_=!0,T=e}finally{try{!S&&E.return&&E.return()}finally{if(_)throw T}}var w=!0,R=!1,k=void 0;try{for(var I,P=i["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(w=(I=P.next()).done);w=!0)!(function(e){var t="audio"===e.track.kind;o.push(e.getStats().then(function(e){if(!t){var n=!0,r=!1,i=void 0;try{for(var o,a=Object.keys(e)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=e[o.value];"track"===s.type&&delete s.audioLevel}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}}return e}).catch(function(){return null}))})(b=I.value)}catch(e){R=!0,k=e}finally{try{!w&&P.return&&P.return()}finally{if(R)throw k}}return Promise.all(o).then(function(e){var t={},n=!0,r=!1,i=void 0;try{for(var o,a=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;if(s){var c=!0,u=!1,l=void 0;try{for(var d,p=Object.keys(s)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(c=(d=p.next()).done);c=!0){var h=d.value;t[h]=s[h]}}catch(e){u=!0,l=e}finally{try{!c&&p.return&&p.return()}finally{if(u)throw l}}}}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}return t})}},{key:"_handleLocalInitialAnswer",value:function(e){f.debug("_handleLocalInitialAnswer(), desc:",e);var t=e.sdpObject;this._localCapabilities=s.a(t),f.debug("local capabilities:",this._localCapabilities)}},{key:"_handleLocalReAnswer",value:function(e){f.debug("_handleLocalReAnswer(), desc:",e);var t=e.sdpObject;this._localCapabilities=s.a(t),f.debug("local capabilities:",this._localCapabilities)}},{key:"_handleRemoteInitialOffer",value:function(e){f.debug("_handleRemoteInitialOffer(), desc:",e);var t=e.sdpObject;this._mids=s.e(t);var n=s.a(t);f.debug("remote capabilities:",n),this._localCapabilities=s.g(n),this._startIceAndDtls(e)}},{key:"_handleRemoteReOffer",value:function(e){f.debug("_handleRemoteReOffer(), desc:",e);var t=e.sdpObject;this._mids=s.e(t);var n=s.a(t);f.debug("remote capabilities:",n),this._localCapabilities=s.g(n)}},{key:"_receiveMedia",value:function(){f.debug("_receiveMedia()");var e=new Set(this._remoteTrackInfos.keys()),t=s.f(this._remoteDescription.sdpObject),n=new Map,r=new Map,i=new Map;f.debug("_receiveMedia() remote track infos:",t);var o=!0,a=!1,c=void 0;try{for(var u,l=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(o=(u=l.next()).done);o=!0){var d=u.value,h=p(d,2),m=h[0],v=h[1];if(!e.has(m)){f.debug("_receiveMedia() new remote track, ssrc:"+m),this._remoteTrackInfos.set(m,v);var g=v.kind,y=v.rtxSsrc,b=v.streamId,S=v.trackId,_=v.cname,T=void 0;this._remoteStreams.has(b)?T=this._remoteStreams.get(b):(f.debug("_receiveMedia() new remote stream, id:"+b),(T=new MediaStream).jitsiRemoteId=b,n.set(b,T),this._remoteStreams.set(b,T));var C=new RTCRtpReceiver(this._dtlsTransport,g),E=this._getParametersForRtpReceiver(g,{ssrc:m,rtxSsrc:y,cname:_});v.track=C.track,C.onerror=function(e){f.error('rtpReceiver "error" event, event:'),f.error(e)},v.stream=T,v.rtpReceiver=C,f.debug("calling rtpReceiver.receive(), parameters:",E);try{C.receive(E);var w=v.track;w.jitsiRemoteId=S,T.addTrack(w),n.has(b)||r.set(w,T)}catch(e){f.error("rtpReceiver.receive() failed:"+e.message),f.error(e)}}}}catch(e){a=!0,c=e}finally{try{!o&&l.return&&l.return()}finally{if(a)throw c}}var R=!0,k=!1,I=void 0;try{for(var P,A=e["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(R=(P=A.next()).done);R=!0){var O=P.value;if(!t.has(O)){f.debug("_receiveMedia() remote track removed, ssrc:"+O);var D=this._remoteTrackInfos.get(O),x=(T=D.stream,D.track);C=D.rtpReceiver;try{C.stop()}catch(e){f.warn("rtpReceiver.stop() failed:"+e)}i.set(x,T),T.removeTrack(x),this._remoteTrackInfos.delete(O)}}}catch(e){k=!0,I=e}finally{try{!R&&A.return&&A.return()}finally{if(k)throw I}}var N=!0,L=!1,M=void 0;try{for(var j,F=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(N=(j=F.next()).done);N=!0){var U=j.value,H=p(U,2);x=H[0],T=H[1],(q=new Event("addtrack")).track=x,T.dispatchEvent(q)}}catch(e){L=!0,M=e}finally{try{!N&&F.return&&F.return()}finally{if(L)throw M}}var B=!0,G=!1,J=void 0;try{for(var V,W=i["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(B=(V=W.next()).done);B=!0){var q,K=V.value,z=p(K,2),Q=z[0],$=z[1];(q=new Event("removetrack")).track=Q,$.dispatchEvent(q)}}catch(e){G=!0,J=e}finally{try{!B&&W.return&&W.return()}finally{if(G)throw J}}var X=!0,Y=!1,Z=void 0;try{for(var ee,te=n.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(X=(ee=te.next()).done);X=!0){var ne=ee.value;0===ne.getTracks().length?(f.warn("ignoring new stream for which no track could be added"),n.delete(ne.jitsiRemoteId),this._remoteStreams.delete(ne.jitsiRemoteId)):this._emitAddStream(ne)}}catch(e){Y=!0,Z=e}finally{try{!X&&te.return&&te.return()}finally{if(Y)throw Z}}var re=!0,ie=!1,oe=void 0;try{for(var ae,se=this._remoteStreams["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(re=(ae=se.next()).done);re=!0){var ce=ae.value,ue=p(ce,2),le=(b=ue[0],ue[1]);le.getTracks().length>0||(this._remoteStreams.delete(b),this._emitRemoveStream(le))}}catch(e){ie=!0,oe=e}finally{try{!re&&se.return&&se.return()}finally{if(ie)throw oe}}}},{key:"_removeStream",value:function(e){if(this._closed)throw new c.a("RTCPeerConnection closed");var t=!0,n=!1,r=void 0;try{for(var i,o=e.getTracks()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;if(this._localTrackInfos.has(a.id)){var s=this._localTrackInfos.get(a.id).rtpSender;try{s.stop()}catch(e){f.warn("rtpSender.stop() failed:"+e)}this._localTrackInfos.delete(a.id)}}}catch(e){n=!0,r=e}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}this._emitNegotiationNeeded()}},{key:"_sendMedia",value:function(){f.debug("_sendMedia()");var e=!0,t=!1,n=void 0;try{for(var r,i=this._localTrackInfos.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(e=(r=i.next()).done);e=!0){var o=r.value;if(!o.sending){var a=o.rtpSender,s=o.ssrc,c=o.rtxSsrc,u=a.track.kind,l=this._getParametersForRtpSender(u,{ssrc:s,rtxSsrc:c});f.debug("calling rtpSender.send(), parameters:",l);try{a.send(l),o.sending=!0}catch(e){f.error("rtpSender.send() failed:"+e.message),f.error(e)}}}}catch(e){t=!0,n=e}finally{try{!e&&i.return&&i.return()}finally{if(t)throw n}}}},{key:"_setDtlsTransport",value:function(e){var t=this,n=new RTCDtlsTransport(e);n.onstatechange=function(){f.debug('dtlsTransport "statechange" event, state:'+n.state),t._emitConnectionStateChange()},n.ondtlsstatechange=function(){f.debug('dtlsTransport "dtlsstatechange" event, state:'+n.state),t._emitConnectionStateChange()},n.onerror=function(e){var n=void 0;e.message?n=e.message:e.error&&(n=e.error.message),f.error('dtlsTransport "error" event, message:'+n),t._emitConnectionStateChange()},this._dtlsTransport=n}},{key:"_setIceGatherer",value:function(e){var t=this,n={gatherPolicy:e.iceTransportPolicy||"all",iceServers:e.iceServers||[]},r=new RTCIceGatherer(n);r.onstatechange=function(){f.debug('iceGatherer "statechange" event, state:'+r.state),t._updateAndEmitIceGatheringStateChange(r.state)},r.onlocalcandidate=function(e){var n=e.candidate,r=e.complete;f.debug('iceGatherer "localcandidate" event, candidate:',n),r||!n||0===Object.keys(n).length?(n=null,t._updateAndEmitIceGatheringStateChange(v.complete),t._emitIceCandidate(null)):t._emitIceCandidate(n)},r.onerror=function(e){var t=e.errorCode,n=e.errorText;f.error('iceGatherer "error" event, errorCode:'+t+", errorText:"+n)};try{r.gather()}catch(e){f.warn("iceGatherer.gather() failed:"+e)}this._iceGatherer=r}},{key:"_setIceTransport",value:function(e){var t=this,n=new RTCIceTransport(e);n.onstatechange=function(){f.debug('iceTransport "statechange" event, state:'+n.state),t._emitIceConnectionStateChange()},n.onicestatechange=function(){f.debug('iceTransport "icestatechange" event, state:'+n.state),"completed"===n.state&&f.debug("nominated candidate pair:",n.getNominatedCandidatePair()),t._emitIceConnectionStateChange()},n.oncandidatepairchange=function(e){f.debug('iceTransport "candidatepairchange" event, pair:'+e.pair)},this._iceTransport=n}},{key:"_setLocalDescription",value:function(e){var t=this;if(this._closed)return Promise.reject(new c.a("RTCPeerConnection closed"));var n=void 0;try{n=new a.a(e)}catch(e){return Promise.reject(new TypeError("invalid RTCSessionDescriptionInit: "+e))}switch(e.type){case"offer":return this.signalingState!==m.stable?Promise.reject(new c.a('invalid signalingState "'+this.signalingState+'"')):Promise.reject(new TypeError('setLocalDescription() with type "offer" not supported'));case"answer":if(this.signalingState!==m.haveRemoteOffer)return Promise.reject(new c.a('invalid signalingState "'+this.signalingState+'"'));var r=Boolean(!this._localDescription);return Promise.resolve().then(function(){return r?t._handleLocalInitialAnswer(n):t._handleLocalReAnswer(n)}).then(function(){f.debug("setLocalDescription() succeed"),t._localDescription=n,t._updateAndEmitSignalingStateChange(m.stable),r&&t._emitBufferedIceCandidates(),t._sendMedia(),t._receiveMedia()}).catch(function(e){throw f.error("setLocalDescription() failed: "+e.message),f.error(e),e});default:return Promise.reject(new TypeError('unsupported description.type "'+e.type+'"'))}}},{key:"_setRemoteDescription",value:function(e){var t=this;if(this._closed)return Promise.reject(new c.a("RTCPeerConnection closed"));var n=void 0;try{n=new a.a(e)}catch(e){return Promise.reject(new TypeError("invalid RTCSessionDescriptionInit: "+e))}switch(e.type){case"offer":if(this.signalingState!==m.stable)return Promise.reject(new c.a('invalid signalingState "'+this.signalingState+'"'));var r=Boolean(!this._remoteDescription);return Promise.resolve().then(function(){return r?t._handleRemoteInitialOffer(n):t._handleRemoteReOffer(n)}).then(function(){f.debug("setRemoteDescription() succeed"),t._remoteDescription=n,t._updateAndEmitSignalingStateChange(m.haveRemoteOffer)}).catch(function(e){throw f.error("setRemoteDescription() failed: "+e),e});case"answer":return this.signalingState!==m.haveLocalOffer?Promise.reject(new c.a('invalid signalingState "'+this.signalingState+'"')):Promise.reject(new TypeError('setRemoteDescription() with type "answer" not supported'));default:return Promise.reject(new TypeError('unsupported description.type "'+e.type+'"'))}}},{key:"_startIceAndDtls",value:function(e){var t=e.sdpObject,n=s.d(t),r=s.c(t),i=s.b(t);switch(e.type){case"offer":this._iceTransport.start(this._iceGatherer,n,"controlled");break;case"answer":this._iceTransport.start(this._iceGatherer,n,"controlling")}var o=!0,a=!1,c=void 0;try{for(var u,l=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(o=(u=l.next()).done);o=!0){var d=u.value;0!==d.port&&9!==d.port&&this._iceTransport.addRemoteCandidate(d)}}catch(e){a=!0,c=e}finally{try{!o&&l.return&&l.return()}finally{if(a)throw c}}switch(this._iceTransport.addRemoteCandidate({}),e.type){case"offer":i.role="server";break;case"answer":i.role="client"}this._dtlsTransport.start(i)}},{key:"_updateAndEmitIceGatheringStateChange",value:function(e){if(!this._closed&&e!==this.iceGatheringState){this._iceGatheringState=e,f.debug('emitting "icegatheringstatechange", iceGatheringState:',this.iceGatheringState);var t=new o.a.Event("icegatheringstatechange");this.dispatchEvent(t)}}},{key:"_updateAndEmitSignalingStateChange",value:function(e){if(e!==this.signalingState){this._signalingState=e,f.debug('emitting "signalingstatechange", signalingState:',this.signalingState);var t=new o.a.Event("signalingstatechange");this.dispatchEvent(t)}}},{key:"connectionState",get:function(){return this._dtlsTransport.state}},{key:"iceConnectionState",get:function(){return this._iceTransport.state}},{key:"iceGatheringState",get:function(){return this._iceGatheringState}},{key:"localDescription",get:function(){return this._localDescription}},{key:"remoteDescription",get:function(){return this._remoteDescription}},{key:"signalingState",get:function(){return this._signalingState}}]),t})();t.a=y}).call(t,"modules/RTC/ortc/RTCPeerConnection.js")},function(e,t,n){e.exports={EventTarget:n(128),Event:n(129)}},function(e,t){function n(){this._listeners={}}Object.defineProperties(n.prototype,{listeners:{get:function(){return this._listeners}}}),n.prototype.addEventListener=function(e,t){var n,r,i;if(e&&t){for(void 0===(n=this._listeners[e])&&(this._listeners[e]=n=[]),r=0;i=n[r];r++)if(i===t)return;n.push(t)}},n.prototype.removeEventListener=function(e,t){var n,r,i;if(e&&t&&void 0!==(n=this._listeners[e])){for(r=0;i=n[r];r++)if(i===t){n.splice(r,1);break}0===n.length&&delete this._listeners[e]}},n.prototype.dispatchEvent=function(e){var t,n,r,i,o,a=!1;if(!e||"string"!=typeof e.type)throw new Error("`event` must have a valid `type` property");e._yaeti&&(e.target=this,e.cancelable=!0);try{e.stopImmediatePropagation=function(){a=!0}}catch(e){}if(t=e.type,n=this._listeners[t]||[],"function"==typeof(r=this["on"+t]))try{r.call(this,e)}catch(e){console.error(e)}for(i=0;(o=n[i])&&!a;i++)try{o.call(this,e)}catch(e){console.error(e)}return!e.defaultPrevented},e.exports=n},function(e,t,n){(function(t){e.exports=t.Event}).call(t,n(20))},function(e,t,n){"use strict";var r=n(15),i=n.n(r),o="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},a=(function(){function e(e,t){for(var n=0;n=r)return e;var i=n[t];switch(t+=1,e){case"%%":return"%";case"%s":return String(i);case"%d":return Number(i);case"%v":return""}})}.apply(null,r)},a=["v","o","s","i","u","e","p","c","b","t","r","z","a"],s=["i","c","b","a"];e.exports=function(e,t){t=t||{},null==e.version&&(e.version=0),null==e.name&&(e.name=" "),e.media.forEach(function(e){null==e.payloads&&(e.payloads="")});var n=t.outerOrder||a,i=t.innerOrder||s,c=[];return n.forEach(function(t){r[t].forEach(function(n){n.name in e&&null!=e[n.name]?c.push(o(t,n,e)):n.push in e&&null!=e[n.push]&&e[n.push].forEach(function(e){c.push(o(t,n,e))})})}),e.media.forEach(function(e){c.push(o("m",r.m[0],e)),i.forEach(function(t){r[t].forEach(function(n){n.name in e&&null!=e[n.name]?c.push(o(t,n,e)):n.push in e&&null!=e[n.push]&&e[n.push].forEach(function(e){c.push(o(t,n,e))})})})}),c.join("\r\n")+"\r\n"}},function(e,t,n){"use strict";function r(e){return e.media.find(function(e){return e.iceUfrag&&0!==e.port})}t.a=function(e){var t=new Map,n=[],r=!0,i=!1,a=void 0;try{for(var s,c=e.media["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(r=(s=c.next()).done);r=!0){var u=s.value,l=u.type;if("audio"===l||"video"===l){var d=!0,p=!1,h=void 0;try{for(var f,m=u.rtp["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(f=m.next()).done);d=!0){var v=f.value,g={clockRate:v.rate,kind:l,mimeType:l+"/"+v.codec,name:v.codec,numChannels:v.encoding||1,parameters:{},preferredPayloadType:v.payload,rtcpFeedback:[]};t.set(g.preferredPayloadType,g)}}catch(e){p=!0,h=e}finally{try{!d&&m.return&&m.return()}finally{if(p)throw h}}var y=!0,b=!1,S=void 0;try{for(var _,T=(u.fmtp||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(y=(_=T.next()).done);y=!0){var C=_.value,E=o.a.parseFmtpConfig(C.config);(g=t.get(C.payload))&&(g.parameters=E)}}catch(e){b=!0,S=e}finally{try{!y&&T.return&&T.return()}finally{if(b)throw S}}var w=!0,R=!1,k=void 0;try{for(var I,P=(u.rtcpFb||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(w=(I=P.next()).done);w=!0){var A=I.value;(g=t.get(A.payload))&&g.rtcpFeedback.push({parameter:A.subtype||"",type:A.type})}}catch(e){R=!0,k=e}finally{try{!w&&P.return&&P.return()}finally{if(R)throw k}}var O=!0,D=!1,x=void 0;try{for(var N,L=(u.ext||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(O=(N=L.next()).done);O=!0){!(function(e){var t=e.value,r=e.uri,i={kind:l,uri:r,preferredId:t};n.find(function(e){return i.kind===e.kind&&i.uri===e.uri})||n.push(i)})(N.value)}}catch(e){D=!0,x=e}finally{try{!O&&L.return&&L.return()}finally{if(D)throw x}}}}}catch(e){i=!0,a=e}finally{try{!r&&c.return&&c.return()}finally{if(i)throw a}}return{codecs:Array.from(t.values()),fecMechanisms:[],headerExtensions:n}},t.b=function(e){var t=r(e),n=t.fingerprint||e.fingerprint,i=void 0;switch(t.setup){case"active":i="client";break;case"passive":i="server";break;case"actpass":i="auto"}return{role:i,fingerprints:[{algorithm:n.type,value:n.hash}]}},t.c=function(e){var t=r(e),n=[],i=!0,o=!1,a=void 0;try{for(var s,c=t.candidates["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value;if(1===u.component){var l={foundation:u.foundation,ip:u.ip,port:u.port,priority:u.priority,protocol:u.transport.toLowerCase(),type:u.type};n.push(l)}}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}return n},t.d=function(e){var t=r(e),n=t.iceUfrag,i=t.icePwd;return{icelite:"ice-lite"===e.icelite,password:i,usernameFragment:n}},t.e=function(e){var t=new Map,n=!0,r=!1,i=void 0;try{for(var o,a=e.media["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;t.set(s.mid,s.type)}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}return t},t.f=function(e){var t=new Map,n=new Map,r=new Set,i=!0,o=!1,a=void 0;try{for(var s,c=e.media["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value,l=u.type;if("audio"===l||"video"===l){var d=!0,p=!1,h=void 0;try{for(var f,m=(u.ssrcGroups||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(f=m.next()).done);d=!0){var v=f.value;if("FID"===v.semantics){var g=v.ssrcs.split(" ").map(function(e){return Number(e)}),y=g[0],b=g[1];n.set(y,b),r.add(b)}}}catch(e){p=!0,h=e}finally{try{!d&&m.return&&m.return()}finally{if(p)throw h}}var S=!0,_=!1,T=void 0;try{for(var C,E=(u.ssrcs||[])["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(S=(C=E.next()).done);S=!0){var w=C.value;y=w.id;if(!r.has(y)){var R=t.get(y);switch(R||(R={kind:l,rtxSsrc:n.get(y),ssrc:y},t.set(y,R)),w.attribute){case"cname":R.cname=w.value;break;case"msid":var k=w.value.split(" "),I=k[0],P=k[1];R.streamId=I,R.trackId=P;break;case"mslabel":var A=w.value;R.streamId=A;break;case"label":var O=w.value;R.trackId=O}}}}catch(e){_=!0,T=e}finally{try{!S&&E.return&&E.return()}finally{if(_)throw T}}}}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}return t},t.g=function(e){var t=RTCRtpReceiver.getCapabilities(),n={codecs:[],fecMechanisms:[],headerExtensions:[]},r=new Map,i=!0,o=!1,a=void 0;try{for(var s,c=e.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){!(function(e){var i=e.name.toLowerCase();if("rtx"===i)return r.set(e.parameters.apt,e.preferredPayloadType),"continue";var o=t.codecs.find(function(t){return t.name.toLowerCase()===i&&t.kind===e.kind&&t.clockRate===e.clockRate});if(!o)return"continue";var a={clockRate:o.clockRate,kind:o.kind,mimeType:o.kind+"/"+o.name,name:o.name,numChannels:o.numChannels||1,parameters:{},preferredPayloadType:e.preferredPayloadType,rtcpFeedback:[]},s=!0,c=!1,u=void 0;try{for(var l,d=Object.keys(e.parameters)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(s=(l=d.next()).done);s=!0){var p=l.value,h=e.parameters[p],f=!0,m=!1,v=void 0;try{for(var g,y=Object.keys(o.parameters)["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(f=(g=y.next()).done);f=!0){var b=g.value,S=o.parameters[b];if(b===p&&S===h){a.parameters[b]=S;break}}}catch(e){m=!0,v=e}finally{try{!f&&y.return&&y.return()}finally{if(m)throw v}}}}catch(e){c=!0,u=e}finally{try{!s&&d.return&&d.return()}finally{if(c)throw u}}var _=!0,T=!1,C=void 0;try{for(var E,w=e.rtcpFeedback["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(_=(E=w.next()).done);_=!0){!(function(e){var t=o.rtcpFeedback.find(function(t){return t.type===e.type&&t.parameter===e.parameter});t&&a.rtcpFeedback.push(t)})(E.value)}}catch(e){T=!0,C=e}finally{try{!_&&w.return&&w.return()}finally{if(T)throw C}}n.codecs.push(a)})(s.value)}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}var u=!0,l=!1,d=void 0;try{for(var p,h=n.codecs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(p=h.next()).done);u=!0){var f=p.value,m=f.preferredPayloadType;if(r.has(m)){var v={clockRate:f.clockRate,kind:f.kind,mimeType:f.kind+"/rtx",name:"rtx",parameters:{apt:m},preferredPayloadType:r.get(m),rtcpFeedback:[]};n.codecs.push(v)}}}catch(e){l=!0,d=e}finally{try{!u&&h.return&&h.return()}finally{if(l)throw d}}var g=!0,y=!1,b=void 0;try{for(var S,_=e.headerExtensions["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(g=(S=_.next()).done);g=!0){!(function(e){var r=t.headerExtensions.find(function(t){return t.kind===e.kind&&t.uri===e.uri});if(r){var i={kind:r.kind,preferredEncrypt:Boolean(e.preferredEncrypt),preferredId:e.preferredId,uri:r.uri};n.headerExtensions.push(i)}})(S.value)}}catch(e){y=!0,b=e}finally{try{!g&&_.return&&_.return()}finally{if(y)throw b}}var T=!0,C=!1,E=void 0;try{for(var w,R=e.fecMechanisms["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(T=(w=R.next()).done);T=!0){!(function(e){var r=t.fecMechanisms.find(function(t){return t===e});r&&n.fecMechanisms.push(r)})(w.value)}}catch(e){C=!0,E=e}finally{try{!T&&R.return&&R.return()}finally{if(C)throw E}}return n};var i=n(15),o=n.n(i)},function(e,t,n){"use strict";n.d(t,"a",function(){return i});var r,i=(r="InvalidStateError",(function(e){function t(e){!(function(e,n){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")})(this);var n=(function(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t})(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return Object.defineProperty(n,"name",{value:r}),n}return(function(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)})(t,Error),t})())},function(e,t,n){"use strict";(function(e){function r(e,t,n){b(["screen"],function(e){return t({stream:e})},n)}function i(e){return"https://chrome.google.com/webstore/detail/"+e.desktopSharingChromeExtId}function o(e,t){"undefined"!=typeof chrome&&chrome&&chrome.runtime?chrome.runtime.sendMessage(t.desktopSharingChromeExtId,{getVersion:!0},function(n){if(!n||!n.version)return h.warn("Extension not installed?: ",chrome.runtime.lastError),void e(!1,!1);var r=n.version;h.log("Extension version is: "+r);var i=(function(e,t){try{for(var n=e.split("."),r=t.split("."),i=Math.max(n.length,r.length),o=0;os}return!1}catch(e){return f.callErrorHandler(e),h.error("Failed to parse extension version",e),!0}})(t.desktopSharingChromeMinExtVersion,r);e(!i,i)}):e(!1,!1)}function a(e,t,n){chrome.runtime.sendMessage(e.desktopSharingChromeExtId,{getStream:!0,sources:e.desktopSharingChromeSources},function(e){if(e)h.log("Response from extension: ",e),c(e,t,n);else{var r=chrome.runtime.lastError;n(r instanceof Error?r:new l.a(d.CHROME_EXTENSION_GENERIC_ERROR,r))}})}function s(e,t,n){return 0===n?Promise.reject():new Promise(function(r,i){var a=n,s=window.setInterval(function(){o(function(e){e?(window.clearInterval(s),r()):0==--a&&(i(),window.clearInterval(s))},e)},t)})}function c(e,t,n){var r=e.streamId,i=e.streamType,o=e.error;if(r)b(["desktop"],function(e){return t({stream:e,sourceId:r,sourceType:i})},n,{desktopStream:r});else{if(""===r)return void n(new l.a(d.CHROME_EXTENSION_USER_CANCELED));n(new l.a(d.CHROME_EXTENSION_GENERIC_ERROR,o))}}function u(e){if(!e.desktopSharingFirefoxDisabled&&!1!==g&&!0!==g)if(e.desktopSharingFirefoxExtId){var t=document.createElement("img");t.onload=function(){h.log("Detected firefox screen sharing extension."),g=!0},t.onerror=function(){h.log("Detected lack of firefox screen sharing extension."),g=!1};var n="chrome://"+e.desktopSharingFirefoxExtId.replace("@",".")+"/content/"+document.location.hostname+".png";t.setAttribute("src",n)}else g=!1}var l=n(12),d=n(17),p=n(1),h=n(0).getLogger(e),f=n(3),m=!1,v=!1,g=null,y=!1,b=null,S={intChromeExtPromise:null,obtainStream:null,init:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{disableDesktopSharing:!1,desktopSharingChromeDisabled:!1,desktopSharingChromeExtId:null,desktopSharingFirefoxDisabled:!1,desktopSharingFirefoxExtId:null},t=arguments[1];this.options=e=e||{},b=t,this.obtainStream=this.options.disableDesktopSharing?null:this._createObtainStreamMethod(e),this.obtainStream||h.info("Desktop sharing disabled")},_createObtainStreamMethod:function(e){var t=this;if(p.b.isNWJS())return function(e,t,n){window.JitsiMeetNW.obtainDesktopStream(t,function(e,t){var r=void 0;r=e&&"InvalidStateError"===e.name?new l.a(d.CHROME_EXTENSION_USER_CANCELED):new l.a(e,t,["desktop"]),"function"==typeof n&&n(r)})};if(p.b.isElectron())return this.obtainScreenOnElectron;if(p.b.isTemasysPluginUsed()){var a=n(37).WebRTCPlugin.plugin;return a.HasScreensharingFeature?a.isScreensharingAvailable?(h.info("Using Temasys plugin for desktop sharing"),r):(h.warn("Screensharing not available with Temasys plugin on this site"),null):(h.warn("Screensharing not supported by this plugin version"),null)}return p.b.isChrome()||p.b.isOpera()?p.b.isVersionLessThan("34")?(h.info("Chrome extension not supported until ver 34"),null):e.desktopSharingChromeDisabled||!1===e.desktopSharingChromeMethod||!e.desktopSharingChromeExtId?null:(h.info("Using Chrome extension for desktop sharing"),this.intChromeExtPromise=(function(e){return t=e,0===$("link[rel=chrome-webstore-item]").length&&$("head").append(''),$("link[rel=chrome-webstore-item]").attr("href",i(t)),new Promise(function(t){o(function(e,n){m=e,v=n,h.info("Chrome extension installed: "+m+" updateRequired: "+v),t()},e)});var t})(e).then(function(){t.intChromeExtPromise=null}),this.obtainScreenFromExtension):p.b.isFirefox()?e.desktopSharingFirefoxDisabled?null:"http:"===window.location.protocol?(h.log("Screen sharing is not supported over HTTP. Use of HTTPS is required."),null):(u(e),this.obtainScreenOnFirefox):(h.log("Screen sharing not supported by the current browser: ",p.b.getName()),null)},isSupported:function(){return null!==this.obtainStream},obtainScreenOnFirefox:function(e,t,n){var i=this,o=!1,a=this.options.desktopSharingFirefoxMaxVersionExtRequired;if("number"==typeof a&&(o=-1===a,a>=0&&(a=String(a))),"string"==typeof a&&(o=!p.b.isVersionGreaterThan(a)),o&&h.log("Jidesha extension required on firefox version "+p.b.getVersion()),o&&!0!==g){if(y&&(y=!1,u(this.options)),null===g)return window.setTimeout(function(){null===g&&(g=!1),i.obtainScreenOnFirefox(t,n)},300),void h.log("Waiting for detection of jidesha on firefox to finish.");g=null,y=!0,n(new l.a(d.FIREFOX_EXTENSION_NEEDED))}else r(0,t,n)},obtainScreenOnElectron:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=arguments[1],n=arguments[2];window.JitsiMeetScreenObtainer&&window.JitsiMeetScreenObtainer.openDesktopPicker?window.JitsiMeetScreenObtainer.openDesktopPicker({desktopSharingSources:e.desktopSharingSources||this.options.desktopSharingChromeSources},function(e,r){return c({streamId:e,streamType:r},t,n)},function(e){return n(new l.a(d.ELECTRON_DESKTOP_PICKER_ERROR,e))}):n(new l.a(d.ELECTRON_DESKTOP_PICKER_NOT_FOUND))},obtainScreenFromExtension:function(e,t,n){var r=this;if(null===this.intChromeExtPromise){var o=this.options,c=o.desktopSharingChromeExtId,u=o.desktopSharingChromeSources,l={desktopSharingChromeExtId:c,desktopSharingChromeSources:e.desktopSharingSources||u};if(m)a(l,t,n);else{if(v&&alert("Jitsi Desktop Streamer requires update. Changes will take effect after next Chrome restart."),p.b.isOpera())return void this.handleExternalInstall(e,t,n);try{chrome.webstore.install(i(this.options),function(i){h.log("Extension installed successfully",i),m=!0,s(r.options,200,10).then(function(){a(l,t,n)}).catch(function(){r.handleExtensionInstallationError(e,t,n)})},this.handleExtensionInstallationError.bind(this,e,t,n))}catch(r){this.handleExtensionInstallationError(e,t,n,r)}}}else this.intChromeExtPromise.then(function(){r.obtainScreenFromExtension(e,t,n)})},handleExternalInstall:function(e,t,n,r){var o=i(this.options);e.listener("waitingForExtension",o),this.checkForChromeExtensionOnInterval(e,t,n,r)},handleExtensionInstallationError:function(e,t,n,r){var o=i(this.options);if(("Inline installs can not be initiated from pop-up windows."===r||"Chrome Web Store installations can only be started by the top frame."===r||"Installs can only be initiated by one of the Chrome Web Store item's verified sites."===r||"Inline installation is not supported for this item. The user will be redirected to the Chrome Web Store."===r)&&e.interval>0&&"function"==typeof e.checkAgain&&"function"==typeof e.listener)this.handleExternalInstall(e,t,n,r);else{var a="Failed to install the extension from "+o;h.log(a,r);var s="Chrome Web Store installations can only be initated by a user gesture."===r?d.CHROME_EXTENSION_USER_GESTURE_REQUIRED:d.CHROME_EXTENSION_INSTALLATION_ERROR;n(new l.a(s,a))}},checkForChromeExtensionOnInterval:function(e,t,n){var r=this;!1!==e.checkAgain()?s(this.options,e.interval,1).then(function(){m=!0,e.listener("extensionFound"),r.obtainScreenFromExtension(e,t,n)}).catch(function(){r.checkForChromeExtensionOnInterval(e,t,n)}):n(new l.a(d.CHROME_EXTENSION_INSTALLATION_ERROR))}};t.a=S}).call(t,"modules/RTC/ScreenObtainer.js")},function(e,t,n){"use strict";(function(t){var r=n(137);e.exports=r({window:t.window})}).call(t,n(20))},function(e,t,n){"use strict";var r=n(11);e.exports=function(e,t){var i=e&&e.window,o={shimChrome:!0,shimFirefox:!0,shimEdge:!0,shimSafari:!0};for(var a in t)hasOwnProperty.call(t,a)&&(o[a]=t[a]);var s=r.log,c=r.detectBrowser(i),u={browserDetails:c,extractVersion:r.extractVersion,disableLog:r.disableLog,disableWarnings:r.disableWarnings},l=n(138)||null,d=n(140)||null,p=n(144)||null,h=n(146)||null,f=n(147)||null;switch(c.browser){case"chrome":if(!l||!l.shimPeerConnection||!o.shimChrome)return s("Chrome shim is not included in this adapter release."),u;s("adapter.js shimming chrome."),u.browserShim=l,f.shimCreateObjectURL(i),l.shimGetUserMedia(i),l.shimMediaStream(i),l.shimSourceObject(i),l.shimPeerConnection(i),l.shimOnTrack(i),l.shimAddTrackRemoveTrack(i),l.shimGetSendersWithDtmf(i),f.shimRTCIceCandidate(i);break;case"firefox":if(!p||!p.shimPeerConnection||!o.shimFirefox)return s("Firefox shim is not included in this adapter release."),u;s("adapter.js shimming firefox."),u.browserShim=p,f.shimCreateObjectURL(i),p.shimGetUserMedia(i),p.shimSourceObject(i),p.shimPeerConnection(i),p.shimOnTrack(i),f.shimRTCIceCandidate(i);break;case"edge":if(!d||!d.shimPeerConnection||!o.shimEdge)return s("MS edge shim is not included in this adapter release."),u;s("adapter.js shimming edge."),u.browserShim=d,f.shimCreateObjectURL(i),d.shimGetUserMedia(i),d.shimPeerConnection(i),d.shimReplaceTrack(i);break;case"safari":if(!h||!o.shimSafari)return s("Safari shim is not included in this adapter release."),u;s("adapter.js shimming safari."),u.browserShim=h,f.shimCreateObjectURL(i),h.shimRTCIceServerUrls(i),h.shimCallbacksAPI(i),h.shimLocalStreamsAPI(i),h.shimRemoteStreamsAPI(i),h.shimTrackEventTransceiver(i),h.shimGetUserMedia(i),h.shimCreateOfferLegacy(i),f.shimRTCIceCandidate(i);break;default:s("Unsupported browser!")}return u}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},i=n(11),o=i.log,a={shimMediaStream:function(e){e.MediaStream=e.MediaStream||e.webkitMediaStream},shimOnTrack:function(e){if("object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection&&!("ontrack"in e.RTCPeerConnection.prototype)){Object.defineProperty(e.RTCPeerConnection.prototype,"ontrack",{get:function(){return this._ontrack},set:function(e){this._ontrack&&this.removeEventListener("track",this._ontrack),this.addEventListener("track",this._ontrack=e)}});var t=e.RTCPeerConnection.prototype.setRemoteDescription;e.RTCPeerConnection.prototype.setRemoteDescription=function(){var n=this;return n._ontrackpoly||(n._ontrackpoly=function(t){t.stream.addEventListener("addtrack",function(r){var i;i=e.RTCPeerConnection.prototype.getReceivers?n.getReceivers().find(function(e){return e.track&&e.track.id===r.track.id}):{track:r.track};var o=new Event("track");o.track=r.track,o.receiver=i,o.transceiver={receiver:i},o.streams=[t.stream],n.dispatchEvent(o)}),t.stream.getTracks().forEach(function(r){var i;i=e.RTCPeerConnection.prototype.getReceivers?n.getReceivers().find(function(e){return e.track&&e.track.id===r.id}):{track:r};var o=new Event("track");o.track=r,o.receiver=i,o.transceiver={receiver:i},o.streams=[t.stream],n.dispatchEvent(o)})},n.addEventListener("addstream",n._ontrackpoly)),t.apply(n,arguments)}}},shimGetSendersWithDtmf:function(e){if("object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection&&!("getSenders"in e.RTCPeerConnection.prototype)&&"createDTMFSender"in e.RTCPeerConnection.prototype){var t=function(e,t){return{track:t,get dtmf(){return void 0===this._dtmf&&("audio"===t.kind?this._dtmf=e.createDTMFSender(t):this._dtmf=null),this._dtmf},_pc:e}};if(!e.RTCPeerConnection.prototype.getSenders){e.RTCPeerConnection.prototype.getSenders=function(){return this._senders=this._senders||[],this._senders.slice()};var n=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addTrack=function(e,r){var i=n.apply(this,arguments);return i||(i=t(this,e),this._senders.push(i)),i};var i=e.RTCPeerConnection.prototype.removeTrack;e.RTCPeerConnection.prototype.removeTrack=function(e){i.apply(this,arguments);var t=this._senders.indexOf(e);-1!==t&&this._senders.splice(t,1)}}var o=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(e){var n=this;n._senders=n._senders||[],o.apply(n,[e]),e.getTracks().forEach(function(e){n._senders.push(t(n,e))})};var a=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){var t=this;t._senders=t._senders||[],a.apply(t,[e]),e.getTracks().forEach(function(e){var n=t._senders.find(function(t){return t.track===e});n&&t._senders.splice(t._senders.indexOf(n),1)})}}else if("object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection&&"getSenders"in e.RTCPeerConnection.prototype&&"createDTMFSender"in e.RTCPeerConnection.prototype&&e.RTCRtpSender&&!("dtmf"in e.RTCRtpSender.prototype)){var s=e.RTCPeerConnection.prototype.getSenders;e.RTCPeerConnection.prototype.getSenders=function(){var e=this,t=s.apply(e,[]);return t.forEach(function(t){t._pc=e}),t},Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get:function(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=this._pc.createDTMFSender(this.track):this._dtmf=null),this._dtmf}})}},shimSourceObject:function(e){var t=e&&e.URL;"object"===(void 0===e?"undefined":r(e))&&(!e.HTMLMediaElement||"srcObject"in e.HTMLMediaElement.prototype||Object.defineProperty(e.HTMLMediaElement.prototype,"srcObject",{get:function(){return this._srcObject},set:function(e){var n=this;this._srcObject=e,this.src&&t.revokeObjectURL(this.src),e?(this.src=t.createObjectURL(e),e.addEventListener("addtrack",function(){n.src&&t.revokeObjectURL(n.src),n.src=t.createObjectURL(e)}),e.addEventListener("removetrack",function(){n.src&&t.revokeObjectURL(n.src),n.src=t.createObjectURL(e)})):this.src=""}}))},shimAddTrackRemoveTrack:function(e){function t(e,t){var n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(function(t){var r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(i.id,"g"),r.id)}),new RTCSessionDescription({type:t.type,sdp:n})}var n=i.detectBrowser(e);if(!(e.RTCPeerConnection.prototype.addTrack&&n.version>=63)){var r=e.RTCPeerConnection.prototype.getLocalStreams;e.RTCPeerConnection.prototype.getLocalStreams=function(){var e=this,t=r.apply(this);return e._reverseStreams=e._reverseStreams||{},t.map(function(t){return e._reverseStreams[t.id]})};var o=e.RTCPeerConnection.prototype.addStream;e.RTCPeerConnection.prototype.addStream=function(t){var n=this;if(n._streams=n._streams||{},n._reverseStreams=n._reverseStreams||{},t.getTracks().forEach(function(e){if(n.getSenders().find(function(t){return t.track===e}))throw new DOMException("Track already exists.","InvalidAccessError")}),!n._reverseStreams[t.id]){var r=new e.MediaStream(t.getTracks());n._streams[t.id]=r,n._reverseStreams[r.id]=t,t=r}o.apply(n,[t])};var a=e.RTCPeerConnection.prototype.removeStream;e.RTCPeerConnection.prototype.removeStream=function(e){this._streams=this._streams||{},this._reverseStreams=this._reverseStreams||{},a.apply(this,[this._streams[e.id]||e]),delete this._reverseStreams[this._streams[e.id]?this._streams[e.id].id:e.id],delete this._streams[e.id]},e.RTCPeerConnection.prototype.addTrack=function(t,n){var r=this;if("closed"===r.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");var i=[].slice.call(arguments,1);if(1!==i.length||!i[0].getTracks().find(function(e){return e===t}))throw new DOMException("The adapter.js addTrack polyfill only supports a single stream which is associated with the specified track.","NotSupportedError");if(r.getSenders().find(function(e){return e.track===t}))throw new DOMException("Track already exists.","InvalidAccessError");r._streams=r._streams||{},r._reverseStreams=r._reverseStreams||{};var o=r._streams[n.id];if(o)o.addTrack(t),Promise.resolve().then(function(){r.dispatchEvent(new Event("negotiationneeded"))});else{var a=new e.MediaStream([t]);r._streams[n.id]=a,r._reverseStreams[a.id]=n,r.addStream(a)}return r.getSenders().find(function(e){return e.track===t})},["createOffer","createAnswer"].forEach(function(n){var r=e.RTCPeerConnection.prototype[n];e.RTCPeerConnection.prototype[n]=function(){var e=this,n=arguments;return arguments.length&&"function"==typeof arguments[0]?r.apply(e,[function(r){var i=t(e,r);n[0].apply(null,[i])},function(e){n[1]&&n[1].apply(null,e)},arguments[2]]):r.apply(e,arguments).then(function(n){return t(e,n)})}});var s=e.RTCPeerConnection.prototype.setLocalDescription;e.RTCPeerConnection.prototype.setLocalDescription=function(){return arguments.length&&arguments[0].type?(arguments[0]=(function(e,t){var n=t.sdp;return Object.keys(e._reverseStreams||[]).forEach(function(t){var r=e._reverseStreams[t],i=e._streams[r.id];n=n.replace(new RegExp(r.id,"g"),i.id)}),new RTCSessionDescription({type:t.type,sdp:n})})(this,arguments[0]),s.apply(this,arguments)):s.apply(this,arguments)};var c=Object.getOwnPropertyDescriptor(e.RTCPeerConnection.prototype,"localDescription");Object.defineProperty(e.RTCPeerConnection.prototype,"localDescription",{get:function(){var e=c.get.apply(this);return""===e.type?e:t(this,e)}}),e.RTCPeerConnection.prototype.removeTrack=function(e){var t=this;if("closed"===t.signalingState)throw new DOMException("The RTCPeerConnection's signalingState is 'closed'.","InvalidStateError");if(!e._pc)throw new DOMException("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.","TypeError");if(e._pc!==t)throw new DOMException("Sender was not created by this connection.","InvalidAccessError");t._streams=t._streams||{};var n;Object.keys(t._streams).forEach(function(r){t._streams[r].getTracks().find(function(t){return e.track===t})&&(n=t._streams[r])}),n&&(1===n.getTracks().length?t.removeStream(t._reverseStreams[n.id]):n.removeTrack(e.track),t.dispatchEvent(new Event("negotiationneeded")))}}},shimPeerConnection:function(e){var t=i.detectBrowser(e);if(e.RTCPeerConnection){var n=e.RTCPeerConnection;e.RTCPeerConnection=function(e,t){if(e&&e.iceServers){for(var r=[],o=0;o0&&"function"==typeof e)return a.apply(this,arguments);if(0===a.length&&(0===arguments.length||"function"!=typeof arguments[0]))return a.apply(this,[]);var o=function(e){var t={};return e.result().forEach(function(e){var n={id:e.id,timestamp:e.timestamp,type:{localcandidate:"local-candidate",remotecandidate:"remote-candidate"}[e.type]||e.type};e.names().forEach(function(t){n[t]=e.stat(t)}),t[n.id]=n}),t},s=function(e){return new Map(Object.keys(e).map(function(t){return[t,e[t]]}))};if(arguments.length>=2){var c=function(e){i[1](s(o(e)))};return a.apply(this,[c,arguments[0]])}return new Promise(function(e,t){a.apply(r,[function(t){e(s(o(t)))},t])}).then(t,n)},t.version<51&&["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){var e=arguments,t=this,r=new Promise(function(r,i){n.apply(t,[e[0],r,i])});return e.length<2?r:r.then(function(){e[1].apply(null,[])},function(t){e.length>=3&&e[2].apply(null,[t])})}}),t.version<52&&["createOffer","createAnswer"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){var e=this;if(arguments.length<1||1===arguments.length&&"object"===r(arguments[0])){var t=1===arguments.length?arguments[0]:void 0;return new Promise(function(r,i){n.apply(e,[r,i,t])})}return n.apply(this,arguments)}}),["setLocalDescription","setRemoteDescription","addIceCandidate"].forEach(function(t){var n=e.RTCPeerConnection.prototype[t];e.RTCPeerConnection.prototype[t]=function(){return arguments[0]=new("addIceCandidate"===t?e.RTCIceCandidate:e.RTCSessionDescription)(arguments[0]),n.apply(this,arguments)}});var s=e.RTCPeerConnection.prototype.addIceCandidate;e.RTCPeerConnection.prototype.addIceCandidate=function(){return arguments[0]?s.apply(this,arguments):(arguments[1]&&arguments[1].apply(null),Promise.resolve())}}};e.exports={shimMediaStream:a.shimMediaStream,shimOnTrack:a.shimOnTrack,shimAddTrackRemoveTrack:a.shimAddTrackRemoveTrack,shimGetSendersWithDtmf:a.shimGetSendersWithDtmf,shimSourceObject:a.shimSourceObject,shimPeerConnection:a.shimPeerConnection,shimGetUserMedia:n(139)}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},i=n(11),o=i.log;e.exports=function(e){var t=i.detectBrowser(e),n=e&&e.navigator,a=function(e){if("object"!==(void 0===e?"undefined":r(e))||e.mandatory||e.optional)return e;var t={};return Object.keys(e).forEach(function(n){if("require"!==n&&"advanced"!==n&&"mediaSource"!==n){var i="object"===r(e[n])?e[n]:{ideal:e[n]};void 0!==i.exact&&"number"==typeof i.exact&&(i.min=i.max=i.exact);var o=function(e,t){return e?e+t.charAt(0).toUpperCase()+t.slice(1):"deviceId"===t?"sourceId":t};if(void 0!==i.ideal){t.optional=t.optional||[];var a={};"number"==typeof i.ideal?(a[o("min",n)]=i.ideal,t.optional.push(a),(a={})[o("max",n)]=i.ideal,t.optional.push(a)):(a[o("",n)]=i.ideal,t.optional.push(a))}void 0!==i.exact&&"number"!=typeof i.exact?(t.mandatory=t.mandatory||{},t.mandatory[o("",n)]=i.exact):["min","max"].forEach(function(e){void 0!==i[e]&&(t.mandatory=t.mandatory||{},t.mandatory[o(e,n)]=i[e])})}}),e.advanced&&(t.optional=(t.optional||[]).concat(e.advanced)),t},s=function(e,i){if(t.version>=61)return i(e);if((e=JSON.parse(JSON.stringify(e)))&&"object"===r(e.audio)){var s=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])};s((e=JSON.parse(JSON.stringify(e))).audio,"autoGainControl","googAutoGainControl"),s(e.audio,"noiseSuppression","googNoiseSuppression"),e.audio=a(e.audio)}if(e&&"object"===r(e.video)){var c=e.video.facingMode;c=c&&("object"===(void 0===c?"undefined":r(c))?c:{ideal:c});var u=t.version<66;if(c&&("user"===c.exact||"environment"===c.exact||"user"===c.ideal||"environment"===c.ideal)&&(!n.mediaDevices.getSupportedConstraints||!n.mediaDevices.getSupportedConstraints().facingMode||u)){delete e.video.facingMode;var l;if("environment"===c.exact||"environment"===c.ideal?l=["back","rear"]:"user"!==c.exact&&"user"!==c.ideal||(l=["front"]),l)return n.mediaDevices.enumerateDevices().then(function(t){var n=(t=t.filter(function(e){return"videoinput"===e.kind})).find(function(e){return l.some(function(t){return-1!==e.label.toLowerCase().indexOf(t)})});return!n&&t.length&&-1!==l.indexOf("back")&&(n=t[t.length-1]),n&&(e.video.deviceId=c.exact?{exact:n.deviceId}:{ideal:n.deviceId}),e.video=a(e.video),o("chrome: "+JSON.stringify(e)),i(e)})}e.video=a(e.video)}return o("chrome: "+JSON.stringify(e)),i(e)},c=function(e){return{name:{PermissionDeniedError:"NotAllowedError",InvalidStateError:"NotReadableError",DevicesNotFoundError:"NotFoundError",ConstraintNotSatisfiedError:"OverconstrainedError",TrackStartError:"NotReadableError",MediaDeviceFailedDueToShutdown:"NotReadableError",MediaDeviceKillSwitchOn:"NotReadableError"}[e.name]||e.name,message:e.message,constraint:e.constraintName,toString:function(){return this.name+(this.message&&": ")+this.message}}};n.getUserMedia=function(e,t,r){s(e,function(e){n.webkitGetUserMedia(e,t,function(e){r&&r(c(e))})})};var u=function(e){return new Promise(function(t,r){n.getUserMedia(e,t,r)})};if(n.mediaDevices||(n.mediaDevices={getUserMedia:u,enumerateDevices:function(){return new Promise(function(t){var n={audio:"audioinput",video:"videoinput"};return e.MediaStreamTrack.getSources(function(e){t(e.map(function(e){return{label:e.label,kind:n[e.kind],deviceId:e.id,groupId:""}}))})})},getSupportedConstraints:function(){return{deviceId:!0,echoCancellation:!0,facingMode:!0,frameRate:!0,height:!0,width:!0}}}),n.mediaDevices.getUserMedia){var l=n.mediaDevices.getUserMedia.bind(n.mediaDevices);n.mediaDevices.getUserMedia=function(e){return s(e,function(e){return l(e).then(function(t){if(e.audio&&!t.getAudioTracks().length||e.video&&!t.getVideoTracks().length)throw t.getTracks().forEach(function(e){e.stop()}),new DOMException("","NotFoundError");return t},function(e){return Promise.reject(c(e))})})}}else n.mediaDevices.getUserMedia=function(e){return u(e)};void 0===n.mediaDevices.addEventListener&&(n.mediaDevices.addEventListener=function(){o("Dummy mediaDevices.addEventListener called.")}),void 0===n.mediaDevices.removeEventListener&&(n.mediaDevices.removeEventListener=function(){o("Dummy mediaDevices.removeEventListener called.")})}},function(e,t,n){"use strict";var r=n(11),i=n(141);e.exports={shimGetUserMedia:n(143),shimPeerConnection:function(e){var t=r.detectBrowser(e);if(e.RTCIceGatherer&&(e.RTCIceCandidate||(e.RTCIceCandidate=function(e){return e}),e.RTCSessionDescription||(e.RTCSessionDescription=function(e){return e}),t.version<15025)){var n=Object.getOwnPropertyDescriptor(e.MediaStreamTrack.prototype,"enabled");Object.defineProperty(e.MediaStreamTrack.prototype,"enabled",{set:function(e){n.set.call(this,e);var t=new Event("enabled");t.enabled=e,this.dispatchEvent(t)}})}!e.RTCRtpSender||"dtmf"in e.RTCRtpSender.prototype||Object.defineProperty(e.RTCRtpSender.prototype,"dtmf",{get:function(){return void 0===this._dtmf&&("audio"===this.track.kind?this._dtmf=new e.RTCDtmfSender(this):"video"===this.track.kind&&(this._dtmf=null)),this._dtmf}}),e.RTCPeerConnection=i(e,t.version)},shimReplaceTrack:function(e){!e.RTCRtpSender||"replaceTrack"in e.RTCRtpSender.prototype||(e.RTCRtpSender.prototype.replaceTrack=e.RTCRtpSender.prototype.setTrack)}}},function(e,t,n){"use strict";function r(e,t,n,r,i){var o=l.writeRtpDescription(e.kind,t);if(o+=l.writeIceParameters(e.iceGatherer.getLocalParameters()),o+=l.writeDtlsParameters(e.dtlsTransport.getLocalParameters(),"offer"===n?"actpass":i||"active"),o+="a=mid:"+e.mid+"\r\n",e.rtpSender&&e.rtpReceiver?o+="a=sendrecv\r\n":e.rtpSender?o+="a=sendonly\r\n":e.rtpReceiver?o+="a=recvonly\r\n":o+="a=inactive\r\n",e.rtpSender){var a="msid:"+r.id+" "+e.rtpSender.track.id+"\r\n";o+="a="+a,o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" "+a,e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" "+a,o+="a=ssrc-group:FID "+e.sendEncodingParameters[0].ssrc+" "+e.sendEncodingParameters[0].rtx.ssrc+"\r\n")}return o+="a=ssrc:"+e.sendEncodingParameters[0].ssrc+" cname:"+l.localCName+"\r\n",e.rtpSender&&e.sendEncodingParameters[0].rtx&&(o+="a=ssrc:"+e.sendEncodingParameters[0].rtx.ssrc+" cname:"+l.localCName+"\r\n"),o}function i(e,t){var n={codecs:[],headerExtensions:[],fecMechanisms:[]},r=function(e,t){e=parseInt(e,10);for(var n=0;n=14393&&-1===e.indexOf("?transport=udp"):(n=!0,!0)}),delete e.url,e.urls=i?r[0]:r,!!r.length}})})(n.iceServers||[],t),this._iceGatherers=[],n.iceCandidatePoolSize)for(var o=n.iceCandidatePoolSize;o>0;o--)this._iceGatherers=new e.RTCIceGatherer({iceServers:n.iceServers,gatherPolicy:n.iceTransportPolicy});else n.iceCandidatePoolSize=0;this._config=n,this.transceivers=[],this._sdpSessionId=l.generateSessionId(),this._sdpSessionVersion=0,this._dtlsRole=void 0,this._isClosed=!1};n.prototype.onicecandidate=null,n.prototype.onaddstream=null,n.prototype.ontrack=null,n.prototype.onremovestream=null,n.prototype.onsignalingstatechange=null,n.prototype.oniceconnectionstatechange=null,n.prototype.onicegatheringstatechange=null,n.prototype.onnegotiationneeded=null,n.prototype.ondatachannel=null,n.prototype._dispatchEvent=function(e,t){this._isClosed||(this.dispatchEvent(t),"function"==typeof this["on"+e]&&this["on"+e](t))},n.prototype._emitGatheringStateChange=function(){var e=new Event("icegatheringstatechange");this._dispatchEvent("icegatheringstatechange",e)},n.prototype.getConfiguration=function(){return this._config},n.prototype.getLocalStreams=function(){return this.localStreams},n.prototype.getRemoteStreams=function(){return this.remoteStreams},n.prototype._createTransceiver=function(e){var t=this.transceivers.length>0,n={track:null,iceGatherer:null,iceTransport:null,dtlsTransport:null,localCapabilities:null,remoteCapabilities:null,rtpSender:null,rtpReceiver:null,kind:e,mid:null,sendEncodingParameters:null,recvEncodingParameters:null,stream:null,associatedRemoteMediaStreams:[],wantReceive:!0};if(this.usingBundle&&t)n.iceTransport=this.transceivers[0].iceTransport,n.dtlsTransport=this.transceivers[0].dtlsTransport;else{var r=this._createIceAndDtlsTransports();n.iceTransport=r.iceTransport,n.dtlsTransport=r.dtlsTransport}return this.transceivers.push(n),n},n.prototype.addTrack=function(t,n){if(this.transceivers.find(function(e){return e.track===t}))throw u("InvalidAccessError","Track already exists.");if("closed"===this.signalingState)throw u("InvalidStateError","Attempted to call addTrack on a closed peerconnection.");for(var r,i=0;i=15025)e.getTracks().forEach(function(t){n.addTrack(t,e)});else{var r=e.clone();e.getTracks().forEach(function(e,t){var n=r.getTracks()[t];e.addEventListener("enabled",function(e){n.enabled=e.enabled})}),r.getTracks().forEach(function(e){n.addTrack(e,r)})}},n.prototype.removeTrack=function(t){if(!(t instanceof e.RTCRtpSender))throw new TypeError("Argument 1 of RTCPeerConnection.removeTrack does not implement interface RTCRtpSender.");var n=this.transceivers.find(function(e){return e.rtpSender===t});if(!n)throw u("InvalidAccessError","Sender was not created by this connection.");var r=n.stream;n.rtpSender.stop(),n.rtpSender=null,n.track=null,n.stream=null,-1===this.transceivers.map(function(e){return e.stream}).indexOf(r)&&this.localStreams.indexOf(r)>-1&&this.localStreams.splice(this.localStreams.indexOf(r),1),this._maybeFireNegotiationNeeded()},n.prototype.removeStream=function(e){var t=this;e.getTracks().forEach(function(e){var n=t.getSenders().find(function(t){return t.track===e});n&&t.removeTrack(n)})},n.prototype.getSenders=function(){return this.transceivers.filter(function(e){return!!e.rtpSender}).map(function(e){return e.rtpSender})},n.prototype.getReceivers=function(){return this.transceivers.filter(function(e){return!!e.rtpReceiver}).map(function(e){return e.rtpReceiver})},n.prototype._createIceGatherer=function(t,n){var r=this;if(n&&t>0)return this.transceivers[0].iceGatherer;if(this._iceGatherers.length)return this._iceGatherers.shift();var i=new e.RTCIceGatherer({iceServers:this._config.iceServers,gatherPolicy:this._config.iceTransportPolicy});return Object.defineProperty(i,"state",{value:"new",writable:!0}),this.transceivers[t].candidates=[],this.transceivers[t].bufferCandidates=function(e){var n=!e.candidate||0===Object.keys(e.candidate).length;i.state=n?"completed":"gathering",null!==r.transceivers[t].candidates&&r.transceivers[t].candidates.push(e.candidate)},i.addEventListener("localcandidate",this.transceivers[t].bufferCandidates),i},n.prototype._gather=function(t,n){var r=this,i=this.transceivers[n].iceGatherer;if(!i.onlocalcandidate){var o=this.transceivers[n].candidates;this.transceivers[n].candidates=null,i.removeEventListener("localcandidate",this.transceivers[n].bufferCandidates),i.onlocalcandidate=function(e){if(!(r.usingBundle&&n>0)){var o=new Event("icecandidate");o.candidate={sdpMid:t,sdpMLineIndex:n};var a=e.candidate,s=!a||0===Object.keys(a).length;s?"new"!==i.state&&"gathering"!==i.state||(i.state="completed"):("new"===i.state&&(i.state="gathering"),a.component=1,o.candidate.candidate=l.writeCandidate(a));var c=l.splitSections(r.localDescription.sdp);c[o.candidate.sdpMLineIndex+1]+=s?"a=end-of-candidates\r\n":"a="+o.candidate.candidate+"\r\n",r.localDescription.sdp=c.join("");var u=r.transceivers.every(function(e){return e.iceGatherer&&"completed"===e.iceGatherer.state});"gathering"!==r.iceGatheringState&&(r.iceGatheringState="gathering",r._emitGatheringStateChange()),s||r._dispatchEvent("icecandidate",o),u&&(r._dispatchEvent("icecandidate",new Event("icecandidate")),r.iceGatheringState="complete",r._emitGatheringStateChange())}},e.setTimeout(function(){o.forEach(function(e){var t=new Event("RTCIceGatherEvent");t.candidate=e,i.onlocalcandidate(t)})},0)}},n.prototype._createIceAndDtlsTransports=function(){var t=this,n=new e.RTCIceTransport(null);n.onicestatechange=function(){t._updateConnectionState()};var r=new e.RTCDtlsTransport(n);return r.ondtlsstatechange=function(){t._updateConnectionState()},r.onerror=function(){Object.defineProperty(r,"state",{value:"failed",writable:!0}),t._updateConnectionState()},{iceTransport:n,dtlsTransport:r}},n.prototype._disposeIceAndDtlsTransports=function(e){var t=this.transceivers[e].iceGatherer;t&&(delete t.onlocalcandidate,delete this.transceivers[e].iceGatherer);var n=this.transceivers[e].iceTransport;n&&(delete n.onicestatechange,delete this.transceivers[e].iceTransport);var r=this.transceivers[e].dtlsTransport;r&&(delete r.ondtlsstatechange,delete r.onerror,delete this.transceivers[e].dtlsTransport)},n.prototype._transceive=function(e,n,r){var o=i(e.localCapabilities,e.remoteCapabilities);n&&e.rtpSender&&(o.encodings=e.sendEncodingParameters,o.rtcp={cname:l.localCName,compound:e.rtcpParameters.compound},e.recvEncodingParameters.length&&(o.rtcp.ssrc=e.recvEncodingParameters[0].ssrc),e.rtpSender.send(o)),r&&e.rtpReceiver&&o.codecs.length>0&&("video"===e.kind&&e.recvEncodingParameters&&t<15019&&e.recvEncodingParameters.forEach(function(e){delete e.rtx}),e.recvEncodingParameters.length&&(o.encodings=e.recvEncodingParameters),o.rtcp={compound:e.rtcpParameters.compound},e.rtcpParameters.cname&&(o.rtcp.cname=e.rtcpParameters.cname),e.sendEncodingParameters.length&&(o.rtcp.ssrc=e.sendEncodingParameters[0].ssrc),e.rtpReceiver.receive(o))},n.prototype.setLocalDescription=function(e){var t=this;if(!o("setLocalDescription",e.type,this.signalingState)||this._isClosed)return Promise.reject(u("InvalidStateError","Can not set local "+e.type+" in state "+t.signalingState));var n,r;if("offer"===e.type)n=l.splitSections(e.sdp),r=n.shift(),n.forEach(function(e,n){var r=l.parseRtpParameters(e);t.transceivers[n].localCapabilities=r}),this.transceivers.forEach(function(e,n){t._gather(e.mid,n)});else if("answer"===e.type){n=l.splitSections(t.remoteDescription.sdp),r=n.shift();var a=l.matchPrefix(r,"a=ice-lite").length>0;n.forEach(function(e,n){var o=t.transceivers[n],s=o.iceGatherer,c=o.iceTransport,u=o.dtlsTransport,d=o.localCapabilities,p=o.remoteCapabilities;if(!(l.isRejected(e)&&0===l.matchPrefix(e,"a=bundle-only").length||o.isDatachannel)){var h=l.getIceParameters(e,r),f=l.getDtlsParameters(e,r);a&&(f.role="server"),t.usingBundle&&0!==n||(t._gather(o.mid,n),"new"===c.state&&c.start(s,h,a?"controlling":"controlled"),"new"===u.state&&u.start(f));var m=i(d,p);t._transceive(o,m.codecs.length>0,!1)}})}switch(this.localDescription={type:e.type,sdp:e.sdp},e.type){case"offer":this._updateSignalingState("have-local-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+e.type+'"')}return Promise.resolve()},n.prototype.setRemoteDescription=function(n){var r=this;if(!o("setRemoteDescription",n.type,this.signalingState)||this._isClosed)return Promise.reject(u("InvalidStateError","Can not set remote "+n.type+" in state "+r.signalingState));var i={};this.remoteStreams.forEach(function(e){i[e.id]=e});var d=[],p=l.splitSections(n.sdp),h=p.shift(),f=l.matchPrefix(h,"a=ice-lite").length>0,m=l.matchPrefix(h,"a=group:BUNDLE ").length>0;this.usingBundle=m;var v=l.matchPrefix(h,"a=ice-options:")[0];switch(this.canTrickleIceCandidates=!!v&&v.substr(14).split(" ").indexOf("trickle")>=0,p.forEach(function(o,c){var u=l.splitLines(o),p=l.getKind(o),v=l.isRejected(o)&&0===l.matchPrefix(o,"a=bundle-only").length,g=u[0].substr(2).split(" ")[2],y=l.getDirection(o,h),b=l.parseMsid(o),S=l.getMid(o)||l.generateIdentifier();if("application"!==p||"DTLS/SCTP"!==g){var _,T,C,E,w,R,k,I,P,A,O,D=l.parseRtpParameters(o);v||(A=l.getIceParameters(o,h),(O=l.getDtlsParameters(o,h)).role="client"),k=l.parseRtpEncodingParameters(o);var x=l.parseRtcpParameters(o),N=l.matchPrefix(o,"a=end-of-candidates",h).length>0,L=l.matchPrefix(o,"a=candidate:").map(function(e){return l.parseCandidate(e)}).filter(function(e){return 1===e.component});if(("offer"===n.type||"answer"===n.type)&&!v&&m&&c>0&&r.transceivers[c]&&(r._disposeIceAndDtlsTransports(c),r.transceivers[c].iceGatherer=r.transceivers[0].iceGatherer,r.transceivers[c].iceTransport=r.transceivers[0].iceTransport,r.transceivers[c].dtlsTransport=r.transceivers[0].dtlsTransport,r.transceivers[c].rtpSender&&r.transceivers[c].rtpSender.setTransport(r.transceivers[0].dtlsTransport),r.transceivers[c].rtpReceiver&&r.transceivers[c].rtpReceiver.setTransport(r.transceivers[0].dtlsTransport)),"offer"!==n.type||v)"answer"!==n.type||v||(T=(_=r.transceivers[c]).iceGatherer,C=_.iceTransport,E=_.dtlsTransport,w=_.rtpReceiver,R=_.sendEncodingParameters,I=_.localCapabilities,r.transceivers[c].recvEncodingParameters=k,r.transceivers[c].remoteCapabilities=D,r.transceivers[c].rtcpParameters=x,L.length&&"new"===C.state&&(!f&&!N||m&&0!==c?L.forEach(function(e){a(_.iceTransport,e)}):C.setRemoteCandidates(L)),m&&0!==c||("new"===C.state&&C.start(T,A,"controlling"),"new"===E.state&&E.start(O)),r._transceive(_,"sendrecv"===y||"recvonly"===y,"sendrecv"===y||"sendonly"===y),!w||"sendrecv"!==y&&"sendonly"!==y?delete _.rtpReceiver:(P=w.track,b?(i[b.stream]||(i[b.stream]=new e.MediaStream),s(P,i[b.stream]),d.push([P,w,i[b.stream]])):(i.default||(i.default=new e.MediaStream),s(P,i.default),d.push([P,w,i.default]))));else{(_=r.transceivers[c]||r._createTransceiver(p)).mid=S,_.iceGatherer||(_.iceGatherer=r._createIceGatherer(c,m)),L.length&&"new"===_.iceTransport.state&&(!N||m&&0!==c?L.forEach(function(e){a(_.iceTransport,e)}):_.iceTransport.setRemoteCandidates(L)),I=e.RTCRtpReceiver.getCapabilities(p),t<15019&&(I.codecs=I.codecs.filter(function(e){return"rtx"!==e.name})),R=_.sendEncodingParameters||[{ssrc:1001*(2*c+2)}];var M=!1;if("sendrecv"===y||"sendonly"===y){if(M=!_.rtpReceiver,w=_.rtpReceiver||new e.RTCRtpReceiver(_.dtlsTransport,p),M){var j;P=w.track,b&&"-"===b.stream||(b?(i[b.stream]||(i[b.stream]=new e.MediaStream,Object.defineProperty(i[b.stream],"id",{get:function(){return b.stream}})),Object.defineProperty(P,"id",{get:function(){return b.track}}),j=i[b.stream]):(i.default||(i.default=new e.MediaStream),j=i.default)),j&&(s(P,j),_.associatedRemoteMediaStreams.push(j)),d.push([P,w,j])}}else _.rtpReceiver&&_.rtpReceiver.track&&(_.associatedRemoteMediaStreams.forEach(function(e){var t=e.getTracks().find(function(e){return e.id===_.rtpReceiver.track.id});t&&(function(e,t){t.removeTrack(e);var n=new Event("removetrack");n.track=e,t.dispatchEvent(n)})(t,e)}),_.associatedRemoteMediaStreams=[]);_.localCapabilities=I,_.remoteCapabilities=D,_.rtpReceiver=w,_.rtcpParameters=x,_.sendEncodingParameters=R,_.recvEncodingParameters=k,r._transceive(r.transceivers[c],!1,M)}}else r.transceivers[c]={mid:S,isDatachannel:!0}}),void 0===this._dtlsRole&&(this._dtlsRole="offer"===n.type?"active":"passive"),this.remoteDescription={type:n.type,sdp:n.sdp},n.type){case"offer":this._updateSignalingState("have-remote-offer");break;case"answer":this._updateSignalingState("stable");break;default:throw new TypeError('unsupported type "'+n.type+'"')}return Object.keys(i).forEach(function(t){var n=i[t];if(n.getTracks().length){if(-1===r.remoteStreams.indexOf(n)){r.remoteStreams.push(n);var o=new Event("addstream");o.stream=n,e.setTimeout(function(){r._dispatchEvent("addstream",o)})}d.forEach(function(e){var t=e[0],i=e[1];n.id===e[2].id&&c(r,t,i,[n])})}}),d.forEach(function(e){e[2]||c(r,e[0],e[1],[])}),e.setTimeout(function(){r&&r.transceivers&&r.transceivers.forEach(function(e){e.iceTransport&&"new"===e.iceTransport.state&&e.iceTransport.getRemoteCandidates().length>0&&(console.warn("Timeout for addRemoteCandidate. Consider sending an end-of-candidates notification"),e.iceTransport.addRemoteCandidate({}))})},4e3),Promise.resolve()},n.prototype.close=function(){this.transceivers.forEach(function(e){e.iceTransport&&e.iceTransport.stop(),e.dtlsTransport&&e.dtlsTransport.stop(),e.rtpSender&&e.rtpSender.stop(),e.rtpReceiver&&e.rtpReceiver.stop()}),this._isClosed=!0,this._updateSignalingState("closed")},n.prototype._updateSignalingState=function(e){this.signalingState=e;var t=new Event("signalingstatechange");this._dispatchEvent("signalingstatechange",t)},n.prototype._maybeFireNegotiationNeeded=function(){var t=this;"stable"===this.signalingState&&!0!==this.needNegotiation&&(this.needNegotiation=!0,e.setTimeout(function(){if(!1!==t.needNegotiation){t.needNegotiation=!1;var e=new Event("negotiationneeded");t._dispatchEvent("negotiationneeded",e)}},0))},n.prototype._updateConnectionState=function(){var e,t={new:0,closed:0,connecting:0,checking:0,connected:0,completed:0,disconnected:0,failed:0};if(this.transceivers.forEach(function(e){t[e.iceTransport.state]++,t[e.dtlsTransport.state]++}),t.connected+=t.completed,e="new",t.failed>0?e="failed":t.connecting>0||t.checking>0?e="connecting":t.disconnected>0?e="disconnected":t.new>0?e="new":(t.connected>0||t.completed>0)&&(e="connected"),e!==this.iceConnectionState){this.iceConnectionState=e;var n=new Event("iceconnectionstatechange");this._dispatchEvent("iceconnectionstatechange",n)}},n.prototype.createOffer=function(){var n=this;if(this._isClosed)return Promise.reject(u("InvalidStateError","Can not call createOffer after close"));var i=this.transceivers.filter(function(e){return"audio"===e.kind}).length,o=this.transceivers.filter(function(e){return"video"===e.kind}).length,a=arguments[0];if(a){if(a.mandatory||a.optional)throw new TypeError("Legacy mandatory/optional constraints not supported.");void 0!==a.offerToReceiveAudio&&(i=!0===a.offerToReceiveAudio?1:!1===a.offerToReceiveAudio?0:a.offerToReceiveAudio),void 0!==a.offerToReceiveVideo&&(o=!0===a.offerToReceiveVideo?1:!1===a.offerToReceiveVideo?0:a.offerToReceiveVideo)}for(this.transceivers.forEach(function(e){"audio"===e.kind?--i<0&&(e.wantReceive=!1):"video"===e.kind&&--o<0&&(e.wantReceive=!1)});i>0||o>0;)i>0&&(this._createTransceiver("audio"),i--),o>0&&(this._createTransceiver("video"),o--);var s=l.writeSessionBoilerplate(this._sdpSessionId,this._sdpSessionVersion++);this.transceivers.forEach(function(r,i){var o=r.track,a=r.kind,s=l.generateIdentifier();r.mid=s,r.iceGatherer||(r.iceGatherer=n._createIceGatherer(i,n.usingBundle));var c=e.RTCRtpSender.getCapabilities(a);t<15019&&(c.codecs=c.codecs.filter(function(e){return"rtx"!==e.name})),c.codecs.forEach(function(e){"H264"===e.name&&void 0===e.parameters["level-asymmetry-allowed"]&&(e.parameters["level-asymmetry-allowed"]="1")});var u=r.sendEncodingParameters||[{ssrc:1001*(2*i+1)}];o&&t>=15019&&"video"===a&&!u[0].rtx&&(u[0].rtx={ssrc:u[0].ssrc+1}),r.wantReceive&&(r.rtpReceiver=new e.RTCRtpReceiver(r.dtlsTransport,a)),r.localCapabilities=c,r.sendEncodingParameters=u}),"max-compat"!==this._config.bundlePolicy&&(s+="a=group:BUNDLE "+this.transceivers.map(function(e){return e.mid}).join(" ")+"\r\n"),s+="a=ice-options:trickle\r\n",this.transceivers.forEach(function(e,t){s+=r(e,e.localCapabilities,"offer",e.stream,n._dtlsRole),s+="a=rtcp-rsize\r\n",!e.iceGatherer||"new"===n.iceGatheringState||0!==t&&n.usingBundle||(e.iceGatherer.getLocalCandidates().forEach(function(e){e.component=1,s+="a="+l.writeCandidate(e)+"\r\n"}),"completed"===e.iceGatherer.state&&(s+="a=end-of-candidates\r\n"))});var c=new e.RTCSessionDescription({type:"offer",sdp:s});return Promise.resolve(c)},n.prototype.createAnswer=function(){var n=this;if(this._isClosed)return Promise.reject(u("InvalidStateError","Can not call createAnswer after close"));var o=l.writeSessionBoilerplate(this._sdpSessionId,this._sdpSessionVersion++);this.usingBundle&&(o+="a=group:BUNDLE "+this.transceivers.map(function(e){return e.mid}).join(" ")+"\r\n");var a=l.splitSections(this.remoteDescription.sdp).length-1;this.transceivers.forEach(function(e,s){if(!(s+1>a))if(e.isDatachannel)o+="m=application 0 DTLS/SCTP 5000\r\nc=IN IP4 0.0.0.0\r\na=mid:"+e.mid+"\r\n";else{if(e.stream){var c;"audio"===e.kind?c=e.stream.getAudioTracks()[0]:"video"===e.kind&&(c=e.stream.getVideoTracks()[0]),c&&t>=15019&&"video"===e.kind&&!e.sendEncodingParameters[0].rtx&&(e.sendEncodingParameters[0].rtx={ssrc:e.sendEncodingParameters[0].ssrc+1})}var u=i(e.localCapabilities,e.remoteCapabilities);!u.codecs.filter(function(e){return"rtx"===e.name.toLowerCase()}).length&&e.sendEncodingParameters[0].rtx&&delete e.sendEncodingParameters[0].rtx,o+=r(e,u,"answer",e.stream,n._dtlsRole),e.rtcpParameters&&e.rtcpParameters.reducedSize&&(o+="a=rtcp-rsize\r\n")}});var s=new e.RTCSessionDescription({type:"answer",sdp:o});return Promise.resolve(s)},n.prototype.addIceCandidate=function(e){var t;if(e&&""!==e.candidate){if(void 0===e.sdpMLineIndex&&!e.sdpMid)throw new TypeError("sdpMLineIndex or sdpMid required");if(!this.remoteDescription)return Promise.reject(u("InvalidStateError","Can not add ICE candidate without a remote description"));var n=e.sdpMLineIndex;if(e.sdpMid)for(var r=0;r0?l.parseCandidate(e.candidate):{};if("tcp"===o.protocol&&(0===o.port||9===o.port))return Promise.resolve();if(o.component&&1!==o.component)return Promise.resolve();if((0===n||n>0&&i.iceTransport!==this.transceivers[0].iceTransport)&&!a(i.iceTransport,o))return Promise.reject(u("OperationError","Can not add ICE candidate"));var s=e.candidate.trim();0===s.indexOf("a=")&&(s=s.substr(2)),(t=l.splitSections(this.remoteDescription.sdp))[n+1]+="a="+(o.type?s:"end-of-candidates")+"\r\n",this.remoteDescription.sdp=t.join("")}else for(var c=0;c55&&"autoGainControl"in n.mediaDevices.getSupportedConstraints())){var d=function(e,t,n){t in e&&!(n in e)&&(e[n]=e[t],delete e[t])},p=n.mediaDevices.getUserMedia.bind(n.mediaDevices);if(n.mediaDevices.getUserMedia=function(e){return"object"===(void 0===e?"undefined":r(e))&&"object"===r(e.audio)&&(e=JSON.parse(JSON.stringify(e)),d(e.audio,"autoGainControl","mozAutoGainControl"),d(e.audio,"noiseSuppression","mozNoiseSuppression")),p(e)},a&&a.prototype.getSettings){var h=a.prototype.getSettings;a.prototype.getSettings=function(){var e=h.apply(this,arguments);return d(e,"mozAutoGainControl","autoGainControl"),d(e,"mozNoiseSuppression","noiseSuppression"),e}}if(a&&a.prototype.applyConstraints){var f=a.prototype.applyConstraints;a.prototype.applyConstraints=function(e){return"audio"===this.kind&&"object"===(void 0===e?"undefined":r(e))&&(e=JSON.parse(JSON.stringify(e)),d(e,"autoGainControl","mozAutoGainControl"),d(e,"noiseSuppression","mozNoiseSuppression")),f.apply(this,[e])}}}n.getUserMedia=function(e,r,o){if(t.version<44)return c(e,r,o);i.deprecated("navigator.getUserMedia","navigator.mediaDevices.getUserMedia"),n.mediaDevices.getUserMedia(e).then(r,o)}}},function(e,t,n){"use strict";var r="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},i=n(11),o={shimLocalStreamsAPI:function(e){if("object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection){if("getLocalStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getLocalStreams=function(){return this._localStreams||(this._localStreams=[]),this._localStreams}),"getStreamById"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getStreamById=function(e){var t=null;return this._localStreams&&this._localStreams.forEach(function(n){n.id===e&&(t=n)}),this._remoteStreams&&this._remoteStreams.forEach(function(n){n.id===e&&(t=n)}),t}),!("addStream"in e.RTCPeerConnection.prototype)){var t=e.RTCPeerConnection.prototype.addTrack;e.RTCPeerConnection.prototype.addStream=function(e){this._localStreams||(this._localStreams=[]),-1===this._localStreams.indexOf(e)&&this._localStreams.push(e);var n=this;e.getTracks().forEach(function(r){t.call(n,r,e)})},e.RTCPeerConnection.prototype.addTrack=function(e,n){n&&(this._localStreams?-1===this._localStreams.indexOf(n)&&this._localStreams.push(n):this._localStreams=[n]),t.call(this,e,n)}}"removeStream"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.removeStream=function(e){this._localStreams||(this._localStreams=[]);var t=this._localStreams.indexOf(e);if(-1!==t){this._localStreams.splice(t,1);var n=this,r=e.getTracks();this.getSenders().forEach(function(e){-1!==r.indexOf(e.track)&&n.removeTrack(e)})}})}},shimRemoteStreamsAPI:function(e){"object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection&&("getRemoteStreams"in e.RTCPeerConnection.prototype||(e.RTCPeerConnection.prototype.getRemoteStreams=function(){return this._remoteStreams?this._remoteStreams:[]}),"onaddstream"in e.RTCPeerConnection.prototype||Object.defineProperty(e.RTCPeerConnection.prototype,"onaddstream",{get:function(){return this._onaddstream},set:function(e){this._onaddstream&&(this.removeEventListener("addstream",this._onaddstream),this.removeEventListener("track",this._onaddstreampoly)),this.addEventListener("addstream",this._onaddstream=e),this.addEventListener("track",this._onaddstreampoly=function(e){var t=e.streams[0];if(this._remoteStreams||(this._remoteStreams=[]),!(this._remoteStreams.indexOf(t)>=0)){this._remoteStreams.push(t);var n=new Event("addstream");n.stream=e.streams[0],this.dispatchEvent(n)}}.bind(this))}}))},shimCallbacksAPI:function(e){if("object"===(void 0===e?"undefined":r(e))&&e.RTCPeerConnection){var t=e.RTCPeerConnection.prototype,n=t.createOffer,i=t.createAnswer,o=t.setLocalDescription,a=t.setRemoteDescription,s=t.addIceCandidate;t.createOffer=function(e,t){var r=arguments.length>=2?arguments[2]:arguments[0],i=n.apply(this,[r]);return t?(i.then(e,t),Promise.resolve()):i},t.createAnswer=function(e,t){var n=arguments.length>=2?arguments[2]:arguments[0],r=i.apply(this,[n]);return t?(r.then(e,t),Promise.resolve()):r};var c=function(e,t,n){var r=o.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r};t.setLocalDescription=c,c=function(e,t,n){var r=a.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.setRemoteDescription=c,c=function(e,t,n){var r=s.apply(this,[e]);return n?(r.then(t,n),Promise.resolve()):r},t.addIceCandidate=c}},shimGetUserMedia:function(e){var t=e&&e.navigator;t.getUserMedia||(t.webkitGetUserMedia?t.getUserMedia=t.webkitGetUserMedia.bind(t):t.mediaDevices&&t.mediaDevices.getUserMedia&&(t.getUserMedia=function(e,n,r){t.mediaDevices.getUserMedia(e).then(n,r)}.bind(t)))},shimRTCIceServerUrls:function(e){var t=e.RTCPeerConnection;e.RTCPeerConnection=function(e,n){if(e&&e.iceServers){for(var r=[],o=0;o=Number.MAX_SAFE_INTEGER&&(t=0),t+1}},function(e,t,n){"use strict";(function(e){function r(e,t,r,i,o,a,s){var c=this;this.audioTransferActive=!0,this.videoTransferActive=!0,this.rtc=e,this.id=t,this.isP2P=a,this.remoteTracks=new Map,this.localTracks=new Map,this._addedStreams=[],this.localSSRCs=new Map,this.localUfrag=null,this.remoteUfrag=null,this.signalingLayer=r,this._peerVideoTypeChanged=this._peerVideoTypeChanged.bind(this),this.signalingLayer.on(T.b,this._peerVideoTypeChanged),this._peerMutedChanged=this._peerMutedChanged.bind(this),this.signalingLayer.on(T.a,this._peerMutedChanged),this.options=s,this.peerconnection=new h.a.RTCPeerConnectionType(i,o),this.updateLog=[],this.stats={},this.statsinterval=null,this.maxstats=0;var u=n(154).Interop;this.interop=new u;var l=n(158);this.simulcast=new l({numOfLayers:w,explodeRemoteSimulcast:!1}),this.sdpConsistency=new b.a(this.toString()),this.localSdpMunger=new d.a(this),this.eventEmitter=e.eventEmitter,this.rtxModifier=new g.a,this.trace=function(e,t){E.debug(e,t),c.updateLog.push({time:new Date,type:e,value:t||""})},this.onicecandidate=null,this.peerconnection.onicecandidate=function(e){f.b.isTemasysPluginUsed()||c.trace("onicecandidate",JSON.stringify(e.candidate,null," ")),null!==c.onicecandidate&&c.onicecandidate(e)},this.peerconnection.onaddstream=function(e){return c._remoteStreamAdded(e.stream)},this.peerconnection.onremovestream=function(e){return c._remoteStreamRemoved(e.stream)},this.onsignalingstatechange=null,this.peerconnection.onsignalingstatechange=function(e){c.trace("onsignalingstatechange",c.signalingState),null!==c.onsignalingstatechange&&c.onsignalingstatechange(e)},this.oniceconnectionstatechange=null,this.peerconnection.oniceconnectionstatechange=function(e){c.trace("oniceconnectionstatechange",c.iceConnectionState),null!==c.oniceconnectionstatechange&&c.oniceconnectionstatechange(e)},this.onnegotiationneeded=null,this.peerconnection.onnegotiationneeded=function(e){c.trace("onnegotiationneeded"),null!==c.onnegotiationneeded&&c.onnegotiationneeded(e)},this.ondatachannel=null,this.peerconnection.ondatachannel=function(e){c.trace("ondatachannel"),null!==c.ondatachannel&&c.ondatachannel(e)},!f.b.isFirefox()&&this.maxstats&&(this.statsinterval=window.setInterval(function(){c.peerconnection.getStats(function(e){for(var t=e.result(),n=new Date,r=0;rc.maxstats&&(o.values.shift(),o.times.shift()),o.endTime=n})})(r)})},1e3)),E.info("Create new "+this)}function i(e){return e&&e.groups&&e.groups.length?e.groups[0].ssrcs[0]:e&&e.ssrcs&&e.ssrcs.length?e.ssrcs[0]:null}t.a=r;var o=n(0),a=(n.n(o),n(15)),s=n.n(a),c=n(3),u=(n.n(c),n(150)),l=n(4),d=n(151),p=n(27),h=n(28),f=n(1),m=n(9),v=n.n(m),g=n(152),y=n(53),b=n(153),S=n(29),_=n(14),T=n(54),C="function"==typeof Symbol&&"symbol"==typeof("function"==typeof Symbol?Symbol.iterator:"@@iterator")?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==("function"==typeof Symbol?Symbol.prototype:"@@prototype")?"symbol":typeof e},E=Object(o.getLogger)(e),w=3,R=["1","2","3"],k=function(e){return void 0===e||null===e?"":"type: "+e.type+"\r\n"+e.sdp};r.prototype.getConnectionState=function(){var e=this.peerconnection.iceConnectionState;return"completed"===e?"connected":e},r.prototype._getDesiredMediaDirection=function(e){var t=!0;return e===l.a?t=this.audioTransferActive:e===l.b&&(t=this.videoTransferActive),t?this.hasAnyTracksOfType(e)?"sendrecv":"recvonly":"inactive"},r.prototype.isSimulcastOn=function(){return!this.options.disableSimulcast&&f.b.supportsSimulcast()&&(!f.b.isFirefox()||this.options.enableFirefoxSimulcast)},r.prototype._peerVideoTypeChanged=function(e,t){if(e){var n=this.getRemoteTracks(e,l.b);n.length&&n[0]._setVideoType(t)}else E.error("No endpointID on peerVideoTypeChanged "+this)},r.prototype._peerMutedChanged=function(e,t,n){if(e){var r=this.getRemoteTracks(e,t);r.length&&r[0].setMute(n)}else E.error("On peerMuteChanged - no endpoint ID")},r.prototype.getLocalTracks=function(e){var t=Array.from(this.localTracks.values());return void 0!==e&&(t=t.filter(function(t){return t.getType()===e})),t},r.prototype.hasAnyTracksOfType=function(e){if(!e)throw new Error('"mediaType" is required');return this.getLocalTracks(e).length>0},r.prototype.getRemoteTracks=function(e,t){var n=[],r=e?[e]:this.remoteTracks.keys(),i=!0,o=!1,a=void 0;try{for(var s,c=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value,l=this.remoteTracks.get(u);if(l){var d=!0,p=!1,h=void 0;try{for(var f,m=l.keys()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(f=m.next()).done);d=!0){var v=f.value;if(!t||t===v){var g=l.get(v);g&&n.push(g)}}}catch(e){p=!0,h=e}finally{try{!d&&m.return&&m.return()}finally{if(p)throw h}}}}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}return n},r.prototype.getTrackBySSRC=function(e){if("number"!=typeof e)throw new Error("SSRC "+e+" is not a number");var t=!0,n=!1,r=void 0;try{for(var i,o=this.localTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(i=o.next()).done);t=!0){var a=i.value;if(this.getLocalSSRC(a)===e)return a}}catch(e){n=!0,r=e}finally{try{!t&&o.return&&o.return()}finally{if(n)throw r}}var s=!0,c=!1,u=void 0;try{for(var l,d=this.getRemoteTracks()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(s=(l=d.next()).done);s=!0){var p=l.value;if(p.getSSRC()===e)return p}}catch(e){c=!0,u=e}finally{try{!s&&d.return&&d.return()}finally{if(c)throw u}}return null},r.prototype._remoteStreamAdded=function(e){var t=this,n=p.a.getStreamID(e);if(p.a.isUserStreamById(n)){(f.b.isChrome()||f.b.isNWJS()||f.b.isElectron()||f.b.isEdge())&&(e.onaddtrack=function(n){t._remoteTrackAdded(e,n.track)},e.onremovetrack=function(n){t._remoteTrackRemoved(e,n.track)});var r=e.getAudioTracks(),i=!0,o=!1,a=void 0;try{for(var s,c=r["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(s=c.next()).done);i=!0){var u=s.value;this._remoteTrackAdded(e,u)}}catch(e){o=!0,a=e}finally{try{!i&&c.return&&c.return()}finally{if(o)throw a}}var l=e.getVideoTracks(),d=!0,h=!1,m=void 0;try{for(var v,g=l["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(v=g.next()).done);d=!0){var y=v.value;this._remoteTrackAdded(e,y)}}catch(e){h=!0,m=e}finally{try{!d&&g.return&&g.return()}finally{if(h)throw m}}}else E.info(this+" ignored remote 'stream added' event for non-user streamid: "+n)},r.prototype._remoteTrackAdded=function(e,t){var n=p.a.getStreamID(e),r=t.kind;if(E.info(this+" remote track added:",n,r),r){var i=new y.a(this.remoteDescription.sdp).media.filter(function(e){return e.startsWith("m="+r)});if(i.length){var o=_.a.findLines(i[0],"a=ssrc:");if((o=o.filter(function(e){var t=f.b.isTemasysPluginUsed()?"mslabel":"msid";return-1!==e.indexOf(t+":"+n)})).length){var a=o[0].substring(7).split(" ")[0],s=Number(a),u=this.signalingLayer.getSSRCOwner(s);if(isNaN(s)||s<0)c.callErrorHandler(new Error("Invalid SSRC: "+a+" for remote track, msid: "+n+" media type: "+r));else if(u){E.log(this+" associated ssrc",u,s);var l=this.signalingLayer.getPeerMediaInfo(u,r);if(l){var d=l.muted,h=l.videoType;this._createRemoteTrack(u,e,t,r,h,s,d)}else c.callErrorHandler(new Error(this+": no peer media info available for "+u))}else c.callErrorHandler(new Error("No SSRC owner known for: "+s+" for remote track, msid: "+n+" media type: "+r))}else c.callErrorHandler(new Error("No SSRC lines for streamId "+n+" for remote track, media type: "+r))}else c.callErrorHandler(new Error("No media lines for type "+r+" found in remote SDP for remote track: "+n))}else c.callErrorHandler(new Error("MediaType undefined for remote track, stream id: "+n))},r.prototype._createRemoteTrack=function(e,t,n,r,i,o,a){var s=new u.a(this.rtc,this.rtc.conference,e,t,n,r,i,o,a,this.isP2P),c=this.remoteTracks.get(e);c||(c=new Map,this.remoteTracks.set(e,c)),c.has(r)&&E.error(this+" overwriting remote track! "+s,e,r),c.set(r,s),this.eventEmitter.emit(v.a.REMOTE_TRACK_ADDED,s)},r.prototype._remoteStreamRemoved=function(e){if(p.a.isUserStream(e)){var t=e.getVideoTracks(),n=!0,r=!1,i=void 0;try{for(var o,a=t["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value;this._remoteTrackRemoved(e,s)}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}var c=e.getAudioTracks(),u=!0,l=!1,d=void 0;try{for(var h,f=c["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(u=(h=f.next()).done);u=!0){var m=h.value;this._remoteTrackRemoved(e,m)}}catch(e){l=!0,d=e}finally{try{!u&&f.return&&f.return()}finally{if(l)throw d}}}else{var v=p.a.getStreamID(e);E.info("Ignored remote 'stream removed' event for non-user stream "+v)}},r.prototype._remoteTrackRemoved=function(e,t){var n=p.a.getStreamID(e),r=t&&p.a.getTrackID(t);E.info(this+" - remote track removed: "+n+", "+r),n?r?this._removeRemoteTrackById(n,r)||E.warn(this+" Removed track not found for msid: "+n+",\n track id: "+r):c.callErrorHandler(new Error(this+" remote track removal failed - no track ID")):c.callErrorHandler(new Error(this+" remote track removal failed - no stream ID"))},r.prototype._getRemoteTrackById=function(e,t){var n=!0,r=!1,i=void 0;try{for(var o,a=this.remoteTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var s=o.value,c=!0,u=!1,l=void 0;try{for(var d,p=s.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(c=(d=p.next()).done);c=!0){var h=d.value;if(h.getStreamId()==e&&h.getTrackId()==t)return h}}catch(e){u=!0,l=e}finally{try{!c&&p.return&&p.return()}finally{if(u)throw l}}}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}},r.prototype.removeRemoteTracks=function(e){var t=[],n=this.remoteTracks.get(e);if(n){var r=n.get(l.a),i=n.get(l.b);r&&t.push(r),i&&t.push(i),this.remoteTracks.delete(e)}return E.debug(this+" removed remote tracks for "+e+" count: "+t.length),t},r.prototype._removeRemoteTrack=function(e){e.dispose();var t=e.getParticipantId(),n=this.remoteTracks.get(t);n?n.delete(e.getType())||E.error("Failed to remove "+e+" - type mapping messed up ?"):E.error("removeRemoteTrack: no remote tracks map for "+t),this.eventEmitter.emit(v.a.REMOTE_TRACK_REMOVED,e)},r.prototype._removeRemoteTrackById=function(e,t){var n=this._getRemoteTrackById(e,t);return n&&this._removeRemoteTrack(n),n},r.prototype.getLocalSSRC=function(e){var t=this._getSSRC(e.rtcId);return t&&t.ssrcs[0]},r.prototype._injectSsrcGroupForUnifiedSimulcast=function(e){var t=s.a.parse(e.sdp),n=t.media.find(function(e){return"video"===e.type});if(n.simulcast_03){var r=[];if(n.ssrcs.forEach(function(e){"msid"===e.attribute&&r.push(e.id)}),n.ssrcGroups=n.ssrcGroups||[],n.ssrcGroups.find(function(e){return"SIM"===e.semantics}))return e;n.ssrcGroups.push({semantics:"SIM",ssrcs:r.join(" ")})}return new RTCSessionDescription({type:e.type,sdp:s.a.write(t)})};var I={signalingState:function(){return this.peerconnection.signalingState},iceConnectionState:function(){return this.peerconnection.iceConnectionState},localDescription:function(){var e=this.peerconnection.localDescription;return e?(this.trace("getLocalDescription::preTransform",k(e)),f.b.usesUnifiedPlan()&&(e=this.interop.toPlanB(e),this.trace("getLocalDescription::postTransform (Plan B)",k(e)),e=this._injectSsrcGroupForUnifiedSimulcast(e),this.trace("getLocalDescription::postTransform (inject ssrc group)",k(e))),f.b.doesVideoMuteByStreamRemove()&&(e=this.localSdpMunger.maybeAddMutedLocalVideoTracksToSDP(e),E.debug("getLocalDescription::postTransform (munge local SDP)",e)),e=(function(e){if(!e)throw new Error("No local description passed in.");var t=new S.a(e.sdp),n=t.selectMedia("audio"),r=!1;n&&"sendrecv"!==n.direction&&(n.direction="sendrecv",r=!0);var i=t.selectMedia("video");return i&&"sendrecv"!==i.direction&&(i.direction="sendrecv",r=!0),r?new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()}):e})(e),e=this.localSdpMunger.transformStreamIdentifiers(e)):(E.debug("getLocalDescription no localDescription found"),{})},remoteDescription:function(){var e=this.peerconnection.remoteDescription;return this.trace("getRemoteDescription::preTransform",k(e)),f.b.usesUnifiedPlan()&&(e=this.interop.toPlanB(e),this.trace("getRemoteDescription::postTransform (Plan B)",k(e))),e||{}}};Object.keys(I).forEach(function(e){Object.defineProperty(r.prototype,e,{get:I[e]})}),r.prototype._getSSRC=function(e){return this.localSSRCs.get(e)},r.prototype.addTrack=function(e){var t=e.rtcId;if(E.info("add "+e+" to: "+this),this.localTracks.has(t))E.error(e+" is already in "+this);else{this.localTracks.set(t,e);var n=e.getOriginalStream();if(n?this._addStream(n):(!f.b.doesVideoMuteByStreamRemove()||e.isAudioTrack()||e.isVideoTrack()&&!e.isMuted())&&E.error(this+" no WebRTC stream for: "+e),f.b.doesVideoMuteByStreamRemove()&&e.isVideoTrack()&&e.isMuted()){var r=this.generateNewStreamSSRCInfo(e);this.sdpConsistency.setPrimarySsrc(r.ssrcs[0]);var i=r.groups.find(function(e){return"SIM"===e.semantics});i&&this.simulcast.setSsrcCache(i.ssrcs);var o=r.groups.filter(function(e){return"FID"===e.semantics});if(o){var a=new Map;o.forEach(function(e){var t=e.ssrcs[0],n=e.ssrcs[1];a.set(t,n)}),this.rtxModifier.setSsrcCache(a)}}}},r.prototype.addTrackUnmute=function(e){if(!this._assertTrackBelongs("addTrackUnmute",e))return!1;E.info("Adding "+e+" as unmute to "+this);var t=e.getOriginalStream();return t?(this._addStream(t),!0):(E.error("Unable to add "+e+" as unmute to "+this+" - no WebRTC stream"),!1)},r.prototype._addStream=function(e){this.peerconnection.addStream(e),this._addedStreams.push(e)},r.prototype._removeStream=function(e){f.b.isFirefox()?this._handleFirefoxRemoveStream(e):this.peerconnection.removeStream(e),this._addedStreams=this._addedStreams.filter(function(t){return t!==e})},r.prototype._assertTrackBelongs=function(e,t){var n=this.localTracks.has(t.rtcId);return n||E.error(e+": "+t+" does not belong to "+this),n},r.prototype.isMediaStreamInPc=function(e){return this._addedStreams.indexOf(e)>-1},r.prototype.removeTrack=function(e){var t=e.getOriginalStream();this.trace("removeStream",e.rtcId,t?t.id:void 0),this._assertTrackBelongs("removeStream",e)&&(this.localTracks.delete(e.rtcId),this.localSSRCs.delete(e.rtcId),t&&(f.b.isFirefox()?this._handleFirefoxRemoveStream(t):this.peerconnection.removeStream(t)))},r.prototype.removeTrackMute=function(e){var t=e.getOriginalStream();return this.trace("removeStreamMute",e.rtcId,t?t.id:null),!!this._assertTrackBelongs("removeStreamMute",e)&&(t?(E.info("Removing "+e+" as mute from "+this),this._removeStream(t),!0):(E.error("removeStreamMute - no WebRTC stream for "+e),!1))},r.prototype._handleFirefoxRemoveStream=function(e){if(e){var t=null,n=null;e.getAudioTracks()&&e.getAudioTracks().length?n=e.getAudioTracks()[0]:e.getVideoTracks()&&e.getVideoTracks().length&&(n=e.getVideoTracks()[0]),n?(this.peerconnection.getSenders().some(function(e){return e.track===n&&(t=e,!0)}),t?this.peerconnection.removeTrack(t):E.log("Cannot remove tracks: no RTPSender.")):E.error("Cannot remove tracks: no tracks.")}},r.prototype.createDataChannel=function(e,t){return this.trace("createDataChannel",e,t),this.peerconnection.createDataChannel(e,t)},r.prototype._ensureSimulcastGroupIsLast=function(e){var t=e.sdp,n=t.indexOf("m=video"),r=t.indexOf("a=ssrc-group:SIM",n),i=t.lastIndexOf("a=ssrc-group");if(-1===r||-1===i||i===r)return e;var o=t.indexOf("\r\n",r),a=t.substring(r,o+2);i=(t=t.replace(a,"")).lastIndexOf("a=ssrc-group");var s=t.indexOf("\r\n",i);return t=t.slice(0,s)+"\r\n"+a.trim()+t.slice(s),new RTCSessionDescription({type:e.type,sdp:t})},r.prototype._adjustLocalMediaDirection=function(e){var t=new S.a(e.sdp),n=!1,r=t.selectMedia("audio");if(r){var i=this._getDesiredMediaDirection(l.a);r.direction!==i&&(r.direction=i,E.info("Adjusted local audio direction to "+i),n=!0)}else E.warn('No "audio" media found int the local description');var o=t.selectMedia("video");if(o){var a=this._getDesiredMediaDirection(l.b);o.direction!==a&&(o.direction=a,E.info("Adjusted local video direction to "+a),n=!0)}else E.warn('No "video" media found in the local description');return n?new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()}):e},r.prototype.setLocalDescription=function(e,t,n){var r=this,i=e;if(this.trace("setLocalDescription::preTransform",k(i)),this.options.disableH264||this.options.preferH264){var o=s.a.parse(i.sdp),a=o.media.find(function(e){return"video"===e.type});this.options.disableH264?_.a.stripVideoCodec(a,"h264"):_.a.preferVideoCodec(a,"h264"),i=new RTCSessionDescription({type:i.type,sdp:s.a.write(o)}),this.trace("setLocalDescription::postTransform (H264)",k(i))}i=this._adjustLocalMediaDirection(i),i=this._ensureSimulcastGroupIsLast(i),f.b.usesUnifiedPlan()&&(i=this.interop.toUnifiedPlan(i),this.trace("setLocalDescription::postTransform (Unified Plan)",k(i))),this.peerconnection.setLocalDescription(i,function(){r.trace("setLocalDescriptionOnSuccess");var e=_.a.getUfrag(i.sdp);e!==r.localUfrag&&(r.localUfrag=e,r.eventEmitter.emit(v.a.LOCAL_UFRAG_CHANGED,r,e)),t()},function(e){r.trace("setLocalDescriptionOnFailure",e),r.eventEmitter.emit(v.a.SET_LOCAL_DESCRIPTION_FAILED,e,r),n(e)})},r.prototype.setAudioTransferActive=function(e){E.debug(this+" audio transfer active: "+e);var t=this.audioTransferActive!==e;return this.audioTransferActive=e,t},r.prototype._insertUnifiedPlanSimulcastReceive=function(e){var t=s.a.parse(e.sdp),n=t.media.find(function(e){return"video"===e.type});return n.rids=[{id:"1",direction:"recv"},{id:"2",direction:"recv"},{id:"3",direction:"recv"}],n.simulcast_03={value:"recv rid="+R.join(";")},new RTCSessionDescription({type:e.type,sdp:s.a.write(t)})},r.prototype.setRemoteDescription=function(e,t,r){var i=this;if(this.trace("setRemoteDescription::preTransform",k(e)),e=this.simulcast.mungeRemoteDescription(e),this.trace("setRemoteDescription::postTransform (simulcast)",k(e)),this.options.preferH264){var o=s.a.parse(e.sdp),a=o.media.find(function(e){return"video"===e.type});_.a.preferVideoCodec(a,"h264"),e=new RTCSessionDescription({type:e.type,sdp:s.a.write(o)})}f.b.usesUnifiedPlan()?(e=new RTCSessionDescription({type:e.type,sdp:this.rtxModifier.stripRtx(e.sdp)}),this.trace("setRemoteDescription::postTransform (stripRtx)",k(e)),e=this.interop.toUnifiedPlan(e),this.trace("setRemoteDescription::postTransform (Plan A)",k(e)),this.isSimulcastOn()&&(e=this._insertUnifiedPlanSimulcastReceive(e),this.trace("setRemoteDescription::postTransform (sim receive)",k(e)))):e=(function(e){if("object"!==(void 0===e?"undefined":C(e))||null===e||"string"!=typeof e.sdp)return E.warn("An empty description was passed as an argument."),e;var t=n(15),r=t.parse(e.sdp);void 0!==r&&void 0!==r.media&&Array.isArray(r.media)&&r.media.forEach(function(e){var t=[],n=[];if(void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){void 0!==e.semantics&&"FID"===e.semantics&&void 0!==e.ssrcs&&t.push(Number(e.ssrcs.split(" ")[0]))}),Array.isArray(e.ssrcs)){var r=void 0;for(r=0;r=0&&(n.push(e.ssrcs[r]),delete e.ssrcs[r]);for(r=0;r=96;u--)if(!a.includes(u)){c=u,a.push(u),n.payloads=a.join(" ");break}return void 0===c?(E.error("Could not find valid payload type to inject."),e):(o.push({codec:"H264",payload:c,rate:9e4}),r.push({config:"level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f",payload:c}),E.debug("Injecting H264 payload type "+c+" into video mLine."),new RTCSessionDescription({type:e.type,sdp:s.a.write(t)}))},r.prototype.setVideoTransferActive=function(e){E.debug(this+" video transfer active: "+e);var t=this.videoTransferActive!==e;return this.videoTransferActive=e,t},r.prototype.generateRecvonlySsrc=function(){var e=_.a.generateSsrc();E.info(this+" generated new recvonly SSRC: "+e),this.sdpConsistency.setPrimarySsrc(e)},r.prototype.clearRecvonlySsrc=function(){E.info("Clearing primary video SSRC!"),this.sdpConsistency.clearVideoSsrcCache()},r.prototype.close=function(){this.trace("stop"),this.signalingLayer.off(T.a,this._peerMutedChanged),this.signalingLayer.off(T.b,this._peerVideoTypeChanged);var e=!0,t=!1,n=void 0;try{for(var r,i=this.remoteTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(e=(r=i.next()).done);e=!0){var o=r.value,a=!0,s=!1,c=void 0;try{for(var u,l=o.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(a=(u=l.next()).done);a=!0){var d=u.value;this._removeRemoteTrack(d)}}catch(e){s=!0,c=e}finally{try{!a&&l.return&&l.return()}finally{if(s)throw c}}}}catch(e){t=!0,n=e}finally{try{!e&&i.return&&i.return()}finally{if(t)throw n}}this.remoteTracks.clear(),this._addedStreams=[],this.rtc._removePeerConnection(this)||E.error("RTC._removePeerConnection returned false"),null!==this.statsinterval&&(window.clearInterval(this.statsinterval),this.statsinterval=null),E.info("Closing "+this+"..."),this.peerconnection.close()},r.prototype.createAnswer=function(e,t,n){if(f.b.supportsRtpSender()&&this.isSimulcastOn()){var r={encodings:[{rid:"1",scaleResolutionDownBy:4},{rid:"2",scaleResolutionDownBy:2},{rid:"3"}]};this.peerconnection.getSenders().find(function(e){return"video"===e.track.kind}).setParameters(r)}this._createOfferOrAnswer(!1,e,t,n)},r.prototype.createOffer=function(e,t,n){this._createOfferOrAnswer(!0,e,t,n)},r.prototype._createOfferOrAnswer=function(e,t,n,r){var i=this,o=e?"Offer":"Answer";this.trace("create"+o,JSON.stringify(r,null," "));var a=function(r){try{if(i.trace("create"+o+"OnSuccess::preTransform",k(r)),f.b.usesUnifiedPlan()&&(r=i.interop.toPlanB(r),i.trace("create"+o+"OnSuccess::postTransform (Plan B)",k(r)),i.isSimulcastOn()&&(r=i._injectSsrcGroupForUnifiedSimulcast(r),i.trace("create"+o+"OnSuccess::postTransform(inject ssrc group)",k(r)))),f.b.isFirefox()||(i.hasAnyTracksOfType(l.b)||i.sdpConsistency.hasPrimarySsrcCached()||i.generateRecvonlySsrc(),r=new RTCSessionDescription({type:r.type,sdp:i.sdpConsistency.makeVideoPrimarySsrcsConsistent(r.sdp)}),i.trace("create"+o+"OnSuccess::postTransform (make primary audio/video ssrcs consistent)",k(r))),i.isSimulcastOn()&&(r=i.simulcast.mungeLocalDescription(r),i.trace("create"+o+"OnSuccess::postTransform (simulcast)",k(r))),!i.options.disableRtx&&f.b.supportsRtx()&&(r=new RTCSessionDescription({type:r.type,sdp:i.rtxModifier.modifyRtxSsrcs(r.sdp)}),i.trace("create"+o+"OnSuccess::postTransform (rtx modifier)",k(r))),!e){var a=new y.a(i.remoteDescription.sdp),c=new y.a(r.sdp);d=a,p=c,f.b.isChrome()&&d&&p&&d.media&&p.media&&d.media.length===p.media.length&&(p.media.forEach(function(e,t){_.a.findLine(d.media[t],"a=setup:actpass",d.session)&&(p.media[t]=e.replace(/a=setup:active/g,"a=setup:passive"))}),p.raw=p.session+p.media.join("")),r=new RTCSessionDescription({type:r.type,sdp:c.raw})}var u=(function(e){var t=new Map,n=new Map;if("object"!==(void 0===e?"undefined":C(e))||null===e||"string"!=typeof e.sdp)return E.warn("An empty description was passed as an argument."),t;var r=s.a.parse(e.sdp);if(!Array.isArray(r.media))return t;var i=!0,o=!1,a=void 0;try{for(var c,u=r.media["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(i=(c=u.next()).done);i=!0){var l=c.value;if(Array.isArray(l.ssrcs)){if(Array.isArray(l.ssrcGroups)){var d=!0,p=!1,h=void 0;try{for(var f,m=l.ssrcGroups["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(d=(f=m.next()).done);d=!0){var v=f.value;if(void 0!==v.semantics&&void 0!==v.ssrcs){var g=v.ssrcs.split(" ").map(function(e){return parseInt(e,10)}),y=g[0];v.ssrcs=g,n.has(y)||n.set(y,[]),n.get(y).push(v)}}}catch(e){p=!0,h=e}finally{try{!d&&m.return&&m.return()}finally{if(p)throw h}}}var b=!0,S=!1,_=void 0;try{for(var T,w=l.ssrcs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(b=(T=w.next()).done);b=!0){var R=T.value;if("msid"===R.attribute){var k=R.value,I=t.get(k);I||(I={ssrcs:[],groups:[],msid:k},t.set(k,I));var P=R.id;if(I.ssrcs.push(P),n.has(P)){var A=n.get(P),O=!0,D=!1,x=void 0;try{for(var N,L=A["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(O=(N=L.next()).done);O=!0){var M=N.value;I.groups.push(M)}}catch(e){D=!0,x=e}finally{try{!O&&L.return&&L.return()}finally{if(D)throw x}}}}}}catch(e){S=!0,_=e}finally{try{!b&&w.return&&w.return()}finally{if(S)throw _}}}}}catch(e){o=!0,a=e}finally{try{!i&&u.return&&u.return()}finally{if(o)throw a}}return t})(r);E.debug("Got local SSRCs MAP: ",u),i._processLocalSSRCsMap(u),t(r)}catch(e){i.trace("create"+o+"OnError",e),i.trace("create"+o+"OnError",k(r)),E.error("create"+o+"OnError",e,k(r)),n(e)}var d,p},c=function(t){i.trace("create"+o+"OnFailure",t);var r=e?v.a.CREATE_OFFER_FAILED:v.a.CREATE_ANSWER_FAILED;i.eventEmitter.emit(r,t,i),n(t)};e?this.peerconnection.createOffer(a,c,r):this.peerconnection.createAnswer(a,c,r)},r.prototype._processLocalSSRCsMap=function(e){var t=!0,n=!1,r=void 0;try{for(var o,a=this.localTracks.values()["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(t=(o=a.next()).done);t=!0){var s=o.value,c=s.getMSID();if(e.has(c)){var u=e.get(c);if(!u)return void E.error("No SSRC found for: "+c+" in "+this);var l=this.localSSRCs.get(s.rtcId),d=i(u),p=i(l);d!==p?(null===p?E.info("Storing new local SSRC for "+s+" in "+this,u):E.error("Overwriting SSRC for "+s+" "+c+" in "+this+" with: ",u),this.localSSRCs.set(s.rtcId,u),this.eventEmitter.emit(v.a.LOCAL_TRACK_SSRC_UPDATED,s,d)):E.debug("The local SSRC("+d+") for "+s+" "+c+"is still up to date in "+this)}else E.warn("No local track matched with: "+c+" in "+this)}}catch(e){n=!0,r=e}finally{try{!t&&a.return&&a.return()}finally{if(n)throw r}}},r.prototype.addIceCandidate=function(e,t,n){this.trace("addIceCandidate",JSON.stringify({candidate:e.candidate,sdpMid:e.sdpMid,sdpMLineIndex:e.sdpMLineIndex,usernameFragment:e.usernameFragment},null," ")),this.peerconnection.addIceCandidate(e,t,n)},r.prototype.getStats=function(e,t){f.b.isFirefox()||f.b.isTemasysPluginUsed()||f.b.isReactNative()?this.peerconnection.getStats(null,e,t||function(){}):f.b.isSafariWithWebrtc()||this.peerconnection.getStats(e)},r.prototype.generateNewStreamSSRCInfo=function(e){var t=e.rtcId,n=this._getSSRC(t);if(n&&E.error("Will overwrite local SSRCs for track ID: "+t),this.isSimulcastOn()){n={ssrcs:[],groups:[]};for(var r=0;r should fake sdp ? : "+m),m){var v=this.tpc.isSimulcastOn()?this.tpc.simulcast.ssrcCache:[this.tpc.sdpConsistency.cachedPrimarySsrc];if(v.length){r=!0,n.direction="sendrecv";var g="injected-"+v[0],y=!0,b=!1,S=void 0;try{for(var _,T=v["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(y=(_=T.next()).done);y=!0){var C=_.value;n.removeSSRC(C),s.debug(this.tpc+" injecting video SSRC: "+C+" for "+d),n.addSSRCAttribute({id:C,attribute:"cname",value:g}),n.addSSRCAttribute({id:C,attribute:"msid",value:d.storedMSID})}}catch(e){b=!0,S=e}finally{try{!y&&T.return&&T.return()}finally{if(b)throw S}}if(v.length>1){var E={ssrcs:v.join(" "),semantics:"SIM"};n.findGroup(E.semantics,E.ssrcs)||(s.debug(this.tpc+" injecting SIM group for "+d,E),n.addSSRCGroup(E))}this.tpc.options.disableRtx||this.tpc.rtxModifier.modifyRtxSsrcs2(n)}else s.error("No SSRCs stored for: "+d+" in "+this.tpc)}}}catch(e){a=!0,c=e}finally{try{!o&&l.return&&l.return()}finally{if(a)throw c}}return r}},{key:"_transformMediaIdentifiers",value:function(e){var t=this.tpc.id,n=!0,r=!1,i=void 0;try{for(var o,a=e.ssrcs["function"==typeof Symbol?Symbol.iterator:"@@iterator"]();!(n=(o=a.next()).done);n=!0){var c=o.value;switch(c.attribute){case"cname":case"label":case"mslabel":c.value=c.value&&c.value+"-"+t;break;case"msid":if(c.value){var u=c.value.split(" ");if(2===u.length){var l=u[0],d=u[1];c.value=l+"-"+t+" "+d+"-"+t}else s.warn("Unable to munge local MSID- weird format detected: "+c.value)}}}}catch(e){r=!0,i=e}finally{try{!n&&a.return&&a.return()}finally{if(r)throw i}}}},{key:"maybeAddMutedLocalVideoTracksToSDP",value:function(e){if(!e)throw new Error("No local description passed in.");var t=new o.a(e.sdp);return this._addMutedLocalVideoTracksToSDP(t)?new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()}):e}},{key:"transformStreamIdentifiers",value:function(e){if(!e||!e.sdp||!e.type)return e;var t=new o.a(e.sdp),n=t.selectMedia("audio");n&&this._transformMediaIdentifiers(n);var r=t.selectMedia("video");return r&&this._transformMediaIdentifiers(r),new RTCSessionDescription({type:e.type,sdp:t.toRawSDP()})}}]),e})();t.a=c}).call(t,"modules/RTC/LocalSdpMunger.js")},function(e,t,n){"use strict";(function(e){function r(e,t,n){c.debug("Updating mline to associate "+n+"rtx ssrc with primary stream, "+t.id);var r=t.id,i=t.msid,o=t.cname,a=e.getRtxSSRC(r);a!==n?(a&&(c.debug(r+" was previously associated with rtx"+a+", removing all references to it"),e.removeSSRC(a),c.debug("groups before filtering for "+a),c.debug(e.dumpSSRCGroups()),e.removeGroupsWithSSRC(a)),e.addSSRCAttribute({id:n,attribute:"cname",value:o}),e.addSSRCAttribute({id:n,attribute:"msid",value:i}),e.addSSRCGroup({semantics:"FID",ssrcs:r+" "+n})):c.debug(n+" was already associated with "+r)}var i=n(0),o=(n.n(i),n(29)),a=n(14),s=(function(){function e(e,t){for(var n=0;n3||!n.media.every(function(e){return-1!==["video","audio","data"].indexOf(e.mid)}))return console.warn("This description does not look like Plan B."),e;var r=[];n.media.forEach(function(e){r.push(e.mid)});var s=!1;if(void 0!==n.groups&&Array.isArray(n.groups)&&(s=n.groups.every(function(e){return"BUNDLE"!==e.type||a.apply(e.mids.sort(),[r.sort()])})),!s)throw new Error("Cannot convert to Unified Plan because m-lines that are not bundled were found.");var c;void 0!==this.cache[e.type]&&(c=o.parse(this.cache[e.type]));var u={audio:{},video:{}},l={},d=0,p=0;if(n.media.forEach(function(n){if(("string"!=typeof n.rtcpMux||"rtcp-mux"!==n.rtcpMux)&&"inactive"!==n.direction)throw new Error("Cannot convert to Unified Plan because m-lines without the rtcp-mux attribute were found.");if("application"!==n.type){var r=n.sources,o=n.ssrcGroups,a=n.candidates,s=n.iceUfrag,h=n.icePwd,f=n.fingerprint,m=n.port;delete n.sources,delete n.ssrcGroups,delete n.candidates,delete n.iceUfrag,delete n.icePwd,delete n.fingerprint,delete n.port,delete n.mid;var v={};void 0!==o&&Array.isArray(o)&&o.forEach(function(e){"SIM"!==e.semantics&&void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&e.ssrcs.forEach(function(t){void 0===v[t]&&(v[t]=[]),v[t].push(e)})});var g={};"object"===(void 0===r?"undefined":i(r))&&Object.keys(r).forEach(function(o){var y;if("offer"!==e.type||r[o].msid)if(void 0!==v[o]&&Array.isArray(v[o])&&v[o].some(function(e){return e.ssrcs.some(function(e){if("object"===i(g[e]))return y=g[e],!0})}),"object"===(void 0===y?"undefined":i(y)))y.sources[o]=r[o],delete r[o].msid;else{if(y=Object.create(n),g[o]=y,void 0!==r[o].msid&&(y.msid=r[o].msid,delete r[o].msid),y.sources={},y.sources[o]=r[o],y.ssrcGroups=v[o],void 0!==c&&void 0!==c.media&&Array.isArray(c.media)&&c.media.forEach(function(e){"object"===i(e.sources)&&Object.keys(e.sources).forEach(function(t){t===o&&(y.mid=e.mid)})}),void 0===y.mid){if("answer"===e.type)throw new Error("An unmapped SSRC was found.");y.mid=[n.type,"-",o].join("")}y.candidates=a,y.iceUfrag=s,y.icePwd=h,y.fingerprint=f,y.port=m,l[y.mid]=y,t.cache.mlU2BMap[p]=d,void 0===t.cache.mlB2UMap[d]&&(t.cache.mlB2UMap[d]=p),p++}else u[n.type][o]=r[o]}),d++}else l[n.mid]=n}),n.media=[],r=[],"answer"===e.type)for(var h=0;h0&&null===(r=t.getFirstSendingIndexFromAnswer(e)))for(var i=0;ir){var o=n.media[r];Object.keys(u[e]).forEach(function(t){o.sources&&o.sources[t]&&console.warn("Replacing an existing SSRC."),o.sources||(o.sources={}),o.sources[t]=u[e][t]})}}}),n.groups.some(function(e){if("BUNDLE"===e.type)return e.mids=r.join(" "),!0}),n.msidSemantic={semantic:"WMS",token:"*"};var m=o.write(n);return this.cache[e.type]=m,new RTCSessionDescription({type:e.type,sdp:m})}},function(e,t,n){var r=n(15);t.write=function(e,t){return void 0!==e&&void 0!==e.media&&Array.isArray(e.media)&&e.media.forEach(function(e){void 0!==e.sources&&0!==Object.keys(e.sources).length&&(e.ssrcs=[],Object.keys(e.sources).forEach(function(t){var n=e.sources[t];Object.keys(n).forEach(function(r){e.ssrcs.push({id:t,attribute:r,value:n[r]})})}),delete e.sources),void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&(e.ssrcs=e.ssrcs.join(" "))})}),void 0!==e&&void 0!==e.groups&&Array.isArray(e.groups)&&e.groups.forEach(function(e){void 0!==e.mids&&Array.isArray(e.mids)&&(e.mids=e.mids.join(" "))}),r.write(e,t)},t.parse=function(e){var t=r.parse(e);return void 0!==t&&void 0!==t.media&&Array.isArray(t.media)&&t.media.forEach(function(e){void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&(e.sources={},e.ssrcs.forEach(function(t){e.sources[t.id]||(e.sources[t.id]={}),e.sources[t.id][t.attribute]=t.value}),delete e.ssrcs),void 0!==e.ssrcGroups&&Array.isArray(e.ssrcGroups)&&e.ssrcGroups.forEach(function(e){"string"==typeof e.ssrcs&&(e.ssrcs=e.ssrcs.split(" "))})}),void 0!==t&&void 0!==t.groups&&Array.isArray(t.groups)&&t.groups.forEach(function(e){"string"==typeof e.mids&&(e.mids=e.mids.split(" "))}),t}},function(e,t){e.exports=function e(t){if(!t)return!1;if(this.length!=t.length)return!1;for(var n=0,r=this.length;n2)return e;if(2==n&&0===r)return e;if(1===n)t=e.ssrcs[0].id;else{var i=e.ssrcGroups.filter(function(e){return"FID"===e.semantics})[0];if(!i)return e;t=parseInt(i.ssrcs.split(" ")[0])}return console.log("SdpSimulcast: current ssrc cache: ",this.ssrcCache),console.log("SdpSimulcast: parsed primary ssrc "+t),-1!==this.ssrcCache.indexOf(t)?(console.log("SdpSimulcast: Have seen primary ssrc before, filling in data from cache"),e=this._fillInSourceDataFromCache(e)):(console.log("SdpSimulcast: Have not seen primary ssrc before, generating source data"),e=this._generateSourceData(e,t)),this.ssrcCache=this._parseSimLayers(e),e},i.prototype.mungeRemoteDescription=function(e){if(!a(e))return e;var t=s.parse(e.sdp),n=this;return o(t,function(e){n.options.explodeRemoteSimulcast?(function(e){if(e&&Array.isArray(e.ssrcGroups))for(var t=u(e),n=[],r=e.ssrcGroups.length;r--;)if("SIM"===e.ssrcGroups[r].semantics){for(var i=e.ssrcGroups[r].ssrcs.split(" "),o=0;o=0)){var i=e[r];Object.keys(i).forEach(function(e){n.push({id:r,attribute:e,value:i[e]})})}})}return n},t.parseSsrcs=function(e){var t={};return void 0!==e.ssrcs&&Array.isArray(e.ssrcs)&&e.ssrcs.forEach(function(e){t[e.id]||(t[e.id]={}),t[e.id][e.attribute]=e.value}),t}},function(e,t,n){"use strict";var r=n(7),i=(function(){function e(e,t){for(var n=0;n.6&&(this._eventFired=!0,this._callback())}},{key:"_isLocalAudioTrack",value:function(e){return e.isAudioTrack()&&e.isLocal()}},{key:"_trackAdded",value:function(e){this._isLocalAudioTrack(e)&&(this.audioTrack=e)}},{key:"_trackMuteChanged",value:function(e){this._isLocalAudioTrack(e)&&e.isMuted()&&(this._eventFired=!1)}}]),e})();t.a=o},function(e,t,n){function r(){this.audioRecorder=new o,this.transcriptionService=new a,this.counter=null,this.startTime=null,this.transcription=null,this.callback=null,this.results=[],this.state=s,this.lineLength=0}function i(e){for(var t=0;t0}var o=n(59),a=n(163),s="before";r.prototype.start=function(){if(this.state!==s)throw new Error("The transcription can only start when it's in the \""+s+'" state. It\'s currently in the "'+this.state+'" state');this.state="recording",this.audioRecorder.start(),this.startTime=new Date},r.prototype.stop=function(e){var t=this;if("recording"!==this.state)throw new Error('The transcription can only stop when it\'s in the "recording" state. It\'s currently in the "'+this.state+'" state');console.log("stopping recording and sending audio files"),this.audioRecorder.stop();var n=function(e,t){if(console.log("retrieved an answer from the transcription service. The answer has an array of length: "+t.wordArray.length),t.wordArray.length>0){var n=t.startTime.getUTCMilliseconds()-e.startTime.getUTCMilliseconds();n<0&&(n=0);var r="[";t.wordArray.forEach(function(e){e.begin+=n,e.end+=n,r+=e.word+","}),r+="]",console.log(r),t.wordArray.name=t.name}e.results.push(t.wordArray),e.counter--,console.log("current counter: "+e.counter),e.maybeMerge()}.bind(null,this);this.audioRecorder.getRecordingResults().forEach(function(e){t.transcriptionService.send(e,n),t.counter++}),this.state="transcribing",this.callback=e},r.prototype.maybeMerge=function(){"transcribing"===this.state&&0===this.counter&&this.merge()},r.prototype.merge=function(){var e=this;console.log("starting merge process!\n The length of the array: "+this.results.length),this.transcription="";var t=this.results,n=[];for(i(t),t.forEach(function(e){return(function(e,t){if(0===e.length)e.push(t);else{if(e[e.length-1].begin<=t.begin)return void e.push(t);for(var n=0;n0&&"break"!==(function(){var i=!1,o=n[0].begin;if(t.forEach(function(e){e[0].begin80&&(this.transcription+="\n ",this.lineLength=4),this.transcription+=" "+e.word,this.lineLength+=e.word.length+1},r.prototype.addTrack=function(e){this.audioRecorder.addTrack(e)},r.prototype.removeTrack=function(e){this.audioRecorder.removeTrack(e)},r.prototype.getTranscription=function(){if("finished"!==this.state)throw new Error('The transcription can only be retrieved when it\'s in the "finished" state. It\'s currently in the "'+this.state+'" state');return this.transcription},r.prototype.getState=function(){return this.state},r.prototype.reset=function(){this.state=s,this.counter=null,this.transcription=null,this.startTime=null,this.callback=null,this.results=[],this.lineLength=0},e.exports=r},function(e,t){e.exports=function(e,t,n,r){this.blob=e,this.name=t,this.startTime=n,this.wordArray=r}},function(e,t,n){var r=n(164),i=n(165),o=n(59),a=function(){this.url=(function(){var e="config does not contain an url to a Sphinx4 https server";if(void 0===config.sphinxURL)console.log(e);else{var t=config.sphinxURL;if(void 0!==t.includes&&t.includes("https://"))return t;console.log(e)}})()};a.prototype=Object.create(r.prototype),a.constructor=a,a.prototype.sendRequest=function(e,t){console.log("sending an audio file to "+this.url),console.log("the audio file being sent: "+e);var n=new XMLHttpRequest;n.onreadystatechange=function(){if(n.readyState===XMLHttpRequest.DONE&&200===n.status)t(n.responseText);else if(n.readyState===XMLHttpRequest.DONE)throw new Error("unable to accept response from sphinx server. status: "+n.status)},n.open("POST",this.url),n.setRequestHeader("Content-Type",o.determineCorrectFileType()),n.send(e),console.log("send "+e)},a.prototype.formatResponse=function(e){var t=JSON.parse(e).objects;t.shift();var n=[];return t.forEach(function(e){return e.filler||n.push(new i(e.word,e.start,e.end))}),n},a.prototype.verify=function(e){if(console.log("response from server:"+e.toString()),"string"!=typeof e)return!1;var t=void 0;try{t=JSON.parse(e)}catch(e){return console.log(e),!1}if(void 0===t.objects)return!1;var n=t.objects;return!(!n[0]||!n[0]["session-id"])},e.exports=a},function(e,t){var n=function(){throw new Error("TranscriptionService is abstract and cannot becreated")};n.prototype.send=function(e,t){var n=this;this.sendRequest(e.blob,function(r){n.verify(r)?e.wordArray=n.formatResponse(r):(console.log("the retrieved response from the server is not valid!"),e.wordArray=[]),t(e)})},n.prototype.sendRequest=function(e,t){throw new Error("TranscriptionService.sendRequest is abstract")},n.prototype.formatResponse=function(e){throw new Error("TranscriptionService.format is abstract")},n.prototype.verify=function(e){throw new Error("TranscriptionService.verify is abstract")},e.exports=n},function(e,t){var n=function(e,t,n){this.word=e,this.begin=t,this.end=n};n.prototype.getWord=function(){return this.word},n.prototype.getBeginTime=function(){return this.begin},n.prototype.getEndTime=function(){return this.end},e.exports=n},function(e,t,n){"use strict";(function(e){var r=n(0),i=(n.n(r),n(167)),o=n(30),a=n(8),s=n.n(a),c=(function(){function e(e,t){for(var n=0;n0&&r(e,c.a.getCurrentlyAvailableMediaDevices()),c.a.setAudioOutputDevice(e)},addEventListener:function(e,t){h.addListener(e,t)},removeEventListener:function(e,t){h.removeListener(e,t)},emitEvent:function(e){for(var t=arguments.length,n=Array(t>1?t-1:0),r=1;rparam[value="video"]');return n.length?(n.length>1&&i.warn("Container with more than one video elements: ",e),n.parent()[0]):void 0},isResizeEventSupported:function(){return!r.b.isTemasysPluginUsed()},setVolume:function(e,t){r.b.isIExplorer()||(e.volume=t)},setAutoPlay:function(e,t){r.b.isIExplorer()||(e.autoplay=t)}};t.a=o}).call(t,"modules/RTC/RTCUIHelper.js")}])},"object"==typeof r&&"object"==typeof n?n.exports=a():"function"==typeof define&&define.amd?define([],a):"object"==typeof r?r.JitsiMeetJS=a():o.JitsiMeetJS=a();var o,a},443); -__d(function(e,t,n,r){i="undefined"!=typeof window?window:this,o=function(e,t){var n=[],r=n.slice,i=n.concat,o=n.push,s=n.indexOf,a={},u=a.toString,l=a.hasOwnProperty,c={},f=e.document,p=function e(t,n){return new e.fn.init(t,n)},d=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,h=/^-ms-/,g=/-([\da-z])/gi,m=function(e,t){return t.toUpperCase()};p.fn=p.prototype={jquery:"2.1.4",constructor:p,selector:"",length:0,toArray:function(){return r.call(this)},get:function(e){return null!=e?e<0?this[e+this.length]:this[e]:r.call(this)},pushStack:function(e){var t=p.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return p.each(this,e,t)},map:function(e){return this.pushStack(p.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(r.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n=0},isPlainObject:function(e){return"object"===p.type(e)&&!e.nodeType&&!p.isWindow(e)&&!(e.constructor&&!l.call(e.constructor.prototype,"isPrototypeOf"))},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?a[u.call(e)]||"object":typeof e},globalEval:function(e){var t,n=eval;(e=p.trim(e))&&(1===e.indexOf("use strict")?((t=f.createElement("script")).text=e,f.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(h,"ms-").replace(g,m)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r=0,i=e.length,o=v(e);if(n){if(o)for(;r0&&t-1 in e))}var y=(function(e){var t,n,r,i,o,s,a,u,l,c,f,p,d,h,g,m,v,y,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,N=se(),k=se(),E=se(),S=function(e,t){return e===t&&(f=!0),0},D=-2147483648,j={}.hasOwnProperty,A=[],L=A.pop,q=A.push,H=A.push,O=A.slice,F=function(e,t){for(var n=0,r=e.length;n+~]|"+R+")"+R+"*"),U=new RegExp("="+R+"*([^\\]'\"]*?)"+R+"*\\]","g"),V=new RegExp(I),Y=new RegExp("^"+W+"$"),G={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+$),PSEUDO:new RegExp("^"+I),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+R+"*(even|odd|(([+-]|)(\\d*)n|)"+R+"*(?:([+-]|)"+R+"*(\\d+)|))"+R+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+R+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+R+"*((?:-\\d)?\\d*)"+R+"*\\)|)(?=[^-]|$)","i")},Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=/'|\\/g,ne=new RegExp("\\\\([\\da-f]{1,6}"+R+"?|("+R+")|.)","ig"),re=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},ie=function(){p()};try{H.apply(A=O.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){H={apply:A.length?function(e,t){q.apply(e,O.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}function oe(e,t,r,i){var o,a,l,c,f,h,v,y,T,C;if((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,r=r||[],c=t.nodeType,"string"!=typeof e||!e||1!==c&&9!==c&&11!==c)return r;if(!i&&g){if(11!==c&&(o=Z.exec(e)))if(l=o[1]){if(9===c){if(!(a=t.getElementById(l))||!a.parentNode)return r;if(a.id===l)return r.push(a),r}else if(t.ownerDocument&&(a=t.ownerDocument.getElementById(l))&&x(t,a)&&a.id===l)return r.push(a),r}else{if(o[2])return H.apply(r,t.getElementsByTagName(e)),r;if((l=o[3])&&n.getElementsByClassName)return H.apply(r,t.getElementsByClassName(l)),r}if(n.qsa&&(!m||!m.test(e))){if(y=v=b,T=t,C=1!==c&&e,1===c&&"object"!==t.nodeName.toLowerCase()){for(h=s(e),(v=t.getAttribute("id"))?y=v.replace(te,"\\$&"):t.setAttribute("id",y),y="[id='"+y+"'] ",f=h.length;f--;)h[f]=y+me(h[f]);T=ee.test(e)&&he(t.parentNode)||t,C=h.join(",")}if(C)try{return H.apply(r,T.querySelectorAll(C)),r}catch(e){}finally{v||t.removeAttribute("id")}}}return u(e.replace(_,"$1"),t,r,i)}function se(){var e=[];return function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}}function ae(e){return e[b]=!0,e}function ue(e){var t=d.createElement("div");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){for(var n=e.split("|"),i=e.length;i--;)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return ae(function(t){return t=+t,ae(function(n,r){for(var i,o=e([],n.length,t),s=o.length;s--;)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}function he(e){return e&&void 0!==e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,s=e?e.ownerDocument||e:w;return s!==d&&9===s.nodeType&&s.documentElement?(d=s,h=s.documentElement,(i=s.defaultView)&&i!==i.top&&(i.addEventListener?i.addEventListener("unload",ie,!1):i.attachEvent&&i.attachEvent("onunload",ie)),g=!o(s),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(s.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=K.test(s.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!s.getElementsByName||!s.getElementsByName(b).length}),n.getById?(r.find.ID=function(e,t){if(void 0!==t.getElementById&&g){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},r.filter.ID=function(e){var t=e.replace(ne,re);return function(e){return e.getAttribute("id")===t}}):(delete r.find.ID,r.filter.ID=function(e){var t=e.replace(ne,re);return function(e){var n=void 0!==e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}}),r.find.TAG=n.getElementsByTagName?function(e,t){return void 0!==t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){for(;n=o[i++];)1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if(g)return t.getElementsByClassName(e)},v=[],m=[],(n.qsa=K.test(s.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&m.push("[*^$]="+R+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||m.push("\\["+R+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||m.push("~="),e.querySelectorAll(":checked").length||m.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||m.push(".#.+[+~]")}),ue(function(e){var t=s.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&m.push("name"+R+"*[*^$|!~]?="),e.querySelectorAll(":enabled").length||m.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),m.push(",.*:")})),(n.matchesSelector=K.test(y=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=y.call(e,"div"),y.call(e,"[s!='']:x"),v.push("!=",I)}),m=m.length&&new RegExp(m.join("|")),v=v.length&&new RegExp(v.join("|")),t=K.test(h.compareDocumentPosition),x=t||K.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},S=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===s||e.ownerDocument===w&&x(w,e)?-1:t===s||t.ownerDocument===w&&x(w,t)?1:c?F(c,e)-F(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],u=[t];if(!i||!o)return e===s?-1:t===s?1:i?-1:o?1:c?F(c,e)-F(c,t):0;if(i===o)return ce(e,t);for(n=e;n=n.parentNode;)a.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;a[r]===u[r];)r++;return r?ce(a[r],u[r]):a[r]===w?-1:u[r]===w?1:0},s):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(U,"='$1']"),n.matchesSelector&&g&&(!v||!v.test(t))&&(!m||!m.test(t)))try{var r=y.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&j.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(S),f){for(;t=e[o++];)t===e[o]&&(i=r.push(o));for(;i--;)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:ae,match:G,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(ne,re),e[3]=(e[3]||e[4]||e[5]||"").replace(ne,re),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&V.test(n)&&(t=s(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(ne,re).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=N[e+" "];return t||(t=new RegExp("(^|"+R+")"+e+"("+R+"|$)"))&&N(e,function(e){return t.test("string"==typeof e.className&&e.className||void 0!==e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace(B," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,v=a&&t.nodeName.toLowerCase(),y=!u&&!a;if(m){if(o){for(;g;){for(f=t;f=f[g];)if(a?f.nodeName.toLowerCase()===v:1===f.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[s?m.firstChild:m.lastChild],s&&y){for(d=(l=(c=m[b]||(m[b]={}))[e]||[])[0]===T&&l[1],p=l[0]===T&&l[2],f=d&&m.childNodes[d];f=++d&&f&&f[g]||(p=d=0)||h.pop();)if(1===f.nodeType&&++p&&f===t){c[e]=[T,d,p];break}}else if(y&&(l=(t[b]||(t[b]={}))[e])&&l[0]===T)p=l[1];else for(;(f=++d&&f&&f[g]||(p=d=0)||h.pop())&&((a?f.nodeName.toLowerCase()!==v:1!==f.nodeType)||!++p||(y&&((f[b]||(f[b]={}))[e]=[T,p]),f!==t)););return(p-=i)===r||p%r==0&&p/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?ae(function(e,n){for(var r,o=i(e,t),s=o.length;s--;)e[r=F(e,o[s])]=!(n[r]=o[s])}):function(e){return i(e,0,n)}):i}},pseudos:{not:ae(function(e){var t=[],n=[],r=a(e.replace(_,"$1"));return r[b]?ae(function(e,t,n,i){for(var o,s=r(e,null,i,[]),a=e.length;a--;)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:ae(function(e){return function(t){return oe(e,t).length>0}}),contains:ae(function(e){return e=e.replace(ne,re),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:ae(function(e){return Y.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(ne,re).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return!1===e.disabled},disabled:function(e){return!0===e.disabled},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return J.test(e.nodeName)},input:function(e){return Q.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:de(function(){return[0]}),last:de(function(e,t){return[t-1]}),eq:de(function(e,t,n){return[n<0?n+t:n]}),even:de(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:de(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){for(var i=e.length;i--;)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xe(e,t,n,r,i){for(var o,s=[],a=0,u=e.length,l=null!=t;a-1&&(o[l]=!(s[l]=f))}}else v=xe(v===s?v.splice(h,v.length):v),i?i(null,s,v,u):H.apply(s,v)})}function we(e){for(var t,n,i,o=e.length,s=r.relative[e[0].type],a=s||r.relative[" "],u=s?1:0,c=ve(function(e){return e===t},a,!0),f=ve(function(e){return F(t,e)>-1},a,!0),p=[function(e,n,r){var i=!s&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&ye(p),u>1&&me(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(_,"$1"),n,u0,i=e.length>0,o=function(o,s,a,u,c){var f,p,h,g=0,m="0",v=o&&[],y=[],x=l,b=o||i&&r.find.TAG("*",c),w=T+=null==x?1:Math.random()||.1,C=b.length;for(c&&(l=s!==d&&s);m!==C&&null!=(f=b[m]);m++){if(i&&f){for(p=0;h=e[p++];)if(h(f,s,a)){u.push(f);break}c&&(T=w)}n&&((f=!h&&f)&&g--,o&&v.push(f))}if(g+=m,n&&m!==g){for(p=0;h=t[p++];)h(v,y,s,a);if(o){if(g>0)for(;m--;)v[m]||y[m]||(y[m]=L.call(u));y=xe(y)}H.apply(u,y),c&&!o&&y.length>0&&g+t.length>1&&oe.uniqueSort(u)}return c&&(T=w,l=x),v};return n?ae(o):o})(o,i))).selector=e}return a},u=oe.select=function(e,t,i,o){var u,l,c,f,p,d="function"==typeof e&&e,h=!o&&s(e=d.selector||e);if(i=i||[],1===h.length){if((l=h[0]=h[0].slice(0)).length>2&&"ID"===(c=l[0]).type&&n.getById&&9===t.nodeType&&g&&r.relative[l[1].type]){if(!(t=(r.find.ID(c.matches[0].replace(ne,re),t)||[])[0]))return i;d&&(t=t.parentNode),e=e.slice(l.shift().value.length)}for(u=G.needsContext.test(e)?0:l.length;u--&&(c=l[u],!r.relative[f=c.type]);)if((p=r.find[f])&&(o=p(c.matches[0].replace(ne,re),ee.test(l[0].type)&&he(t.parentNode)||t))){if(l.splice(u,1),!(e=o.length&&me(l)))return H.apply(i,o),i;break}}return(d||a(e,h))(o,t,!g,i,ee.test(e)&&he(t.parentNode)||t),i},n.sortStable=b.split("").sort(S).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("div"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe})(e);p.find=y,p.expr=y.selectors,p.expr[":"]=p.expr.pseudos,p.unique=y.uniqueSort,p.text=y.getText,p.isXMLDoc=y.isXML,p.contains=y.contains;var x=p.expr.match.needsContext,b=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^.[^:#\[\.,]*$/;function T(e,t,n){if(p.isFunction(t))return p.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return p.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(w.test(t))return p.filter(t,e,n);t=p.filter(t,e)}return p.grep(e,function(e){return s.call(t,e)>=0!==n})}p.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?p.find.matchesSelector(r,e)?[r]:[]:p.find.matches(e,p.grep(t,function(e){return 1===e.nodeType}))},p.fn.extend({find:function(e){var t,n=this.length,r=[],i=this;if("string"!=typeof e)return this.pushStack(p(e).filter(function(){for(t=0;t1?p.unique(r):r)).selector=this.selector?this.selector+" "+e:e,r},filter:function(e){return this.pushStack(T(this,e||[],!1))},not:function(e){return this.pushStack(T(this,e||[],!0))},is:function(e){return!!T(this,"string"==typeof e&&x.test(e)?p(e):e||[],!1).length}});var C,N=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/;(p.fn.init=function(e,t){var n,r;if(!e)return this;if("string"==typeof e){if(!(n="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:N.exec(e))||!n[1]&&t)return!t||t.jquery?(t||C).find(e):this.constructor(t).find(e);if(n[1]){if(t=t instanceof p?t[0]:t,p.merge(this,p.parseHTML(n[1],t&&t.nodeType?t.ownerDocument||t:f,!0)),b.test(n[1])&&p.isPlainObject(t))for(n in t)p.isFunction(this[n])?this[n](t[n]):this.attr(n,t[n]);return this}return(r=f.getElementById(n[2]))&&r.parentNode&&(this.length=1,this[0]=r),this.context=f,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):p.isFunction(e)?void 0!==C.ready?C.ready(e):e(p):(void 0!==e.selector&&(this.selector=e.selector,this.context=e.context),p.makeArray(e,this))}).prototype=p.fn,C=p(f);var k=/^(?:parents|prev(?:Until|All))/,E={children:!0,contents:!0,next:!0,prev:!0};p.extend({dir:function(e,t,n){for(var r=[],i=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(i&&p(e).is(n))break;r.push(e)}return r},sibling:function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}}),p.fn.extend({has:function(e){var t=p(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&p.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?p.unique(o):o)},index:function(e){return e?"string"==typeof e?s.call(p(e),this[0]):s.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(p.unique(p.merge(this.get(),p(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function S(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}p.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return p.dir(e,"parentNode")},parentsUntil:function(e,t,n){return p.dir(e,"parentNode",n)},next:function(e){return S(e,"nextSibling")},prev:function(e){return S(e,"previousSibling")},nextAll:function(e){return p.dir(e,"nextSibling")},prevAll:function(e){return p.dir(e,"previousSibling")},nextUntil:function(e,t,n){return p.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return p.dir(e,"previousSibling",n)},siblings:function(e){return p.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return p.sibling(e.firstChild)},contents:function(e){return e.contentDocument||p.merge([],e.childNodes)}},function(e,t){p.fn[e]=function(n,r){var i=p.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=p.filter(r,i)),this.length>1&&(E[e]||p.unique(i),k.test(e)&&i.reverse()),this.pushStack(i)}});var D=/\S+/g,j={};p.Callbacks=function(e){var t,n,r,i,o,s,a=[],u=!(e="string"==typeof e?j[e]||(function(e){var t=j[e]={};return p.each(e.match(D)||[],function(e,n){t[n]=!0}),t})(e):p.extend({},e)).once&&[],l=function l(f){for(t=e.memory&&f,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&s-1;)a.splice(n,1),r&&(n<=o&&o--,n<=s&&s--)}),this},has:function(e){return e?p.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=void 0,this},disabled:function(){return!a},lock:function(){return u=void 0,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!a||n&&!u||(t=[e,(t=t||[]).slice?t.slice():t],r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},p.extend({Deferred:function(e){var t=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return p.Deferred(function(n){p.each(t,function(t,o){var s=p.isFunction(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&p.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[o[0]+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?p.extend(e,r):r}},i={};return r.pipe=r.then,p.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t,n,i,o=0,s=r.call(arguments),a=s.length,u=1!==a||e&&p.isFunction(e.promise)?a:0,l=1===u?e:p.Deferred(),c=function(e,n,i){return function(o){n[e]=this,i[e]=arguments.length>1?r.call(arguments):o,i===t?l.notifyWith(n,i):--u||l.resolveWith(n,i)}};if(a>1)for(t=new Array(a),n=new Array(a),i=new Array(a);o0||(A.resolveWith(f,[p]),p.fn.triggerHandler&&(p(f).triggerHandler("ready"),p(f).off("ready"))))}});function L(){f.removeEventListener("DOMContentLoaded",L,!1),e.removeEventListener("load",L,!1),p.ready()}p.ready.promise=function(t){return A||(A=p.Deferred(),"complete"===f.readyState?setTimeout(p.ready):(f.addEventListener("DOMContentLoaded",L,!1),e.addEventListener("load",L,!1))),A.promise(t)},p.ready.promise();var q=p.access=function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===p.type(n)){i=!0;for(a in n)p.access(e,t,a,n[a],!0,o,s)}else if(void 0!==r&&(i=!0,p.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(p(e),n)})),t))for(;a1,null,!0)},removeData:function(e){return this.each(function(){F.remove(this,e)})}}),p.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=O.get(e,t),n&&(!r||p.isArray(n)?r=O.access(e,t,p.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=p.queue(e,t),r=n.length,i=n.shift(),o=p._queueHooks(e,t),s=function(){p.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return O.get(e,n)||O.access(e,n,{empty:p.Callbacks("once memory").add(function(){O.remove(e,[t+"queue",n])})})}}),p.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.lengthx",c.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue})();var _="undefined";c.focusinBubbles="onfocusin"in e;var z=/^key/,X=/^(?:mouse|pointer|contextmenu)|click/,U=/^(?:focusinfocus|focusoutblur)$/,V=/^([^.]*)(?:\.(.+)|)$/;function Y(){return!0}function G(){return!1}function Q(){try{return f.activeElement}catch(e){}}p.event={global:{},add:function(e,t,n,r,i){var o,s,a,u,l,c,f,d,h,g,m,v=O.get(e);if(v)for(n.handler&&(n=(o=n).handler,i=o.selector),n.guid||(n.guid=p.guid++),(u=v.events)||(u=v.events={}),(s=v.handle)||(s=v.handle=function(t){return typeof p!==_&&p.event.triggered!==t.type?p.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(D)||[""]).length;l--;)h=m=(a=V.exec(t[l])||[])[1],g=(a[2]||"").split(".").sort(),h&&(f=p.event.special[h]||{},h=(i?f.delegateType:f.bindType)||h,f=p.event.special[h]||{},c=p.extend({type:h,origType:m,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&p.expr.match.needsContext.test(i),namespace:g.join(".")},o),(d=u[h])||((d=u[h]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,g,s)||e.addEventListener&&e.addEventListener(h,s,!1)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),p.event.global[h]=!0)},remove:function(e,t,n,r,i){var o,s,a,u,l,c,f,d,h,g,m,v=O.hasData(e)&&O.get(e);if(v&&(u=v.events)){for(l=(t=(t||"").match(D)||[""]).length;l--;)if(h=m=(a=V.exec(t[l])||[])[1],g=(a[2]||"").split(".").sort(),h){for(f=p.event.special[h]||{},d=u[h=(r?f.delegateType:f.bindType)||h]||[],a=a[2]&&new RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=d.length;o--;)c=d[o],!i&&m!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));s&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,g,v.handle)||p.removeEvent(e,h,v.handle),delete u[h])}else for(h in u)p.event.remove(e,h+t[l],n,r,!0);p.isEmptyObject(u)&&(delete v.handle,O.remove(e,"events"))}},trigger:function(t,n,r,i){var o,s,a,u,c,d,h,g=[r||f],m=l.call(t,"type")?t.type:t,v=l.call(t,"namespace")?t.namespace.split("."):[];if(s=a=r=r||f,3!==r.nodeType&&8!==r.nodeType&&!U.test(m+p.event.triggered)&&(m.indexOf(".")>=0&&(m=(v=m.split(".")).shift(),v.sort()),c=m.indexOf(":")<0&&"on"+m,(t=t[p.expando]?t:new p.Event(m,"object"==typeof t&&t)).isTrigger=i?2:3,t.namespace=v.join("."),t.namespace_re=t.namespace?new RegExp("(^|\\.)"+v.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:p.makeArray(n,[t]),h=p.event.special[m]||{},i||!h.trigger||!1!==h.trigger.apply(r,n))){if(!i&&!h.noBubble&&!p.isWindow(r)){for(u=h.delegateType||m,U.test(u+m)||(s=s.parentNode);s;s=s.parentNode)g.push(s),a=s;a===(r.ownerDocument||f)&&g.push(a.defaultView||a.parentWindow||e)}for(o=0;(s=g[o++])&&!t.isPropagationStopped();)t.type=o>1?u:h.bindType||m,(d=(O.get(s,"events")||{})[t.type]&&O.get(s,"handle"))&&d.apply(s,n),(d=c&&s[c])&&d.apply&&p.acceptData(s)&&(t.result=d.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,i||t.isDefaultPrevented()||h._default&&!1!==h._default.apply(g.pop(),n)||!p.acceptData(r)||c&&p.isFunction(r[m])&&!p.isWindow(r)&&((a=r[c])&&(r[c]=null),p.event.triggered=m,r[m](),p.event.triggered=void 0,a&&(r[c]=a)),t.result}},dispatch:function(e){e=p.event.fix(e);var t,n,i,o,s,a=[],u=r.call(arguments),l=(O.get(this,"events")||{})[e.type]||[],c=p.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||!1!==c.preDispatch.call(this,e)){for(a=p.event.handlers.call(this,e,l),t=0;(o=a[t++])&&!e.isPropagationStopped();)for(e.currentTarget=o.elem,n=0;(s=o.handlers[n++])&&!e.isImmediatePropagationStopped();)e.namespace_re&&!e.namespace_re.test(s.namespace)||(e.handleObj=s,e.data=s.data,void 0!==(i=((p.event.special[s.origType]||{}).handle||s.handler).apply(o.elem,u))&&!1===(e.result=i)&&(e.preventDefault(),e.stopPropagation()));return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(!0!==u.disabled||"click"!==e.type){for(r=[],n=0;n=0:p.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return a]*)\/>/gi,K=/<([\w:]+)/,Z=/<|&#?\w+;/,ee=/<(?:script|style|link)/i,te=/checked\s*(?:[^=]|=\s*.checked.)/i,ne=/^$|\/(?:java|ecma)script/i,re=/^true\/(.*)/,ie=/^\s*\s*$/g,oe={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};oe.optgroup=oe.option,oe.tbody=oe.tfoot=oe.colgroup=oe.caption=oe.thead,oe.th=oe.td;function se(e,t){return p.nodeName(e,"table")&&p.nodeName(11!==t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ae(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ue(e){var t=re.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function le(e,t){for(var n=0,r=e.length;n0&&le(s,!u&&fe(e,"script")),a},buildFragment:function(e,t,n,r){for(var i,o,s,a,u,l,c=t.createDocumentFragment(),f=[],d=0,h=e.length;d")+a[2],l=a[0];l--;)o=o.lastChild;p.merge(f,o.childNodes),(o=c.firstChild).textContent=""}else f.push(t.createTextNode(i));for(c.textContent="",d=0;i=f[d++];)if((!r||-1===p.inArray(i,r))&&(u=p.contains(i.ownerDocument,i),o=fe(c.appendChild(i),"script"),u&&le(o),n))for(l=0;i=o[l++];)ne.test(i.type||"")&&n.push(i);return c},cleanData:function(e){for(var t,n,r,i,o=p.event.special,s=0;void 0!==(n=e[s]);s++){if(p.acceptData(n)&&(i=n[O.expando])&&(t=O.cache[i])){if(t.events)for(r in t.events)o[r]?p.event.remove(n,r):p.removeEvent(n,r,t.handle);O.cache[i]&&delete O.cache[i]}delete F.cache[n[F.expando]]}}}),p.fn.extend({text:function(e){return q(this,function(e){return void 0===e?p.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){se(this,e).appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=se(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){for(var n,r=e?p.filter(e,this):this,i=0;null!=(n=r[i]);i++)t||1!==n.nodeType||p.cleanData(fe(n)),n.parentNode&&(t&&p.contains(n.ownerDocument,n)&&le(fe(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(p.cleanData(fe(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return p.clone(this,e,t)})},html:function(e){return q(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!ee.test(e)&&!oe[(K.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(J,"<$1>");try{for(;n1&&"string"==typeof g&&!c.checkClone&&te.test(g))return this.each(function(n){var r=d.eq(n);m&&(e[0]=g.call(this,n,r.html())),r.domManip(e,t)});if(f&&(r=(n=p.buildFragment(e,this[0].ownerDocument,!1,this)).firstChild,1===n.childNodes.length&&(n=r),r)){for(s=(o=p.map(fe(n,"script"),ae)).length;l")).appendTo(t.documentElement))[0].contentDocument).write(),t.close(),n=ge(e,t),de.detach()),he[e]=n),n}var ve=/^margin/,ye=new RegExp("^("+W+")(?!px)[a-z%]+$","i"),xe=function(t){return t.ownerDocument.defaultView.opener?t.ownerDocument.defaultView.getComputedStyle(t,null):e.getComputedStyle(t,null)};function be(e,t,n){var r,i,o,s,a=e.style;return(n=n||xe(e))&&(s=n.getPropertyValue(t)||n[t]),n&&(""!==s||p.contains(e.ownerDocument,e)||(s=p.style(e,t)),ye.test(s)&&ve.test(t)&&(r=a.width,i=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=s,s=n.width,a.width=r,a.minWidth=i,a.maxWidth=o)),void 0!==s?s+"":s}function we(e,t){return{get:function(){if(!e())return(this.get=t).apply(this,arguments);delete this.get}}}!(function(){var t,n,r=f.documentElement,i=f.createElement("div"),o=f.createElement("div");o.style&&(o.style.backgroundClip="content-box",o.cloneNode(!0).style.backgroundClip="",c.clearCloneStyle="content-box"===o.style.backgroundClip,i.style.cssText="border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;position:absolute",i.appendChild(o),e.getComputedStyle&&p.extend(c,{pixelPosition:function(){return s(),t},boxSizingReliable:function(){return null==n&&s(),n},reliableMarginRight:function(){var t,n=o.appendChild(f.createElement("div"));return n.style.cssText=o.style.cssText="-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;display:block;margin:0;border:0;padding:0",n.style.marginRight=n.style.width="0",o.style.width="1px",r.appendChild(i),t=!parseFloat(e.getComputedStyle(n,null).marginRight),r.removeChild(i),o.removeChild(n),t}}));function s(){o.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;display:block;margin-top:1%;top:1%;border:1px;padding:1px;width:4px;position:absolute",o.innerHTML="",r.appendChild(i);var s=e.getComputedStyle(o,null);t="1%"!==s.top,n="4px"===s.width,r.removeChild(i)}})(),p.swap=function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i};var Te=/^(none|table(?!-c[ea]).+)/,Ce=new RegExp("^("+W+")(.*)$","i"),Ne=new RegExp("^([+-])=("+W+")","i"),ke={position:"absolute",visibility:"hidden",display:"block"},Ee={letterSpacing:"0",fontWeight:"400"},Se=["Webkit","O","Moz","ms"];function De(e,t){if(t in e)return t;for(var n=t[0].toUpperCase()+t.slice(1),r=t,i=Se.length;i--;)if((t=Se[i]+n)in e)return t;return r}function je(e,t,n){var r=Ce.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ae(e,t,n,r,i){for(var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;o<4;o+=2)"margin"===n&&(s+=p.css(e,n+$[o],!0,i)),r?("content"===n&&(s-=p.css(e,"padding"+$[o],!0,i)),"margin"!==n&&(s-=p.css(e,"border"+$[o]+"Width",!0,i))):(s+=p.css(e,"padding"+$[o],!0,i),"padding"!==n&&(s+=p.css(e,"border"+$[o]+"Width",!0,i)));return s}function Le(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=xe(e),s="border-box"===p.css(e,"boxSizing",!1,o);if(i<=0||null==i){if(((i=be(e,t,o))<0||null==i)&&(i=e.style[t]),ye.test(i))return i;r=s&&(c.boxSizingReliable()||i===e.style[t]),i=parseFloat(i)||0}return i+Ae(e,t,n||(s?"border":"content"),r,o)+"px"}function qe(e,t){for(var n,r,i,o=[],s=0,a=e.length;s1)},show:function(){return qe(this,!0)},hide:function(){return qe(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){I(this)?p(this).show():p(this).hide()})}});function He(e,t,n,r,i){return new He.prototype.init(e,t,n,r,i)}p.Tween=He,He.prototype={constructor:He,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(p.cssNumber[n]?"":"px")},cur:function(){var e=He.propHooks[this.prop];return e&&e.get?e.get(this):He.propHooks._default.get(this)},run:function(e){var t,n=He.propHooks[this.prop];return this.options.duration?this.pos=t=p.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):He.propHooks._default.set(this),this}},He.prototype.init.prototype=He.prototype,He.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=p.css(e.elem,e.prop,""))&&"auto"!==t?t:0:e.elem[e.prop]},set:function(e){p.fx.step[e.prop]?p.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[p.cssProps[e.prop]]||p.cssHooks[e.prop])?p.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},He.propHooks.scrollTop=He.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},p.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},p.fx=He.prototype.init,p.fx.step={};var Oe,Fe,Pe=/^(?:toggle|show|hide)$/,Re=new RegExp("^(?:([+-])=|)("+W+")([a-z%]*)$","i"),Me=/queueHooks$/,We=[function(e,t,n){var r,i,o,s,a,u,l,c=this,f={},d=e.style,h=e.nodeType&&I(e),g=O.get(e,"fxshow");n.queue||(null==(a=p._queueHooks(e,"fx")).unqueued&&(a.unqueued=0,u=a.empty.fire,a.empty.fire=function(){a.unqueued||u()}),a.unqueued++,c.always(function(){c.always(function(){a.unqueued--,p.queue(e,"fx").length||a.empty.fire()})}));1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],l=p.css(e,"display"),"inline"===("none"===l?O.get(e,"olddisplay")||me(e.nodeName):l)&&"none"===p.css(e,"float")&&(d.display="inline-block"));n.overflow&&(d.overflow="hidden",c.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(r in t)if(i=t[r],Pe.exec(i)){if(delete t[r],o=o||"toggle"===i,i===(h?"hide":"show")){if("show"!==i||!g||void 0===g[r])continue;h=!0}f[r]=g&&g[r]||p.style(e,r)}else l=void 0;if(p.isEmptyObject(f))"inline"===("none"===l?me(e.nodeName):l)&&(d.display=l);else{g?"hidden"in g&&(h=g.hidden):g=O.access(e,"fxshow",{}),o&&(g.hidden=!h),h?p(e).show():c.done(function(){p(e).hide()}),c.done(function(){var t;O.remove(e,"fxshow");for(t in f)p.style(e,t,f[t])});for(r in f)s=_e(h?g[r]:0,r,c),r in g||(g[r]=s.start,h&&(s.end=s.start,s.start="width"===r||"height"===r?1:0))}}],$e={"*":[function(e,t){var n=this.createTween(e,t),r=n.cur(),i=Re.exec(t),o=i&&i[3]||(p.cssNumber[e]?"":"px"),s=(p.cssNumber[e]||"px"!==o&&+r)&&Re.exec(p.css(n.elem,e)),a=1,u=20;if(s&&s[3]!==o){o=o||s[3],i=i||[],s=+r||1;do{s/=a=a||".5",p.style(n.elem,e,s+o)}while(a!==(a=n.cur()/r)&&1!==a&&--u)}return i&&(s=n.start=+s||+r||0,n.unit=o,n.end=i[1]?s+(i[1]+1)*i[2]:+i[2]),n}]};function Ie(){return setTimeout(function(){Oe=void 0}),Oe=p.now()}function Be(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=$[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function _e(e,t,n){for(var r,i=($e[t]||[]).concat($e["*"]),o=0,s=i.length;o1)},removeAttr:function(e){return this.each(function(){p.removeAttr(this,e)})}}),p.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return typeof e.getAttribute===_?p.prop(e,t,n):(1===o&&p.isXMLDoc(e)||(t=t.toLowerCase(),r=p.attrHooks[t]||(p.expr.match.bool.test(t)?Xe:void 0)),void 0===n?r&&"get"in r&&null!==(i=r.get(e,t))?i:null==(i=p.find.attr(e,t))?void 0:i:null!==n?r&&"set"in r&&void 0!==(i=r.set(e,n,t))?i:(e.setAttribute(t,n+""),n):void p.removeAttr(e,t))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(D);if(o&&1===e.nodeType)for(;n=o[i++];)r=p.propFix[n]||n,p.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!c.radioValue&&"radio"===t&&p.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}}}),Xe={set:function(e,t,n){return!1===t?p.removeAttr(e,n):e.setAttribute(n,n),n}},p.each(p.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Ue[t]||p.find.attr;Ue[t]=function(e,t,r){var i,o;return r||(o=Ue[t],Ue[t]=i,i=null!=n(e,t,r)?t.toLowerCase():null,Ue[t]=o),i}});var Ve=/^(?:input|select|textarea|button)$/i;p.fn.extend({prop:function(e,t){return q(this,p.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[p.propFix[e]||e]})}}),p.extend({propFix:{for:"htmlFor",class:"className"},prop:function(e,t,n){var r,i,o=e.nodeType;if(e&&3!==o&&8!==o&&2!==o)return(1!==o||!p.isXMLDoc(e))&&(t=p.propFix[t]||t,i=p.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||Ve.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),c.optSelected||(p.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),p.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){p.propFix[this.toLowerCase()]=this});var Ye=/[\t\r\n\f]/g;p.fn.extend({addClass:function(e){var t,n,r,i,o,s,a="string"==typeof e&&e,u=0,l=this.length;if(p.isFunction(e))return this.each(function(t){p(this).addClass(e.call(this,t,this.className))});if(a)for(t=(e||"").match(D)||[];u=0;)r=r.replace(" "+i+" "," ");s=e?p.trim(r):"",n.className!==s&&(n.className=s)}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):p.isFunction(e)?this.each(function(n){p(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n)for(var t,r=0,i=p(this),o=e.match(D)||[];t=o[r++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else n!==_&&"boolean"!==n||(this.className&&O.set(this,"__className__",this.className),this.className=this.className||!1===e?"":O.get(this,"__className__")||"")})},hasClass:function(e){for(var t=" "+e+" ",n=0,r=this.length;n=0)return!0;return!1}});var Ge=/\r/g;p.fn.extend({val:function(e){var t,n,r,i=this[0];if(arguments.length)return r=p.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,p(this).val()):e)?i="":"number"==typeof i?i+="":p.isArray(i)&&(i=p.map(i,function(e){return null==e?"":e+""})),(t=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=p.valHooks[i.type]||p.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(Ge,""):null==n?"":n}}),p.extend({valHooks:{option:{get:function(e){var t=p.find.attr(e,"value");return null!=t?t:p.trim(p.text(e))}},select:{get:function(e){for(var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||i<0,s=o?null:[],a=o?i+1:r.length,u=i<0?a:o?i:0;u=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),p.each(["radio","checkbox"],function(){p.valHooks[this]={set:function(e,t){if(p.isArray(t))return e.checked=p.inArray(p(e).val(),t)>=0}},c.checkOn||(p.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),p.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){p.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),p.fn.extend({hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)}});var Qe=p.now(),Je=/\?/;p.parseJSON=function(e){return JSON.parse(e+"")},p.parseXML=function(e){var t;if(!e||"string"!=typeof e)return null;try{t=(new DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&!t.getElementsByTagName("parsererror").length||p.error("Invalid XML: "+e),t};var Ke=/#.*$/,Ze=/([?&])_=[^&]*/,et=/^(.*?):[ \t]*([^\r\n]*)$/gm,tt=/^(?:GET|HEAD)$/,nt=/^\/\//,rt=/^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,it={},ot={},st="*/".concat("*"),at=e.location.href,ut=rt.exec(at.toLowerCase())||[];function lt(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(D)||[];if(p.isFunction(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function ct(e,t,n,r){var i={},o=e===ot;function s(a){var u;return i[a]=!0,p.each(e[a]||[],function(e,a){var l=a(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),s(l),!1)}),u}return s(t.dataTypes[0])||!i["*"]&&s("*")}function ft(e,t){var n,r,i=p.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&p.extend(!0,e,r),e}p.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:at,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(ut[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":st,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ft(ft(e,p.ajaxSettings),t):ft(p.ajaxSettings,e)},ajaxPrefilter:lt(it),ajaxTransport:lt(ot),ajax:function(e,t){"object"==typeof e&&(t=e,e=void 0),t=t||{};var n,r,i,o,s,a,u,l,c=p.ajaxSetup({},t),f=c.context||c,d=c.context&&(f.nodeType||f.jquery)?p(f):p.event,h=p.Deferred(),g=p.Callbacks("once memory"),m=c.statusCode||{},v={},y={},x=0,b="canceled",w={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!o)for(o={};t=et.exec(i);)o[t[1].toLowerCase()]=t[2];t=o[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?i:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=y[n]=y[n]||e,v[e]=t),this},overrideMimeType:function(e){return x||(c.mimeType=e),this},statusCode:function(e){var t;if(e)if(x<2)for(t in e)m[t]=[m[t],e[t]];else w.always(e[w.status]);return this},abort:function(e){var t=e||b;return n&&n.abort(t),T(0,t),this}};if(h.promise(w).complete=g.add,w.success=w.done,w.error=w.fail,c.url=((e||c.url||at)+"").replace(Ke,"").replace(nt,ut[1]+"//"),c.type=t.method||t.type||c.method||c.type,c.dataTypes=p.trim(c.dataType||"*").toLowerCase().match(D)||[""],null==c.crossDomain&&(a=rt.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ut[1]&&a[2]===ut[2]&&(a[3]||("http:"===a[1]?"80":"443"))===(ut[3]||("http:"===ut[1]?"80":"443")))),c.data&&c.processData&&"string"!=typeof c.data&&(c.data=p.param(c.data,c.traditional)),ct(it,c,t,w),2===x)return w;(u=p.event&&c.global)&&0==p.active++&&p.event.trigger("ajaxStart"),c.type=c.type.toUpperCase(),c.hasContent=!tt.test(c.type),r=c.url,c.hasContent||(c.data&&(r=c.url+=(Je.test(r)?"&":"?")+c.data,delete c.data),!1===c.cache&&(c.url=Ze.test(r)?r.replace(Ze,"$1_="+Qe++):r+(Je.test(r)?"&":"?")+"_="+Qe++)),c.ifModified&&(p.lastModified[r]&&w.setRequestHeader("If-Modified-Since",p.lastModified[r]),p.etag[r]&&w.setRequestHeader("If-None-Match",p.etag[r])),(c.data&&c.hasContent&&!1!==c.contentType||t.contentType)&&w.setRequestHeader("Content-Type",c.contentType),w.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+("*"!==c.dataTypes[0]?", "+st+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)w.setRequestHeader(l,c.headers[l]);if(c.beforeSend&&(!1===c.beforeSend.call(f,w,c)||2===x))return w.abort();b="abort";for(l in{success:1,error:1,complete:1})w[l](c[l]);if(n=ct(ot,c,t,w)){w.readyState=1,u&&d.trigger("ajaxSend",[w,c]),c.async&&c.timeout>0&&(s=setTimeout(function(){w.abort("timeout")},c.timeout));try{x=1,n.send(v,T)}catch(e){if(!(x<2))throw e;T(-1,e)}}else T(-1,"No Transport");function T(e,t,o,a){var l,v,y,b,T,C=t;2!==x&&(x=2,s&&clearTimeout(s),n=void 0,i=a||"",w.readyState=e>0?4:0,l=e>=200&&e<300||304===e,o&&(b=(function(e,t,n){for(var r,i,o,s,a=e.contents,u=e.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in a)if(a[i]&&a[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}s||(s=i)}o=o||s}if(o)return o!==u[0]&&u.unshift(o),n[o]})(c,w,o)),b=(function(e,t,n,r){var i,o,s,a,u,l={},c=e.dataTypes.slice();if(c[1])for(s in e.converters)l[s.toLowerCase()]=e.converters[s];for(o=c.shift();o;)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(s=l[u+" "+o]||l["* "+o]))for(i in l)if((a=i.split(" "))[1]===o&&(s=l[u+" "+a[0]]||l["* "+a[0]])){!0===s?s=l[i]:!0!==l[i]&&(o=a[0],c.unshift(a[1]));break}if(!0!==s)if(s&&e.throws)t=s(t);else try{t=s(t)}catch(e){return{state:"parsererror",error:s?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}})(c,b,w,l),l?(c.ifModified&&((T=w.getResponseHeader("Last-Modified"))&&(p.lastModified[r]=T),(T=w.getResponseHeader("etag"))&&(p.etag[r]=T)),204===e||"HEAD"===c.type?C="nocontent":304===e?C="notmodified":(C=b.state,v=b.data,l=!(y=b.error))):(y=C,!e&&C||(C="error",e<0&&(e=0))),w.status=e,w.statusText=(t||C)+"",l?h.resolveWith(f,[v,C,w]):h.rejectWith(f,[w,C,y]),w.statusCode(m),m=void 0,u&&d.trigger(l?"ajaxSuccess":"ajaxError",[w,c,l?v:y]),g.fireWith(f,[w,C]),u&&(d.trigger("ajaxComplete",[w,c]),--p.active||p.event.trigger("ajaxStop")))}return w},getJSON:function(e,t,n){return p.get(e,t,n,"json")},getScript:function(e,t){return p.get(e,void 0,t,"script")}}),p.each(["get","post"],function(e,t){p[t]=function(e,n,r,i){return p.isFunction(n)&&(i=i||r,r=n,n=void 0),p.ajax({url:e,type:t,dataType:i,data:n,success:r})}}),p._evalUrl=function(e){return p.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,throws:!0})},p.fn.extend({wrapAll:function(e){var t;return p.isFunction(e)?this.each(function(t){p(this).wrapAll(e.call(this,t))}):(this[0]&&(t=p(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return p.isFunction(e)?this.each(function(t){p(this).wrapInner(e.call(this,t))}):this.each(function(){var t=p(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=p.isFunction(e);return this.each(function(n){p(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()}}),p.expr.filters.hidden=function(e){return e.offsetWidth<=0&&e.offsetHeight<=0},p.expr.filters.visible=function(e){return!p.expr.filters.hidden(e)};var pt=/%20/g,dt=/\[\]$/,ht=/\r?\n/g,gt=/^(?:submit|button|image|reset|file)$/i,mt=/^(?:input|select|textarea|keygen)/i;function vt(e,t,n,r){var i;if(p.isArray(t))p.each(t,function(t,i){n||dt.test(e)?r(e,i):vt(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==p.type(t))r(e,t);else for(i in t)vt(e+"["+i+"]",t[i],n,r)}p.param=function(e,t){var n,r=[],i=function(e,t){t=p.isFunction(t)?t():null==t?"":t,r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(void 0===t&&(t=p.ajaxSettings&&p.ajaxSettings.traditional),p.isArray(e)||e.jquery&&!p.isPlainObject(e))p.each(e,function(){i(this.name,this.value)});else for(n in e)vt(n,e[n],t,i);return r.join("&").replace(pt,"+")},p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=p.prop(this,"elements");return e?p.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!p(this).is(":disabled")&&mt.test(this.nodeName)&&!gt.test(e)&&(this.checked||!B.test(e))}).map(function(e,t){var n=p(this).val();return null==n?null:p.isArray(n)?p.map(n,function(e){return{name:t.name,value:e.replace(ht,"\r\n")}}):{name:t.name,value:n.replace(ht,"\r\n")}}).get()}}),p.ajaxSettings.xhr=function(){try{return new XMLHttpRequest}catch(e){}};var yt=0,xt={},bt={0:200,1223:204},wt=p.ajaxSettings.xhr();e.attachEvent&&e.attachEvent("onunload",function(){for(var e in xt)xt[e]()}),c.cors=!!wt&&"withCredentials"in wt,c.ajax=wt=!!wt,p.ajaxTransport(function(e){var t;if(c.cors||wt&&!e.crossDomain)return{send:function(n,r){var i,o=e.xhr(),s=++yt;if(o.open(e.type,e.url,e.async,e.username,e.password),e.xhrFields)for(i in e.xhrFields)o[i]=e.xhrFields[i];e.mimeType&&o.overrideMimeType&&o.overrideMimeType(e.mimeType),e.crossDomain||n["X-Requested-With"]||(n["X-Requested-With"]="XMLHttpRequest");for(i in n)o.setRequestHeader(i,n[i]);t=function(e){return function(){t&&(delete xt[s],t=o.onload=o.onerror=null,"abort"===e?o.abort():"error"===e?r(o.status,o.statusText):r(bt[o.status]||o.status,o.statusText,"string"==typeof o.responseText?{text:o.responseText}:void 0,o.getAllResponseHeaders()))}},o.onload=t(),o.onerror=t("error"),t=xt[s]=t("abort");try{o.send(e.hasContent&&e.data||null)}catch(e){if(t)throw e}},abort:function(){t&&t()}}}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return p.globalEval(e),e}}}),p.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),p.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,i){t=p("