summaryrefslogtreecommitdiff
path: root/StoneIsland/platforms/ios/Stone Island/Plugins
diff options
context:
space:
mode:
Diffstat (limited to 'StoneIsland/platforms/ios/Stone Island/Plugins')
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/README20
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h26
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m38
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h30
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m112
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h37
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m256
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h70
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m367
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h112
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m1075
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h34
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m156
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h85
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m266
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h45
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m483
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h28
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m89
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h50
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m501
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h5
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m30
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h28
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m772
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h16
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m176
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h22
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m278
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h79
-rw-r--r--StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m608
31 files changed, 5894 insertions, 0 deletions
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/README b/StoneIsland/platforms/ios/Stone Island/Plugins/README
new file mode 100644
index 00000000..87df09f2
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/README
@@ -0,0 +1,20 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+
+Put the .h and .m files of your plugin here. The .js files of your plugin belong in the www folder.
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h
new file mode 100644
index 00000000..7cfb3063
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.h
@@ -0,0 +1,26 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 <Cordova/CDVPlugin.h>
+
+@interface CDVLogger : CDVPlugin
+
+- (void)logLevel:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m
new file mode 100644
index 00000000..ccfa3a51
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-console/CDVLogger.m
@@ -0,0 +1,38 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVLogger.h"
+#import <Cordova/CDV.h>
+
+@implementation CDVLogger
+
+/* log a message */
+- (void)logLevel:(CDVInvokedUrlCommand*)command
+{
+ id level = [command argumentAtIndex:0];
+ id message = [command argumentAtIndex:1];
+
+ if ([level isEqualToString:@"LOG"]) {
+ NSLog(@"%@", message);
+ } else {
+ NSLog(@"%@: %@", level, message);
+ }
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h
new file mode 100644
index 00000000..a146d882
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.h
@@ -0,0 +1,30 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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/UIKit.h>
+#import <Cordova/CDVPlugin.h>
+
+@interface CDVDevice : CDVPlugin
+{}
+
++ (NSString*)cordovaVersion;
+
+- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m
new file mode 100644
index 00000000..4d75a574
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-device/CDVDevice.m
@@ -0,0 +1,112 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include "TargetConditionals.h"
+
+#import <Cordova/CDV.h>
+#import "CDVDevice.h"
+
+@implementation UIDevice (ModelVersion)
+
+- (NSString*)modelVersion
+{
+ size_t size;
+
+ sysctlbyname("hw.machine", NULL, &size, NULL, 0);
+ char* machine = malloc(size);
+ sysctlbyname("hw.machine", machine, &size, NULL, 0);
+ NSString* platform = [NSString stringWithUTF8String:machine];
+ free(machine);
+
+ return platform;
+}
+
+@end
+
+@interface CDVDevice () {}
+@end
+
+@implementation CDVDevice
+
+- (NSString*)uniqueAppInstanceIdentifier:(UIDevice*)device
+{
+ NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
+ static NSString* UUID_KEY = @"CDVUUID";
+
+ // Check user defaults first to maintain backwards compaitibility with previous versions
+ // which didn't user identifierForVendor
+ NSString* app_uuid = [userDefaults stringForKey:UUID_KEY];
+ if (app_uuid == nil) {
+ if ([device respondsToSelector:@selector(identifierForVendor)]) {
+ app_uuid = [[device identifierForVendor] UUIDString];
+ } else {
+ CFUUIDRef uuid = CFUUIDCreate(NULL);
+ app_uuid = (__bridge_transfer NSString *)CFUUIDCreateString(NULL, uuid);
+ CFRelease(uuid);
+ }
+
+ [userDefaults setObject:app_uuid forKey:UUID_KEY];
+ [userDefaults synchronize];
+ }
+
+ return app_uuid;
+}
+
+- (void)getDeviceInfo:(CDVInvokedUrlCommand*)command
+{
+ NSDictionary* deviceProperties = [self deviceProperties];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:deviceProperties];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (NSDictionary*)deviceProperties
+{
+ UIDevice* device = [UIDevice currentDevice];
+
+ return @{
+ @"manufacturer": @"Apple",
+ @"model": [device modelVersion],
+ @"platform": @"iOS",
+ @"version": [device systemVersion],
+ @"uuid": [self uniqueAppInstanceIdentifier:device],
+ @"cordova": [[self class] cordovaVersion],
+ @"isVirtual": @([self isVirtual])
+ };
+}
+
++ (NSString*)cordovaVersion
+{
+ return CDV_VERSION;
+}
+
+- (BOOL)isVirtual
+{
+ #if TARGET_OS_SIMULATOR
+ return true;
+ #elif TARGET_IPHONE_SIMULATOR
+ return true;
+ #else
+ return false;
+ #endif
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h
new file mode 100644
index 00000000..9253f6a9
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.h
@@ -0,0 +1,37 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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/Foundation.h>
+#import <UIKit/UIKit.h>
+#import <AudioToolbox/AudioServices.h>
+#import <Cordova/CDVPlugin.h>
+
+@interface CDVNotification : CDVPlugin <UIAlertViewDelegate>{}
+
+- (void)alert:(CDVInvokedUrlCommand*)command;
+- (void)confirm:(CDVInvokedUrlCommand*)command;
+- (void)prompt:(CDVInvokedUrlCommand*)command;
+- (void)beep:(CDVInvokedUrlCommand*)command;
+
+@end
+
+@interface CDVAlertView : UIAlertView {}
+@property (nonatomic, copy) NSString* callbackId;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m
new file mode 100644
index 00000000..0dd3d2cb
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-dialogs/CDVNotification.m
@@ -0,0 +1,256 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVNotification.h"
+
+#define DIALOG_TYPE_ALERT @"alert"
+#define DIALOG_TYPE_PROMPT @"prompt"
+
+static void soundCompletionCallback(SystemSoundID ssid, void* data);
+static NSMutableArray *alertList = nil;
+
+@implementation CDVNotification
+
+/*
+ * showDialogWithMessage - Common method to instantiate the alert view for alert, confirm, and prompt notifications.
+ * Parameters:
+ * message The alert view message.
+ * title The alert view title.
+ * buttons The array of customized strings for the buttons.
+ * defaultText The input text for the textbox (if textbox exists).
+ * callbackId The commmand callback id.
+ * dialogType The type of alert view [alert | prompt].
+ */
+- (void)showDialogWithMessage:(NSString*)message title:(NSString*)title buttons:(NSArray*)buttons defaultText:(NSString*)defaultText callbackId:(NSString*)callbackId dialogType:(NSString*)dialogType
+{
+
+ int count = (int)[buttons count];
+#ifdef __IPHONE_8_0
+ if (NSClassFromString(@"UIAlertController")) {
+
+ UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
+
+ if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.3) {
+
+ CGRect alertFrame = [UIScreen mainScreen].applicationFrame;
+
+ if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
+ // swap the values for the app frame since it is now in landscape
+ CGFloat temp = alertFrame.size.width;
+ alertFrame.size.width = alertFrame.size.height;
+ alertFrame.size.height = temp;
+ }
+
+ alertController.view.frame = alertFrame;
+ }
+
+ __weak CDVNotification* weakNotif = self;
+
+ for (int n = 0; n < count; n++) {
+ [alertController addAction:[UIAlertAction actionWithTitle:[buttons objectAtIndex:n]
+ style:UIAlertActionStyleDefault
+ handler:^(UIAlertAction * action)
+ {
+ CDVPluginResult* result;
+
+ if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT])
+ {
+ NSString* value0 = [[alertController.textFields objectAtIndex:0] text];
+ NSDictionary* info = @{
+ @"buttonIndex":@(n + 1),
+ @"input1":(value0 ? value0 : [NSNull null])
+ };
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info];
+ }
+ else
+ {
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(n + 1)];
+ }
+
+ [weakNotif.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }]];
+ }
+
+ if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) {
+
+ [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField) {
+ textField.text = defaultText;
+ }];
+ }
+
+ if(!alertList)
+ alertList = [[NSMutableArray alloc] init];
+ [alertList addObject:alertController];
+
+ if ([alertList count]==1) {
+ [self presentAlertcontroller];
+ }
+
+ }
+ else
+ {
+#endif
+
+ CDVAlertView* alertView = [[CDVAlertView alloc]
+ initWithTitle:title
+ message:message
+ delegate:self
+ cancelButtonTitle:nil
+ otherButtonTitles:nil];
+
+ alertView.callbackId = callbackId;
+
+
+
+ for (int n = 0; n < count; n++) {
+ [alertView addButtonWithTitle:[buttons objectAtIndex:n]];
+ }
+
+ if ([dialogType isEqualToString:DIALOG_TYPE_PROMPT]) {
+ alertView.alertViewStyle = UIAlertViewStylePlainTextInput;
+ UITextField* textField = [alertView textFieldAtIndex:0];
+ textField.text = defaultText;
+ }
+
+ [alertView show];
+#ifdef __IPHONE_8_0
+ }
+#endif
+
+}
+
+- (void)alert:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSString* message = [command argumentAtIndex:0];
+ NSString* title = [command argumentAtIndex:1];
+ NSString* buttons = [command argumentAtIndex:2];
+
+ [self showDialogWithMessage:message title:title buttons:@[buttons] defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT];
+}
+
+- (void)confirm:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSString* message = [command argumentAtIndex:0];
+ NSString* title = [command argumentAtIndex:1];
+ NSArray* buttons = [command argumentAtIndex:2];
+
+ [self showDialogWithMessage:message title:title buttons:buttons defaultText:nil callbackId:callbackId dialogType:DIALOG_TYPE_ALERT];
+}
+
+- (void)prompt:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSString* message = [command argumentAtIndex:0];
+ NSString* title = [command argumentAtIndex:1];
+ NSArray* buttons = [command argumentAtIndex:2];
+ NSString* defaultText = [command argumentAtIndex:3];
+
+ [self showDialogWithMessage:message title:title buttons:buttons defaultText:defaultText callbackId:callbackId dialogType:DIALOG_TYPE_PROMPT];
+}
+
+/**
+ * Callback invoked when an alert dialog's buttons are clicked.
+ */
+- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+ CDVAlertView* cdvAlertView = (CDVAlertView*)alertView;
+ CDVPluginResult* result;
+
+ // Determine what gets returned to JS based on the alert view type.
+ if (alertView.alertViewStyle == UIAlertViewStyleDefault) {
+ // For alert and confirm, return button index as int back to JS.
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)(buttonIndex + 1)];
+ } else {
+ // For prompt, return button index and input text back to JS.
+ NSString* value0 = [[alertView textFieldAtIndex:0] text];
+ NSDictionary* info = @{
+ @"buttonIndex":@(buttonIndex + 1),
+ @"input1":(value0 ? value0 : [NSNull null])
+ };
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:info];
+ }
+ [self.commandDelegate sendPluginResult:result callbackId:cdvAlertView.callbackId];
+}
+
+- (void)didPresentAlertView:(UIAlertView*)alertView
+{
+ //show keyboard on iOS 8
+ if (alertView.alertViewStyle == UIAlertViewStylePlainTextInput){
+ [[alertView textFieldAtIndex:0] selectAll:nil];
+ }
+}
+
+static void playBeep(int count) {
+ SystemSoundID completeSound;
+ NSInteger cbDataCount = count;
+ NSURL* audioPath = [[NSBundle mainBundle] URLForResource:@"CDVNotification.bundle/beep" withExtension:@"wav"];
+ #if __has_feature(objc_arc)
+ AudioServicesCreateSystemSoundID((__bridge CFURLRef)audioPath, &completeSound);
+ #else
+ AudioServicesCreateSystemSoundID((CFURLRef)audioPath, &completeSound);
+ #endif
+ AudioServicesAddSystemSoundCompletion(completeSound, NULL, NULL, soundCompletionCallback, (void*)(cbDataCount-1));
+ AudioServicesPlaySystemSound(completeSound);
+}
+
+static void soundCompletionCallback(SystemSoundID ssid, void* data) {
+ int count = (int)data;
+ AudioServicesRemoveSystemSoundCompletion (ssid);
+ AudioServicesDisposeSystemSoundID(ssid);
+ if (count > 0) {
+ playBeep(count);
+ }
+}
+
+- (void)beep:(CDVInvokedUrlCommand*)command
+{
+ NSNumber* count = [command argumentAtIndex:0 withDefault:[NSNumber numberWithInt:1]];
+ playBeep([count intValue]);
+}
+
+-(UIViewController *)getTopPresentedViewController {
+ UIViewController *presentingViewController = self.viewController;
+ while(presentingViewController.presentedViewController != nil && ![presentingViewController.presentedViewController isBeingDismissed])
+ {
+ presentingViewController = presentingViewController.presentedViewController;
+ }
+ return presentingViewController;
+}
+
+-(void)presentAlertcontroller {
+
+ __weak CDVNotification* weakNotif = self;
+ [self.getTopPresentedViewController presentViewController:[alertList firstObject] animated:YES completion:^{
+ [alertList removeObject:[alertList firstObject]];
+ if ([alertList count]>0) {
+ [weakNotif presentAlertcontroller];
+ }
+ }];
+
+}
+
+@end
+
+@implementation CDVAlertView
+
+@synthesize callbackId;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h
new file mode 100644
index 00000000..cce2738f
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.h
@@ -0,0 +1,70 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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/UIKit.h>
+#import <CoreLocation/CoreLocation.h>
+#import <Cordova/CDVPlugin.h>
+
+enum CDVLocationStatus {
+ PERMISSIONDENIED = 1,
+ POSITIONUNAVAILABLE,
+ TIMEOUT
+};
+typedef NSUInteger CDVLocationStatus;
+
+// simple object to keep track of location information
+@interface CDVLocationData : NSObject {
+ CDVLocationStatus locationStatus;
+ NSMutableArray* locationCallbacks;
+ NSMutableDictionary* watchCallbacks;
+ CLLocation* locationInfo;
+}
+
+@property (nonatomic, assign) CDVLocationStatus locationStatus;
+@property (nonatomic, strong) CLLocation* locationInfo;
+@property (nonatomic, strong) NSMutableArray* locationCallbacks;
+@property (nonatomic, strong) NSMutableDictionary* watchCallbacks;
+
+@end
+
+@interface CDVLocation : CDVPlugin <CLLocationManagerDelegate>{
+ @private BOOL __locationStarted;
+ @private BOOL __highAccuracyEnabled;
+ CDVLocationData* locationData;
+}
+
+@property (nonatomic, strong) CLLocationManager* locationManager;
+@property (nonatomic, strong) CDVLocationData* locationData;
+
+- (void)getLocation:(CDVInvokedUrlCommand*)command;
+- (void)addWatch:(CDVInvokedUrlCommand*)command;
+- (void)clearWatch:(CDVInvokedUrlCommand*)command;
+- (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallback;
+- (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message;
+- (void)startLocation:(BOOL)enableHighAccuracy;
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateToLocation:(CLLocation*)newLocation
+ fromLocation:(CLLocation*)oldLocation;
+
+- (void)locationManager:(CLLocationManager*)manager
+ didFailWithError:(NSError*)error;
+
+- (BOOL)isLocationServicesEnabled;
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m
new file mode 100644
index 00000000..e4b34a11
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-geolocation/CDVLocation.m
@@ -0,0 +1,367 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVLocation.h"
+
+#pragma mark Constants
+
+#define kPGLocationErrorDomain @"kPGLocationErrorDomain"
+#define kPGLocationDesiredAccuracyKey @"desiredAccuracy"
+#define kPGLocationForcePromptKey @"forcePrompt"
+#define kPGLocationDistanceFilterKey @"distanceFilter"
+#define kPGLocationFrequencyKey @"frequency"
+
+#pragma mark -
+#pragma mark Categories
+
+@implementation CDVLocationData
+
+@synthesize locationStatus, locationInfo, locationCallbacks, watchCallbacks;
+- (CDVLocationData*)init
+{
+ self = (CDVLocationData*)[super init];
+ if (self) {
+ self.locationInfo = nil;
+ self.locationCallbacks = nil;
+ self.watchCallbacks = nil;
+ }
+ return self;
+}
+
+@end
+
+#pragma mark -
+#pragma mark CDVLocation
+
+@implementation CDVLocation
+
+@synthesize locationManager, locationData;
+
+- (void)pluginInitialize
+{
+ self.locationManager = [[CLLocationManager alloc] init];
+ self.locationManager.delegate = self; // Tells the location manager to send updates to this object
+ __locationStarted = NO;
+ __highAccuracyEnabled = NO;
+ self.locationData = nil;
+}
+
+- (BOOL)isAuthorized
+{
+ BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+
+
+ if (authorizationStatusClassPropertyAvailable) {
+ NSUInteger authStatus = [CLLocationManager authorizationStatus];
+#ifdef __IPHONE_8_0
+ if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { //iOS 8.0+
+ return (authStatus == kCLAuthorizationStatusAuthorizedWhenInUse) || (authStatus == kCLAuthorizationStatusAuthorizedAlways) || (authStatus == kCLAuthorizationStatusNotDetermined);
+ }
+#endif
+ return (authStatus == kCLAuthorizationStatusAuthorized) || (authStatus == kCLAuthorizationStatusNotDetermined);
+ }
+
+ // by default, assume YES (for iOS < 4.2)
+ return YES;
+}
+
+- (BOOL)isLocationServicesEnabled
+{
+ BOOL locationServicesEnabledInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 3.x
+ BOOL locationServicesEnabledClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(locationServicesEnabled)]; // iOS 4.x
+
+ if (locationServicesEnabledClassPropertyAvailable) { // iOS 4.x
+ return [CLLocationManager locationServicesEnabled];
+ } else if (locationServicesEnabledInstancePropertyAvailable) { // iOS 2.x, iOS 3.x
+ return [(id)self.locationManager locationServicesEnabled];
+ } else {
+ return NO;
+ }
+}
+
+- (void)startLocation:(BOOL)enableHighAccuracy
+{
+ if (![self isLocationServicesEnabled]) {
+ [self returnLocationError:PERMISSIONDENIED withMessage:@"Location services are not enabled."];
+ return;
+ }
+ if (![self isAuthorized]) {
+ NSString* message = nil;
+ BOOL authStatusAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+
+ if (authStatusAvailable) {
+ NSUInteger code = [CLLocationManager authorizationStatus];
+ if (code == kCLAuthorizationStatusNotDetermined) {
+ // could return POSITION_UNAVAILABLE but need to coordinate with other platforms
+ message = @"User undecided on application's use of location services.";
+ } else if (code == kCLAuthorizationStatusRestricted) {
+ message = @"Application's use of location services is restricted.";
+ }
+ }
+ // PERMISSIONDENIED is only PositionError that makes sense when authorization denied
+ [self returnLocationError:PERMISSIONDENIED withMessage:message];
+
+ return;
+ }
+
+#ifdef __IPHONE_8_0
+ NSUInteger code = [CLLocationManager authorizationStatus];
+ if (code == kCLAuthorizationStatusNotDetermined && ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)] || [self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])) { //iOS8+
+ __highAccuracyEnabled = enableHighAccuracy;
+ if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"]){
+ [self.locationManager requestWhenInUseAuthorization];
+ } else if([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"]) {
+ [self.locationManager requestAlwaysAuthorization];
+ } else {
+ NSLog(@"[Warning] No NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription key is defined in the Info.plist file.");
+ }
+ return;
+ }
+#endif
+
+ // Tell the location manager to start notifying us of location updates. We
+ // first stop, and then start the updating to ensure we get at least one
+ // update, even if our location did not change.
+ [self.locationManager stopUpdatingLocation];
+ [self.locationManager startUpdatingLocation];
+ __locationStarted = YES;
+ if (enableHighAccuracy) {
+ __highAccuracyEnabled = YES;
+ // Set distance filter to 5 for a high accuracy. Setting it to "kCLDistanceFilterNone" could provide a
+ // higher accuracy, but it's also just spamming the callback with useless reports which drain the battery.
+ self.locationManager.distanceFilter = 5;
+ // Set desired accuracy to Best.
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
+ } else {
+ __highAccuracyEnabled = NO;
+ self.locationManager.distanceFilter = 10;
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers;
+ }
+}
+
+- (void)_stopLocation
+{
+ if (__locationStarted) {
+ if (![self isLocationServicesEnabled]) {
+ return;
+ }
+
+ [self.locationManager stopUpdatingLocation];
+ __locationStarted = NO;
+ __highAccuracyEnabled = NO;
+ }
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateToLocation:(CLLocation*)newLocation
+ fromLocation:(CLLocation*)oldLocation
+{
+ CDVLocationData* cData = self.locationData;
+
+ cData.locationInfo = newLocation;
+ if (self.locationData.locationCallbacks.count > 0) {
+ for (NSString* callbackId in self.locationData.locationCallbacks) {
+ [self returnLocationInfo:callbackId andKeepCallback:NO];
+ }
+
+ [self.locationData.locationCallbacks removeAllObjects];
+ }
+ if (self.locationData.watchCallbacks.count > 0) {
+ for (NSString* timerId in self.locationData.watchCallbacks) {
+ [self returnLocationInfo:[self.locationData.watchCallbacks objectForKey:timerId] andKeepCallback:YES];
+ }
+ } else {
+ // No callbacks waiting on us anymore, turn off listening.
+ [self _stopLocation];
+ }
+}
+
+- (void)getLocation:(CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ NSString* callbackId = command.callbackId;
+ BOOL enableHighAccuracy = [[command argumentAtIndex:0] boolValue];
+
+ if ([self isLocationServicesEnabled] == NO) {
+ NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
+ [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"];
+ [posError setObject:@"Location services are disabled." forKey:@"message"];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ } else {
+ if (!self.locationData) {
+ self.locationData = [[CDVLocationData alloc] init];
+ }
+ CDVLocationData* lData = self.locationData;
+ if (!lData.locationCallbacks) {
+ lData.locationCallbacks = [NSMutableArray arrayWithCapacity:1];
+ }
+
+ if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) {
+ // add the callbackId into the array so we can call back when get data
+ if (callbackId != nil) {
+ [lData.locationCallbacks addObject:callbackId];
+ }
+ // Tell the location manager to start notifying us of heading updates
+ [self startLocation:enableHighAccuracy];
+ } else {
+ [self returnLocationInfo:callbackId andKeepCallback:NO];
+ }
+ }
+ }];
+}
+
+- (void)addWatch:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSString* timerId = [command argumentAtIndex:0];
+ BOOL enableHighAccuracy = [[command argumentAtIndex:1] boolValue];
+
+ if (!self.locationData) {
+ self.locationData = [[CDVLocationData alloc] init];
+ }
+ CDVLocationData* lData = self.locationData;
+
+ if (!lData.watchCallbacks) {
+ lData.watchCallbacks = [NSMutableDictionary dictionaryWithCapacity:1];
+ }
+
+ // add the callbackId into the dictionary so we can call back whenever get data
+ [lData.watchCallbacks setObject:callbackId forKey:timerId];
+
+ if ([self isLocationServicesEnabled] == NO) {
+ NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
+ [posError setObject:[NSNumber numberWithInt:PERMISSIONDENIED] forKey:@"code"];
+ [posError setObject:@"Location services are disabled." forKey:@"message"];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ } else {
+ if (!__locationStarted || (__highAccuracyEnabled != enableHighAccuracy)) {
+ // Tell the location manager to start notifying us of location updates
+ [self startLocation:enableHighAccuracy];
+ }
+ }
+}
+
+- (void)clearWatch:(CDVInvokedUrlCommand*)command
+{
+ NSString* timerId = [command argumentAtIndex:0];
+
+ if (self.locationData && self.locationData.watchCallbacks && [self.locationData.watchCallbacks objectForKey:timerId]) {
+ [self.locationData.watchCallbacks removeObjectForKey:timerId];
+ if([self.locationData.watchCallbacks count] == 0) {
+ [self _stopLocation];
+ }
+ }
+}
+
+- (void)stopLocation:(CDVInvokedUrlCommand*)command
+{
+ [self _stopLocation];
+}
+
+- (void)returnLocationInfo:(NSString*)callbackId andKeepCallback:(BOOL)keepCallback
+{
+ CDVPluginResult* result = nil;
+ CDVLocationData* lData = self.locationData;
+
+ if (lData && !lData.locationInfo) {
+ // return error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageToErrorObject:POSITIONUNAVAILABLE];
+ } else if (lData && lData.locationInfo) {
+ CLLocation* lInfo = lData.locationInfo;
+ NSMutableDictionary* returnInfo = [NSMutableDictionary dictionaryWithCapacity:8];
+ NSNumber* timestamp = [NSNumber numberWithDouble:([lInfo.timestamp timeIntervalSince1970] * 1000)];
+ [returnInfo setObject:timestamp forKey:@"timestamp"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.speed] forKey:@"velocity"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.verticalAccuracy] forKey:@"altitudeAccuracy"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.horizontalAccuracy] forKey:@"accuracy"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.course] forKey:@"heading"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.altitude] forKey:@"altitude"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.latitude] forKey:@"latitude"];
+ [returnInfo setObject:[NSNumber numberWithDouble:lInfo.coordinate.longitude] forKey:@"longitude"];
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo];
+ [result setKeepCallbackAsBool:keepCallback];
+ }
+ if (result) {
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+}
+
+- (void)returnLocationError:(NSUInteger)errorCode withMessage:(NSString*)message
+{
+ NSMutableDictionary* posError = [NSMutableDictionary dictionaryWithCapacity:2];
+
+ [posError setObject:[NSNumber numberWithUnsignedInteger:errorCode] forKey:@"code"];
+ [posError setObject:message ? message:@"" forKey:@"message"];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:posError];
+
+ for (NSString* callbackId in self.locationData.locationCallbacks) {
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+
+ [self.locationData.locationCallbacks removeAllObjects];
+
+ for (NSString* callbackId in self.locationData.watchCallbacks) {
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+}
+
+- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
+{
+ NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]);
+
+ CDVLocationData* lData = self.locationData;
+ if (lData && __locationStarted) {
+ // TODO: probably have to once over the various error codes and return one of:
+ // PositionError.PERMISSION_DENIED = 1;
+ // PositionError.POSITION_UNAVAILABLE = 2;
+ // PositionError.TIMEOUT = 3;
+ NSUInteger positionError = POSITIONUNAVAILABLE;
+ if (error.code == kCLErrorDenied) {
+ positionError = PERMISSIONDENIED;
+ }
+ [self returnLocationError:positionError withMessage:[error localizedDescription]];
+ }
+
+ if (error.code != kCLErrorLocationUnknown) {
+ [self.locationManager stopUpdatingLocation];
+ __locationStarted = NO;
+ }
+}
+
+//iOS8+
+-(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
+{
+ if(!__locationStarted){
+ [self startLocation:__highAccuracyEnabled];
+ }
+}
+
+- (void)dealloc
+{
+ self.locationManager.delegate = nil;
+}
+
+- (void)onReset
+{
+ [self _stopLocation];
+ [self.locationManager stopUpdatingHeading];
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h
new file mode 100644
index 00000000..6bb0ec16
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.h
@@ -0,0 +1,112 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 <Cordova/CDVPlugin.h>
+#import <Cordova/CDVInvokedUrlCommand.h>
+#import <Cordova/CDVScreenOrientationDelegate.h>
+
+#ifdef __CORDOVA_4_0_0
+ #import <Cordova/CDVUIWebViewDelegate.h>
+#else
+ #import <Cordova/CDVWebViewDelegate.h>
+#endif
+
+@class CDVInAppBrowserViewController;
+
+@interface CDVInAppBrowser : CDVPlugin {
+}
+
+@property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
+@property (nonatomic, copy) NSString* callbackId;
+@property (nonatomic, copy) NSRegularExpression *callbackIdPattern;
+
+- (void)open:(CDVInvokedUrlCommand*)command;
+- (void)close:(CDVInvokedUrlCommand*)command;
+- (void)injectScriptCode:(CDVInvokedUrlCommand*)command;
+- (void)show:(CDVInvokedUrlCommand*)command;
+
+@end
+
+@interface CDVInAppBrowserOptions : NSObject {}
+
+@property (nonatomic, assign) BOOL location;
+@property (nonatomic, assign) BOOL toolbar;
+@property (nonatomic, copy) NSString* closebuttoncaption;
+@property (nonatomic, copy) NSString* toolbarposition;
+@property (nonatomic, assign) BOOL clearcache;
+@property (nonatomic, assign) BOOL clearsessioncache;
+
+@property (nonatomic, copy) NSString* presentationstyle;
+@property (nonatomic, copy) NSString* transitionstyle;
+
+@property (nonatomic, assign) BOOL enableviewportscale;
+@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction;
+@property (nonatomic, assign) BOOL allowinlinemediaplayback;
+@property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction;
+@property (nonatomic, assign) BOOL suppressesincrementalrendering;
+@property (nonatomic, assign) BOOL hidden;
+@property (nonatomic, assign) BOOL disallowoverscroll;
+
++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options;
+
+@end
+
+@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{
+ @private
+ NSString* _userAgent;
+ NSString* _prevUserAgent;
+ NSInteger _userAgentLockToken;
+ CDVInAppBrowserOptions *_browserOptions;
+
+#ifdef __CORDOVA_4_0_0
+ CDVUIWebViewDelegate* _webViewDelegate;
+#else
+ CDVWebViewDelegate* _webViewDelegate;
+#endif
+
+}
+
+@property (nonatomic, strong) IBOutlet UIWebView* webView;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton;
+@property (nonatomic, strong) IBOutlet UILabel* addressLabel;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton;
+@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner;
+@property (nonatomic, strong) IBOutlet UIToolbar* toolbar;
+
+@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate;
+@property (nonatomic, weak) CDVInAppBrowser* navigationDelegate;
+@property (nonatomic) NSURL* currentURL;
+
+- (void)close;
+- (void)navigateTo:(NSURL*)url;
+- (void)showLocationBar:(BOOL)show;
+- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition;
+- (void)setCloseButtonTitle:(NSString*)title;
+
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions;
+
+@end
+
+@interface CDVInAppBrowserNavigationController : UINavigationController
+
+@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate;
+
+@end
+
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m
new file mode 100644
index 00000000..b342ca73
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-inappbrowser/CDVInAppBrowser.m
@@ -0,0 +1,1075 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVInAppBrowser.h"
+#import <Cordova/CDVPluginResult.h>
+#import <Cordova/CDVUserAgentUtil.h>
+
+#define kInAppBrowserTargetSelf @"_self"
+#define kInAppBrowserTargetSystem @"_system"
+#define kInAppBrowserTargetBlank @"_blank"
+
+#define kInAppBrowserToolbarBarPositionBottom @"bottom"
+#define kInAppBrowserToolbarBarPositionTop @"top"
+
+#define TOOLBAR_HEIGHT 44.0
+#define LOCATIONBAR_HEIGHT 21.0
+#define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT))
+
+#pragma mark CDVInAppBrowser
+
+@interface CDVInAppBrowser () {
+ NSInteger _previousStatusBarStyle;
+}
+@end
+
+@implementation CDVInAppBrowser
+
+- (void)pluginInitialize
+{
+ _previousStatusBarStyle = -1;
+ _callbackIdPattern = nil;
+}
+
+- (id)settingForKey:(NSString*)key
+{
+ return [self.commandDelegate.settings objectForKey:[key lowercaseString]];
+}
+
+- (void)onReset
+{
+ [self close:nil];
+}
+
+- (void)close:(CDVInvokedUrlCommand*)command
+{
+ if (self.inAppBrowserViewController == nil) {
+ NSLog(@"IAB.close() called but it was already closed.");
+ return;
+ }
+ // Things are cleaned up in browserExit.
+ [self.inAppBrowserViewController close];
+}
+
+- (BOOL) isSystemUrl:(NSURL*)url
+{
+ if ([[url host] isEqualToString:@"itunes.apple.com"]) {
+ return YES;
+ }
+
+ return NO;
+}
+
+- (void)open:(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult;
+
+ NSString* url = [command argumentAtIndex:0];
+ NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
+ NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
+
+ self.callbackId = command.callbackId;
+
+ if (url != nil) {
+#ifdef __CORDOVA_4_0_0
+ NSURL* baseUrl = [self.webViewEngine URL];
+#else
+ NSURL* baseUrl = [self.webView.request URL];
+#endif
+ NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
+
+ if ([self isSystemUrl:absoluteUrl]) {
+ target = kInAppBrowserTargetSystem;
+ }
+
+ if ([target isEqualToString:kInAppBrowserTargetSelf]) {
+ [self openInCordovaWebView:absoluteUrl withOptions:options];
+ } else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
+ [self openInSystem:absoluteUrl];
+ } else { // _blank or anything else
+ [self openInInAppBrowser:absoluteUrl withOptions:options];
+ }
+
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"];
+ }
+
+ [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
+{
+ CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
+
+ if (browserOptions.clearcache) {
+ NSHTTPCookie *cookie;
+ NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
+ for (cookie in [storage cookies])
+ {
+ if (![cookie.domain isEqual: @".^filecookies^"]) {
+ [storage deleteCookie:cookie];
+ }
+ }
+ }
+
+ if (browserOptions.clearsessioncache) {
+ NSHTTPCookie *cookie;
+ NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
+ for (cookie in [storage cookies])
+ {
+ if (![cookie.domain isEqual: @".^filecookies^"] && cookie.isSessionOnly) {
+ [storage deleteCookie:cookie];
+ }
+ }
+ }
+
+ if (self.inAppBrowserViewController == nil) {
+ NSString* userAgent = [CDVUserAgentUtil originalUserAgent];
+ NSString* overrideUserAgent = [self settingForKey:@"OverrideUserAgent"];
+ NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"];
+ if(overrideUserAgent){
+ userAgent = overrideUserAgent;
+ }
+ if(appendUserAgent){
+ userAgent = [userAgent stringByAppendingString: appendUserAgent];
+ }
+ self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:userAgent prevUserAgent:[self.commandDelegate userAgent] browserOptions: browserOptions];
+ self.inAppBrowserViewController.navigationDelegate = self;
+
+ if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
+ self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController;
+ }
+ }
+
+ [self.inAppBrowserViewController showLocationBar:browserOptions.location];
+ [self.inAppBrowserViewController showToolBar:browserOptions.toolbar :browserOptions.toolbarposition];
+ if (browserOptions.closebuttoncaption != nil) {
+ [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption];
+ }
+ // Set Presentation Style
+ UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
+ if (browserOptions.presentationstyle != nil) {
+ if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"pagesheet"]) {
+ presentationStyle = UIModalPresentationPageSheet;
+ } else if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"formsheet"]) {
+ presentationStyle = UIModalPresentationFormSheet;
+ }
+ }
+ self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
+
+ // Set Transition Style
+ UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
+ if (browserOptions.transitionstyle != nil) {
+ if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"fliphorizontal"]) {
+ transitionStyle = UIModalTransitionStyleFlipHorizontal;
+ } else if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"crossdissolve"]) {
+ transitionStyle = UIModalTransitionStyleCrossDissolve;
+ }
+ }
+ self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
+
+ // prevent webView from bouncing
+ if (browserOptions.disallowoverscroll) {
+ if ([self.inAppBrowserViewController.webView respondsToSelector:@selector(scrollView)]) {
+ ((UIScrollView*)[self.inAppBrowserViewController.webView scrollView]).bounces = NO;
+ } else {
+ for (id subview in self.inAppBrowserViewController.webView.subviews) {
+ if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
+ ((UIScrollView*)subview).bounces = NO;
+ }
+ }
+ }
+ }
+
+ // UIWebView options
+ self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
+ self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
+ self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
+ if (IsAtLeastiOSVersion(@"6.0")) {
+ self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
+ self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
+ }
+
+ [self.inAppBrowserViewController navigateTo:url];
+ if (!browserOptions.hidden) {
+ [self show:nil];
+ }
+}
+
+- (void)show:(CDVInvokedUrlCommand*)command
+{
+ if (self.inAppBrowserViewController == nil) {
+ NSLog(@"Tried to show IAB after it was closed.");
+ return;
+ }
+ if (_previousStatusBarStyle != -1) {
+ NSLog(@"Tried to show IAB while already shown");
+ return;
+ }
+
+ _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
+
+ __block CDVInAppBrowserNavigationController* nav = [[CDVInAppBrowserNavigationController alloc]
+ initWithRootViewController:self.inAppBrowserViewController];
+ nav.orientationDelegate = self.inAppBrowserViewController;
+ nav.navigationBarHidden = YES;
+ nav.modalPresentationStyle = self.inAppBrowserViewController.modalPresentationStyle;
+
+ __weak CDVInAppBrowser* weakSelf = self;
+
+ // Run later to avoid the "took a long time" log message.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (weakSelf.inAppBrowserViewController != nil) {
+ [weakSelf.viewController presentViewController:nav animated:YES completion:nil];
+ }
+ });
+}
+
+- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
+{
+ NSURLRequest* request = [NSURLRequest requestWithURL:url];
+
+#ifdef __CORDOVA_4_0_0
+ // the webview engine itself will filter for this according to <allow-navigation> policy
+ // in config.xml for cordova-ios-4.0
+ [self.webViewEngine loadRequest:request];
+#else
+ if ([self.commandDelegate URLIsWhitelisted:url]) {
+ [self.webView loadRequest:request];
+ } else { // this assumes the InAppBrowser can be excepted from the white-list
+ [self openInInAppBrowser:url withOptions:options];
+ }
+#endif
+}
+
+- (void)openInSystem:(NSURL*)url
+{
+ [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
+ [[UIApplication sharedApplication] openURL:url];
+}
+
+// This is a helper method for the inject{Script|Style}{Code|File} API calls, which
+// provides a consistent method for injecting JavaScript code into the document.
+//
+// If a wrapper string is supplied, then the source string will be JSON-encoded (adding
+// quotes) and wrapped using string formatting. (The wrapper string should have a single
+// '%@' marker).
+//
+// If no wrapper is supplied, then the source string is executed directly.
+
+- (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
+{
+ // Ensure an iframe bridge is created to communicate with the CDVInAppBrowserViewController
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){_cdvIframeBridge=d.getElementById('_cdvIframeBridge');if(!_cdvIframeBridge) {var e = _cdvIframeBridge = d.createElement('iframe');e.id='_cdvIframeBridge'; e.style.display='none';d.body.appendChild(e);}})(document)"];
+
+ if (jsWrapper != nil) {
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:@[source] options:0 error:nil];
+ NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ if (sourceArrayString) {
+ NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
+ NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
+ }
+ } else {
+ [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
+ }
+}
+
+- (void)injectScriptCode:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper = nil;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+encodeURIComponent(JSON.stringify([eval(%%@)]));", command.callbackId];
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectScriptFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleCode:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (void)injectStyleFile:(CDVInvokedUrlCommand*)command
+{
+ NSString* jsWrapper;
+
+ if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
+ jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
+ } else {
+ jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
+ }
+ [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
+}
+
+- (BOOL)isValidCallbackId:(NSString *)callbackId
+{
+ NSError *err = nil;
+ // Initialize on first use
+ if (self.callbackIdPattern == nil) {
+ self.callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"^InAppBrowser[0-9]{1,10}$" options:0 error:&err];
+ if (err != nil) {
+ // Couldn't initialize Regex; No is safer than Yes.
+ return NO;
+ }
+ }
+ if ([self.callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
+ return YES;
+ }
+ return NO;
+}
+
+/**
+ * The iframe bridge provided for the InAppBrowser is capable of executing any oustanding callback belonging
+ * to the InAppBrowser plugin. Care has been taken that other callbacks cannot be triggered, and that no
+ * other code execution is possible.
+ *
+ * To trigger the bridge, the iframe (or any other resource) should attempt to load a url of the form:
+ *
+ * gap-iab://<callbackId>/<arguments>
+ *
+ * where <callbackId> is the string id of the callback to trigger (something like "InAppBrowser0123456789")
+ *
+ * If present, the path component of the special gap-iab:// url is expected to be a URL-escaped JSON-encoded
+ * value to pass to the callback. [NSURL path] should take care of the URL-unescaping, and a JSON_EXCEPTION
+ * is returned if the JSON is invalid.
+ */
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ NSURL* url = request.URL;
+ BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
+
+ // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
+ // and the path, if present, should be a JSON-encoded value to pass to the callback.
+ if ([[url scheme] isEqualToString:@"gap-iab"]) {
+ NSString* scriptCallbackId = [url host];
+ CDVPluginResult* pluginResult = nil;
+
+ if ([self isValidCallbackId:scriptCallbackId]) {
+ NSString* scriptResult = [url path];
+ NSError* __autoreleasing error = nil;
+
+ // The message should be a JSON-encoded array of the result of the script which executed.
+ if ((scriptResult != nil) && ([scriptResult length] > 1)) {
+ scriptResult = [scriptResult substringFromIndex:1];
+ NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
+ if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
+ }
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
+ return NO;
+ }
+ }
+ //if is an app store link, let the system handle it, otherwise it fails to load it
+ else if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) {
+ [theWebView stopLoading];
+ [self openInSystem:url];
+ return NO;
+ }
+ else if ((self.callbackId != nil) && isTopLevelNavigation) {
+ // Send a loadstart event for each top-level navigation (includes redirects).
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
+ [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ }
+
+ return YES;
+}
+
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+ if (self.callbackId != nil) {
+ // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected).
+ NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsDictionary:@{@"type":@"loadstop", @"url":url}];
+ [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ }
+}
+
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
+{
+ if (self.callbackId != nil) {
+ NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
+ messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInteger:error.code], @"message": error.localizedDescription}];
+ [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ }
+}
+
+- (void)browserExit
+{
+ if (self.callbackId != nil) {
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+ messageAsDictionary:@{@"type":@"exit"}];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+ self.callbackId = nil;
+ }
+ // Set navigationDelegate to nil to ensure no callbacks are received from it.
+ self.inAppBrowserViewController.navigationDelegate = nil;
+ // Don't recycle the ViewController since it may be consuming a lot of memory.
+ // Also - this is required for the PDF/User-Agent bug work-around.
+ self.inAppBrowserViewController = nil;
+
+ if (IsAtLeastiOSVersion(@"7.0")) {
+ if (_previousStatusBarStyle != -1) {
+ [[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle];
+ }
+ }
+
+ _previousStatusBarStyle = -1; // this value was reset before reapplying it. caused statusbar to stay black on ios7
+}
+
+@end
+
+#pragma mark CDVInAppBrowserViewController
+
+@implementation CDVInAppBrowserViewController
+
+@synthesize currentURL;
+
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions
+{
+ self = [super init];
+ if (self != nil) {
+ _userAgent = userAgent;
+ _prevUserAgent = prevUserAgent;
+ _browserOptions = browserOptions;
+#ifdef __CORDOVA_4_0_0
+ _webViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:self];
+#else
+ _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
+#endif
+
+ [self createViews];
+ }
+
+ return self;
+}
+
+// Prevent crashes on closing windows
+-(void)dealloc {
+ self.webView.delegate = nil;
+}
+
+- (void)createViews
+{
+ // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
+
+ CGRect webViewBounds = self.view.bounds;
+ BOOL toolbarIsAtBottom = ![_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop];
+ webViewBounds.size.height -= _browserOptions.location ? FOOTER_HEIGHT : TOOLBAR_HEIGHT;
+ self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
+
+ self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+
+ [self.view addSubview:self.webView];
+ [self.view sendSubviewToBack:self.webView];
+
+ self.webView.delegate = _webViewDelegate;
+ self.webView.backgroundColor = [UIColor whiteColor];
+
+ self.webView.clearsContextBeforeDrawing = YES;
+ self.webView.clipsToBounds = YES;
+ self.webView.contentMode = UIViewContentModeScaleToFill;
+ self.webView.multipleTouchEnabled = YES;
+ self.webView.opaque = YES;
+ self.webView.scalesPageToFit = NO;
+ self.webView.userInteractionEnabled = YES;
+
+ self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
+ self.spinner.alpha = 1.000;
+ self.spinner.autoresizesSubviews = YES;
+ self.spinner.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin);
+ self.spinner.clearsContextBeforeDrawing = NO;
+ self.spinner.clipsToBounds = NO;
+ self.spinner.contentMode = UIViewContentModeScaleToFill;
+ self.spinner.frame = CGRectMake(CGRectGetMidX(self.webView.frame), CGRectGetMidY(self.webView.frame), 20.0, 20.0);
+ self.spinner.hidden = NO;
+ self.spinner.hidesWhenStopped = YES;
+ self.spinner.multipleTouchEnabled = NO;
+ self.spinner.opaque = NO;
+ self.spinner.userInteractionEnabled = NO;
+ [self.spinner stopAnimating];
+
+ self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)];
+ self.closeButton.enabled = YES;
+
+ UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
+
+ UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
+ fixedSpaceButton.width = 20;
+
+ float toolbarY = toolbarIsAtBottom ? self.view.bounds.size.height - TOOLBAR_HEIGHT : 0.0;
+ CGRect toolbarFrame = CGRectMake(0.0, toolbarY, self.view.bounds.size.width, TOOLBAR_HEIGHT);
+
+ self.toolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame];
+ self.toolbar.alpha = 1.000;
+ self.toolbar.autoresizesSubviews = YES;
+ self.toolbar.autoresizingMask = toolbarIsAtBottom ? (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin) : UIViewAutoresizingFlexibleWidth;
+ self.toolbar.barStyle = UIBarStyleBlackOpaque;
+ self.toolbar.clearsContextBeforeDrawing = NO;
+ self.toolbar.clipsToBounds = NO;
+ self.toolbar.contentMode = UIViewContentModeScaleToFill;
+ self.toolbar.hidden = NO;
+ self.toolbar.multipleTouchEnabled = NO;
+ self.toolbar.opaque = NO;
+ self.toolbar.userInteractionEnabled = YES;
+
+ CGFloat labelInset = 5.0;
+ float locationBarY = toolbarIsAtBottom ? self.view.bounds.size.height - FOOTER_HEIGHT : self.view.bounds.size.height - LOCATIONBAR_HEIGHT;
+
+ self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, locationBarY, self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)];
+ self.addressLabel.adjustsFontSizeToFitWidth = NO;
+ self.addressLabel.alpha = 1.000;
+ self.addressLabel.autoresizesSubviews = YES;
+ self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
+ self.addressLabel.backgroundColor = [UIColor clearColor];
+ self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
+ self.addressLabel.clearsContextBeforeDrawing = YES;
+ self.addressLabel.clipsToBounds = YES;
+ self.addressLabel.contentMode = UIViewContentModeScaleToFill;
+ self.addressLabel.enabled = YES;
+ self.addressLabel.hidden = NO;
+ self.addressLabel.lineBreakMode = NSLineBreakByTruncatingTail;
+
+ if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumScaleFactor:")]) {
+ [self.addressLabel setValue:@(10.0/[UIFont labelFontSize]) forKey:@"minimumScaleFactor"];
+ } else if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumFontSize:")]) {
+ [self.addressLabel setValue:@(10.0) forKey:@"minimumFontSize"];
+ }
+
+ self.addressLabel.multipleTouchEnabled = NO;
+ self.addressLabel.numberOfLines = 1;
+ self.addressLabel.opaque = NO;
+ self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0);
+ self.addressLabel.text = NSLocalizedString(@"Loading...", nil);
+ self.addressLabel.textAlignment = NSTextAlignmentLeft;
+ self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000];
+ self.addressLabel.userInteractionEnabled = NO;
+
+ NSString* frontArrowString = NSLocalizedString(@"â–º", nil); // create arrow from Unicode char
+ self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)];
+ self.forwardButton.enabled = YES;
+ self.forwardButton.imageInsets = UIEdgeInsetsZero;
+
+ NSString* backArrowString = NSLocalizedString(@"â—„", nil); // create arrow from Unicode char
+ self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)];
+ self.backButton.enabled = YES;
+ self.backButton.imageInsets = UIEdgeInsetsZero;
+
+ [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]];
+
+ self.view.backgroundColor = [UIColor grayColor];
+ [self.view addSubview:self.toolbar];
+ [self.view addSubview:self.addressLabel];
+ [self.view addSubview:self.spinner];
+}
+
+- (void) setWebViewFrame : (CGRect) frame {
+ NSLog(@"Setting the WebView's frame to %@", NSStringFromCGRect(frame));
+ [self.webView setFrame:frame];
+}
+
+- (void)setCloseButtonTitle:(NSString*)title
+{
+ // the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically
+ // but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one)
+ self.closeButton = nil;
+ self.closeButton = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)];
+ self.closeButton.enabled = YES;
+ self.closeButton.tintColor = [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1];
+
+ NSMutableArray* items = [self.toolbar.items mutableCopy];
+ [items replaceObjectAtIndex:0 withObject:self.closeButton];
+ [self.toolbar setItems:items];
+}
+
+- (void)showLocationBar:(BOOL)show
+{
+ CGRect locationbarFrame = self.addressLabel.frame;
+
+ BOOL toolbarVisible = !self.toolbar.hidden;
+
+ // prevent double show/hide
+ if (show == !(self.addressLabel.hidden)) {
+ return;
+ }
+
+ if (show) {
+ self.addressLabel.hidden = NO;
+
+ if (toolbarVisible) {
+ // toolBar at the bottom, leave as is
+ // put locationBar on top of the toolBar
+
+ CGRect webViewBounds = self.view.bounds;
+ webViewBounds.size.height -= FOOTER_HEIGHT;
+ [self setWebViewFrame:webViewBounds];
+
+ locationbarFrame.origin.y = webViewBounds.size.height;
+ self.addressLabel.frame = locationbarFrame;
+ } else {
+ // no toolBar, so put locationBar at the bottom
+
+ CGRect webViewBounds = self.view.bounds;
+ webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
+ [self setWebViewFrame:webViewBounds];
+
+ locationbarFrame.origin.y = webViewBounds.size.height;
+ self.addressLabel.frame = locationbarFrame;
+ }
+ } else {
+ self.addressLabel.hidden = YES;
+
+ if (toolbarVisible) {
+ // locationBar is on top of toolBar, hide locationBar
+
+ // webView take up whole height less toolBar height
+ CGRect webViewBounds = self.view.bounds;
+ webViewBounds.size.height -= TOOLBAR_HEIGHT;
+ [self setWebViewFrame:webViewBounds];
+ } else {
+ // no toolBar, expand webView to screen dimensions
+ [self setWebViewFrame:self.view.bounds];
+ }
+ }
+}
+
+- (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition
+{
+ CGRect toolbarFrame = self.toolbar.frame;
+ CGRect locationbarFrame = self.addressLabel.frame;
+
+ BOOL locationbarVisible = !self.addressLabel.hidden;
+
+ // prevent double show/hide
+ if (show == !(self.toolbar.hidden)) {
+ return;
+ }
+
+ if (show) {
+ self.toolbar.hidden = NO;
+ CGRect webViewBounds = self.view.bounds;
+
+ if (locationbarVisible) {
+ // locationBar at the bottom, move locationBar up
+ // put toolBar at the bottom
+ webViewBounds.size.height -= FOOTER_HEIGHT;
+ locationbarFrame.origin.y = webViewBounds.size.height;
+ self.addressLabel.frame = locationbarFrame;
+ self.toolbar.frame = toolbarFrame;
+ } else {
+ // no locationBar, so put toolBar at the bottom
+ CGRect webViewBounds = self.view.bounds;
+ webViewBounds.size.height -= TOOLBAR_HEIGHT;
+ self.toolbar.frame = toolbarFrame;
+ }
+
+ if ([toolbarPosition isEqualToString:kInAppBrowserToolbarBarPositionTop]) {
+ toolbarFrame.origin.y = 0;
+ webViewBounds.origin.y += toolbarFrame.size.height;
+ [self setWebViewFrame:webViewBounds];
+ } else {
+ toolbarFrame.origin.y = (webViewBounds.size.height + LOCATIONBAR_HEIGHT);
+ }
+ [self setWebViewFrame:webViewBounds];
+
+ } else {
+ self.toolbar.hidden = YES;
+
+ if (locationbarVisible) {
+ // locationBar is on top of toolBar, hide toolBar
+ // put locationBar at the bottom
+
+ // webView take up whole height less locationBar height
+ CGRect webViewBounds = self.view.bounds;
+ webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
+ [self setWebViewFrame:webViewBounds];
+
+ // move locationBar down
+ locationbarFrame.origin.y = webViewBounds.size.height;
+ self.addressLabel.frame = locationbarFrame;
+ } else {
+ // no locationBar, expand webView to screen dimensions
+ [self setWebViewFrame:self.view.bounds];
+ }
+ }
+}
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+}
+
+- (void)viewDidUnload
+{
+ [self.webView loadHTMLString:nil baseURL:nil];
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+ [super viewDidUnload];
+}
+
+- (UIStatusBarStyle)preferredStatusBarStyle
+{
+ return UIStatusBarStyleDefault;
+}
+
+- (BOOL)prefersStatusBarHidden {
+ return NO;
+}
+
+- (void)close
+{
+ [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
+ self.currentURL = nil;
+
+ if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) {
+ [self.navigationDelegate browserExit];
+ }
+
+ __weak UIViewController* weakSelf = self;
+
+ // Run later to avoid the "took a long time" log message.
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if ([weakSelf respondsToSelector:@selector(presentingViewController)]) {
+ [[weakSelf presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+ } else {
+ [[weakSelf parentViewController] dismissViewControllerAnimated:YES completion:nil];
+ }
+ });
+}
+
+- (void)navigateTo:(NSURL*)url
+{
+ NSURLRequest* request = [NSURLRequest requestWithURL:url];
+
+ if (_userAgentLockToken != 0) {
+ [self.webView loadRequest:request];
+ } else {
+ __weak CDVInAppBrowserViewController* weakSelf = self;
+ [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
+ _userAgentLockToken = lockToken;
+ [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
+ [weakSelf.webView loadRequest:request];
+ }];
+ }
+}
+
+- (void)goBack:(id)sender
+{
+ [self.webView goBack];
+}
+
+- (void)goForward:(id)sender
+{
+ [self.webView goForward];
+}
+
+- (void)viewWillAppear:(BOOL)animated
+{
+ if (IsAtLeastiOSVersion(@"7.0")) {
+ [[UIApplication sharedApplication] setStatusBarStyle:[self preferredStatusBarStyle]];
+ }
+ [self rePositionViews];
+
+ [super viewWillAppear:animated];
+}
+
+//
+// On iOS 7 the status bar is part of the view's dimensions, therefore it's height has to be taken into account.
+// The height of it could be hardcoded as 20 pixels, but that would assume that the upcoming releases of iOS won't
+// change that value.
+//
+- (float) getStatusBarOffset {
+ CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
+ float statusBarOffset = IsAtLeastiOSVersion(@"7.0") ? MIN(statusBarFrame.size.width, statusBarFrame.size.height) : 0.0;
+ return statusBarOffset;
+}
+
+- (void) rePositionViews {
+ if ([_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]) {
+ [self.webView setFrame:CGRectMake(self.webView.frame.origin.x, TOOLBAR_HEIGHT, self.webView.frame.size.width, self.webView.frame.size.height)];
+ [self.toolbar setFrame:CGRectMake(self.toolbar.frame.origin.x, [self getStatusBarOffset], self.toolbar.frame.size.width, self.toolbar.frame.size.height)];
+ }
+}
+
+#pragma mark UIWebViewDelegate
+
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+ // loading url, start spinner, update back/forward
+
+ self.addressLabel.text = NSLocalizedString(@"Loading...", nil);
+ self.backButton.enabled = theWebView.canGoBack;
+ self.forwardButton.enabled = theWebView.canGoForward;
+
+ [self.spinner startAnimating];
+
+ return [self.navigationDelegate webViewDidStartLoad:theWebView];
+}
+
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
+
+ if (isTopLevelNavigation) {
+ self.currentURL = request.URL;
+ }
+ return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+ // update url, stop spinner, update back/forward
+
+ self.addressLabel.text = [self.currentURL absoluteString];
+ self.backButton.enabled = theWebView.canGoBack;
+ self.forwardButton.enabled = theWebView.canGoForward;
+
+ [self.spinner stopAnimating];
+
+ // Work around a bug where the first time a PDF is opened, all UIWebViews
+ // reload their User-Agent from NSUserDefaults.
+ // This work-around makes the following assumptions:
+ // 1. The app has only a single Cordova Webview. If not, then the app should
+ // take it upon themselves to load a PDF in the background as a part of
+ // their start-up flow.
+ // 2. That the PDF does not require any additional network requests. We change
+ // the user-agent here back to that of the CDVViewController, so requests
+ // from it must pass through its white-list. This *does* break PDFs that
+ // contain links to other remote PDF/websites.
+ // More info at https://issues.apache.org/jira/browse/CB-2225
+ BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
+ if (isPDF) {
+ [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
+ }
+
+ [self.navigationDelegate webViewDidFinishLoad:theWebView];
+}
+
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
+{
+ // log fail message, stop spinner, update back/forward
+ NSLog(@"webView:didFailLoadWithError - %ld: %@", (long)error.code, [error localizedDescription]);
+
+ self.backButton.enabled = theWebView.canGoBack;
+ self.forwardButton.enabled = theWebView.canGoForward;
+ [self.spinner stopAnimating];
+
+ self.addressLabel.text = NSLocalizedString(@"Load Error", nil);
+
+ [self.navigationDelegate webView:theWebView didFailLoadWithError:error];
+}
+
+#pragma mark CDVScreenOrientationDelegate
+
+- (BOOL)shouldAutorotate
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
+ return [self.orientationDelegate shouldAutorotate];
+ }
+ return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
+ return [self.orientationDelegate supportedInterfaceOrientations];
+ }
+
+ return 1 << UIInterfaceOrientationPortrait;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
+ return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
+ }
+
+ return YES;
+}
+
+@end
+
+@implementation CDVInAppBrowserOptions
+
+- (id)init
+{
+ if (self = [super init]) {
+ // default values
+ self.location = YES;
+ self.toolbar = YES;
+ self.closebuttoncaption = nil;
+ self.toolbarposition = kInAppBrowserToolbarBarPositionBottom;
+ self.clearcache = NO;
+ self.clearsessioncache = NO;
+
+ self.enableviewportscale = NO;
+ self.mediaplaybackrequiresuseraction = NO;
+ self.allowinlinemediaplayback = NO;
+ self.keyboarddisplayrequiresuseraction = YES;
+ self.suppressesincrementalrendering = NO;
+ self.hidden = NO;
+ self.disallowoverscroll = NO;
+ }
+
+ return self;
+}
+
++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
+{
+ CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init];
+
+ // NOTE: this parsing does not handle quotes within values
+ NSArray* pairs = [options componentsSeparatedByString:@","];
+
+ // parse keys and values, set the properties
+ for (NSString* pair in pairs) {
+ NSArray* keyvalue = [pair componentsSeparatedByString:@"="];
+
+ if ([keyvalue count] == 2) {
+ NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
+ NSString* value = [keyvalue objectAtIndex:1];
+ NSString* value_lc = [value lowercaseString];
+
+ BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"];
+ NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
+ [numberFormatter setAllowsFloats:YES];
+ BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil;
+
+ // set the property according to the key name
+ if ([obj respondsToSelector:NSSelectorFromString(key)]) {
+ if (isNumber) {
+ [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key];
+ } else if (isBoolean) {
+ [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key];
+ } else {
+ [obj setValue:value forKey:key];
+ }
+ }
+ }
+ }
+
+ return obj;
+}
+
+@end
+
+@implementation CDVInAppBrowserNavigationController : UINavigationController
+
+- (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
+ if ( self.presentedViewController) {
+ [super dismissViewControllerAnimated:flag completion:completion];
+ }
+}
+
+- (void) viewDidLoad {
+
+ CGRect frame = [UIApplication sharedApplication].statusBarFrame;
+
+ // simplified from: http://stackoverflow.com/a/25669695/219684
+
+ UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:[self invertFrameIfNeeded:frame]];
+ bgToolbar.barStyle = UIBarStyleDefault;
+ [bgToolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
+ [self.view addSubview:bgToolbar];
+
+ [super viewDidLoad];
+}
+
+- (CGRect) invertFrameIfNeeded:(CGRect)rect {
+ // We need to invert since on iOS 7 frames are always in Portrait context
+ if (!IsAtLeastiOSVersion(@"8.0")) {
+ if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
+ CGFloat temp = rect.size.width;
+ rect.size.width = rect.size.height;
+ rect.size.height = temp;
+ }
+ rect.origin = CGPointZero;
+ }
+ return rect;
+}
+
+#pragma mark CDVScreenOrientationDelegate
+
+- (BOOL)shouldAutorotate
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
+ return [self.orientationDelegate shouldAutorotate];
+ }
+ return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
+ return [self.orientationDelegate supportedInterfaceOrientations];
+ }
+
+ return 1 << UIInterfaceOrientationPortrait;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+ if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
+ return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
+ }
+
+ return YES;
+}
+
+
+@end
+
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h
new file mode 100644
index 00000000..8add0279
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.h
@@ -0,0 +1,34 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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/Foundation.h>
+#import <Cordova/CDVPlugin.h>
+#import "CDVReachability.h"
+
+@interface CDVConnection : CDVPlugin {
+ NSString* type;
+ NSString* _callbackId;
+
+ CDVReachability* internetReach;
+}
+
+@property (copy) NSString* connectionType;
+@property (strong) CDVReachability* internetReach;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m
new file mode 100644
index 00000000..6715322a
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVConnection.m
@@ -0,0 +1,156 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 <CoreTelephony/CTTelephonyNetworkInfo.h>
+
+#import "CDVConnection.h"
+#import "CDVReachability.h"
+
+@interface CDVConnection (PrivateMethods)
+- (void)updateOnlineStatus;
+- (void)sendPluginResult;
+@end
+
+@implementation CDVConnection
+
+@synthesize connectionType, internetReach;
+
+- (void)getConnectionInfo:(CDVInvokedUrlCommand*)command
+{
+ _callbackId = command.callbackId;
+ [self sendPluginResult];
+}
+
+- (void)sendPluginResult
+{
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:self.connectionType];
+
+ [result setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:result callbackId:_callbackId];
+}
+
+- (NSString*)w3cConnectionTypeFor:(CDVReachability*)reachability
+{
+ NetworkStatus networkStatus = [reachability currentReachabilityStatus];
+
+ switch (networkStatus) {
+ case NotReachable:
+ return @"none";
+
+ case ReachableViaWWAN:
+ {
+ BOOL isConnectionRequired = [reachability connectionRequired];
+ if (isConnectionRequired) {
+ return @"none";
+ } else {
+ if ([[[UIDevice currentDevice] systemVersion] compare:@"7.0" options:NSNumericSearch] != NSOrderedAscending) {
+ CTTelephonyNetworkInfo *telephonyInfo = [CTTelephonyNetworkInfo new];
+ if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyGPRS]) {
+ return @"2g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyEdge]) {
+ return @"2g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyWCDMA]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSDPA]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyHSUPA]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMA1x]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORev0]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevA]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyCDMAEVDORevB]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyeHRPD]) {
+ return @"3g";
+ } else if ([telephonyInfo.currentRadioAccessTechnology isEqualToString:CTRadioAccessTechnologyLTE]) {
+ return @"4g";
+ }
+ }
+ return @"cellular";
+ }
+ }
+ case ReachableViaWiFi:
+ return @"wifi";
+
+ default:
+ return @"unknown";
+ }
+}
+
+- (BOOL)isCellularConnection:(NSString*)theConnectionType
+{
+ return [theConnectionType isEqualToString:@"2g"] ||
+ [theConnectionType isEqualToString:@"3g"] ||
+ [theConnectionType isEqualToString:@"4g"] ||
+ [theConnectionType isEqualToString:@"cellular"];
+}
+
+- (void)updateReachability:(CDVReachability*)reachability
+{
+ if (reachability) {
+ // check whether the connection type has changed
+ NSString* newConnectionType = [self w3cConnectionTypeFor:reachability];
+ if ([newConnectionType isEqualToString:self.connectionType]) { // the same as before, remove dupes
+ return;
+ } else {
+ self.connectionType = [self w3cConnectionTypeFor:reachability];
+ }
+ }
+ [self sendPluginResult];
+}
+
+- (void)updateConnectionType:(NSNotification*)note
+{
+ CDVReachability* curReach = [note object];
+
+ if ((curReach != nil) && [curReach isKindOfClass:[CDVReachability class]]) {
+ [self updateReachability:curReach];
+ }
+}
+
+- (void)onPause
+{
+ [self.internetReach stopNotifier];
+}
+
+- (void)onResume
+{
+ [self.internetReach startNotifier];
+ [self updateReachability:self.internetReach];
+}
+
+- (void)pluginInitialize
+{
+ self.connectionType = @"none";
+ self.internetReach = [CDVReachability reachabilityForInternetConnection];
+ self.connectionType = [self w3cConnectionTypeFor:self.internetReach];
+ [self.internetReach startNotifier];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateConnectionType:)
+ name:kReachabilityChangedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateConnectionType:)
+ name:CTRadioAccessTechnologyDidChangeNotification object:nil];
+ if (UIApplicationDidEnterBackgroundNotification && UIApplicationWillEnterForegroundNotification) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
+ }
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h
new file mode 100644
index 00000000..01a95c35
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.h
@@ -0,0 +1,85 @@
+/*
+
+ File: Reachability.h
+ Abstract: Basic demonstration of how to use the SystemConfiguration Reachability APIs.
+ Version: 2.2
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under
+ Apple's copyrights in this original Apple software (the "Apple Software"), to
+ use, reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions
+ of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+ to endorse or promote products derived from the Apple Software without specific
+ prior written permission from Apple. Except as expressly stated in this notice,
+ no other rights or licenses, express or implied, are granted by Apple herein,
+ including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be
+ incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+ DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+ CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+ APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2010 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <Foundation/Foundation.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+#import <netinet/in.h>
+
+typedef enum {
+ NotReachable = 0,
+ ReachableViaWWAN, // this value has been swapped with ReachableViaWiFi for Cordova backwards compat. reasons
+ ReachableViaWiFi // this value has been swapped with ReachableViaWWAN for Cordova backwards compat. reasons
+} NetworkStatus;
+#define kReachabilityChangedNotification @"kNetworkReachabilityChangedNotification"
+
+@interface CDVReachability : NSObject
+{
+ BOOL localWiFiRef;
+ SCNetworkReachabilityRef reachabilityRef;
+}
+
+// reachabilityWithHostName- Use to check the reachability of a particular host name.
++ (CDVReachability*)reachabilityWithHostName:(NSString*)hostName;
+
+// reachabilityWithAddress- Use to check the reachability of a particular IP address.
++ (CDVReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
+
+// reachabilityForInternetConnection- checks whether the default route is available.
+// Should be used by applications that do not connect to a particular host
++ (CDVReachability*)reachabilityForInternetConnection;
+
+// reachabilityForLocalWiFi- checks whether a local wifi connection is available.
++ (CDVReachability*)reachabilityForLocalWiFi;
+
+// Start listening for reachability notifications on the current run loop
+- (BOOL)startNotifier;
+- (void)stopNotifier;
+
+- (NetworkStatus)currentReachabilityStatus;
+// WWAN may be available, but not active until a connection has been established.
+// WiFi may require a connection for VPN on Demand.
+- (BOOL)connectionRequired;
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m
new file mode 100644
index 00000000..1399867e
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-network-information/CDVReachability.m
@@ -0,0 +1,266 @@
+/*
+
+ File: Reachability.m
+ Abstract: Basic demonstration of how to use the SystemConfiguration Reachability APIs.
+ Version: 2.2
+
+ Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
+ ("Apple") in consideration of your agreement to the following terms, and your
+ use, installation, modification or redistribution of this Apple software
+ constitutes acceptance of these terms. If you do not agree with these terms,
+ please do not use, install, modify or redistribute this Apple software.
+
+ In consideration of your agreement to abide by the following terms, and subject
+ to these terms, Apple grants you a personal, non-exclusive license, under
+ Apple's copyrights in this original Apple software (the "Apple Software"), to
+ use, reproduce, modify and redistribute the Apple Software, with or without
+ modifications, in source and/or binary forms; provided that if you redistribute
+ the Apple Software in its entirety and without modifications, you must retain
+ this notice and the following text and disclaimers in all such redistributions
+ of the Apple Software.
+ Neither the name, trademarks, service marks or logos of Apple Inc. may be used
+ to endorse or promote products derived from the Apple Software without specific
+ prior written permission from Apple. Except as expressly stated in this notice,
+ no other rights or licenses, express or implied, are granted by Apple herein,
+ including but not limited to any patent rights that may be infringed by your
+ derivative works or by other works in which the Apple Software may be
+ incorporated.
+
+ The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ COMBINATION WITH YOUR PRODUCTS.
+
+ IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
+ DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
+ CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
+ APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ Copyright (C) 2010 Apple Inc. All Rights Reserved.
+
+*/
+
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+
+#import <CoreFoundation/CoreFoundation.h>
+
+#import "CDVReachability.h"
+
+#define kShouldPrintReachabilityFlags 0
+
+static void CDVPrintReachabilityFlags(SCNetworkReachabilityFlags flags, const char* comment)
+{
+#if kShouldPrintReachabilityFlags
+ NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
+ (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-',
+ (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-',
+
+ (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-',
+ (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+ (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-',
+ (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-',
+ (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-',
+ comment
+ );
+#endif
+}
+
+@implementation CDVReachability
+
+static void CDVReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
+{
+#pragma unused (target, flags)
+ // NSCAssert(info != NULL, @"info was NULL in ReachabilityCallback");
+ // NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], @"info was wrong class in ReachabilityCallback");
+
+ // Converted the asserts above to conditionals, with safe return from the function
+ if (info == NULL) {
+ NSLog(@"info was NULL in ReachabilityCallback");
+ return;
+ }
+
+ if (![(__bridge NSObject*)info isKindOfClass :[CDVReachability class]]) {
+ NSLog(@"info was wrong class in ReachabilityCallback");
+ return;
+ }
+
+ // We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
+ // in case someon uses the Reachability object in a different thread.
+ @autoreleasepool {
+ CDVReachability* noteObject = (__bridge CDVReachability*)info;
+ // Post a notification to notify the client that the network reachability changed.
+ [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification object:noteObject];
+ }
+}
+
+- (BOOL)startNotifier
+{
+ BOOL retVal = NO;
+ SCNetworkReachabilityContext context = {0, (__bridge void*)(self), NULL, NULL, NULL};
+
+ if (SCNetworkReachabilitySetCallback(reachabilityRef, CDVReachabilityCallback, &context)) {
+ if (SCNetworkReachabilityScheduleWithRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
+ retVal = YES;
+ }
+ }
+ return retVal;
+}
+
+- (void)stopNotifier
+{
+ if (reachabilityRef != NULL) {
+ SCNetworkReachabilityUnscheduleFromRunLoop(reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
+ }
+}
+
+- (void)dealloc
+{
+ [self stopNotifier];
+ if (reachabilityRef != NULL) {
+ CFRelease(reachabilityRef);
+ }
+}
+
++ (CDVReachability*)reachabilityWithHostName:(NSString*)hostName;
+{
+ CDVReachability* retVal = NULL;
+ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
+ if (reachability != NULL) {
+ retVal = [[self alloc] init];
+ if (retVal != NULL) {
+ retVal->reachabilityRef = reachability;
+ retVal->localWiFiRef = NO;
+ }
+ else {
+ CFRelease(reachability);
+ }
+ }
+ return retVal;
+}
+
++ (CDVReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress;
+{
+ SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
+ CDVReachability* retVal = NULL;
+ if (reachability != NULL) {
+ retVal = [[self alloc] init];
+ if (retVal != NULL) {
+ retVal->reachabilityRef = reachability;
+ retVal->localWiFiRef = NO;
+ }
+ else {
+ CFRelease(reachability);
+ }
+ }
+ return retVal;
+}
+
++ (CDVReachability*)reachabilityForInternetConnection;
+{
+ struct sockaddr_in zeroAddress;
+ bzero(&zeroAddress, sizeof(zeroAddress));
+ zeroAddress.sin_len = sizeof(zeroAddress);
+ zeroAddress.sin_family = AF_INET;
+ return [self reachabilityWithAddress:&zeroAddress];
+}
+
++ (CDVReachability*)reachabilityForLocalWiFi;
+{
+ struct sockaddr_in localWifiAddress;
+ bzero(&localWifiAddress, sizeof(localWifiAddress));
+ localWifiAddress.sin_len = sizeof(localWifiAddress);
+ localWifiAddress.sin_family = AF_INET;
+ // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
+ localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
+ CDVReachability* retVal = [self reachabilityWithAddress:&localWifiAddress];
+ if (retVal != NULL) {
+ retVal->localWiFiRef = YES;
+ }
+ return retVal;
+}
+
+#pragma mark Network Flag Handling
+
+- (NetworkStatus)localWiFiStatusForFlags:(SCNetworkReachabilityFlags)flags
+{
+ CDVPrintReachabilityFlags(flags, "localWiFiStatusForFlags");
+
+ BOOL retVal = NotReachable;
+ if ((flags & kSCNetworkReachabilityFlagsReachable) && (flags & kSCNetworkReachabilityFlagsIsDirect)) {
+ retVal = ReachableViaWiFi;
+ }
+ return retVal;
+}
+
+- (NetworkStatus)networkStatusForFlags:(SCNetworkReachabilityFlags)flags
+{
+ CDVPrintReachabilityFlags(flags, "networkStatusForFlags");
+ if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
+ // if target host is not reachable
+ return NotReachable;
+ }
+
+ NetworkStatus retVal = NotReachable;
+
+ if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
+ // if target host is reachable and no connection is required
+ // then we'll assume (for now) that your on Wi-Fi
+ retVal = ReachableViaWiFi;
+ }
+
+ if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
+ ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))) {
+ // ... and the connection is on-demand (or on-traffic) if the
+ // calling application is using the CFSocketStream or higher APIs
+
+ if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
+ // ... and no [user] intervention is needed
+ retVal = ReachableViaWiFi;
+ }
+ }
+
+ if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
+ // ... but WWAN connections are OK if the calling application
+ // is using the CFNetwork (CFSocketStream?) APIs.
+ retVal = ReachableViaWWAN;
+ }
+ return retVal;
+}
+
+- (BOOL)connectionRequired;
+{
+ NSAssert(reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
+ SCNetworkReachabilityFlags flags;
+ if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
+ return flags & kSCNetworkReachabilityFlagsConnectionRequired;
+ }
+ return NO;
+}
+
+- (NetworkStatus)currentReachabilityStatus
+{
+ NSAssert(reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
+ NetworkStatus retVal = NotReachable;
+ SCNetworkReachabilityFlags flags;
+ if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) {
+ if (localWiFiRef) {
+ retVal = [self localWiFiStatusForFlags:flags];
+ } else {
+ retVal = [self networkStatusForFlags:flags];
+ }
+ }
+ return retVal;
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h
new file mode 100644
index 00000000..ec5d6022
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.h
@@ -0,0 +1,45 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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/Foundation.h>
+#import <Cordova/CDVPlugin.h>
+
+typedef struct {
+ BOOL iPhone;
+ BOOL iPad;
+ BOOL iPhone4;
+ BOOL iPhone5;
+ BOOL iPhone6;
+ BOOL iPhone6Plus;
+ BOOL retina;
+
+} CDV_iOSDevice;
+
+@interface CDVSplashScreen : CDVPlugin {
+ UIActivityIndicatorView* _activityView;
+ UIImageView* _imageView;
+ NSString* _curImageName;
+ BOOL _visible;
+ BOOL _destroyed;
+}
+
+- (void)show:(CDVInvokedUrlCommand*)command;
+- (void)hide:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m
new file mode 100644
index 00000000..8ad8116b
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVSplashScreen.m
@@ -0,0 +1,483 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVSplashScreen.h"
+#import <Cordova/CDVViewController.h>
+#import <Cordova/CDVScreenOrientationDelegate.h>
+#import "CDVViewController+SplashScreen.h"
+
+#define kSplashScreenDurationDefault 3000.0f
+#define kFadeDurationDefault 500.0f
+
+
+@implementation CDVSplashScreen
+
+- (void)pluginInitialize
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil];
+
+ [self setVisible:YES];
+}
+
+- (void)show:(CDVInvokedUrlCommand*)command
+{
+ [self setVisible:YES];
+}
+
+- (void)hide:(CDVInvokedUrlCommand*)command
+{
+ [self setVisible:NO andForce:YES];
+}
+
+- (void)pageDidLoad
+{
+ id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
+
+ // if value is missing, default to yes
+ if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
+ [self setVisible:NO];
+ }
+}
+
+- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
+{
+ [self updateImage];
+}
+
+- (void)createViews
+{
+ /*
+ * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style.
+ *
+ * whiteLarge = UIActivityIndicatorViewStyleWhiteLarge
+ * white = UIActivityIndicatorViewStyleWhite
+ * gray = UIActivityIndicatorViewStyleGray
+ *
+ */
+
+ // Determine whether rotation should be enabled for this device
+ // Per iOS HIG, landscape is only supported on iPad and iPhone 6+
+ CDV_iOSDevice device = [self getCurrentDevice];
+ BOOL autorotateValue = (device.iPad || device.iPhone6Plus) ?
+ [(CDVViewController *)self.viewController shouldAutorotateDefaultValue] :
+ NO;
+
+ [(CDVViewController *)self.viewController setEnabledAutorotation:autorotateValue];
+
+ NSString* topActivityIndicator = [self.commandDelegate.settings objectForKey:[@"TopActivityIndicator" lowercaseString]];
+ UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+
+ if ([topActivityIndicator isEqualToString:@"whiteLarge"])
+ {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
+ }
+ else if ([topActivityIndicator isEqualToString:@"white"])
+ {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
+ }
+ else if ([topActivityIndicator isEqualToString:@"gray"])
+ {
+ topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+ }
+
+ UIView* parentView = self.viewController.view;
+ parentView.userInteractionEnabled = NO; // disable user interaction while splashscreen is shown
+ _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
+ _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2);
+ _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin
+ | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
+ [_activityView startAnimating];
+
+ // Set the frame & image later.
+ _imageView = [[UIImageView alloc] init];
+ [parentView addSubview:_imageView];
+
+ id showSplashScreenSpinnerValue = [self.commandDelegate.settings objectForKey:[@"ShowSplashScreenSpinner" lowercaseString]];
+ // backwards compatibility - if key is missing, default to true
+ if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue])
+ {
+ [parentView addSubview:_activityView];
+ }
+
+ // Frame is required when launching in portrait mode.
+ // Bounds for landscape since it captures the rotation.
+ [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil];
+ [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
+
+ [self updateImage];
+ _destroyed = NO;
+}
+
+- (void)hideViews
+{
+ [_imageView setAlpha:0];
+ [_activityView setAlpha:0];
+}
+
+- (void)destroyViews
+{
+ _destroyed = YES;
+ [(CDVViewController *)self.viewController setEnabledAutorotation:[(CDVViewController *)self.viewController shouldAutorotateDefaultValue]];
+
+ [_imageView removeFromSuperview];
+ [_activityView removeFromSuperview];
+ _imageView = nil;
+ _activityView = nil;
+ _curImageName = nil;
+
+ self.viewController.view.userInteractionEnabled = YES; // re-enable user interaction upon completion
+ @try {
+ [self.viewController.view removeObserver:self forKeyPath:@"frame"];
+ [self.viewController.view removeObserver:self forKeyPath:@"bounds"];
+ }
+ @catch (NSException *exception) {
+ // When reloading the page from a remotely connected Safari, there
+ // are no observers, so the removeObserver method throws an exception,
+ // that we can safely ignore.
+ // Alternatively we can check whether there are observers before calling removeObserver
+ }
+}
+
+- (CDV_iOSDevice) getCurrentDevice
+{
+ CDV_iOSDevice device;
+
+ UIScreen* mainScreen = [UIScreen mainScreen];
+ CGFloat mainScreenHeight = mainScreen.bounds.size.height;
+ CGFloat mainScreenWidth = mainScreen.bounds.size.width;
+
+ int limit = MAX(mainScreenHeight,mainScreenWidth);
+
+ device.iPad = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
+ device.iPhone = (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone);
+ device.retina = ([mainScreen scale] == 2.0);
+ device.iPhone4 = (device.iPhone && limit == 480.0);
+ device.iPhone5 = (device.iPhone && limit == 568.0);
+ // note these below is not a true device detect, for example if you are on an
+ // iPhone 6/6+ but the app is scaled it will prob set iPhone5 as true, but
+ // this is appropriate for detecting the runtime screen environment
+ device.iPhone6 = (device.iPhone && limit == 667.0);
+ device.iPhone6Plus = (device.iPhone && limit == 736.0);
+
+ return device;
+}
+
+- (NSString*)getImageName:(UIInterfaceOrientation)currentOrientation delegate:(id<CDVScreenOrientationDelegate>)orientationDelegate device:(CDV_iOSDevice)device
+{
+ // Use UILaunchImageFile if specified in plist. Otherwise, use Default.
+ NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"];
+
+ NSUInteger supportedOrientations = [orientationDelegate supportedInterfaceOrientations];
+
+ // Checks to see if the developer has locked the orientation to use only one of Portrait or Landscape
+ BOOL supportsLandscape = (supportedOrientations & UIInterfaceOrientationMaskLandscape);
+ BOOL supportsPortrait = (supportedOrientations & UIInterfaceOrientationMaskPortrait || supportedOrientations & UIInterfaceOrientationMaskPortraitUpsideDown);
+ // this means there are no mixed orientations in there
+ BOOL isOrientationLocked = !(supportsPortrait && supportsLandscape);
+
+ if (imageName)
+ {
+ imageName = [imageName stringByDeletingPathExtension];
+ }
+ else
+ {
+ imageName = @"Default";
+ }
+
+ // Add Asset Catalog specific prefixes
+ if ([imageName isEqualToString:@"LaunchImage"])
+ {
+ if (device.iPhone4 || device.iPhone5 || device.iPad) {
+ imageName = [imageName stringByAppendingString:@"-700"];
+ } else if(device.iPhone6) {
+ imageName = [imageName stringByAppendingString:@"-800"];
+ } else if(device.iPhone6Plus) {
+ imageName = [imageName stringByAppendingString:@"-800"];
+ if (currentOrientation == UIInterfaceOrientationPortrait || currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
+ {
+ imageName = [imageName stringByAppendingString:@"-Portrait"];
+ }
+ }
+ }
+
+ if (device.iPhone5)
+ { // does not support landscape
+ imageName = [imageName stringByAppendingString:@"-568h"];
+ }
+ else if (device.iPhone6)
+ { // does not support landscape
+ imageName = [imageName stringByAppendingString:@"-667h"];
+ }
+ else if (device.iPhone6Plus)
+ { // supports landscape
+ if (isOrientationLocked)
+ {
+ imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"")];
+ }
+ else
+ {
+ switch (currentOrientation)
+ {
+ case UIInterfaceOrientationLandscapeLeft:
+ case UIInterfaceOrientationLandscapeRight:
+ imageName = [imageName stringByAppendingString:@"-Landscape"];
+ break;
+ default:
+ break;
+ }
+ }
+ imageName = [imageName stringByAppendingString:@"-736h"];
+
+ }
+ else if (device.iPad)
+ { // supports landscape
+ if (isOrientationLocked)
+ {
+ imageName = [imageName stringByAppendingString:(supportsLandscape ? @"-Landscape" : @"-Portrait")];
+ }
+ else
+ {
+ switch (currentOrientation)
+ {
+ case UIInterfaceOrientationLandscapeLeft:
+ case UIInterfaceOrientationLandscapeRight:
+ imageName = [imageName stringByAppendingString:@"-Landscape"];
+ break;
+
+ case UIInterfaceOrientationPortrait:
+ case UIInterfaceOrientationPortraitUpsideDown:
+ default:
+ imageName = [imageName stringByAppendingString:@"-Portrait"];
+ break;
+ }
+ }
+ }
+
+ return imageName;
+}
+
+- (UIInterfaceOrientation)getCurrentOrientation
+{
+ UIInterfaceOrientation iOrientation = [UIApplication sharedApplication].statusBarOrientation;
+ UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation;
+
+ bool landscape;
+
+ if (dOrientation == UIDeviceOrientationUnknown || dOrientation == UIDeviceOrientationFaceUp || dOrientation == UIDeviceOrientationFaceDown) {
+ // If the device is laying down, use the UIInterfaceOrientation based on the status bar.
+ landscape = UIInterfaceOrientationIsLandscape(iOrientation);
+ } else {
+ // If the device is not laying down, use UIDeviceOrientation.
+ landscape = UIDeviceOrientationIsLandscape(dOrientation);
+
+ // There's a bug in iOS!!!! http://openradar.appspot.com/7216046
+ // So values needs to be reversed for landscape!
+ if (dOrientation == UIDeviceOrientationLandscapeLeft)
+ {
+ iOrientation = UIInterfaceOrientationLandscapeRight;
+ }
+ else if (dOrientation == UIDeviceOrientationLandscapeRight)
+ {
+ iOrientation = UIInterfaceOrientationLandscapeLeft;
+ }
+ else if (dOrientation == UIDeviceOrientationPortrait)
+ {
+ iOrientation = UIInterfaceOrientationPortrait;
+ }
+ else if (dOrientation == UIDeviceOrientationPortraitUpsideDown)
+ {
+ iOrientation = UIInterfaceOrientationPortraitUpsideDown;
+ }
+ }
+
+ return iOrientation;
+}
+
+// Sets the view's frame and image.
+- (void)updateImage
+{
+ NSString* imageName = [self getImageName:[self getCurrentOrientation] delegate:(id<CDVScreenOrientationDelegate>)self.viewController device:[self getCurrentDevice]];
+
+ if (![imageName isEqualToString:_curImageName])
+ {
+ UIImage* img = [UIImage imageNamed:imageName];
+ _imageView.image = img;
+ _curImageName = imageName;
+ }
+
+ // Check that splash screen's image exists before updating bounds
+ if (_imageView.image)
+ {
+ [self updateBounds];
+ }
+ else
+ {
+ NSLog(@"WARNING: The splashscreen image named %@ was not found", imageName);
+ }
+}
+
+- (void)updateBounds
+{
+ UIImage* img = _imageView.image;
+ CGRect imgBounds = (img) ? CGRectMake(0, 0, img.size.width, img.size.height) : CGRectZero;
+
+ CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size;
+ UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
+ CGAffineTransform imgTransform = CGAffineTransformIdentity;
+
+ /* If and only if an iPhone application is landscape-only as per
+ * UISupportedInterfaceOrientations, the view controller's orientation is
+ * landscape. In this case the image must be rotated in order to appear
+ * correctly.
+ */
+ CDV_iOSDevice device = [self getCurrentDevice];
+ if (UIInterfaceOrientationIsLandscape(orientation) && !device.iPhone6Plus && !device.iPad)
+ {
+ imgTransform = CGAffineTransformMakeRotation(M_PI / 2);
+ imgBounds.size = CGSizeMake(imgBounds.size.height, imgBounds.size.width);
+ }
+
+ // There's a special case when the image is the size of the screen.
+ if (CGSizeEqualToSize(screenSize, imgBounds.size))
+ {
+ CGRect statusFrame = [self.viewController.view convertRect:[UIApplication sharedApplication].statusBarFrame fromView:nil];
+ if (!(IsAtLeastiOSVersion(@"7.0")))
+ {
+ imgBounds.origin.y -= statusFrame.size.height;
+ }
+ }
+ else if (imgBounds.size.width > 0)
+ {
+ CGRect viewBounds = self.viewController.view.bounds;
+ CGFloat imgAspect = imgBounds.size.width / imgBounds.size.height;
+ CGFloat viewAspect = viewBounds.size.width / viewBounds.size.height;
+ // This matches the behaviour of the native splash screen.
+ CGFloat ratio;
+ if (viewAspect > imgAspect)
+ {
+ ratio = viewBounds.size.width / imgBounds.size.width;
+ }
+ else
+ {
+ ratio = viewBounds.size.height / imgBounds.size.height;
+ }
+ imgBounds.size.height *= ratio;
+ imgBounds.size.width *= ratio;
+ }
+
+ _imageView.transform = imgTransform;
+ _imageView.frame = imgBounds;
+}
+
+- (void)setVisible:(BOOL)visible
+{
+ [self setVisible:visible andForce:NO];
+}
+
+- (void)setVisible:(BOOL)visible andForce:(BOOL)force
+{
+ if (visible != _visible || force)
+ {
+ _visible = visible;
+
+ id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreen" lowercaseString]];
+ id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:[@"FadeSplashScreenDuration" lowercaseString]];
+
+ float fadeDuration = fadeSplashScreenDuration == nil ? kFadeDurationDefault : [fadeSplashScreenDuration floatValue];
+
+ id splashDurationString = [self.commandDelegate.settings objectForKey: [@"SplashScreenDelay" lowercaseString]];
+ float splashDuration = splashDurationString == nil ? kSplashScreenDurationDefault : [splashDurationString floatValue];
+
+ id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:[@"AutoHideSplashScreen" lowercaseString]];
+ BOOL autoHideSplashScreen = true;
+
+ if (autoHideSplashScreenValue != nil) {
+ autoHideSplashScreen = [autoHideSplashScreenValue boolValue];
+ }
+
+ if (!autoHideSplashScreen) {
+ // CB-10412 SplashScreenDelay does not make sense if the splashscreen is hidden manually
+ splashDuration = 0;
+ }
+
+
+ if (fadeSplashScreenValue == nil)
+ {
+ fadeSplashScreenValue = @"true";
+ }
+
+ if (![fadeSplashScreenValue boolValue])
+ {
+ fadeDuration = 0;
+ }
+ else if (fadeDuration < 30)
+ {
+ // [CB-9750] This value used to be in decimal seconds, so we will assume that if someone specifies 10
+ // they mean 10 seconds, and not the meaningless 10ms
+ fadeDuration *= 1000;
+ }
+
+ if (_visible)
+ {
+ if (_imageView == nil)
+ {
+ [self createViews];
+ }
+ }
+ else if (fadeDuration == 0 && splashDuration == 0)
+ {
+ [self destroyViews];
+ }
+ else
+ {
+ __weak __typeof(self) weakSelf = self;
+ float effectiveSplashDuration;
+
+ // [CB-10562] AutoHideSplashScreen may be "true" but we should still be able to hide the splashscreen manually.
+ if (!autoHideSplashScreen || force) {
+ effectiveSplashDuration = (fadeDuration) / 1000;
+ } else {
+ effectiveSplashDuration = (splashDuration - fadeDuration) / 1000;
+ }
+
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (uint64_t) effectiveSplashDuration * NSEC_PER_SEC), dispatch_get_main_queue(), CFBridgingRelease(CFBridgingRetain(^(void) {
+ if (!_destroyed) {
+ [UIView transitionWithView:self.viewController.view
+ duration:(fadeDuration / 1000)
+ options:UIViewAnimationOptionTransitionNone
+ animations:^(void) {
+ [weakSelf hideViews];
+ }
+ completion:^(BOOL finished) {
+ // Always destroy views, otherwise you could have an
+ // invisible splashscreen that is overlayed over your active views
+ // which causes that no touch events are passed
+ if (!_destroyed) {
+ [weakSelf destroyViews];
+ // TODO: It might also be nice to have a js event happen here -jm
+ }
+ }
+ ];
+ }
+ })));
+ }
+ }
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h
new file mode 100644
index 00000000..a948ea31
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.h
@@ -0,0 +1,28 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 <Cordova/CDVViewController.h>
+
+@interface CDVViewController (SplashScreen)
+
+@property (nonatomic, assign) BOOL enabledAutorotation;
+@property (nonatomic, readonly) BOOL shouldAutorotateDefaultValue;
+
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m
new file mode 100644
index 00000000..e483def6
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-splashscreen/CDVViewController+SplashScreen.m
@@ -0,0 +1,89 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 "CDVViewController+SplashScreen.h"
+#import <objc/runtime.h>
+
+@implementation CDVViewController (SplashScreen)
+
+@dynamic enabledAutorotation;
+
+- (void)setEnabledAutorotation:(BOOL)value
+{
+ objc_setAssociatedObject(self,
+ @selector(enabledAutorotation),
+ [NSNumber numberWithBool:value],
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (BOOL)enabledAutorotation
+{
+ NSNumber *number = (NSNumber *)objc_getAssociatedObject(self, @selector(enabledAutorotation));
+
+ // Defaulting to YES to correspond parent CDVViewController behavior
+ if (number == nil)
+ {
+ return YES;
+ }
+
+ return [number boolValue];
+}
+
++ (void)load
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ Class class = [self class];
+
+ SEL originalSelector = @selector(shouldAutorotate);
+ SEL swizzledSelector = @selector(splash_shouldAutorotate);
+
+ Method originalMethod = class_getInstanceMethod(class, originalSelector);
+ Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
+
+ BOOL didAddMethod = class_addMethod(class,
+ originalSelector,
+ method_getImplementation(swizzledMethod),
+ method_getTypeEncoding(swizzledMethod));
+
+ if (didAddMethod) {
+ class_replaceMethod(class,
+ swizzledSelector,
+ method_getImplementation(originalMethod),
+ method_getTypeEncoding(originalMethod));
+ } else {
+ method_exchangeImplementations(originalMethod, swizzledMethod);
+ }
+ });
+}
+
+#pragma mark - Method Swizzling
+
+- (BOOL)splash_shouldAutorotate
+{
+ return self.enabledAutorotation;
+}
+
+
+- (BOOL)shouldAutorotateDefaultValue
+{
+ return [self splash_shouldAutorotate];
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h
new file mode 100644
index 00000000..0be08cc3
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.h
@@ -0,0 +1,50 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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 <Cordova/CDVPlugin.h>
+#import <Cordova/CDVInvokedUrlCommand.h>
+
+@interface CDVStatusBar : CDVPlugin {
+ @protected
+ BOOL _statusBarOverlaysWebView;
+ UIView* _statusBarBackgroundView;
+ BOOL _uiviewControllerBasedStatusBarAppearance;
+ UIColor* _statusBarBackgroundColor;
+ NSString* _eventsCallbackId;
+}
+
+@property (atomic, assign) BOOL statusBarOverlaysWebView;
+@property (atomic, assign) BOOL statusBarVisible;
+
+- (void) overlaysWebView:(CDVInvokedUrlCommand*)command;
+
+- (void) styleDefault:(CDVInvokedUrlCommand*)command;
+- (void) styleLightContent:(CDVInvokedUrlCommand*)command;
+- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command;
+- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command;
+
+- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command;
+- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command;
+
+- (void) hide:(CDVInvokedUrlCommand*)command;
+- (void) show:(CDVInvokedUrlCommand*)command;
+
+- (void) _ready:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m
new file mode 100644
index 00000000..58fbc296
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-statusbar/CDVStatusBar.m
@@ -0,0 +1,501 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+ */
+
+/*
+ NOTE: plugman/cordova cli should have already installed this,
+ but you need the value UIViewControllerBasedStatusBarAppearance
+ in your Info.plist as well to set the styles in iOS 7
+ */
+
+#import "CDVStatusBar.h"
+#import <objc/runtime.h>
+#import <Cordova/CDVViewController.h>
+
+static const void *kHideStatusBar = &kHideStatusBar;
+static const void *kStatusBarStyle = &kStatusBarStyle;
+
+@interface CDVViewController (StatusBar)
+
+@property (nonatomic, retain) id sb_hideStatusBar;
+@property (nonatomic, retain) id sb_statusBarStyle;
+
+@end
+
+@implementation CDVViewController (StatusBar)
+
+@dynamic sb_hideStatusBar;
+@dynamic sb_statusBarStyle;
+
+- (id)sb_hideStatusBar {
+ return objc_getAssociatedObject(self, kHideStatusBar);
+}
+
+- (void)setSb_hideStatusBar:(id)newHideStatusBar {
+ objc_setAssociatedObject(self, kHideStatusBar, newHideStatusBar, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (id)sb_statusBarStyle {
+ return objc_getAssociatedObject(self, kStatusBarStyle);
+}
+
+- (void)setSb_statusBarStyle:(id)newStatusBarStyle {
+ objc_setAssociatedObject(self, kStatusBarStyle, newStatusBarStyle, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (BOOL) prefersStatusBarHidden {
+ return [self.sb_hideStatusBar boolValue];
+}
+
+- (UIStatusBarStyle)preferredStatusBarStyle
+{
+ return (UIStatusBarStyle)[self.sb_statusBarStyle intValue];
+}
+
+@end
+
+
+@interface CDVStatusBar () <UIScrollViewDelegate>
+- (void)fireTappedEvent;
+- (void)updateIsVisible:(BOOL)visible;
+@end
+
+@implementation CDVStatusBar
+
+- (id)settingForKey:(NSString*)key
+{
+ return [self.commandDelegate.settings objectForKey:[key lowercaseString]];
+}
+
+- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
+{
+ if ([keyPath isEqual:@"statusBarHidden"]) {
+ NSNumber* newValue = [change objectForKey:NSKeyValueChangeNewKey];
+ [self updateIsVisible:![newValue boolValue]];
+ }
+}
+
+-(void)cordovaViewWillAppear:(NSNotification*)notification
+{
+ [self resizeWebView];
+}
+
+-(void)statusBarDidChangeFrame:(NSNotification*)notification
+{
+ //add a small delay for iOS 7 ( 0.1 seconds )
+ __weak CDVStatusBar* weakSelf = self;
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
+ [weakSelf resizeWebView];
+ });
+}
+
+- (void)pluginInitialize
+{
+ BOOL isiOS7 = (IsAtLeastiOSVersion(@"7.0"));
+
+ // init
+ NSNumber* uiviewControllerBasedStatusBarAppearance = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
+ _uiviewControllerBasedStatusBarAppearance = (uiviewControllerBasedStatusBarAppearance == nil || [uiviewControllerBasedStatusBarAppearance boolValue]) && isiOS7;
+
+ // observe the statusBarHidden property
+ [[UIApplication sharedApplication] addObserver:self forKeyPath:@"statusBarHidden" options:NSKeyValueObservingOptionNew context:NULL];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(statusBarDidChangeFrame:) name: UIApplicationDidChangeStatusBarFrameNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(cordovaViewWillAppear:) name: @"CDVViewWillAppearNotification" object:nil];
+
+ _statusBarOverlaysWebView = YES; // default
+
+ [self initializeStatusBarBackgroundView];
+
+ self.viewController.view.autoresizesSubviews = YES;
+
+ NSString* setting;
+
+ setting = @"StatusBarBackgroundColor";
+ if ([self settingForKey:setting]) {
+ [self _backgroundColorByHexString:[self settingForKey:setting]];
+ }
+
+ setting = @"StatusBarStyle";
+ if ([self settingForKey:setting]) {
+ [self setStatusBarStyle:[self settingForKey:setting]];
+ }
+
+ // blank scroll view to intercept status bar taps
+ self.webView.scrollView.scrollsToTop = NO;
+ UIScrollView *fakeScrollView = [[UIScrollView alloc] initWithFrame:UIScreen.mainScreen.bounds];
+ fakeScrollView.delegate = self;
+ fakeScrollView.scrollsToTop = YES;
+ [self.viewController.view addSubview:fakeScrollView]; // Add scrollview to the view heirarchy so that it will begin accepting status bar taps
+ [self.viewController.view sendSubviewToBack:fakeScrollView]; // Send it to the very back of the view heirarchy
+ fakeScrollView.contentSize = CGSizeMake(UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height * 2.0f); // Make the scroll view longer than the screen itself
+ fakeScrollView.contentOffset = CGPointMake(0.0f, UIScreen.mainScreen.bounds.size.height); // Scroll down so a tap will take scroll view back to the top
+
+ _statusBarVisible = ![UIApplication sharedApplication].isStatusBarHidden;
+}
+
+- (void)onReset {
+ _eventsCallbackId = nil;
+}
+
+- (void)fireTappedEvent {
+ if (_eventsCallbackId == nil) {
+ return;
+ }
+ NSDictionary* payload = @{@"type": @"tap"};
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:payload];
+ [result setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId];
+}
+
+- (void)updateIsVisible:(BOOL)visible {
+ if (_eventsCallbackId == nil) {
+ return;
+ }
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:visible];
+ [result setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:result callbackId:_eventsCallbackId];
+}
+
+- (void) _ready:(CDVInvokedUrlCommand*)command
+{
+ _eventsCallbackId = command.callbackId;
+ [self updateIsVisible:![UIApplication sharedApplication].statusBarHidden];
+ NSString* setting = @"StatusBarOverlaysWebView";
+ if ([self settingForKey:setting]) {
+ self.statusBarOverlaysWebView = [(NSNumber*)[self settingForKey:setting] boolValue];
+ if (self.statusBarOverlaysWebView) {
+ [self resizeWebView];
+ }
+ }
+}
+
+- (void) initializeStatusBarBackgroundView
+{
+ CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
+
+ if ([[UIApplication sharedApplication]statusBarOrientation] == UIInterfaceOrientationPortraitUpsideDown &&
+ statusBarFrame.size.height + statusBarFrame.origin.y == [self.viewController.view.window bounds].size.height) {
+
+ // When started in upside-down orientation on iOS 7, status bar will be bound to lower edge of the
+ // screen (statusBarFrame.origin.y will be somewhere around screen height). In this case we need to
+ // correct frame's coordinates
+ statusBarFrame.origin.y = 0;
+ }
+
+ statusBarFrame = [self invertFrameIfNeeded:statusBarFrame];
+
+ _statusBarBackgroundView = [[UIView alloc] initWithFrame:statusBarFrame];
+ _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
+ _statusBarBackgroundView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleBottomMargin);
+ _statusBarBackgroundView.autoresizesSubviews = YES;
+}
+
+- (CGRect) invertFrameIfNeeded:(CGRect)rect {
+ // landscape is where (width > height). On iOS < 8, we need to invert since frames are
+ // always in Portrait context. Do not run this on ios 8 or above to avoid breaking ipad pro multitask layout
+ if (!IsAtLeastiOSVersion(@"8.0") && UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
+ CGFloat temp = rect.size.width;
+ rect.size.width = rect.size.height;
+ rect.size.height = temp;
+ rect.origin = CGPointZero;
+ }
+
+ return rect;
+}
+
+- (void) setStatusBarOverlaysWebView:(BOOL)statusBarOverlaysWebView
+{
+ // we only care about the latest iOS version or a change in setting
+ if (!IsAtLeastiOSVersion(@"7.0") || statusBarOverlaysWebView == _statusBarOverlaysWebView) {
+ return;
+ }
+
+ _statusBarOverlaysWebView = statusBarOverlaysWebView;
+
+ [self resizeWebView];
+
+ if (statusBarOverlaysWebView) {
+
+ [_statusBarBackgroundView removeFromSuperview];
+
+ } else {
+
+ [self initializeStatusBarBackgroundView];
+ [self.webView.superview addSubview:_statusBarBackgroundView];
+
+ }
+
+}
+
+- (BOOL) statusBarOverlaysWebView
+{
+ return _statusBarOverlaysWebView;
+}
+
+- (void) overlaysWebView:(CDVInvokedUrlCommand*)command
+{
+ id value = [command argumentAtIndex:0];
+ if (!([value isKindOfClass:[NSNumber class]])) {
+ value = [NSNumber numberWithBool:YES];
+ }
+
+ self.statusBarOverlaysWebView = [value boolValue];
+}
+
+- (void) refreshStatusBarAppearance
+{
+ SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
+ if ([self.viewController respondsToSelector:sel]) {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [self.viewController performSelector:sel withObject:nil];
+#pragma clang diagnostic pop
+ }
+}
+
+- (void) setStyleForStatusBar:(UIStatusBarStyle)style
+{
+ if (_uiviewControllerBasedStatusBarAppearance) {
+ CDVViewController* vc = (CDVViewController*)self.viewController;
+ vc.sb_statusBarStyle = [NSNumber numberWithInt:style];
+ [self refreshStatusBarAppearance];
+
+ } else {
+ [[UIApplication sharedApplication] setStatusBarStyle:style];
+ }
+}
+
+- (void) setStatusBarStyle:(NSString*)statusBarStyle
+{
+ // default, lightContent, blackTranslucent, blackOpaque
+ NSString* lcStatusBarStyle = [statusBarStyle lowercaseString];
+
+ if ([lcStatusBarStyle isEqualToString:@"default"]) {
+ [self styleDefault:nil];
+ } else if ([lcStatusBarStyle isEqualToString:@"lightcontent"]) {
+ [self styleLightContent:nil];
+ } else if ([lcStatusBarStyle isEqualToString:@"blacktranslucent"]) {
+ [self styleBlackTranslucent:nil];
+ } else if ([lcStatusBarStyle isEqualToString:@"blackopaque"]) {
+ [self styleBlackOpaque:nil];
+ }
+}
+
+- (void) styleDefault:(CDVInvokedUrlCommand*)command
+{
+ [self setStyleForStatusBar:UIStatusBarStyleDefault];
+}
+
+- (void) styleLightContent:(CDVInvokedUrlCommand*)command
+{
+ [self setStyleForStatusBar:UIStatusBarStyleLightContent];
+}
+
+- (void) styleBlackTranslucent:(CDVInvokedUrlCommand*)command
+{
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000
+ # define TRANSLUCENT_STYLE UIStatusBarStyleBlackTranslucent
+ #else
+ # define TRANSLUCENT_STYLE UIStatusBarStyleLightContent
+ #endif
+ [self setStyleForStatusBar:TRANSLUCENT_STYLE];
+}
+
+- (void) styleBlackOpaque:(CDVInvokedUrlCommand*)command
+{
+ #if __IPHONE_OS_VERSION_MAX_ALLOWED < 70000
+ # define OPAQUE_STYLE UIStatusBarStyleBlackOpaque
+ #else
+ # define OPAQUE_STYLE UIStatusBarStyleLightContent
+ #endif
+ [self setStyleForStatusBar:OPAQUE_STYLE];
+}
+
+- (void) backgroundColorByName:(CDVInvokedUrlCommand*)command
+{
+ id value = [command argumentAtIndex:0];
+ if (!([value isKindOfClass:[NSString class]])) {
+ value = @"black";
+ }
+
+ SEL selector = NSSelectorFromString([value stringByAppendingString:@"Color"]);
+ if ([UIColor respondsToSelector:selector]) {
+ _statusBarBackgroundView.backgroundColor = [UIColor performSelector:selector];
+ }
+}
+
+- (void) _backgroundColorByHexString:(NSString*)hexString
+{
+ unsigned int rgbValue = 0;
+ NSScanner* scanner = [NSScanner scannerWithString:hexString];
+ [scanner setScanLocation:1];
+ [scanner scanHexInt:&rgbValue];
+
+ _statusBarBackgroundColor = [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16)/255.0 green:((rgbValue & 0xFF00) >> 8)/255.0 blue:(rgbValue & 0xFF)/255.0 alpha:1.0];
+ _statusBarBackgroundView.backgroundColor = _statusBarBackgroundColor;
+}
+
+- (void) backgroundColorByHexString:(CDVInvokedUrlCommand*)command
+{
+ NSString* value = [command argumentAtIndex:0];
+ if (!([value isKindOfClass:[NSString class]])) {
+ value = @"#000000";
+ }
+
+ if (![value hasPrefix:@"#"] || [value length] < 7) {
+ return;
+ }
+
+ [self _backgroundColorByHexString:value];
+}
+
+- (void) hideStatusBar
+{
+ if (_uiviewControllerBasedStatusBarAppearance) {
+ CDVViewController* vc = (CDVViewController*)self.viewController;
+ vc.sb_hideStatusBar = [NSNumber numberWithBool:YES];
+ [self refreshStatusBarAppearance];
+
+ } else {
+ UIApplication* app = [UIApplication sharedApplication];
+ [app setStatusBarHidden:YES];
+ }
+}
+
+- (void) hide:(CDVInvokedUrlCommand*)command
+{
+ _statusBarVisible = NO;
+ UIApplication* app = [UIApplication sharedApplication];
+
+ if (!app.isStatusBarHidden)
+ {
+
+ [self hideStatusBar];
+
+ if (IsAtLeastiOSVersion(@"7.0")) {
+ [_statusBarBackgroundView removeFromSuperview];
+ }
+
+ [self resizeWebView];
+
+ _statusBarBackgroundView.hidden = YES;
+ }
+}
+
+- (void) showStatusBar
+{
+ if (_uiviewControllerBasedStatusBarAppearance) {
+ CDVViewController* vc = (CDVViewController*)self.viewController;
+ vc.sb_hideStatusBar = [NSNumber numberWithBool:NO];
+ [self refreshStatusBarAppearance];
+
+ } else {
+ UIApplication* app = [UIApplication sharedApplication];
+ [app setStatusBarHidden:NO];
+ }
+}
+
+- (void) show:(CDVInvokedUrlCommand*)command
+{
+ _statusBarVisible = YES;
+ UIApplication* app = [UIApplication sharedApplication];
+
+ if (app.isStatusBarHidden)
+ {
+ BOOL isIOS7 = (IsAtLeastiOSVersion(@"7.0"));
+
+ [self showStatusBar];
+ [self resizeWebView];
+
+ if (isIOS7) {
+
+ if (!self.statusBarOverlaysWebView) {
+
+ // there is a possibility that when the statusbar was hidden, it was in a different orientation
+ // from the current one. Therefore we need to expand the statusBarBackgroundView as well to the
+ // statusBar's current size
+ CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
+ statusBarFrame = [self invertFrameIfNeeded:statusBarFrame];
+ CGRect sbBgFrame = _statusBarBackgroundView.frame;
+ sbBgFrame.size = statusBarFrame.size;
+ _statusBarBackgroundView.frame = sbBgFrame;
+ [self.webView.superview addSubview:_statusBarBackgroundView];
+
+ }
+
+ }
+
+ _statusBarBackgroundView.hidden = NO;
+ }
+}
+
+-(void)resizeWebView
+{
+ BOOL isIOS7 = (IsAtLeastiOSVersion(@"7.0"));
+
+ if (isIOS7) {
+ CGRect bounds = [self.viewController.view.window bounds];
+ if (CGRectEqualToRect(bounds, CGRectZero)) {
+ bounds = [[UIScreen mainScreen] bounds];
+ }
+ bounds = [self invertFrameIfNeeded:bounds];
+
+ self.viewController.view.frame = bounds;
+
+ self.webView.frame = bounds;
+
+ CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
+ statusBarFrame = [self invertFrameIfNeeded:statusBarFrame];
+ CGRect frame = self.webView.frame;
+ CGFloat height = statusBarFrame.size.height;
+
+ if (!self.statusBarOverlaysWebView) {
+ if (_statusBarVisible) {
+ // CB-10158 If a full screen video is playing the status bar height will be 0, set it to 20 if _statusBarVisible
+ frame.origin.y = height > 0 ? height: 20;
+ }
+ } else {
+ // Even if overlay is used, we want to handle in-call/recording/hotspot larger status bar
+ frame.origin.y = height >= 20 ? height - 20 : 0;
+ }
+ frame.size.height -= frame.origin.y;
+ self.webView.frame = frame;
+ } else {
+ CGRect bounds = [[UIScreen mainScreen] applicationFrame];
+ self.viewController.view.frame = bounds;
+ }
+}
+
+- (void) dealloc
+{
+ [[UIApplication sharedApplication] removeObserver:self forKeyPath:@"statusBarHidden"];
+ [[NSNotificationCenter defaultCenter]removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+}
+
+
+#pragma mark - UIScrollViewDelegate
+
+- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView
+{
+ [self fireTappedEvent];
+ return NO;
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h
new file mode 100644
index 00000000..d7da331d
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.h
@@ -0,0 +1,5 @@
+#import <Foundation/Foundation.h>
+
+@interface NSString (URLEncoding)
+@property (readonly) NSString *URLEncodedString;
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m
new file mode 100644
index 00000000..b737626c
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/NSString+URLEncoding.m
@@ -0,0 +1,30 @@
+#import "NSString+URLEncoding.h"
+
+@implementation NSString (URLEncoding)
+- (NSString*)URLEncodedString
+{
+ NSString* result = (NSString *)CFBridgingRelease(
+ CFURLCreateStringByAddingPercentEscapes(
+ kCFAllocatorDefault,
+ (CFStringRef)self,
+ CFSTR("#%"), // don't escape these
+ NULL, // allow escaping these
+ kCFStringEncodingUTF8
+ )
+ );
+
+ // we may have a URL with more than one '#' now - which iOS doesn't allow, so escape all but the first one
+ NSArray *parts = [result componentsSeparatedByString:@"#"];
+ NSString *finalResult = parts[0];
+ for (int i=1; i<parts.count; i++) {
+ NSString *part = [parts objectAtIndex:i];
+ if (i==1) {
+ finalResult = [finalResult stringByAppendingString:@"#"];
+ } else {
+ finalResult = [finalResult stringByAppendingString:@"%23"];
+ }
+ finalResult = [finalResult stringByAppendingString:part];
+ }
+ return finalResult;
+}
+@end \ No newline at end of file
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h
new file mode 100644
index 00000000..0c731450
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.h
@@ -0,0 +1,28 @@
+#import <Cordova/CDV.h>
+#import <MessageUI/MFMailComposeViewController.h>
+
+@interface SocialSharing : CDVPlugin <UIPopoverControllerDelegate, MFMailComposeViewControllerDelegate, UIDocumentInteractionControllerDelegate>
+
+@property (nonatomic, strong) MFMailComposeViewController *globalMailComposer;
+@property (nonatomic, strong) UIDocumentInteractionController * documentInteractionController;
+@property (retain) NSString * tempStoredFile;
+@property (retain) CDVInvokedUrlCommand * command;
+
+- (void)available:(CDVInvokedUrlCommand*)command;
+- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command;
+- (void)share:(CDVInvokedUrlCommand*)command;
+- (void)shareWithOptions:(CDVInvokedUrlCommand*)command;
+- (void)canShareVia:(CDVInvokedUrlCommand*)command;
+- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command;
+- (void)shareVia:(CDVInvokedUrlCommand*)command;
+- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command;
+- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command;
+- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command;
+- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command;
+- (void)shareViaSMS:(CDVInvokedUrlCommand*)command;
+- (void)shareViaEmail:(CDVInvokedUrlCommand*)command;
+- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command;
+
+- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m
new file mode 100644
index 00000000..014925bd
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/cordova-plugin-x-socialsharing/SocialSharing.m
@@ -0,0 +1,772 @@
+#import "SocialSharing.h"
+#import "NSString+URLEncoding.h"
+#import <Cordova/CDV.h>
+#import <Social/Social.h>
+#import <Foundation/NSException.h>
+#import <MessageUI/MFMessageComposeViewController.h>
+#import <MessageUI/MFMailComposeViewController.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+
+static NSString *const kShareOptionMessage = @"message";
+static NSString *const kShareOptionSubject = @"subject";
+static NSString *const kShareOptionFiles = @"files";
+static NSString *const kShareOptionUrl = @"url";
+
+@implementation SocialSharing {
+ UIPopoverController *_popover;
+ NSString *_popupCoordinates;
+}
+
+- (void)pluginInitialize {
+ if ([self isEmailAvailable]) {
+ [self cycleTheGlobalMailComposer];
+ }
+}
+
+- (void)available:(CDVInvokedUrlCommand*)command {
+ BOOL avail = NO;
+ if (NSClassFromString(@"UIActivityViewController")) {
+ avail = YES;
+ }
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:avail];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (NSString*)getIPadPopupCoordinates {
+ if (_popupCoordinates != nil) {
+ return _popupCoordinates;
+ }
+ if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) {
+ return [(UIWebView*)self.webView stringByEvaluatingJavaScriptFromString:@"window.plugins.socialsharing.iPadPopupCoordinates();"];
+ } else {
+ // prolly a wkwebview, ignoring for now
+ return nil;
+ }
+}
+
+- (void)setIPadPopupCoordinates:(CDVInvokedUrlCommand*)command {
+ _popupCoordinates = [command.arguments objectAtIndex:0];
+}
+
+- (CGRect)getPopupRectFromIPadPopupCoordinates:(NSArray*)comps {
+ CGRect rect = CGRectZero;
+ if ([comps count] == 4) {
+ rect = CGRectMake([[comps objectAtIndex:0] integerValue], [[comps objectAtIndex:1] integerValue], [[comps objectAtIndex:2] integerValue], [[comps objectAtIndex:3] integerValue]);
+ }
+ return rect;
+}
+
+- (void)share:(CDVInvokedUrlCommand*)command {
+ [self shareInternal:command
+ withOptions:@{
+ kShareOptionMessage: [command.arguments objectAtIndex:0],
+ kShareOptionSubject: [command.arguments objectAtIndex:1],
+ kShareOptionFiles: [command.arguments objectAtIndex:2],
+ kShareOptionUrl: [command.arguments objectAtIndex:3]
+ }
+ isBooleanResponse:YES
+];
+}
+
+- (void)shareWithOptions:(CDVInvokedUrlCommand*)command {
+ NSDictionary* options = [command.arguments objectAtIndex:0];
+ [self shareInternal:command
+ withOptions:options
+ isBooleanResponse:NO
+ ];
+}
+
+- (void)shareInternal:(CDVInvokedUrlCommand*)command withOptions:(NSDictionary*)options isBooleanResponse:(BOOL)boolResponse {
+ [self.commandDelegate runInBackground:^{ //avoid main thread block especially if sharing big files from url
+ if (!NSClassFromString(@"UIActivityViewController")) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+
+ NSString *message = options[kShareOptionMessage];
+ NSString *subject = options[kShareOptionSubject];
+ NSArray *filenames = options[kShareOptionFiles];
+ NSString *urlString = options[kShareOptionUrl];
+
+ NSMutableArray *activityItems = [[NSMutableArray alloc] init];
+
+ if (message != (id)[NSNull null] && message != nil) {
+ [activityItems addObject:message];
+ }
+
+ if (filenames != (id)[NSNull null] && filenames != nil && filenames.count > 0) {
+ NSMutableArray *files = [[NSMutableArray alloc] init];
+ for (NSString* filename in filenames) {
+ NSObject *file = [self getImage:filename];
+ if (file == nil) {
+ file = [self getFile:filename];
+ }
+ if (file != nil) {
+ [files addObject:file];
+ }
+ }
+ [activityItems addObjectsFromArray:files];
+ }
+
+ if (urlString != (id)[NSNull null] && urlString != nil) {
+ [activityItems addObject:[NSURL URLWithString:[urlString URLEncodedString]]];
+ }
+
+ UIActivity *activity = [[UIActivity alloc] init];
+ NSArray *applicationActivities = [[NSArray alloc] initWithObjects:activity, nil];
+ UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
+ if (subject != (id)[NSNull null] && subject != nil) {
+ [activityVC setValue:subject forKey:@"subject"];
+ }
+
+ if ([activityVC respondsToSelector:(@selector(setCompletionWithItemsHandler:))]) {
+ [activityVC setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray * returnedItems, NSError * activityError) {
+ [self cleanupStoredFiles];
+ if (boolResponse) {
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:completed]
+ callbackId:command.callbackId];
+ } else {
+ NSDictionary * result = @{@"completed":@(completed), @"app":activityType == nil ? @"" : activityType};
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result]
+ callbackId:command.callbackId];
+ }
+ }];
+ } else {
+ // let's suppress this warning otherwise folks will start opening issues while it's not relevant
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ [activityVC setCompletionHandler:^(NSString *activityType, BOOL completed) {
+ [self cleanupStoredFiles];
+ NSDictionary * result = @{@"completed":@(completed), @"app":activityType};
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+#pragma GCC diagnostic warning "-Wdeprecated-declarations"
+ }
+
+ NSArray * socialSharingExcludeActivities = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"SocialSharingExcludeActivities"];
+ if (socialSharingExcludeActivities!=nil && [socialSharingExcludeActivities count] > 0) {
+ activityVC.excludedActivityTypes = socialSharingExcludeActivities;
+ }
+
+ dispatch_async(dispatch_get_main_queue(), ^(void){
+ // iPad on iOS >= 8 needs a different approach
+ if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad) {
+ NSString* iPadCoords = [self getIPadPopupCoordinates];
+ if (iPadCoords != nil && ![iPadCoords isEqual:@"-1,-1,-1,-1"]) {
+ NSArray *comps = [iPadCoords componentsSeparatedByString:@","];
+ CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
+ activityVC.popoverPresentationController.sourceView = self.webView;
+ activityVC.popoverPresentationController.sourceRect = rect;
+#endif
+ } else {
+ _popover = [[UIPopoverController alloc] initWithContentViewController:activityVC];
+ _popover.delegate = self;
+ [_popover presentPopoverFromRect:rect inView:self.webView permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
+ }
+ } else if ([activityVC respondsToSelector:@selector(popoverPresentationController)]) {
+#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 // iOS 8.0 supported
+ activityVC.popoverPresentationController.sourceView = self.webView;
+ // position the popup at the bottom, just like iOS < 8 did (and iPhone still does on iOS 8)
+ NSArray *comps = [NSArray arrayWithObjects:
+ [NSNumber numberWithInt:(self.viewController.view.frame.size.width/2)-200],
+ [NSNumber numberWithInt:self.viewController.view.frame.size.height],
+ [NSNumber numberWithInt:400],
+ [NSNumber numberWithInt:400],
+ nil];
+ CGRect rect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ activityVC.popoverPresentationController.sourceRect = rect;
+#endif
+ }
+ }
+ [[self getTopMostViewController] presentViewController:activityVC animated:YES completion:nil];
+ });
+ }];
+}
+
+- (void)shareViaTwitter:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:SLServiceTypeTwitter];
+}
+
+- (void)shareViaFacebook:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
+}
+
+- (void)shareViaFacebookWithPasteMessageHint:(CDVInvokedUrlCommand*)command {
+ // If Fb app is installed a message is not prefilled.
+ // When shared through the default iOS widget (iOS Settings > Facebook) the message is prefilled already.
+ NSString *message = [command.arguments objectAtIndex:0];
+ if (message != (id)[NSNull null]) {
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1000 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
+ BOOL fbAppInstalled = [[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"fb://"]]; // requires whitelisting on iOS9
+ if (fbAppInstalled) {
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ [pasteboard setValue:message forPasteboardType:@"public.text"];
+ NSString *hint = [command.arguments objectAtIndex:4];
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"" message:hint delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
+ [alert show];
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2800 * NSEC_PER_MSEC), dispatch_get_main_queue(), ^{
+ [alert dismissWithClickedButtonIndex:-1 animated:YES];
+ });
+ }
+ });
+ }
+ [self shareViaInternal:command type:SLServiceTypeFacebook];
+}
+
+- (void)shareVia:(CDVInvokedUrlCommand*)command {
+ [self shareViaInternal:command type:[command.arguments objectAtIndex:4]];
+}
+
+- (void)canShareVia:(CDVInvokedUrlCommand*)command {
+ NSString *via = [command.arguments objectAtIndex:4];
+ CDVPluginResult * pluginResult;
+ if ([@"sms" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaSMS]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"email" caseInsensitiveCompare:via] == NSOrderedSame && [self isEmailAvailable]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"whatsapp" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaWhatsApp]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([@"instagram" caseInsensitiveCompare:via] == NSOrderedSame && [self canShareViaInstagram]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else if ([self isAvailableForSharing:command type:via]) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void)canShareViaEmail:(CDVInvokedUrlCommand*)command {
+ if ([self isEmailAvailable]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+- (bool)isEmailAvailable {
+ Class messageClass = (NSClassFromString(@"MFMailComposeViewController"));
+ return messageClass != nil && [messageClass canSendMail];
+}
+
+- (bool)isAvailableForSharing:(CDVInvokedUrlCommand*)command
+ type:(NSString *) type {
+ // isAvailableForServiceType returns true if you pass it a type that is not
+ // in the defined constants, this is probably a bug on apples part
+ if(!([type isEqualToString:SLServiceTypeFacebook]
+ || [type isEqualToString:SLServiceTypeTwitter]
+ || [type isEqualToString:SLServiceTypeTencentWeibo]
+ || [type isEqualToString:SLServiceTypeSinaWeibo])) {
+ return false;
+ }
+ // wrapped in try-catch, because isAvailableForServiceType may crash if an invalid type is passed
+ @try {
+ return [SLComposeViewController isAvailableForServiceType:type];
+ }
+ @catch (NSException* exception) {
+ return false;
+ }
+}
+
+- (void)shareViaInternal:(CDVInvokedUrlCommand*)command
+ type:(NSString *) type {
+
+ NSString *message = [command.arguments objectAtIndex:0];
+ // subject is not supported by the SLComposeViewController
+ NSArray *filenames = [command.arguments objectAtIndex:2];
+ NSString *urlString = [command.arguments objectAtIndex:3];
+
+ // boldly invoke the target app, because the phone will display a nice message asking to configure the app
+ SLComposeViewController *composeViewController = [SLComposeViewController composeViewControllerForServiceType:type];
+ if (message != (id)[NSNull null]) {
+ [composeViewController setInitialText:message];
+ }
+
+ for (NSString* filename in filenames) {
+ UIImage* image = [self getImage:filename];
+ if (image != nil) {
+ [composeViewController addImage:image];
+ }
+ }
+
+ if (urlString != (id)[NSNull null]) {
+ [composeViewController addURL:[NSURL URLWithString:[urlString URLEncodedString]]];
+ }
+
+ [composeViewController setCompletionHandler:^(SLComposeViewControllerResult result) {
+ if (SLComposeViewControllerResultCancelled == result) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"cancelled"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else if ([self isAvailableForSharing:command type:type]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:SLComposeViewControllerResultDone == result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+ // required for iOS6 (issues #162 and #167)
+ [self.viewController dismissViewControllerAnimated:YES completion:nil];
+ }];
+ [[self getTopMostViewController] presentViewController:composeViewController animated:YES completion:nil];
+}
+
+- (void)shareViaEmail:(CDVInvokedUrlCommand*)command {
+ if ([self isEmailAvailable]) {
+
+ if (TARGET_IPHONE_SIMULATOR && IsAtLeastiOSVersion(@"8.0")) {
+ UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"SocialSharing plugin"
+ message:@"Sharing via email is not supported on the iOS 8 simulator."
+ delegate:nil
+ cancelButtonTitle:@"OK"
+ otherButtonTitles:nil];
+ [alert show];
+ return;
+ }
+
+ [self cycleTheGlobalMailComposer];
+
+ self.globalMailComposer.mailComposeDelegate = self;
+
+ if ([command.arguments objectAtIndex:0] != (id)[NSNull null]) {
+ NSString *message = [command.arguments objectAtIndex:0];
+ BOOL isHTML = [message rangeOfString:@"<[^>]+>" options:NSRegularExpressionSearch].location != NSNotFound;
+ [self.globalMailComposer setMessageBody:message isHTML:isHTML];
+ }
+
+ if ([command.arguments objectAtIndex:1] != (id)[NSNull null]) {
+ [self.globalMailComposer setSubject: [command.arguments objectAtIndex:1]];
+ }
+
+ if ([command.arguments objectAtIndex:2] != (id)[NSNull null]) {
+ [self.globalMailComposer setToRecipients:[command.arguments objectAtIndex:2]];
+ }
+
+ if ([command.arguments objectAtIndex:3] != (id)[NSNull null]) {
+ [self.globalMailComposer setCcRecipients:[command.arguments objectAtIndex:3]];
+ }
+
+ if ([command.arguments objectAtIndex:4] != (id)[NSNull null]) {
+ [self.globalMailComposer setBccRecipients:[command.arguments objectAtIndex:4]];
+ }
+
+ if ([command.arguments objectAtIndex:5] != (id)[NSNull null]) {
+ NSArray* attachments = [command.arguments objectAtIndex:5];
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ for (NSString* path in attachments) {
+ NSURL *file = [self getFile:path];
+ NSData* data = [fileManager contentsAtPath:file.path];
+
+ NSString* fileName;
+ NSString* mimeType;
+ NSString* basename = [self getBasenameFromAttachmentPath:path];
+
+ if ([basename hasPrefix:@"data:"]) {
+ mimeType = (NSString*)[[[basename substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0];
+ fileName = @"attachment.";
+ fileName = [fileName stringByAppendingString:(NSString*)[[mimeType componentsSeparatedByString: @"/"] lastObject]];
+ NSString *base64content = (NSString*)[[basename componentsSeparatedByString: @","] lastObject];
+ data = [SocialSharing dataFromBase64String:base64content];
+ } else {
+ fileName = [basename pathComponents].lastObject;
+ mimeType = [self getMimeTypeFromFileExtension:[basename pathExtension]];
+ }
+ [self.globalMailComposer addAttachmentData:data mimeType:mimeType fileName:fileName];
+ }
+ }
+
+ // remember the command, because we need it in the didFinishWithResult method
+ _command = command;
+
+ [self.commandDelegate runInBackground:^{
+ [[self getTopMostViewController] presentViewController:self.globalMailComposer animated:YES completion:nil];
+ }];
+
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+- (UIViewController*) getTopMostViewController {
+ UIViewController *presentingViewController = [[[UIApplication sharedApplication] delegate] window].rootViewController;
+ while (presentingViewController.presentedViewController != nil) {
+ presentingViewController = presentingViewController.presentedViewController;
+ }
+ return presentingViewController;
+}
+
+- (NSString*) getBasenameFromAttachmentPath:(NSString*)path {
+ if ([path hasPrefix:@"base64:"]) {
+ NSString* pathWithoutPrefix = [path stringByReplacingOccurrencesOfString:@"base64:" withString:@""];
+ return [pathWithoutPrefix substringToIndex:[pathWithoutPrefix rangeOfString:@"//"].location];
+ }
+ return [path componentsSeparatedByString: @"?"][0];
+}
+
+- (NSString*) getMimeTypeFromFileExtension:(NSString*)extension {
+ if (!extension) {
+ return nil;
+ }
+ // Get the UTI from the file's extension
+ CFStringRef ext = (CFStringRef)CFBridgingRetain(extension);
+ CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL);
+ // Converting UTI to a mime type
+ NSString *result = (NSString*)CFBridgingRelease(UTTypeCopyPreferredTagWithClass(type, kUTTagClassMIMEType));
+ CFRelease(ext);
+ CFRelease(type);
+ return result;
+}
+
+/**
+ * Delegate will be called after the mail composer did finish an action
+ * to dismiss the view.
+ */
+- (void) mailComposeController:(MFMailComposeViewController*)controller
+ didFinishWithResult:(MFMailComposeResult)result
+ error:(NSError*)error {
+ bool ok = result == MFMailComposeResultSent;
+ [self.globalMailComposer dismissViewControllerAnimated:YES completion:^{[self cycleTheGlobalMailComposer];}];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
+}
+
+-(void)cycleTheGlobalMailComposer {
+ // we are cycling the damned GlobalMailComposer: http://stackoverflow.com/questions/25604552/i-have-real-misunderstanding-with-mfmailcomposeviewcontroller-in-swift-ios8-in/25604976#25604976
+ self.globalMailComposer = nil;
+ self.globalMailComposer = [[MFMailComposeViewController alloc] init];
+}
+
+- (bool)canShareViaSMS {
+ Class messageClass = (NSClassFromString(@"MFMessageComposeViewController"));
+ return messageClass != nil && [messageClass canSendText];
+}
+
+- (void)shareViaSMS:(CDVInvokedUrlCommand*)command {
+ if ([self canShareViaSMS]) {
+ NSDictionary* options = [command.arguments objectAtIndex:0];
+ NSString *phonenumbers = [command.arguments objectAtIndex:1];
+ NSString *message = [options objectForKey:@"message"];
+ NSString *subject = [options objectForKey:@"subject"];
+ NSString *image = [options objectForKey:@"image"];
+
+ MFMessageComposeViewController *picker = [[MFMessageComposeViewController alloc] init];
+ picker.messageComposeDelegate = (id) self;
+ if (message != (id)[NSNull null]) {
+ picker.body = message;
+ }
+ if (subject != (id)[NSNull null]) {
+ [picker setSubject:subject];
+ }
+ if (image != nil && image != (id)[NSNull null]) {
+ BOOL canSendAttachments = [[MFMessageComposeViewController class] respondsToSelector:@selector(canSendAttachments)];
+ if (canSendAttachments) {
+ NSURL *file = [self getFile:image];
+ if (file != nil) {
+ [picker addAttachmentURL:file withAlternateFilename:nil];
+ }
+ }
+ }
+
+ if (phonenumbers != (id)[NSNull null]) {
+ [picker setRecipients:[phonenumbers componentsSeparatedByString:@","]];
+ }
+ // remember the command, because we need it in the didFinishWithResult method
+ _command = command;
+ [self.commandDelegate runInBackground:^{
+ picker.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
+ [[self getTopMostViewController] presentViewController:picker animated:YES completion:nil];
+ }];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+// Dismisses the SMS composition interface when users taps Cancel or Send
+- (void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result {
+ bool ok = result == MessageComposeResultSent;
+ [[self getTopMostViewController] dismissViewControllerAnimated:YES completion:nil];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:ok];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:_command.callbackId];
+}
+
+- (bool)canShareViaInstagram {
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"instagram://app"]]; // requires whitelisting on iOS9
+}
+
+- (bool)canShareViaWhatsApp {
+ return [[UIApplication sharedApplication] canOpenURL: [NSURL URLWithString:@"whatsapp://app"]]; // requires whitelisting on iOS9
+}
+
+// this is only an internal test method for now, can be used to open a share sheet with 'Open in xx' links for tumblr, drive, dropbox, ..
+- (void)openImage:(NSString *)imageName {
+ UIImage* image =[self getImage:imageName];
+ if (image != nil) {
+ NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/myTempImage.jpg"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
+ _documentInteractionController.UTI = @""; // TODO find the scheme for google drive and create a shareViaGoogleDrive function
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
+ }
+}
+
+- (void)shareViaInstagram:(CDVInvokedUrlCommand*)command {
+
+ // on iOS9 canShareVia('instagram'..) will only work if instagram:// is whitelisted.
+ // If it's not, this method will ask permission to the user on iOS9 for opening the app,
+ // which is of course better than Instagram sharing not working at all because you forgot to whitelist it.
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('instagram'..)
+ if (!IsAtLeastiOSVersion(@"9.0")) {
+ if (![self canShareViaInstagram]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+ }
+
+ NSString *message = [command.arguments objectAtIndex:0];
+ // subject is not supported by the SLComposeViewController
+ NSArray *filenames = [command.arguments objectAtIndex:2];
+
+ // only use the first image (for now.. maybe we can share in a loop?)
+ UIImage* image = nil;
+ for (NSString* filename in filenames) {
+ image = [self getImage:filename];
+ break;
+ }
+
+// NSData *imageObj = [NSData dataFromBase64String:objectAtIndex0];
+ NSString *tmpDir = NSTemporaryDirectory();
+ NSString *path = [tmpDir stringByAppendingPathComponent:@"instagram.igo"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:path atomically:YES];
+
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:path]];
+ _documentInteractionController.delegate = self;
+ _documentInteractionController.UTI = @"com.instagram.exclusivegram";
+
+ if (message != (id)[NSNull null]) {
+ // no longer working, so ..
+ _documentInteractionController.annotation = @{@"InstagramCaption" : message};
+
+ // .. we put the message on the clipboard (you app can prompt the user to paste it)
+ UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
+ [pasteboard setValue:message forPasteboardType:@"public.text"];
+ }
+
+ // remember the command for the delegate method
+ _command = command;
+
+ // test for #513
+ dispatch_async(dispatch_get_main_queue(), ^(void){
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.webView animated:YES];
+ });
+}
+
+- (void)shareViaWhatsApp:(CDVInvokedUrlCommand*)command {
+
+ // on iOS9 canShareVia('whatsapp'..) will only work if whatsapp:// is whitelisted.
+ // If it's not, this method will ask permission to the user on iOS9 for opening the app,
+ // which is of course better than WhatsApp sharing not working at all because you forgot to whitelist it.
+ // Tradeoff: on iOS9 this method will always return true, so make sure to whitelist it and call canShareVia('whatsapp'..)
+ if (!IsAtLeastiOSVersion(@"9.0")) {
+ if (![self canShareViaWhatsApp]) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"not available"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ return;
+ }
+ }
+
+ NSString *message = [command.arguments objectAtIndex:0];
+ // subject is not supported by the SLComposeViewController
+ NSArray *filenames = [command.arguments objectAtIndex:2];
+ NSString *urlString = [command.arguments objectAtIndex:3];
+ NSString *abid = [command.arguments objectAtIndex:4];
+
+ // only use the first image (for now.. maybe we can share in a loop?)
+ UIImage* image = nil;
+ for (NSString* filename in filenames) {
+ image = [self getImage:filename];
+ break;
+ }
+
+ // with WhatsApp, we can share an image OR text+url.. image wins if set
+ if (image != nil) {
+ NSString * savePath = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/whatsAppTmp.wai"];
+ [UIImageJPEGRepresentation(image, 1.0) writeToFile:savePath atomically:YES];
+ _documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:savePath]];
+ _documentInteractionController.UTI = @"net.whatsapp.image";
+ _documentInteractionController.delegate = self;
+ _command = command;
+ [_documentInteractionController presentOpenInMenuFromRect:CGRectZero inView:self.viewController.view animated: YES];
+ } else {
+ // append an url to a message, if both are passed
+ NSString * shareString = @"";
+ if (message != (id)[NSNull null]) {
+ shareString = message;
+ }
+ if (urlString != (id)[NSNull null]) {
+ if ([shareString isEqual: @""]) {
+ shareString = urlString;
+ } else {
+ shareString = [NSString stringWithFormat:@"%@ %@", shareString, [urlString URLEncodedString]];
+ }
+ }
+ NSString * encodedShareString = [shareString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+ // also encode the '=' character
+ encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"=" withString:@"%3D"];
+ encodedShareString = [encodedShareString stringByReplacingOccurrencesOfString:@"&" withString:@"%26"];
+ NSString * abidString = @"";
+ if (abid != (id)[NSNull null]) {
+ abidString = [NSString stringWithFormat:@"abid=%@&", abid];
+ }
+ NSString * encodedShareStringForWhatsApp = [NSString stringWithFormat:@"whatsapp://send?%@text=%@", abidString, encodedShareString];
+
+ NSURL *whatsappURL = [NSURL URLWithString:encodedShareStringForWhatsApp];
+ [[UIApplication sharedApplication] openURL: whatsappURL];
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }
+}
+
+- (void)saveToPhotoAlbum:(CDVInvokedUrlCommand*)command {
+ self.command = command;
+ NSArray *filenames = [command.arguments objectAtIndex:0];
+ [self.commandDelegate runInBackground:^{
+ bool shared = false;
+ for (NSString* filename in filenames) {
+ UIImage* image = [self getImage:filename];
+ if (image != nil) {
+ shared = true;
+ UIImageWriteToSavedPhotosAlbum(image, self, @selector(thisImage:wasSavedToPhotoAlbumWithError:contextInfo:), nil);
+ }
+ }
+ if (!shared) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no valid image was passed"];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ }
+ }];
+}
+
+// called from saveToPhotoAlbum, note that we only send feedback for the first image that's being saved (not keeping the callback)
+// but since the UIImageWriteToSavedPhotosAlbum function is only called with valid images that should not be a problem
+- (void)thisImage:(UIImage *)image wasSavedToPhotoAlbumWithError:(NSError *)error contextInfo:(void*)ctxInfo {
+ if (error) {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.localizedDescription];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ } else {
+ CDVPluginResult * pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.command.callbackId];
+ }
+}
+
+-(UIImage*)getImage: (NSString *)imageName {
+ UIImage *image = nil;
+ if (imageName != (id)[NSNull null]) {
+ if ([imageName hasPrefix:@"http"]) {
+ image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageName]]];
+ } else if ([imageName hasPrefix:@"www/"]) {
+ image = [UIImage imageNamed:imageName];
+ } else if ([imageName hasPrefix:@"file://"]) {
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:[[NSURL URLWithString:imageName] path]]];
+ } else if ([imageName hasPrefix:@"data:"]) {
+ // using a base64 encoded string
+ NSURL *imageURL = [NSURL URLWithString:imageName];
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
+ image = [UIImage imageWithData:imageData];
+ } else if ([imageName hasPrefix:@"assets-library://"]) {
+ // use assets-library
+ NSURL *imageURL = [NSURL URLWithString:imageName];
+ NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
+ image = [UIImage imageWithData:imageData];
+ } else {
+ // assume anywhere else, on the local filesystem
+ image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imageName]];
+ }
+ }
+ return image;
+}
+
+-(NSURL*)getFile: (NSString *)fileName {
+ NSURL *file = nil;
+ if (fileName != (id)[NSNull null]) {
+ if ([fileName hasPrefix:@"http"]) {
+ NSURL *url = [NSURL URLWithString:fileName];
+ NSData *fileData = [NSData dataWithContentsOfURL:url];
+ NSString *name = (NSString*)[[fileName componentsSeparatedByString: @"/"] lastObject];
+ file = [NSURL fileURLWithPath:[self storeInFile:[name componentsSeparatedByString: @"?"][0] fileData:fileData]];
+ } else if ([fileName hasPrefix:@"www/"]) {
+ NSString *bundlePath = [[NSBundle mainBundle] bundlePath];
+ NSString *fullPath = [NSString stringWithFormat:@"%@/%@", bundlePath, fileName];
+ file = [NSURL fileURLWithPath:fullPath];
+ } else if ([fileName hasPrefix:@"file://"]) {
+ // stripping the first 6 chars, because the path should start with / instead of file://
+ file = [NSURL fileURLWithPath:[fileName substringFromIndex:6]];
+ } else if ([fileName hasPrefix:@"data:"]) {
+ // using a base64 encoded string
+ // extract some info from the 'fileName', which is for example: data:text/calendar;base64,<encoded stuff here>
+ NSString *fileType = (NSString*)[[[fileName substringFromIndex:5] componentsSeparatedByString: @";"] objectAtIndex:0];
+ fileType = (NSString*)[[fileType componentsSeparatedByString: @"/"] lastObject];
+ NSString *base64content = (NSString*)[[fileName componentsSeparatedByString: @","] lastObject];
+ NSData *fileData = [SocialSharing dataFromBase64String:base64content];
+ file = [NSURL fileURLWithPath:[self storeInFile:[NSString stringWithFormat:@"%@.%@", @"file", fileType] fileData:fileData]];
+ } else {
+ // assume anywhere else, on the local filesystem
+ file = [NSURL fileURLWithPath:fileName];
+ }
+ }
+ return file;
+}
+
+-(NSString*) storeInFile: (NSString*) fileName
+ fileData: (NSData*) fileData {
+ NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+ NSString *documentsDirectory = [paths objectAtIndex:0];
+ NSString *filePath = [documentsDirectory stringByAppendingPathComponent:fileName];
+ [fileData writeToFile:filePath atomically:YES];
+ _tempStoredFile = filePath;
+ return filePath;
+}
+
+- (void) cleanupStoredFiles {
+ if (_tempStoredFile != nil) {
+ NSError *error;
+ [[NSFileManager defaultManager]removeItemAtPath:_tempStoredFile error:&error];
+ }
+}
+
++ (NSData*) dataFromBase64String:(NSString*)aString {
+ return [[NSData alloc] initWithBase64EncodedString:aString options:0];
+}
+
+#pragma mark - UIPopoverControllerDelegate methods
+
+- (void)popoverController:(UIPopoverController *)popoverController willRepositionPopoverToRect:(inout CGRect *)rect inView:(inout UIView **)view {
+ NSArray *comps = [[self getIPadPopupCoordinates] componentsSeparatedByString:@","];
+ CGRect newRect = [self getPopupRectFromIPadPopupCoordinates:comps];
+ rect->origin = newRect.origin;
+}
+
+- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
+ _popover = nil;
+}
+
+#pragma mark - UIDocumentInteractionControllerDelegate methods
+
+- (void) documentInteractionController: (UIDocumentInteractionController *) controller willBeginSendingToApplication: (NSString *) application {
+ // note that the application actually contains the app bundle id which was picked (for whatsapp and instagram only)
+ NSLog(@"SocialSharing app selected: %@", application);
+}
+
+- (void) documentInteractionControllerDidDismissOpenInMenu: (UIDocumentInteractionController *) controller {
+ if (self.command != nil) {
+ CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:result callbackId: self.command.callbackId];
+ }
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h
new file mode 100644
index 00000000..63935dc3
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.h
@@ -0,0 +1,16 @@
+#import <Cordova/CDVPlugin.h>
+#import <objc/runtime.h>
+
+@interface IonicKeyboard : CDVPlugin <UIScrollViewDelegate> {
+ @protected
+ id _keyboardShowObserver, _keyboardHideObserver;
+ IMP wkOriginalImp, uiOriginalImp, nilImp;
+ Method wkMethod, uiMethod;
+}
+
+@property (readwrite, assign) BOOL hideKeyboardAccessoryBar;
+@property (readwrite, assign) BOOL disableScroll;
+//@property (readwrite, assign) BOOL styleDark;
+
+@end
+
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m
new file mode 100644
index 00000000..d072ca7f
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/ionic-plugin-keyboard/IonicKeyboard.m
@@ -0,0 +1,176 @@
+#import "IonicKeyboard.h"
+// #import "UIWebViewExtension.h"
+#import <Cordova/CDVAvailability.h>
+
+@implementation IonicKeyboard
+
+@synthesize hideKeyboardAccessoryBar = _hideKeyboardAccessoryBar;
+@synthesize disableScroll = _disableScroll;
+//@synthesize styleDark = _styleDark;
+
+- (void)pluginInitialize {
+
+ Class wkClass = NSClassFromString([@[@"UI", @"Web", @"Browser", @"View"] componentsJoinedByString:@""]);
+ wkMethod = class_getInstanceMethod(wkClass, @selector(inputAccessoryView));
+ wkOriginalImp = method_getImplementation(wkMethod);
+ Class uiClass = NSClassFromString([@[@"WK", @"Content", @"View"] componentsJoinedByString:@""]);
+ uiMethod = class_getInstanceMethod(uiClass, @selector(inputAccessoryView));
+ uiOriginalImp = method_getImplementation(uiMethod);
+ nilImp = imp_implementationWithBlock(^(id _s) {
+ return nil;
+ });
+
+ //set defaults
+ self.hideKeyboardAccessoryBar = YES;
+ self.disableScroll = NO;
+ //self.styleDark = NO;
+
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+ __weak IonicKeyboard* weakSelf = self;
+ _keyboardShowObserver = [nc addObserverForName:UIKeyboardWillShowNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification* notification) {
+
+ CGRect keyboardFrame = [notification.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ keyboardFrame = [self.viewController.view convertRect:keyboardFrame fromView:nil];
+
+ [weakSelf.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.plugins.Keyboard.isVisible = true; cordova.fireWindowEvent('native.keyboardshow', { 'keyboardHeight': %@ }); ", [@(keyboardFrame.size.height) stringValue]]];
+
+ //deprecated
+ [weakSelf.commandDelegate evalJs:[NSString stringWithFormat:@"cordova.fireWindowEvent('native.showkeyboard', { 'keyboardHeight': %@ }); ", [@(keyboardFrame.size.height) stringValue]]];
+ }];
+
+ _keyboardHideObserver = [nc addObserverForName:UIKeyboardWillHideNotification
+ object:nil
+ queue:[NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification* notification) {
+ [weakSelf.commandDelegate evalJs:@"cordova.plugins.Keyboard.isVisible = false; cordova.fireWindowEvent('native.keyboardhide'); "];
+
+ //deprecated
+ [weakSelf.commandDelegate evalJs:@"cordova.fireWindowEvent('native.hidekeyboard'); "];
+ }];
+}
+
+- (BOOL)disableScroll {
+ return _disableScroll;
+}
+
+- (void)setDisableScroll:(BOOL)disableScroll {
+ if (disableScroll == _disableScroll) {
+ return;
+ }
+ if (disableScroll) {
+ self.webView.scrollView.scrollEnabled = NO;
+ self.webView.scrollView.delegate = self;
+ }
+ else {
+ self.webView.scrollView.scrollEnabled = YES;
+ self.webView.scrollView.delegate = nil;
+ }
+
+ _disableScroll = disableScroll;
+}
+
+//keyboard swizzling inspired by:
+//https://github.com/cjpearson/cordova-plugin-keyboard/
+
+- (BOOL)hideKeyboardAccessoryBar {
+ return _hideKeyboardAccessoryBar;
+}
+
+- (void)setHideKeyboardAccessoryBar:(BOOL)hideKeyboardAccessoryBar {
+ if (hideKeyboardAccessoryBar == _hideKeyboardAccessoryBar) {
+ return;
+ }
+
+ if (hideKeyboardAccessoryBar) {
+ method_setImplementation(wkMethod, nilImp);
+ method_setImplementation(uiMethod, nilImp);
+ } else {
+ method_setImplementation(wkMethod, wkOriginalImp);
+ method_setImplementation(uiMethod, uiOriginalImp);
+ }
+
+ _hideKeyboardAccessoryBar = hideKeyboardAccessoryBar;
+}
+
+/*
+- (BOOL)styleDark {
+ return _styleDark;
+}
+
+- (void)setStyleDark:(BOOL)styleDark {
+ if (styleDark == _styleDark) {
+ return;
+ }
+ if (styleDark) {
+ self.webView.styleDark = YES;
+ }
+ else {
+ self.webView.styleDark = NO;
+ }
+
+ _styleDark = styleDark;
+}
+*/
+
+
+/* ------------------------------------------------------------- */
+
+- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
+ [scrollView setContentOffset: CGPointZero];
+}
+
+/* ------------------------------------------------------------- */
+
+- (void)dealloc {
+ NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+
+ [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil];
+ [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil];
+}
+
+/* ------------------------------------------------------------- */
+
+- (void) disableScroll:(CDVInvokedUrlCommand*)command {
+ if (!command.arguments || ![command.arguments count]){
+ return;
+ }
+ id value = [command.arguments objectAtIndex:0];
+ if (value != [NSNull null]) {
+ self.disableScroll = [value boolValue];
+ }
+}
+
+- (void) hideKeyboardAccessoryBar:(CDVInvokedUrlCommand*)command {
+ if (!command.arguments || ![command.arguments count]){
+ return;
+ }
+ id value = [command.arguments objectAtIndex:0];
+ if (value != [NSNull null]) {
+ self.hideKeyboardAccessoryBar = [value boolValue];
+ }
+}
+
+- (void) close:(CDVInvokedUrlCommand*)command {
+ [self.webView endEditing:YES];
+}
+
+- (void) show:(CDVInvokedUrlCommand*)command {
+ NSLog(@"Showing keyboard not supported in iOS due to platform limitations.");
+}
+
+/*
+- (void) styleDark:(CDVInvokedUrlCommand*)command {
+ if (!command.arguments || ![command.arguments count]){
+ return;
+ }
+ id value = [command.arguments objectAtIndex:0];
+
+ self.styleDark = [value boolValue];
+}
+*/
+
+@end
+
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h
new file mode 100644
index 00000000..9970762b
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.h
@@ -0,0 +1,22 @@
+//
+// AppDelegate+notification.h
+// pushtest
+//
+// Created by Robert Easterday on 10/26/12.
+//
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate (notification)
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:( void (^)(UIBackgroundFetchResult))completionHandler;
+- (void)pushPluginOnApplicationDidBecomeActive:(UIApplication *)application;
+- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler;
+- (id) getCommandInstance:(NSString*)className;
+
+@property (nonatomic, retain) NSDictionary *launchNotification;
+@property (nonatomic, retain) NSNumber *coldstart;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m
new file mode 100644
index 00000000..fc18dd79
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/AppDelegate+notification.m
@@ -0,0 +1,278 @@
+//
+// AppDelegate+notification.m
+// pushtest
+//
+// Created by Robert Easterday on 10/26/12.
+//
+//
+
+#import "AppDelegate+notification.h"
+#import "PushPlugin.h"
+#import <objc/runtime.h>
+
+static char launchNotificationKey;
+static char coldstartKey;
+
+@implementation AppDelegate (notification)
+
+- (id) getCommandInstance:(NSString*)className
+{
+ return [self.viewController getCommandInstance:className];
+}
+
+// its dangerous to override a method from within a category.
+// Instead we will use method swizzling. we set this up in the load call.
++ (void)load
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ Class class = [self class];
+
+ SEL originalSelector = @selector(init);
+ SEL swizzledSelector = @selector(pushPluginSwizzledInit);
+
+ Method original = class_getInstanceMethod(class, originalSelector);
+ Method swizzled = class_getInstanceMethod(class, swizzledSelector);
+
+ BOOL didAddMethod =
+ class_addMethod(class,
+ originalSelector,
+ method_getImplementation(swizzled),
+ method_getTypeEncoding(swizzled));
+
+ if (didAddMethod) {
+ class_replaceMethod(class,
+ swizzledSelector,
+ method_getImplementation(original),
+ method_getTypeEncoding(original));
+ } else {
+ method_exchangeImplementations(original, swizzled);
+ }
+ });
+}
+
+- (AppDelegate *)pushPluginSwizzledInit
+{
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(createNotificationChecker:)
+ name:UIApplicationDidFinishLaunchingNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]addObserver:self
+ selector:@selector(pushPluginOnApplicationDidBecomeActive:)
+ name:UIApplicationDidBecomeActiveNotification
+ object:nil];
+
+ // This actually calls the original init method over in AppDelegate. Equivilent to calling super
+ // on an overrided method, this is not recursive, although it appears that way. neat huh?
+ return [self pushPluginSwizzledInit];
+}
+
+// This code will be called immediately after application:didFinishLaunchingWithOptions:. We need
+// to process notifications in cold-start situations
+- (void)createNotificationChecker:(NSNotification *)notification
+{
+ NSLog(@"createNotificationChecker");
+ if (notification)
+ {
+ NSDictionary *launchOptions = [notification userInfo];
+ if (launchOptions) {
+ NSLog(@"coldstart");
+ self.launchNotification = [launchOptions objectForKey: @"UIApplicationLaunchOptionsRemoteNotificationKey"];
+ self.coldstart = [NSNumber numberWithBool:YES];
+ } else {
+ NSLog(@"not coldstart");
+ self.coldstart = [NSNumber numberWithBool:NO];
+ }
+ }
+}
+
+- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
+}
+
+- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ [pushHandler didFailToRegisterForRemoteNotificationsWithError:error];
+}
+
+- (void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
+ NSLog(@"clicked on the shade");
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ pushHandler.notificationMessage = userInfo;
+ pushHandler.isInline = NO;
+ [pushHandler notificationReceived];
+}
+
+- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
+ NSLog(@"didReceiveNotification with fetchCompletionHandler");
+
+ // app is in the foreground so call notification callback
+ if (application.applicationState == UIApplicationStateActive) {
+ NSLog(@"app active");
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ pushHandler.notificationMessage = userInfo;
+ pushHandler.isInline = YES;
+ [pushHandler notificationReceived];
+
+ completionHandler(UIBackgroundFetchResultNewData);
+ }
+ // app is in background or in stand by
+ else {
+ NSLog(@"app in-active");
+
+ // do some convoluted logic to find out if this should be a silent push.
+ long silent = 0;
+ id aps = [userInfo objectForKey:@"aps"];
+ id contentAvailable = [aps objectForKey:@"content-available"];
+ if ([contentAvailable isKindOfClass:[NSString class]] && [contentAvailable isEqualToString:@"1"]) {
+ silent = 1;
+ } else if ([contentAvailable isKindOfClass:[NSNumber class]]) {
+ silent = [contentAvailable integerValue];
+ }
+
+ if (silent == 1) {
+ NSLog(@"this should be a silent push");
+ void (^safeHandler)(UIBackgroundFetchResult) = ^(UIBackgroundFetchResult result){
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completionHandler(result);
+ });
+ };
+
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+
+ if (pushHandler.handlerObj == nil) {
+ pushHandler.handlerObj = [NSMutableDictionary dictionaryWithCapacity:2];
+ }
+
+ id notId = [userInfo objectForKey:@"notId"];
+ if (notId != nil) {
+ NSLog(@"Push Plugin notId %@", notId);
+ [pushHandler.handlerObj setObject:safeHandler forKey:notId];
+ } else {
+ NSLog(@"Push Plugin notId handler");
+ [pushHandler.handlerObj setObject:safeHandler forKey:@"handler"];
+ }
+
+ pushHandler.notificationMessage = userInfo;
+ pushHandler.isInline = NO;
+ [pushHandler notificationReceived];
+ } else {
+ NSLog(@"just put it in the shade");
+ //save it for later
+ self.launchNotification = userInfo;
+
+ completionHandler(UIBackgroundFetchResultNewData);
+ }
+ }
+}
+
+- (BOOL)userHasRemoteNotificationsEnabled {
+ UIApplication *application = [UIApplication sharedApplication];
+ if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
+ return application.currentUserNotificationSettings.types != UIUserNotificationTypeNone;
+ } else {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+ return application.enabledRemoteNotificationTypes != UIRemoteNotificationTypeNone;
+#pragma GCC diagnostic pop
+ }
+}
+
+- (void)pushPluginOnApplicationDidBecomeActive:(NSNotification *)notification {
+
+ NSLog(@"active");
+
+ UIApplication *application = notification.object;
+
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ if (pushHandler.clearBadge) {
+ NSLog(@"PushPlugin clearing badge");
+ //zero badge
+ application.applicationIconBadgeNumber = 0;
+ } else {
+ NSLog(@"PushPlugin skip clear badge");
+ }
+
+ if (self.launchNotification) {
+ pushHandler.isInline = NO;
+ pushHandler.coldstart = [self.coldstart boolValue];
+ pushHandler.notificationMessage = self.launchNotification;
+ self.launchNotification = nil;
+ self.coldstart = [NSNumber numberWithBool:NO];
+ [pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO];
+ }
+}
+
+
+- (void)application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier
+forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler {
+
+ NSLog(@"Push Plugin handleActionWithIdentifier %@", identifier);
+ NSMutableDictionary *userInfo = [notification mutableCopy];
+ [userInfo setObject:identifier forKey:@"actionCallback"];
+ NSLog(@"Push Plugin userInfo %@", userInfo);
+
+ if (application.applicationState == UIApplicationStateActive) {
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+ pushHandler.notificationMessage = userInfo;
+ pushHandler.isInline = NO;
+ [pushHandler notificationReceived];
+ } else {
+ void (^safeHandler)() = ^(void){
+ dispatch_async(dispatch_get_main_queue(), ^{
+ completionHandler();
+ });
+ };
+
+ PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"];
+
+ if (pushHandler.handlerObj == nil) {
+ pushHandler.handlerObj = [NSMutableDictionary dictionaryWithCapacity:2];
+ }
+
+ id notId = [userInfo objectForKey:@"notId"];
+ if (notId != nil) {
+ NSLog(@"Push Plugin notId %@", notId);
+ [pushHandler.handlerObj setObject:safeHandler forKey:notId];
+ } else {
+ NSLog(@"Push Plugin notId handler");
+ [pushHandler.handlerObj setObject:safeHandler forKey:@"handler"];
+ }
+
+ pushHandler.notificationMessage = userInfo;
+ pushHandler.isInline = NO;
+
+ [pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO];
+ }
+}
+
+// The accessors use an Associative Reference since you can't define a iVar in a category
+// http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html
+- (NSMutableArray *)launchNotification
+{
+ return objc_getAssociatedObject(self, &launchNotificationKey);
+}
+
+- (void)setLaunchNotification:(NSDictionary *)aDictionary
+{
+ objc_setAssociatedObject(self, &launchNotificationKey, aDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (NSNumber *)coldstart
+{
+ return objc_getAssociatedObject(self, &coldstartKey);
+}
+
+- (void)setColdstart:(NSNumber *)aNumber
+{
+ objc_setAssociatedObject(self, &coldstartKey, aNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+- (void)dealloc
+{
+ self.launchNotification = nil; // clear the association and release the object
+ self.coldstart = nil;
+}
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h
new file mode 100644
index 00000000..4cc1dcb0
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.h
@@ -0,0 +1,79 @@
+/*
+ Copyright 2009-2011 Urban Airship Inc. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binaryform must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided withthe distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+#import <Cordova/CDV.h>
+#import <Cordova/CDVPlugin.h>
+
+@protocol GGLInstanceIDDelegate;
+@protocol GCMReceiverDelegate;
+@interface PushPlugin : CDVPlugin
+{
+ NSDictionary *notificationMessage;
+ BOOL isInline;
+ NSString *notificationCallbackId;
+ NSString *callback;
+ BOOL clearBadge;
+
+ NSMutableDictionary *handlerObj;
+ void (^completionHandler)(UIBackgroundFetchResult);
+
+ BOOL ready;
+}
+
+@property (nonatomic, copy) NSString *callbackId;
+@property (nonatomic, copy) NSString *notificationCallbackId;
+@property (nonatomic, copy) NSString *callback;
+
+@property (nonatomic, strong) NSDictionary *notificationMessage;
+@property BOOL isInline;
+@property BOOL coldstart;
+@property BOOL clearBadge;
+@property (nonatomic, strong) NSMutableDictionary *handlerObj;
+
+- (void)init:(CDVInvokedUrlCommand*)command;
+- (void)unregister:(CDVInvokedUrlCommand*)command;
+- (void)subscribe:(CDVInvokedUrlCommand*)command;
+- (void)unsubscribe:(CDVInvokedUrlCommand*)command;
+
+- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
+- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+
+- (void)setNotificationMessage:(NSDictionary *)notification;
+- (void)notificationReceived;
+
+- (void)willSendDataMessageWithID:(NSString *)messageID error:(NSError *)error;
+- (void)didSendDataMessageWithID:(NSString *)messageID;
+- (void)didDeleteMessagesOnServer;
+
+// FCM Features
+@property(nonatomic, assign) BOOL usesFCM;
+@property(nonatomic, strong) NSNumber *fcmSandbox;
+@property(nonatomic, strong) NSString *fcmSenderId;
+@property(nonatomic, strong) NSDictionary *fcmRegistrationOptions;
+@property(nonatomic, strong) NSString *fcmRegistrationToken;
+@property(nonatomic, strong) NSArray *fcmTopics;
+
+@end
diff --git a/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m
new file mode 100644
index 00000000..90475d10
--- /dev/null
+++ b/StoneIsland/platforms/ios/Stone Island/Plugins/phonegap-plugin-push/PushPlugin.m
@@ -0,0 +1,608 @@
+/*
+ Copyright 2009-2011 Urban Airship Inc. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+ 2. Redistributions in binaryform must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided withthe distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// See GGLInstanceID.h
+#define GMP_NO_MODULES true
+
+#import "PushPlugin.h"
+@import FirebaseInstanceID;
+@import FirebaseMessaging;
+@import FirebaseAnalytics;
+
+@implementation PushPlugin : CDVPlugin
+
+@synthesize notificationMessage;
+@synthesize isInline;
+@synthesize coldstart;
+
+@synthesize callbackId;
+@synthesize notificationCallbackId;
+@synthesize callback;
+@synthesize clearBadge;
+@synthesize handlerObj;
+
+@synthesize usesFCM;
+@synthesize fcmSandbox;
+@synthesize fcmSenderId;
+@synthesize fcmRegistrationOptions;
+@synthesize fcmRegistrationToken;
+@synthesize fcmTopics;
+
+-(void)initRegistration;
+{
+ NSString * registrationToken = [[FIRInstanceID instanceID] token];
+
+ if (registrationToken != nil) {
+ NSLog(@"FCM Registration Token: %@", registrationToken);
+ [self setFcmRegistrationToken: registrationToken];
+
+ id topics = [self fcmTopics];
+ if (topics != nil) {
+ for (NSString *topic in topics) {
+ NSLog(@"subscribe to topic: %@", topic);
+ id pubSub = [FIRMessaging messaging];
+ [pubSub subscribeToTopic:topic];
+ }
+ }
+
+ [self registerWithToken:registrationToken];
+ } else {
+ NSLog(@"FCM token is null");
+ }
+
+}
+
+// FCM refresh token
+// Unclear how this is testable under normal circumstances
+- (void)onTokenRefresh {
+#if !TARGET_IPHONE_SIMULATOR
+ // A rotation of the registration tokens is happening, so the app needs to request a new token.
+ NSLog(@"The FCM registration token needs to be changed.");
+ [[FIRInstanceID instanceID] token];
+ [self initRegistration];
+#endif
+}
+
+// contains error info
+- (void)sendDataMessageFailure:(NSNotification *)notification {
+ NSLog(@"sendDataMessageFailure");
+}
+- (void)sendDataMessageSuccess:(NSNotification *)notification {
+ NSLog(@"sendDataMessageSuccess");
+}
+
+- (void)didSendDataMessageWithID:messageID {
+ NSLog(@"didSendDataMessageWithID");
+}
+
+- (void)willSendDataMessageWithID:messageID error:error {
+ NSLog(@"willSendDataMessageWithID");
+}
+
+- (void)didDeleteMessagesOnServer {
+ NSLog(@"didDeleteMessagesOnServer");
+ // Some messages sent to this device were deleted on the GCM server before reception, likely
+ // because the TTL expired. The client should notify the app server of this, so that the app
+ // server can resend those messages.
+}
+
+- (void)unregister:(CDVInvokedUrlCommand*)command;
+{
+ NSArray* topics = [command argumentAtIndex:0];
+
+ if (topics != nil) {
+ id pubSub = [FIRMessaging messaging];
+ for (NSString *topic in topics) {
+ NSLog(@"unsubscribe from topic: %@", topic);
+ [pubSub unsubscribeFromTopic:topic];
+ }
+ } else {
+ [[UIApplication sharedApplication] unregisterForRemoteNotifications];
+ [self successWithMessage:command.callbackId withMsg:@"unregistered"];
+ }
+}
+
+- (void)subscribe:(CDVInvokedUrlCommand*)command;
+{
+ NSString* topic = [command argumentAtIndex:0];
+
+ if (topic != nil) {
+ NSLog(@"subscribe from topic: %@", topic);
+ id pubSub = [FIRMessaging messaging];
+ [pubSub subscribeToTopic:topic];
+ NSLog(@"Successfully subscribe to topic %@", topic);
+ [self successWithMessage:command.callbackId withMsg:[NSString stringWithFormat:@"Successfully subscribe to topic %@", topic]];
+ } else {
+ NSLog(@"There is no topic to subscribe");
+ [self successWithMessage:command.callbackId withMsg:@"There is no topic to subscribe"];
+ }
+}
+
+- (void)unsubscribe:(CDVInvokedUrlCommand*)command;
+{
+ NSString* topic = [command argumentAtIndex:0];
+
+ if (topic != nil) {
+ NSLog(@"unsubscribe from topic: %@", topic);
+ id pubSub = [FIRMessaging messaging];
+ [pubSub unsubscribeFromTopic:topic];
+ NSLog(@"Successfully unsubscribe from topic %@", topic);
+ [self successWithMessage:command.callbackId withMsg:[NSString stringWithFormat:@"Successfully unsubscribe from topic %@", topic]];
+ } else {
+ NSLog(@"There is no topic to unsubscribe");
+ [self successWithMessage:command.callbackId withMsg:@"There is no topic to unsubscribe"];
+ }
+}
+
+- (void)init:(CDVInvokedUrlCommand*)command;
+{
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self selector:@selector(onTokenRefresh)
+ name:kFIRInstanceIDTokenRefreshNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self selector:@selector(sendDataMessageFailure:)
+ name:FIRMessagingSendErrorNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self selector:@selector(sendDataMessageSuccess:)
+ name:FIRMessagingSendSuccessNotification object:nil];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self selector:@selector(didDeleteMessagesOnServer)
+ name:FIRMessagingMessagesDeletedNotification object:nil];
+
+ [self.commandDelegate runInBackground:^ {
+
+ NSLog(@"Push Plugin register called");
+ self.callbackId = command.callbackId;
+
+ NSMutableDictionary* options = [command.arguments objectAtIndex:0];
+ NSMutableDictionary* iosOptions = [options objectForKey:@"ios"];
+
+ NSArray* topics = [iosOptions objectForKey:@"topics"];
+ [self setFcmTopics:topics];
+
+ UIUserNotificationType UserNotificationTypes = UIUserNotificationTypeNone;
+
+ id badgeArg = [iosOptions objectForKey:@"badge"];
+ id soundArg = [iosOptions objectForKey:@"sound"];
+ id alertArg = [iosOptions objectForKey:@"alert"];
+ id clearBadgeArg = [iosOptions objectForKey:@"clearBadge"];
+
+ if (([badgeArg isKindOfClass:[NSString class]] && [badgeArg isEqualToString:@"true"]) || [badgeArg boolValue])
+ {
+ UserNotificationTypes |= UIUserNotificationTypeBadge;
+ }
+
+ if (([soundArg isKindOfClass:[NSString class]] && [soundArg isEqualToString:@"true"]) || [soundArg boolValue])
+ {
+ UserNotificationTypes |= UIUserNotificationTypeSound;
+ }
+
+ if (([alertArg isKindOfClass:[NSString class]] && [alertArg isEqualToString:@"true"]) || [alertArg boolValue])
+ {
+ UserNotificationTypes |= UIUserNotificationTypeAlert;
+ }
+
+ UserNotificationTypes |= UIUserNotificationActivationModeBackground;
+
+ if (clearBadgeArg == nil || ([clearBadgeArg isKindOfClass:[NSString class]] && [clearBadgeArg isEqualToString:@"false"]) || ![clearBadgeArg boolValue]) {
+ NSLog(@"PushPlugin.register: setting badge to false");
+ clearBadge = NO;
+ } else {
+ NSLog(@"PushPlugin.register: setting badge to true");
+ clearBadge = YES;
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+ }
+ NSLog(@"PushPlugin.register: clear badge is set to %d", clearBadge);
+
+ isInline = NO;
+
+ NSLog(@"PushPlugin.register: better button setup");
+ // setup action buttons
+ NSMutableSet *categories = [[NSMutableSet alloc] init];
+ id categoryOptions = [iosOptions objectForKey:@"categories"];
+ if (categoryOptions != nil && [categoryOptions isKindOfClass:[NSDictionary class]]) {
+ for (id key in categoryOptions) {
+ NSLog(@"categories: key %@", key);
+ id category = [categoryOptions objectForKey:key];
+
+ id yesButton = [category objectForKey:@"yes"];
+ UIMutableUserNotificationAction *yesAction;
+ if (yesButton != nil && [yesButton isKindOfClass:[NSDictionary class]]) {
+ yesAction = [self createAction: yesButton];
+ }
+ id noButton = [category objectForKey:@"no"];
+ UIMutableUserNotificationAction *noAction;
+ if (noButton != nil && [noButton isKindOfClass:[NSDictionary class]]) {
+ noAction = [self createAction: noButton];
+ }
+ id maybeButton = [category objectForKey:@"maybe"];
+ UIMutableUserNotificationAction *maybeAction;
+ if (maybeButton != nil && [maybeButton isKindOfClass:[NSDictionary class]]) {
+ maybeAction = [self createAction: maybeButton];
+ }
+
+ // First create the category
+ UIMutableUserNotificationCategory *notificationCategory = [[UIMutableUserNotificationCategory alloc] init];
+
+ // Identifier to include in your push payload and local notification
+ notificationCategory.identifier = key;
+
+ NSMutableArray *categoryArray = [[NSMutableArray alloc] init];
+ NSMutableArray *minimalCategoryArray = [[NSMutableArray alloc] init];
+ if (yesButton != nil) {
+ [categoryArray addObject:yesAction];
+ [minimalCategoryArray addObject:yesAction];
+ }
+ if (noButton != nil) {
+ [categoryArray addObject:noAction];
+ [minimalCategoryArray addObject:noAction];
+ }
+ if (maybeButton != nil) {
+ [categoryArray addObject:maybeAction];
+ }
+
+ // Add the actions to the category and set the action context
+ [notificationCategory setActions:categoryArray forContext:UIUserNotificationActionContextDefault];
+
+ // Set the actions to present in a minimal context
+ [notificationCategory setActions:minimalCategoryArray forContext:UIUserNotificationActionContextMinimal];
+
+ NSLog(@"Adding category %@", key);
+ [categories addObject:notificationCategory];
+ }
+
+ }
+
+ if ([[UIApplication sharedApplication]respondsToSelector:@selector(registerUserNotificationSettings:)]) {
+ UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UserNotificationTypes categories:categories];
+ [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
+ [[UIApplication sharedApplication] registerForRemoteNotifications];
+ }
+
+ // Read GoogleService-Info.plist
+ NSString *path = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
+
+ // Load the file content and read the data into arrays
+ NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:path];
+ fcmSenderId = [dict objectForKey:@"GCM_SENDER_ID"];
+ BOOL isGcmEnabled = [[dict valueForKey:@"IS_GCM_ENABLED"] boolValue];
+
+ NSLog(@"FCM Sender ID %@", fcmSenderId);
+
+ // GCM options
+ [self setFcmSenderId: fcmSenderId];
+ if(isGcmEnabled && [[self fcmSenderId] length] > 0) {
+ NSLog(@"Using FCM Notification");
+ [self setUsesFCM: YES];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if([FIRApp defaultApp] == nil)
+ [FIRApp configure];
+ [self initRegistration];
+ });
+ } else {
+ NSLog(@"Using APNS Notification");
+ [self setUsesFCM:NO];
+ }
+ id fcmSandboxArg = [iosOptions objectForKey:@"fcmSandbox"];
+
+ [self setFcmSandbox:@NO];
+ if ([self usesFCM] &&
+ (([fcmSandboxArg isKindOfClass:[NSString class]] && [fcmSandboxArg isEqualToString:@"true"]) ||
+ [fcmSandboxArg boolValue]))
+ {
+ NSLog(@"Using FCM Sandbox");
+ [self setFcmSandbox:@YES];
+ }
+
+ if (notificationMessage) { // if there is a pending startup notification
+ dispatch_async(dispatch_get_main_queue(), ^{
+ // delay to allow JS event handlers to be setup
+ [self performSelector:@selector(notificationReceived) withObject:nil afterDelay: 0.5];
+ });
+ }
+ }];
+}
+
+- (UIMutableUserNotificationAction *)createAction:(NSDictionary *)dictionary {
+
+ UIMutableUserNotificationAction *myAction = [[UIMutableUserNotificationAction alloc] init];
+
+ myAction = [[UIMutableUserNotificationAction alloc] init];
+ myAction.identifier = [dictionary objectForKey:@"callback"];
+ myAction.title = [dictionary objectForKey:@"title"];
+ id mode =[dictionary objectForKey:@"foreground"];
+ if (mode == nil || ([mode isKindOfClass:[NSString class]] && [mode isEqualToString:@"false"]) || ![mode boolValue]) {
+ myAction.activationMode = UIUserNotificationActivationModeBackground;
+ } else {
+ myAction.activationMode = UIUserNotificationActivationModeForeground;
+ }
+ id destructive = [dictionary objectForKey:@"destructive"];
+ if (destructive == nil || ([destructive isKindOfClass:[NSString class]] && [destructive isEqualToString:@"false"]) || ![destructive boolValue]) {
+ myAction.destructive = NO;
+ } else {
+ myAction.destructive = YES;
+ }
+ myAction.authenticationRequired = NO;
+
+ return myAction;
+}
+
+- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
+ if (self.callbackId == nil) {
+ NSLog(@"Unexpected call to didRegisterForRemoteNotificationsWithDeviceToken, ignoring: %@", deviceToken);
+ return;
+ }
+ NSLog(@"Push Plugin register success: %@", deviceToken);
+
+ NSMutableDictionary *results = [NSMutableDictionary dictionary];
+ NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""]
+ stringByReplacingOccurrencesOfString:@">" withString:@""]
+ stringByReplacingOccurrencesOfString: @" " withString: @""];
+ [results setValue:token forKey:@"deviceToken"];
+
+#if !TARGET_IPHONE_SIMULATOR
+ // Get Bundle Info for Remote Registration (handy if you have more than one app)
+ [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"] forKey:@"appName"];
+ [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forKey:@"appVersion"];
+
+ // Check what Notifications the user has turned on. We registered for all three, but they may have manually disabled some or all of them.
+
+ NSUInteger rntypes = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
+
+ // Set the defaults to disabled unless we find otherwise...
+ NSString *pushBadge = @"disabled";
+ NSString *pushAlert = @"disabled";
+ NSString *pushSound = @"disabled";
+
+ // Check what Registered Types are turned on. This is a bit tricky since if two are enabled, and one is off, it will return a number 2... not telling you which
+ // one is actually disabled. So we are literally checking to see if rnTypes matches what is turned on, instead of by number. The "tricky" part is that the
+ // single notification types will only match if they are the ONLY one enabled. Likewise, when we are checking for a pair of notifications, it will only be
+ // true if those two notifications are on. This is why the code is written this way
+ if(rntypes & UIUserNotificationTypeBadge){
+ pushBadge = @"enabled";
+ }
+ if(rntypes & UIUserNotificationTypeAlert) {
+ pushAlert = @"enabled";
+ }
+ if(rntypes & UIUserNotificationTypeSound) {
+ pushSound = @"enabled";
+ }
+
+ [results setValue:pushBadge forKey:@"pushBadge"];
+ [results setValue:pushAlert forKey:@"pushAlert"];
+ [results setValue:pushSound forKey:@"pushSound"];
+
+ // Get the users Device Model, Display Name, Token & Version Number
+ UIDevice *dev = [UIDevice currentDevice];
+ [results setValue:dev.name forKey:@"deviceName"];
+ [results setValue:dev.model forKey:@"deviceModel"];
+ [results setValue:dev.systemVersion forKey:@"deviceSystemVersion"];
+
+ if(![self usesFCM]) {
+ [self registerWithToken: token];
+ }
+#endif
+}
+
+- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
+{
+ if (self.callbackId == nil) {
+ NSLog(@"Unexpected call to didFailToRegisterForRemoteNotificationsWithError, ignoring: %@", error);
+ return;
+ }
+ NSLog(@"Push Plugin register failed");
+ [self failWithMessage:self.callbackId withMsg:@"" withError:error];
+}
+
+- (void)notificationReceived {
+ NSLog(@"Notification received");
+
+ if (notificationMessage && self.callbackId != nil)
+ {
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:4];
+ NSMutableDictionary* additionalData = [NSMutableDictionary dictionaryWithCapacity:4];
+
+
+ for (id key in notificationMessage) {
+ if ([key isEqualToString:@"aps"]) {
+ id aps = [notificationMessage objectForKey:@"aps"];
+
+ for(id key in aps) {
+ NSLog(@"Push Plugin key: %@", key);
+ id value = [aps objectForKey:key];
+
+ if ([key isEqualToString:@"alert"]) {
+ if ([value isKindOfClass:[NSDictionary class]]) {
+ for (id messageKey in value) {
+ id messageValue = [value objectForKey:messageKey];
+ if ([messageKey isEqualToString:@"body"]) {
+ [message setObject:messageValue forKey:@"message"];
+ } else if ([messageKey isEqualToString:@"title"]) {
+ [message setObject:messageValue forKey:@"title"];
+ } else {
+ [additionalData setObject:messageValue forKey:messageKey];
+ }
+ }
+ }
+ else {
+ [message setObject:value forKey:@"message"];
+ }
+ } else if ([key isEqualToString:@"title"]) {
+ [message setObject:value forKey:@"title"];
+ } else if ([key isEqualToString:@"badge"]) {
+ [message setObject:value forKey:@"count"];
+ } else if ([key isEqualToString:@"sound"]) {
+ [message setObject:value forKey:@"sound"];
+ } else if ([key isEqualToString:@"image"]) {
+ [message setObject:value forKey:@"image"];
+ } else {
+ [additionalData setObject:value forKey:key];
+ }
+ }
+ } else {
+ [additionalData setObject:[notificationMessage objectForKey:key] forKey:key];
+ }
+ }
+
+ if (isInline) {
+ [additionalData setObject:[NSNumber numberWithBool:YES] forKey:@"foreground"];
+ } else {
+ [additionalData setObject:[NSNumber numberWithBool:NO] forKey:@"foreground"];
+ }
+
+ if (coldstart) {
+ [additionalData setObject:[NSNumber numberWithBool:YES] forKey:@"coldstart"];
+ } else {
+ [additionalData setObject:[NSNumber numberWithBool:NO] forKey:@"coldstart"];
+ }
+
+ [message setObject:additionalData forKey:@"additionalData"];
+
+ // send notification message
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+
+ self.coldstart = NO;
+ self.notificationMessage = nil;
+ }
+}
+
+- (void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand *)command
+{
+ NSMutableDictionary* options = [command.arguments objectAtIndex:0];
+ int badge = [[options objectForKey:@"badge"] intValue] ?: 0;
+
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
+
+ NSString* message = [NSString stringWithFormat:@"app badge count set to %d", badge];
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+}
+
+- (void)getApplicationIconBadgeNumber:(CDVInvokedUrlCommand *)command
+{
+ NSInteger badge = [UIApplication sharedApplication].applicationIconBadgeNumber;
+
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(int)badge];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+}
+
+- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command
+{
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
+
+ NSString* message = [NSString stringWithFormat:@"cleared all notifications"];
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+}
+
+- (void)hasPermission:(CDVInvokedUrlCommand *)command
+{
+ BOOL enabled = NO;
+ id<UIApplicationDelegate> appDelegate = [UIApplication sharedApplication].delegate;
+ if ([appDelegate respondsToSelector:@selector(userHasRemoteNotificationsEnabled)]) {
+ enabled = [appDelegate performSelector:@selector(userHasRemoteNotificationsEnabled)];
+ }
+
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:1];
+ [message setObject:[NSNumber numberWithBool:enabled] forKey:@"isEnabled"];
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId];
+}
+
+-(void)successWithMessage:(NSString *)myCallbackId withMsg:(NSString *)message
+{
+ if (myCallbackId != nil)
+ {
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ [self.commandDelegate sendPluginResult:commandResult callbackId:myCallbackId];
+ }
+}
+
+-(void)registerWithToken:(NSString*)token; {
+ // Send result to trigger 'registration' event but keep callback
+ NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
+ [message setObject:token forKey:@"registrationId"];
+ if ([self usesFCM]) {
+ [message setObject:@"FCM" forKey:@"registrationType"];
+ } else {
+ [message setObject:@"APNS" forKey:@"registrationType"];
+ }
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+}
+
+
+-(void)failWithMessage:(NSString *)myCallbackId withMsg:(NSString *)message withError:(NSError *)error
+{
+ NSString *errorMessage = (error) ? [NSString stringWithFormat:@"%@ - %@", message, [error localizedDescription]] : message;
+ CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage];
+
+ [self.commandDelegate sendPluginResult:commandResult callbackId:myCallbackId];
+}
+
+-(void) finish:(CDVInvokedUrlCommand*)command
+{
+ NSLog(@"Push Plugin finish called");
+
+ [self.commandDelegate runInBackground:^ {
+ NSString* notId = [command.arguments objectAtIndex:0];
+
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [NSTimer scheduledTimerWithTimeInterval:0.1
+ target:self
+ selector:@selector(stopBackgroundTask:)
+ userInfo:notId
+ repeats:NO];
+ });
+
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+ }];
+}
+
+-(void)stopBackgroundTask:(NSTimer*)timer
+{
+ UIApplication *app = [UIApplication sharedApplication];
+
+ NSLog(@"Push Plugin stopBackgroundTask called");
+
+ if (handlerObj) {
+ NSLog(@"Push Plugin handlerObj");
+ completionHandler = [handlerObj[[timer userInfo]] copy];
+ if (completionHandler) {
+ NSLog(@"Push Plugin: stopBackgroundTask (remaining t: %f)", app.backgroundTimeRemaining);
+ completionHandler(UIBackgroundFetchResultNewData);
+ completionHandler = nil;
+ }
+ }
+}
+
+@end