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