You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/01/22 02:57:59 UTC
[10/52] [partial] support for 2.4.0rc1. "vendored" the platform libs
in. added Gord and Braden as contributors. removed dependency on unzip and
axed the old download-cordova code.
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m
new file mode 100644
index 0000000..217f611
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.m
@@ -0,0 +1,521 @@
+/*
+ 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 "CDVLocalStorage.h"
+#import "CDV.h"
+
+@interface CDVLocalStorage ()
+
+@property (nonatomic, readwrite, strong) NSMutableArray* backupInfo; // array of CDVBackupInfo objects
+@property (nonatomic, readwrite, weak) id <UIWebViewDelegate> webviewDelegate;
+
+@end
+
+@implementation CDVLocalStorage
+
+@synthesize backupInfo, webviewDelegate;
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
+{
+ self = (CDVLocalStorage*)[super initWithWebView:theWebView settings:classSettings];
+ if (self) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive)
+ name:UIApplicationWillResignActiveNotification object:nil];
+
+ self.backupInfo = [[self class] createBackupInfoWithCloudBackup:[(NSString*)[classSettings objectForKey:@"backupType"] isEqualToString:@"cloud"]];
+
+ // over-ride current webview delegate (for restore reasons)
+ self.webviewDelegate = theWebView.delegate;
+ theWebView.delegate = self;
+ }
+
+ return self;
+}
+
+#pragma mark -
+#pragma mark Plugin interface methods
+
++ (NSMutableArray*)createBackupInfoWithTargetDir:(NSString*)targetDir backupDir:(NSString*)backupDir targetDirNests:(BOOL)targetDirNests backupDirNests:(BOOL)backupDirNests rename:(BOOL)rename
+{
+ /*
+ This "helper" does so much work and has so many options it would probably be clearer to refactor the whole thing.
+ Basically, there are three database locations:
+
+ 1. "Normal" dir -- LIB/<nested dires WebKit/LocalStorage etc>/<normal filenames>
+ 2. "Caches" dir -- LIB/Caches/<normal filenames>
+ 3. "Backup" dir -- DOC/Backups/<renamed filenames>
+
+ And between these three, there are various migration paths, most of which only consider 2 of the 3, which is why this helper is based on 2 locations and has a notion of "direction".
+ */
+ NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:3];
+
+ NSString* original;
+ NSString* backup;
+ CDVBackupInfo* backupItem;
+
+ // ////////// LOCALSTORAGE
+
+ original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0.localstorage":@"file__0.localstorage"];
+ backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")];
+ backup = [backup stringByAppendingPathComponent:(rename ? @"localstorage.appdata.db":@"file__0.localstorage")];
+
+ backupItem = [[CDVBackupInfo alloc] init];
+ backupItem.backup = backup;
+ backupItem.original = original;
+ backupItem.label = @"localStorage database";
+
+ [backupInfo addObject:backupItem];
+
+ // ////////// WEBSQL MAIN DB
+
+ original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/Databases.db":@"Databases.db"];
+ backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")];
+ backup = [backup stringByAppendingPathComponent:(rename ? @"websqlmain.appdata.db":@"Databases.db")];
+
+ backupItem = [[CDVBackupInfo alloc] init];
+ backupItem.backup = backup;
+ backupItem.original = original;
+ backupItem.label = @"websql main database";
+
+ [backupInfo addObject:backupItem];
+
+ // ////////// WEBSQL DATABASES
+
+ original = [targetDir stringByAppendingPathComponent:targetDirNests ? @"WebKit/LocalStorage/file__0":@"file__0"];
+ backup = [backupDir stringByAppendingPathComponent:(backupDirNests ? @"WebKit/LocalStorage":@"")];
+ backup = [backup stringByAppendingPathComponent:(rename ? @"websqldbs.appdata.db":@"file__0")];
+
+ backupItem = [[CDVBackupInfo alloc] init];
+ backupItem.backup = backup;
+ backupItem.original = original;
+ backupItem.label = @"websql databases";
+
+ [backupInfo addObject:backupItem];
+
+ return backupInfo;
+}
+
++ (NSMutableArray*)createBackupInfoWithCloudBackup:(BOOL)cloudBackup
+{
+ // create backup info from backup folder to caches folder
+ NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ NSString* cacheFolder = [appLibraryFolder stringByAppendingPathComponent:@"Caches"];
+ NSString* backupsFolder = [appDocumentsFolder stringByAppendingPathComponent:@"Backups"];
+
+ // create the backups folder, if needed
+ [[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder withIntermediateDirectories:YES attributes:nil error:nil];
+
+ [self addSkipBackupAttributeToItemAtURL:[NSURL fileURLWithPath:backupsFolder] skip:!cloudBackup];
+
+ return [self createBackupInfoWithTargetDir:cacheFolder backupDir:backupsFolder targetDirNests:NO backupDirNests:NO rename:YES];
+}
+
++ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL*)URL skip:(BOOL)skip
+{
+ NSAssert(IsAtLeastiOSVersion(@"5.1"), @"Cannot mark files for NSURLIsExcludedFromBackupKey on iOS less than 5.1");
+
+ NSError* error = nil;
+ BOOL success = [URL setResourceValue:[NSNumber numberWithBool:skip] forKey:NSURLIsExcludedFromBackupKey error:&error];
+ if (!success) {
+ NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
+ }
+ return success;
+}
+
++ (BOOL)copyFrom:(NSString*)src to:(NSString*)dest error:(NSError * __autoreleasing*)error
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+
+ if (![fileManager fileExistsAtPath:src]) {
+ NSString* errorString = [NSString stringWithFormat:@"%@ file does not exist.", src];
+ if (error != NULL) {
+ (*error) = [NSError errorWithDomain:kCDVLocalStorageErrorDomain
+ code:kCDVLocalStorageFileOperationError
+ userInfo:[NSDictionary dictionaryWithObject:errorString
+ forKey:NSLocalizedDescriptionKey]];
+ }
+ return NO;
+ }
+
+ // generate unique filepath in temp directory
+ CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
+ CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
+ NSString* tempBackup = [[NSTemporaryDirectory () stringByAppendingPathComponent:(__bridge NSString*)uuidString] stringByAppendingPathExtension:@"bak"];
+ CFRelease(uuidString);
+ CFRelease(uuidRef);
+
+ BOOL destExists = [fileManager fileExistsAtPath:dest];
+
+ // backup the dest
+ if (destExists && ![fileManager copyItemAtPath:dest toPath:tempBackup error:error]) {
+ return NO;
+ }
+
+ // remove the dest
+ if (destExists && ![fileManager removeItemAtPath:dest error:error]) {
+ return NO;
+ }
+
+ // create path to dest
+ if (!destExists && ![fileManager createDirectoryAtPath:[dest stringByDeletingLastPathComponent] withIntermediateDirectories:YES attributes:nil error:error]) {
+ return NO;
+ }
+
+ // copy src to dest
+ if ([fileManager copyItemAtPath:src toPath:dest error:error]) {
+ // success - cleanup - delete the backup to the dest
+ if ([fileManager fileExistsAtPath:tempBackup]) {
+ [fileManager removeItemAtPath:tempBackup error:error];
+ }
+ return YES;
+ } else {
+ // failure - we restore the temp backup file to dest
+ [fileManager copyItemAtPath:tempBackup toPath:dest error:error];
+ // cleanup - delete the backup to the dest
+ if ([fileManager fileExistsAtPath:tempBackup]) {
+ [fileManager removeItemAtPath:tempBackup error:error];
+ }
+ return NO;
+ }
+}
+
+- (BOOL)shouldBackup
+{
+ for (CDVBackupInfo* info in self.backupInfo) {
+ if ([info shouldBackup]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+- (BOOL)shouldRestore
+{
+ for (CDVBackupInfo* info in self.backupInfo) {
+ if ([info shouldRestore]) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+/* copy from webkitDbLocation to persistentDbLocation */
+- (void)backup:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+
+ NSError* __autoreleasing error = nil;
+ CDVPluginResult* result = nil;
+ NSString* message = nil;
+
+ for (CDVBackupInfo* info in self.backupInfo) {
+ if ([info shouldBackup]) {
+ [[self class] copyFrom:info.original to:info.backup error:&error];
+
+ if (callbackId) {
+ if (error == nil) {
+ message = [NSString stringWithFormat:@"Backed up: %@", info.label];
+ NSLog(@"%@", message);
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ } else {
+ message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) backup: %@", info.label, [error localizedDescription]];
+ NSLog(@"%@", message);
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+ }
+ }
+ }
+}
+
+/* copy from persistentDbLocation to webkitDbLocation */
+- (void)restore:(CDVInvokedUrlCommand*)command
+{
+ NSError* __autoreleasing error = nil;
+ CDVPluginResult* result = nil;
+ NSString* message = nil;
+
+ for (CDVBackupInfo* info in self.backupInfo) {
+ if ([info shouldRestore]) {
+ [[self class] copyFrom:info.backup to:info.original error:&error];
+
+ if (error == nil) {
+ message = [NSString stringWithFormat:@"Restored: %@", info.label];
+ NSLog(@"%@", message);
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ } else {
+ message = [NSString stringWithFormat:@"Error in CDVLocalStorage (%@) restore: %@", info.label, [error localizedDescription]];
+ NSLog(@"%@", message);
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:message];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ }
+ }
+}
+
++ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType
+{
+ [self __verifyAndFixDatabaseLocations];
+ [self __restoreLegacyDatabaseLocationsWithBackupType:backupType];
+}
+
++ (void)__verifyAndFixDatabaseLocations
+{
+ NSBundle* mainBundle = [NSBundle mainBundle];
+ NSString* bundlePath = [[mainBundle bundlePath] stringByDeletingLastPathComponent];
+ NSString* bundleIdentifier = [[mainBundle infoDictionary] objectForKey:@"CFBundleIdentifier"];
+ NSString* appPlistPath = [bundlePath stringByAppendingPathComponent:[NSString stringWithFormat:@"Library/Preferences/%@.plist", bundleIdentifier]];
+
+ NSMutableDictionary* appPlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:appPlistPath];
+ BOOL modified = [[self class] __verifyAndFixDatabaseLocationsWithAppPlistDict:appPlistDict
+ bundlePath:bundlePath
+ fileManager:[NSFileManager defaultManager]];
+
+ if (modified) {
+ BOOL ok = [appPlistDict writeToFile:appPlistPath atomically:YES];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+ NSLog(@"Fix applied for database locations?: %@", ok ? @"YES" : @"NO");
+ }
+}
+
++ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
+ bundlePath:(NSString*)bundlePath
+ fileManager:(NSFileManager*)fileManager
+{
+ NSString* libraryCaches = @"Library/Caches";
+ NSString* libraryWebKit = @"Library/WebKit";
+
+ NSArray* keysToCheck = [NSArray arrayWithObjects:
+ @"WebKitLocalStorageDatabasePathPreferenceKey",
+ @"WebDatabaseDirectory",
+ nil];
+
+ BOOL dirty = NO;
+
+ for (NSString* key in keysToCheck) {
+ NSString* value = [appPlistDict objectForKey:key];
+ // verify key exists, and path is in app bundle, if not - fix
+ if ((value != nil) && ![value hasPrefix:bundlePath]) {
+ // the pathSuffix to use may be wrong - OTA upgrades from < 5.1 to 5.1 do keep the old path Library/WebKit,
+ // while Xcode synced ones do change the storage location to Library/Caches
+ NSString* newBundlePath = [bundlePath stringByAppendingPathComponent:libraryCaches];
+ if (![fileManager fileExistsAtPath:newBundlePath]) {
+ newBundlePath = [bundlePath stringByAppendingPathComponent:libraryWebKit];
+ }
+ [appPlistDict setValue:newBundlePath forKey:key];
+ dirty = YES;
+ }
+ }
+
+ return dirty;
+}
+
++ (void)__restoreLegacyDatabaseLocationsWithBackupType:(NSString*)backupType
+{
+ // on iOS 6, if you toggle between cloud/local backup, you must move database locations. Default upgrade from iOS5.1 to iOS6 is like a toggle from local to cloud.
+ if (!IsAtLeastiOSVersion(@"6.0")) {
+ return;
+ }
+
+ NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains (NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+ NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+
+ NSMutableArray* backupInfo = [NSMutableArray arrayWithCapacity:0];
+
+ if ([backupType isEqualToString:@"cloud"]) {
+ // We would like to restore old backups/caches databases to the new destination (nested in lib folder)
+ [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appDocumentsFolder stringByAppendingPathComponent:@"Backups"] targetDirNests:YES backupDirNests:NO rename:YES]];
+ [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:appLibraryFolder backupDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] targetDirNests:YES backupDirNests:NO rename:NO]];
+ } else {
+ // For ios6 local backups we also want to restore from Backups dir -- but we don't need to do that here, since the plugin will do that itself.
+ [backupInfo addObjectsFromArray:[self createBackupInfoWithTargetDir:[appLibraryFolder stringByAppendingPathComponent:@"Caches"] backupDir:appLibraryFolder targetDirNests:NO backupDirNests:YES rename:NO]];
+ }
+
+ NSFileManager* manager = [NSFileManager defaultManager];
+
+ for (CDVBackupInfo* info in backupInfo) {
+ if ([manager fileExistsAtPath:info.backup]) {
+ if ([info shouldRestore]) {
+ NSLog(@"Restoring old webstorage backup. From: '%@' To: '%@'.", info.backup, info.original);
+ [self copyFrom:info.backup to:info.original error:nil];
+ }
+ NSLog(@"Removing old webstorage backup: '%@'.", info.backup);
+ [manager removeItemAtPath:info.backup error:nil];
+ }
+ }
+
+ [[NSUserDefaults standardUserDefaults] setBool:[backupType isEqualToString:@"cloud"] forKey:@"WebKitStoreWebDataForBackup"];
+}
+
+#pragma mark -
+#pragma mark Notification handlers
+
+- (void)onResignActive
+{
+ UIDevice* device = [UIDevice currentDevice];
+ NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
+
+ BOOL isMultitaskingSupported = [device respondsToSelector:@selector(isMultitaskingSupported)] && [device isMultitaskingSupported];
+
+ if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
+ exitsOnSuspend = [NSNumber numberWithBool:NO];
+ }
+
+ if (exitsOnSuspend) {
+ [self backup:nil];
+ } else if (isMultitaskingSupported) {
+ __block UIBackgroundTaskIdentifier backgroundTaskID = UIBackgroundTaskInvalid;
+
+ backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
+ [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
+ backgroundTaskID = UIBackgroundTaskInvalid;
+ NSLog (@"Background task to backup WebSQL/LocalStorage expired.");
+ }];
+ CDVLocalStorage __weak* weakSelf = self;
+ [self.commandDelegate runInBackground:^{
+ [weakSelf backup:nil];
+
+ [[UIApplication sharedApplication] endBackgroundTask:backgroundTaskID];
+ backgroundTaskID = UIBackgroundTaskInvalid;
+ }];
+ }
+}
+
+- (void)onAppTerminate
+{
+ [self onResignActive];
+}
+
+#pragma mark -
+#pragma mark UIWebviewDelegate implementation and forwarding
+
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+ [self restore:nil];
+
+ return [self.webviewDelegate webViewDidStartLoad:theWebView];
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+ return [self.webviewDelegate webViewDidFinishLoad:theWebView];
+}
+
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
+{
+ return [self.webviewDelegate webView:theWebView didFailLoadWithError:error];
+}
+
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+ return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
+}
+
+#pragma mark -
+#pragma mark Over-rides
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock:
+}
+
+@end
+
+#pragma mark -
+#pragma mark CDVBackupInfo implementation
+
+@implementation CDVBackupInfo
+
+@synthesize original, backup, label;
+
+- (BOOL)file:(NSString*)aPath isNewerThanFile:(NSString*)bPath
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSError* __autoreleasing error = nil;
+
+ NSDictionary* aPathAttribs = [fileManager attributesOfItemAtPath:aPath error:&error];
+ NSDictionary* bPathAttribs = [fileManager attributesOfItemAtPath:bPath error:&error];
+
+ NSDate* aPathModDate = [aPathAttribs objectForKey:NSFileModificationDate];
+ NSDate* bPathModDate = [bPathAttribs objectForKey:NSFileModificationDate];
+
+ if ((nil == aPathModDate) && (nil == bPathModDate)) {
+ return NO;
+ }
+
+ return [aPathModDate compare:bPathModDate] == NSOrderedDescending || bPathModDate == nil;
+}
+
+- (BOOL)item:(NSString*)aPath isNewerThanItem:(NSString*)bPath
+{
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+
+ BOOL aPathIsDir = NO, bPathIsDir = NO;
+ BOOL aPathExists = [fileManager fileExistsAtPath:aPath isDirectory:&aPathIsDir];
+
+ [fileManager fileExistsAtPath:bPath isDirectory:&bPathIsDir];
+
+ if (!aPathExists) {
+ return NO;
+ }
+
+ if (!(aPathIsDir && bPathIsDir)) { // just a file
+ return [self file:aPath isNewerThanFile:bPath];
+ }
+
+ // essentially we want rsync here, but have to settle for our poor man's implementation
+ // we get the files in aPath, and see if it is newer than the file in bPath
+ // (it is newer if it doesn't exist in bPath) if we encounter the FIRST file that is newer,
+ // we return YES
+ NSDirectoryEnumerator* directoryEnumerator = [fileManager enumeratorAtPath:aPath];
+ NSString* path;
+
+ while ((path = [directoryEnumerator nextObject])) {
+ NSString* aPathFile = [aPath stringByAppendingPathComponent:path];
+ NSString* bPathFile = [bPath stringByAppendingPathComponent:path];
+
+ BOOL isNewer = [self file:aPathFile isNewerThanFile:bPathFile];
+ if (isNewer) {
+ return YES;
+ }
+ }
+
+ return NO;
+}
+
+- (BOOL)shouldBackup
+{
+ return [self item:self.original isNewerThanItem:self.backup];
+}
+
+- (BOOL)shouldRestore
+{
+ return [self item:self.backup isNewerThanItem:self.original];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h
new file mode 100644
index 0000000..7087d43
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.h
@@ -0,0 +1,104 @@
+/*
+ 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 "CDVPlugin.h"
+
+enum CDVHeadingStatus {
+ HEADINGSTOPPED = 0,
+ HEADINGSTARTING,
+ HEADINGRUNNING,
+ HEADINGERROR
+};
+typedef NSUInteger CDVHeadingStatus;
+
+enum CDVLocationStatus {
+ PERMISSIONDENIED = 1,
+ POSITIONUNAVAILABLE,
+ TIMEOUT
+};
+typedef NSUInteger CDVLocationStatus;
+
+// simple object to keep track of heading information
+@interface CDVHeadingData : NSObject {}
+
+@property (nonatomic, assign) CDVHeadingStatus headingStatus;
+@property (nonatomic, strong) CLHeading* headingInfo;
+@property (nonatomic, strong) NSMutableArray* headingCallbacks;
+@property (nonatomic, copy) NSString* headingFilter;
+@property (nonatomic, strong) NSDate* headingTimestamp;
+@property (assign) NSInteger timeout;
+
+@end
+
+// 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;
+ CDVHeadingData* headingData;
+ CDVLocationData* locationData;
+}
+
+@property (nonatomic, strong) CLLocationManager* locationManager;
+@property (strong) CDVHeadingData* headingData;
+@property (nonatomic, strong) CDVLocationData* locationData;
+
+- (BOOL)hasHeadingSupport;
+- (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;
+
+- (void)getHeading:(CDVInvokedUrlCommand*)command;
+- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain;
+- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command;
+- (void)stopHeading:(CDVInvokedUrlCommand*)command;
+- (void)startHeadingWithFilter:(CLLocationDegrees)filter;
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateHeading :(CLHeading*)heading;
+
+- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m
new file mode 100644
index 0000000..9814f35
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocation.m
@@ -0,0 +1,633 @@
+/*
+ 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"
+#import "CDVViewController.h"
+#import "NSArray+Comparisons.h"
+
+#pragma mark Constants
+
+#define kPGLocationErrorDomain @"kPGLocationErrorDomain"
+#define kPGLocationDesiredAccuracyKey @"desiredAccuracy"
+#define kPGLocationForcePromptKey @"forcePrompt"
+#define kPGLocationDistanceFilterKey @"distanceFilter"
+#define kPGLocationFrequencyKey @"frequency"
+
+#pragma mark -
+#pragma mark Categories
+
+@interface NSError (JSONMethods)
+
+- (NSString*)JSONRepresentation;
+
+@end
+
+@interface CLLocation (JSONMethods)
+
+- (NSString*)JSONRepresentation;
+
+@end
+
+@interface CLHeading (JSONMethods)
+
+- (NSString*)JSONRepresentation;
+
+@end
+
+#pragma mark -
+#pragma mark CDVHeadingData
+
+@implementation CDVHeadingData
+
+@synthesize headingStatus, headingInfo, headingCallbacks, headingFilter, headingTimestamp, timeout;
+- (CDVHeadingData*)init
+{
+ self = (CDVHeadingData*)[super init];
+ if (self) {
+ self.headingStatus = HEADINGSTOPPED;
+ self.headingInfo = nil;
+ self.headingCallbacks = nil;
+ self.headingFilter = nil;
+ self.headingTimestamp = nil;
+ self.timeout = 10;
+ }
+ return self;
+}
+
+@end
+
+@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, headingData, locationData;
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
+{
+ self = (CDVLocation*)[super initWithWebView:(UIWebView*)theWebView];
+ if (self) {
+ self.locationManager = [[CLLocationManager alloc] init];
+ self.locationManager.delegate = self; // Tells the location manager to send updates to this object
+ __locationStarted = NO;
+ __highAccuracyEnabled = NO;
+ self.headingData = nil;
+ self.locationData = nil;
+ }
+ return self;
+}
+
+- (BOOL)hasHeadingSupport
+{
+ BOOL headingInstancePropertyAvailable = [self.locationManager respondsToSelector:@selector(headingAvailable)]; // iOS 3.x
+ BOOL headingClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(headingAvailable)]; // iOS 4.x
+
+ if (headingInstancePropertyAvailable) { // iOS 3.x
+ return [(id)self.locationManager headingAvailable];
+ } else if (headingClassPropertyAvailable) { // iOS 4.x
+ return [CLLocationManager headingAvailable];
+ } else { // iOS 2.x
+ return NO;
+ }
+}
+
+- (BOOL)isAuthorized
+{
+ BOOL authorizationStatusClassPropertyAvailable = [CLLocationManager respondsToSelector:@selector(authorizationStatus)]; // iOS 4.2+
+
+ if (authorizationStatusClassPropertyAvailable) {
+ NSUInteger authStatus = [CLLocationManager authorizationStatus];
+ 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;
+ }
+
+ // 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 to distance filter to "none" - which should be the minimum for best results.
+ self.locationManager.distanceFilter = kCLDistanceFilterNone;
+ // Set desired accuracy to Best.
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
+ } else {
+ __highAccuracyEnabled = NO;
+ // TODO: Set distance filter to 10 meters? and desired accuracy to nearest ten meters? arbitrary.
+ self.locationManager.distanceFilter = 10;
+ self.locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters;
+ }
+}
+
+- (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
+{
+ NSString* callbackId = command.callbackId;
+ BOOL enableHighAccuracy = [[command.arguments objectAtIndex: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.arguments objectAtIndex:0];
+ BOOL enableHighAccuracy = [[command.arguments objectAtIndex: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.arguments objectAtIndex:0];
+
+ if (self.locationData && self.locationData.watchCallbacks && [self.locationData.watchCallbacks objectForKey:timerId]) {
+ [self.locationData.watchCallbacks removeObjectForKey:timerId];
+ }
+}
+
+- (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 numberWithInt: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];
+ }
+}
+
+// called to get the current heading
+// Will call location manager to startUpdatingHeading if necessary
+
+- (void)getHeading:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil];
+ NSNumber* filter = [options valueForKey:@"filter"];
+
+ if (filter) {
+ [self watchHeadingFilter:command];
+ return;
+ }
+ if ([self hasHeadingSupport] == NO) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ } else {
+ // heading retrieval does is not affected by disabling locationServices and authorization of app for location services
+ if (!self.headingData) {
+ self.headingData = [[CDVHeadingData alloc] init];
+ }
+ CDVHeadingData* hData = self.headingData;
+
+ if (!hData.headingCallbacks) {
+ hData.headingCallbacks = [NSMutableArray arrayWithCapacity:1];
+ }
+ // add the callbackId into the array so we can call back when get data
+ [hData.headingCallbacks addObject:callbackId];
+
+ if ((hData.headingStatus != HEADINGRUNNING) && (hData.headingStatus != HEADINGERROR)) {
+ // Tell the location manager to start notifying us of heading updates
+ [self startHeadingWithFilter:0.2];
+ } else {
+ [self returnHeadingInfo:callbackId keepCallback:NO];
+ }
+ }
+}
+
+// called to request heading updates when heading changes by a certain amount (filter)
+- (void)watchHeadingFilter:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil];
+ NSNumber* filter = [options valueForKey:@"filter"];
+ CDVHeadingData* hData = self.headingData;
+
+ if ([self hasHeadingSupport] == NO) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:20];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ } else {
+ if (!hData) {
+ self.headingData = [[CDVHeadingData alloc] init];
+ hData = self.headingData;
+ }
+ if (hData.headingStatus != HEADINGRUNNING) {
+ // Tell the location manager to start notifying us of heading updates
+ [self startHeadingWithFilter:[filter doubleValue]];
+ } else {
+ // if already running check to see if due to existing watch filter
+ if (hData.headingFilter && ![hData.headingFilter isEqualToString:callbackId]) {
+ // new watch filter being specified
+ // send heading data one last time to clear old successCallback
+ [self returnHeadingInfo:hData.headingFilter keepCallback:NO];
+ }
+ }
+ // save the new filter callback and update the headingFilter setting
+ hData.headingFilter = callbackId;
+ // check if need to stop and restart in order to change value???
+ self.locationManager.headingFilter = [filter doubleValue];
+ }
+}
+
+- (void)returnHeadingInfo:(NSString*)callbackId keepCallback:(BOOL)bRetain
+{
+ CDVPluginResult* result = nil;
+ CDVHeadingData* hData = self.headingData;
+
+ self.headingData.headingTimestamp = [NSDate date];
+
+ if (hData && (hData.headingStatus == HEADINGERROR)) {
+ // return error
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0];
+ } else if (hData && (hData.headingStatus == HEADINGRUNNING) && hData.headingInfo) {
+ // if there is heading info, return it
+ CLHeading* hInfo = hData.headingInfo;
+ NSMutableDictionary* returnInfo = [NSMutableDictionary dictionaryWithCapacity:4];
+ NSNumber* timestamp = [NSNumber numberWithDouble:([hInfo.timestamp timeIntervalSince1970] * 1000)];
+ [returnInfo setObject:timestamp forKey:@"timestamp"];
+ [returnInfo setObject:[NSNumber numberWithDouble:hInfo.magneticHeading] forKey:@"magneticHeading"];
+ id trueHeading = __locationStarted ? (id)[NSNumber numberWithDouble : hInfo.trueHeading] : (id)[NSNull null];
+ [returnInfo setObject:trueHeading forKey:@"trueHeading"];
+ [returnInfo setObject:[NSNumber numberWithDouble:hInfo.headingAccuracy] forKey:@"headingAccuracy"];
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:returnInfo];
+ [result setKeepCallbackAsBool:bRetain];
+ }
+ if (result) {
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+}
+
+- (void)stopHeading:(CDVInvokedUrlCommand*)command
+{
+ // CDVHeadingData* hData = self.headingData;
+ if (self.headingData && (self.headingData.headingStatus != HEADINGSTOPPED)) {
+ if (self.headingData.headingFilter) {
+ // callback one last time to clear callback
+ [self returnHeadingInfo:self.headingData.headingFilter keepCallback:NO];
+ self.headingData.headingFilter = nil;
+ }
+ [self.locationManager stopUpdatingHeading];
+ NSLog(@"heading STOPPED");
+ self.headingData = nil;
+ }
+}
+
+// helper method to check the orientation and start updating headings
+- (void)startHeadingWithFilter:(CLLocationDegrees)filter
+{
+ if ([self.locationManager respondsToSelector:@selector(headingOrientation)]) {
+ UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation];
+ if (currentOrientation != UIDeviceOrientationUnknown) {
+ CDVViewController* cdvViewController = (CDVViewController*)self.viewController;
+
+ if ([cdvViewController supportsOrientation:currentOrientation]) {
+ self.locationManager.headingOrientation = (CLDeviceOrientation)currentOrientation;
+ // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same
+ }
+ }
+ }
+ self.locationManager.headingFilter = filter;
+ [self.locationManager startUpdatingHeading];
+ self.headingData.headingStatus = HEADINGSTARTING;
+}
+
+- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager*)manager
+{
+ return YES;
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateHeading:(CLHeading*)heading
+{
+ CDVHeadingData* hData = self.headingData;
+
+ // normally we would clear the delegate to stop getting these notifications, but
+ // we are sharing a CLLocationManager to get location data as well, so we do a nil check here
+ // ideally heading and location should use their own CLLocationManager instances
+ if (hData == nil) {
+ return;
+ }
+
+ // save the data for next call into getHeadingData
+ hData.headingInfo = heading;
+ BOOL bTimeout = NO;
+ if (!hData.headingFilter && hData.headingTimestamp) {
+ bTimeout = fabs([hData.headingTimestamp timeIntervalSinceNow]) > hData.timeout;
+ }
+
+ if (hData.headingStatus == HEADINGSTARTING) {
+ hData.headingStatus = HEADINGRUNNING; // so returnHeading info will work
+
+ // this is the first update
+ for (NSString* callbackId in hData.headingCallbacks) {
+ [self returnHeadingInfo:callbackId keepCallback:NO];
+ }
+
+ [hData.headingCallbacks removeAllObjects];
+ }
+ if (hData.headingFilter) {
+ [self returnHeadingInfo:hData.headingFilter keepCallback:YES];
+ } else if (bTimeout) {
+ [self stopHeading:nil];
+ }
+ hData.headingStatus = HEADINGRUNNING; // to clear any error
+}
+
+- (void)locationManager:(CLLocationManager*)manager didFailWithError:(NSError*)error
+{
+ NSLog(@"locationManager::didFailWithError %@", [error localizedFailureReason]);
+
+ // Compass Error
+ if ([error code] == kCLErrorHeadingFailure) {
+ CDVHeadingData* hData = self.headingData;
+ if (hData) {
+ if (hData.headingStatus == HEADINGSTARTING) {
+ // heading error during startup - report error
+ for (NSString* callbackId in hData.headingCallbacks) {
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0];
+ [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+ }
+
+ [hData.headingCallbacks removeAllObjects];
+ } // else for frequency watches next call to getCurrentHeading will report error
+ if (hData.headingFilter) {
+ CDVPluginResult* resultFilter = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:0];
+ [self.commandDelegate sendPluginResult:resultFilter callbackId:hData.headingFilter];
+ }
+ hData.headingStatus = HEADINGERROR;
+ }
+ }
+ // Location Error
+ else {
+ 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]];
+ }
+ }
+
+ [self.locationManager stopUpdatingLocation];
+ __locationStarted = NO;
+}
+
+- (void)dealloc
+{
+ self.locationManager.delegate = nil;
+}
+
+- (void)onReset
+{
+ [self _stopLocation];
+ [self.locationManager stopUpdatingHeading];
+ self.headingData = nil;
+}
+
+@end
+
+#pragma mark -
+#pragma mark CLLocation(JSONMethods)
+
+@implementation CLLocation (JSONMethods)
+
+- (NSString*)JSONRepresentation
+{
+ return [NSString stringWithFormat:
+ @"{ timestamp: %.00f, \
+ coords: { latitude: %f, longitude: %f, altitude: %.02f, heading: %.02f, speed: %.02f, accuracy: %.02f, altitudeAccuracy: %.02f } \
+ }",
+ [self.timestamp timeIntervalSince1970] * 1000.0,
+ self.coordinate.latitude,
+ self.coordinate.longitude,
+ self.altitude,
+ self.course,
+ self.speed,
+ self.horizontalAccuracy,
+ self.verticalAccuracy
+ ];
+}
+
+@end
+
+#pragma mark NSError(JSONMethods)
+
+@implementation NSError (JSONMethods)
+
+- (NSString*)JSONRepresentation
+{
+ return [NSString stringWithFormat:
+ @"{ code: %d, message: '%@'}",
+ self.code,
+ [self localizedDescription]
+ ];
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h b/lib/cordova-ios/CordovaLib/Classes/CDVLogger.h
new file mode 100644
index 0000000..eeba63c
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/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 "CDVPlugin.h"
+
+@interface CDVLogger : CDVPlugin
+
+- (void)logLevel:(CDVInvokedUrlCommand*)command;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m b/lib/cordova-ios/CordovaLib/Classes/CDVLogger.m
new file mode 100644
index 0000000..a37cf8a
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/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 "CDV.h"
+
+@implementation CDVLogger
+
+/* log a message */
+- (void)logLevel:(CDVInvokedUrlCommand*)command
+{
+ id level = [command.arguments objectAtIndex:0];
+ id message = [command.arguments objectAtIndex:1];
+
+ if ([level isEqualToString:@"LOG"]) {
+ NSLog(@"%@", message);
+ } else {
+ NSLog(@"%@: %@", level, message);
+ }
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h
new file mode 100644
index 0000000..1eedb54
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.h
@@ -0,0 +1,36 @@
+/*
+ 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 "CDVPlugin.h"
+
+@interface CDVNotification : CDVPlugin <UIAlertViewDelegate>{}
+
+- (void)alert:(CDVInvokedUrlCommand*)command;
+- (void)confirm:(CDVInvokedUrlCommand*)command;
+- (void)vibrate:(CDVInvokedUrlCommand*)command;
+
+@end
+
+@interface CDVAlertView : UIAlertView {}
+@property (nonatomic, copy) NSString* callbackId;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m
new file mode 100644
index 0000000..992239e
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVNotification.m
@@ -0,0 +1,109 @@
+/*
+ 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"
+#import "NSDictionary+Extensions.h"
+
+@implementation CDVNotification
+
+- (void)showDialogWithMessage:(NSString*)message title:(NSString*)title buttons:(NSString*)buttons callbackId:(NSString*)callbackId
+{
+ CDVAlertView* alertView = [[CDVAlertView alloc]
+ initWithTitle:title
+ message:message
+ delegate:self
+ cancelButtonTitle:nil
+ otherButtonTitles:nil];
+
+ alertView.callbackId = callbackId;
+
+ NSArray* labels = [buttons componentsSeparatedByString:@","];
+ int count = [labels count];
+
+ for (int n = 0; n < count; n++) {
+ [alertView addButtonWithTitle:[labels objectAtIndex:n]];
+ }
+
+ [alertView show];
+}
+
+- (void)alert:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSArray* arguments = command.arguments;
+ int argc = [arguments count];
+
+ NSString* message = argc > 0 ? [arguments objectAtIndex:0] : nil;
+ NSString* title = argc > 1 ? [arguments objectAtIndex:1] : nil;
+ NSString* buttons = argc > 2 ? [arguments objectAtIndex:2] : nil;
+
+ if (!title) {
+ title = NSLocalizedString(@"Alert", @"Alert");
+ }
+ if (!buttons) {
+ buttons = NSLocalizedString(@"OK", @"OK");
+ }
+
+ [self showDialogWithMessage:message title:title buttons:buttons callbackId:callbackId];
+}
+
+- (void)confirm:(CDVInvokedUrlCommand*)command
+{
+ NSString* callbackId = command.callbackId;
+ NSArray* arguments = command.arguments;
+ int argc = [arguments count];
+
+ NSString* message = argc > 0 ? [arguments objectAtIndex:0] : nil;
+ NSString* title = argc > 1 ? [arguments objectAtIndex:1] : nil;
+ NSString* buttons = argc > 2 ? [arguments objectAtIndex:2] : nil;
+
+ if (!title) {
+ title = NSLocalizedString(@"Confirm", @"Confirm");
+ }
+ if (!buttons) {
+ buttons = NSLocalizedString(@"OK,Cancel", @"OK,Cancel");
+ }
+
+ [self showDialogWithMessage:message title:title buttons:buttons callbackId:callbackId];
+}
+
+/**
+ Callback invoked when an alert dialog's buttons are clicked.
+ Passes the index + label back to JS
+ */
+- (void)alertView:(UIAlertView*)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
+{
+ CDVAlertView* cdvAlertView = (CDVAlertView*)alertView;
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:++buttonIndex];
+
+ [self.commandDelegate sendPluginResult:result callbackId:cdvAlertView.callbackId];
+}
+
+- (void)vibrate:(CDVInvokedUrlCommand*)command
+{
+ AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+}
+
+@end
+
+@implementation CDVAlertView
+
+@synthesize callbackId;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
new file mode 100644
index 0000000..8c59a2b
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
@@ -0,0 +1,64 @@
+/*
+ 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 "CDVPluginResult.h"
+#import "NSMutableArray+QueueAdditions.h"
+#import "CDVCommandDelegate.h"
+
+#define CDVPluginHandleOpenURLNotification @"CDVPluginHandleOpenURLNotification"
+#define CDVPluginResetNotification @"CDVPluginResetNotification"
+#define CDVLocalNotification @"CDVLocalNotification"
+
+@interface CDVPlugin : NSObject {}
+
+@property (nonatomic, weak) UIWebView* webView;
+@property (nonatomic, strong) NSDictionary* settings;
+@property (nonatomic, weak) UIViewController* viewController;
+@property (nonatomic, weak) id <CDVCommandDelegate> commandDelegate;
+
+@property (readonly, assign) BOOL hasPendingOperation;
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings;
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView;
+
+- (void)handleOpenURL:(NSNotification*)notification;
+- (void)onAppTerminate;
+- (void)onMemoryWarning;
+- (void)onReset;
+- (void)dispose;
+
+/*
+ // see initWithWebView implementation
+ - (void) onPause {}
+ - (void) onResume {}
+ - (void) onOrientationWillChange {}
+ - (void) onOrientationDidChange {}
+ - (void)didReceiveLocalNotification:(NSNotification *)notification;
+ */
+
+- (id)appDelegate;
+
+// TODO(agrieve): Deprecate these in favour of using CDVCommandDelegate directly.
+- (NSString*)writeJavascript:(NSString*)javascript;
+- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId;
+- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m
new file mode 100644
index 0000000..53023c7
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m
@@ -0,0 +1,148 @@
+/*
+ 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 "CDVPlugin.h"
+
+@interface CDVPlugin ()
+
+@property (readwrite, assign) BOOL hasPendingOperation;
+
+@end
+
+@implementation CDVPlugin
+@synthesize webView, settings, viewController, commandDelegate, hasPendingOperation;
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings
+{
+ self = [self initWithWebView:theWebView];
+ if (self) {
+ self.settings = classSettings;
+ self.hasPendingOperation = NO;
+ }
+ return self;
+}
+
+- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView
+{
+ self = [super init];
+ if (self) {
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppTerminate) name:UIApplicationWillTerminateNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:nil];
+
+ self.webView = theWebView;
+
+ // You can listen to more app notifications, see:
+ // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4
+
+ /*
+ // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler
+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil];
+
+ // Added in 2.3.0+
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];
+
+ */
+ }
+ return self;
+}
+
+- (void)dispose
+{
+ viewController = nil;
+ commandDelegate = nil;
+ webView = nil;
+}
+
+/*
+// NOTE: for onPause and onResume, calls into JavaScript must not call or trigger any blocking UI, like alerts
+- (void) onPause {}
+- (void) onResume {}
+- (void) onOrientationWillChange {}
+- (void) onOrientationDidChange {}
+*/
+
+/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
+- (void)handleOpenURL:(NSNotification*)notification
+{
+ // override to handle urls sent to your app
+ // register your url schemes in your App-Info.plist
+
+ NSURL* url = [notification object];
+
+ if ([url isKindOfClass:[NSURL class]]) {
+ /* Do your thing! */
+ }
+}
+
+/* NOTE: calls into JavaScript must not call or trigger any blocking UI, like alerts */
+- (void)onAppTerminate
+{
+ // override this if you need to do any cleanup on app exit
+}
+
+- (void)onMemoryWarning
+{
+ // override to remove caches, etc
+}
+
+- (void)onReset
+{
+ // Override to cancel any long-running requests when the WebView navigates or refreshes.
+}
+
+- (void)dealloc
+{
+ [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock:
+}
+
+- (id)appDelegate
+{
+ return [[UIApplication sharedApplication] delegate];
+}
+
+- (NSString*)writeJavascript:(NSString*)javascript
+{
+ return [self.webView stringByEvaluatingJavaScriptFromString:javascript];
+}
+
+- (NSString*)success:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
+{
+ [self.commandDelegate evalJs:[pluginResult toSuccessCallbackString:callbackId]];
+ return @"";
+}
+
+- (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callbackId
+{
+ [self.commandDelegate evalJs:[pluginResult toErrorCallbackString:callbackId]];
+ return @"";
+}
+
+// default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything.
+//- (void)didReceiveLocalNotification:(NSNotification *)notification
+//{
+// // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification
+//}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h
new file mode 100644
index 0000000..8683205
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h
@@ -0,0 +1,61 @@
+/*
+ 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>
+
+typedef enum {
+ CDVCommandStatus_NO_RESULT = 0,
+ CDVCommandStatus_OK,
+ CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION,
+ CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION,
+ CDVCommandStatus_INSTANTIATION_EXCEPTION,
+ CDVCommandStatus_MALFORMED_URL_EXCEPTION,
+ CDVCommandStatus_IO_EXCEPTION,
+ CDVCommandStatus_INVALID_ACTION,
+ CDVCommandStatus_JSON_EXCEPTION,
+ CDVCommandStatus_ERROR
+} CDVCommandStatus;
+
+@interface CDVPluginResult : NSObject {}
+
+@property (nonatomic, strong, readonly) NSNumber* status;
+@property (nonatomic, strong, readonly) id message;
+@property (nonatomic, strong) NSNumber* keepCallback;
+
+- (CDVPluginResult*)init;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage;
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;
+
++ (void)setVerbose:(BOOL)verbose;
++ (BOOL)isVerbose;
+
+- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;
+
+- (NSString*)toJSONString;
+- (NSString*)toSuccessCallbackString:(NSString*)callbackId;
+- (NSString*)toErrorCallbackString:(NSString*)callbackId;
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m
new file mode 100644
index 0000000..d9ba08f
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m
@@ -0,0 +1,181 @@
+/*
+ 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 "CDVPluginResult.h"
+#import "CDVJSON.h"
+#import "CDVDebug.h"
+#import "NSData+Base64.h"
+
+@interface CDVPluginResult ()
+
+- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage;
+
+@end
+
+@implementation CDVPluginResult
+@synthesize status, message, keepCallback;
+
+static NSArray* org_apache_cordova_CommandStatusMsgs;
+
++ (void)initialize
+{
+ org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result",
+ @"OK",
+ @"Class not found",
+ @"Illegal access",
+ @"Instantiation error",
+ @"Malformed url",
+ @"IO error",
+ @"Invalid action",
+ @"JSON error",
+ @"Error",
+ nil];
+}
+
+- (CDVPluginResult*)init
+{
+ return [self initWithStatus:CDVCommandStatus_NO_RESULT message:nil];
+}
+
+- (CDVPluginResult*)initWithStatus:(CDVCommandStatus)statusOrdinal message:(id)theMessage
+{
+ self = [super init];
+ if (self) {
+ status = [NSNumber numberWithInt:statusOrdinal];
+ message = theMessage;
+ keepCallback = [NSNumber numberWithBool:NO];
+ }
+ return self;
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:[org_apache_cordova_CommandStatusMsgs objectAtIndex:statusOrdinal]];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsString:(NSString*)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArray:(NSArray*)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsInt:(int)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithInt:theMessage]];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDouble:(double)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithDouble:theMessage]];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsBool:(BOOL)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:[NSNumber numberWithBool:theMessage]];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsDictionary:(NSDictionary*)theMessage
+{
+ return [[self alloc] initWithStatus:statusOrdinal message:theMessage];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
+{
+ NSDictionary* arrDict = [NSDictionary dictionaryWithObjectsAndKeys:
+ @"ArrayBuffer", @"CDVType",
+ [theMessage base64EncodedString], @"data",
+ nil];
+
+ return [[self alloc] initWithStatus:statusOrdinal message:arrDict];
+}
+
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
+{
+ NSDictionary* errDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"code"];
+
+ return [[self alloc] initWithStatus:statusOrdinal message:errDict];
+}
+
+- (void)setKeepCallbackAsBool:(BOOL)bKeepCallback
+{
+ [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];
+}
+
+- (NSString*)toJSONString
+{
+ NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:
+ self.status, @"status",
+ self.message ? self. message:[NSNull null], @"message",
+ self.keepCallback, @"keepCallback",
+ nil];
+
+ NSError* error = nil;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:dict
+ options:NSJSONWritingPrettyPrinted
+ error:&error];
+ NSString* resultString = nil;
+
+ if (error != nil) {
+ NSLog(@"toJSONString error: %@", [error localizedDescription]);
+ } else {
+ resultString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ }
+
+ if ([[self class] isVerbose]) {
+ NSLog(@"PluginResult:toJSONString - %@", resultString);
+ }
+ return resultString;
+}
+
+- (NSString*)toSuccessCallbackString:(NSString*)callbackId
+{
+ NSString* successCB = [NSString stringWithFormat:@"cordova.callbackSuccess('%@',%@);", callbackId, [self toJSONString]];
+
+ if ([[self class] isVerbose]) {
+ NSLog(@"PluginResult toSuccessCallbackString: %@", successCB);
+ }
+ return successCB;
+}
+
+- (NSString*)toErrorCallbackString:(NSString*)callbackId
+{
+ NSString* errorCB = [NSString stringWithFormat:@"cordova.callbackError('%@',%@);", callbackId, [self toJSONString]];
+
+ if ([[self class] isVerbose]) {
+ NSLog(@"PluginResult toErrorCallbackString: %@", errorCB);
+ }
+ return errorCB;
+}
+
+static BOOL gIsVerbose = NO;
++ (void)setVerbose:(BOOL)verbose
+{
+ gIsVerbose = verbose;
+}
+
++ (BOOL)isVerbose
+{
+ return gIsVerbose;
+}
+
+@end
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h b/lib/cordova-ios/CordovaLib/Classes/CDVReachability.h
new file mode 100644
index 0000000..01a95c3
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/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