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/03/25 22:53:25 UTC

[2/6] 2.6.0rc1 used for libs now. Bumped npm version to 2.6.0. added androids local.properties to gitignore.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/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
index f5b50eb..6e25a27 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.h
@@ -23,10 +23,10 @@
 #import "NSMutableArray+QueueAdditions.h"
 #import "CDVCommandDelegate.h"
 
-#define CDVPageDidLoadNotification @"CDVPageDidLoadNotification"
-#define CDVPluginHandleOpenURLNotification @"CDVPluginHandleOpenURLNotification"
-#define CDVPluginResetNotification @"CDVPluginResetNotification"
-#define CDVLocalNotification @"CDVLocalNotification"
+NSString* const CDVPageDidLoadNotification;
+NSString* const CDVPluginHandleOpenURLNotification;
+NSString* const CDVPluginResetNotification;
+NSString* const CDVLocalNotification;
 
 @interface CDVPlugin : NSObject {}
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/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
index a42d241..8c932a0 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPlugin.m
@@ -19,6 +19,11 @@
 
 #import "CDVPlugin.h"
 
+NSString* const CDVPageDidLoadNotification = @"CDVPageDidLoadNotification";
+NSString* const CDVPluginHandleOpenURLNotification = @"CDVPluginHandleOpenURLNotification";
+NSString* const CDVPluginResetNotification = @"CDVPluginResetNotification";
+NSString* const CDVLocalNotification = @"CDVLocalNotification";
+
 @interface CDVPlugin ()
 
 @property (readwrite, assign) BOOL hasPendingOperation;
@@ -41,7 +46,7 @@
         [[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];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:theWebView];
 
         self.webView = theWebView;
     }
@@ -64,7 +69,7 @@
     // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil];
 
     // Added in 2.5.0
-    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:nil];
+    // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:self.webView];
 }
 
 - (void)dispose

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/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
index 8683205..11b5377 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.h
@@ -37,6 +37,9 @@ typedef enum {
 @property (nonatomic, strong, readonly) NSNumber* status;
 @property (nonatomic, strong, readonly) id message;
 @property (nonatomic, strong)           NSNumber* keepCallback;
+// This property can be used to scope the lifetime of another object. For example,
+// Use it to store the associated NSData when `message` is created using initWithBytesNoCopy.
+@property (nonatomic, strong) id associatedObject;
 
 - (CDVPluginResult*)init;
 + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal;
@@ -47,6 +50,7 @@ typedef enum {
 + (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 messageAsMultipart:(NSArray*)theMessages;
 + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode;
 
 + (void)setVerbose:(BOOL)verbose;
@@ -54,6 +58,9 @@ typedef enum {
 
 - (void)setKeepCallbackAsBool:(BOOL)bKeepCallback;
 
+- (NSString*)argumentsAsJSON;
+
+// These methods are used by the legacy plugin return result method
 - (NSString*)toJSONString;
 - (NSString*)toSuccessCallbackString:(NSString*)callbackId;
 - (NSString*)toErrorCallbackString:(NSString*)callbackId;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/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
index d9ba08f..af7c528 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVPluginResult.m
@@ -29,10 +29,40 @@
 @end
 
 @implementation CDVPluginResult
-@synthesize status, message, keepCallback;
+@synthesize status, message, keepCallback, associatedObject;
 
 static NSArray* org_apache_cordova_CommandStatusMsgs;
 
+id messageFromArrayBuffer(NSData* data)
+{
+    return @{
+               @"CDVType" : @"ArrayBuffer",
+               @"data" :[data base64EncodedString]
+    };
+}
+
+id massageMessage(id message)
+{
+    if ([message isKindOfClass:[NSData class]]) {
+        return messageFromArrayBuffer(message);
+    }
+    return message;
+}
+
+id messageFromMultipart(NSArray* theMessages)
+{
+    NSMutableArray* messages = [NSMutableArray arrayWithArray:theMessages];
+
+    for (NSUInteger i = 0; i < messages.count; ++i) {
+        [messages replaceObjectAtIndex:i withObject:massageMessage([messages objectAtIndex:i])];
+    }
+
+    return @{
+               @"CDVType" : @"MultiPart",
+               @"messages" : messages
+    };
+}
+
 + (void)initialize
 {
     org_apache_cordova_CommandStatusMsgs = [[NSArray alloc] initWithObjects:@"No result",
@@ -101,17 +131,17 @@ static NSArray* org_apache_cordova_CommandStatusMsgs;
 
 + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsArrayBuffer:(NSData*)theMessage
 {
-    NSDictionary* arrDict = [NSDictionary dictionaryWithObjectsAndKeys:
-        @"ArrayBuffer", @"CDVType",
-        [theMessage base64EncodedString], @"data",
-        nil];
+    return [[self alloc] initWithStatus:statusOrdinal message:messageFromArrayBuffer(theMessage)];
+}
 
-    return [[self alloc] initWithStatus:statusOrdinal message:arrDict];
++ (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageAsMultipart:(NSArray*)theMessages
+{
+    return [[self alloc] initWithStatus:statusOrdinal message:messageFromMultipart(theMessages)];
 }
 
 + (CDVPluginResult*)resultWithStatus:(CDVCommandStatus)statusOrdinal messageToErrorObject:(int)errorCode
 {
-    NSDictionary* errDict = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:errorCode] forKey:@"code"];
+    NSDictionary* errDict = @{@"code" :[NSNumber numberWithInt:errorCode]};
 
     return [[self alloc] initWithStatus:statusOrdinal message:errDict];
 }
@@ -121,6 +151,19 @@ static NSArray* org_apache_cordova_CommandStatusMsgs;
     [self setKeepCallback:[NSNumber numberWithBool:bKeepCallback]];
 }
 
+- (NSString*)argumentsAsJSON
+{
+    id arguments = (self.message == nil ? [NSNull null] : self.message);
+    NSArray* argumentsWrappedInArray = [NSArray arrayWithObject:arguments];
+
+    NSString* argumentsJSON = [argumentsWrappedInArray JSONString];
+
+    argumentsJSON = [argumentsJSON substringWithRange:NSMakeRange(1, [argumentsJSON length] - 2)];
+
+    return argumentsJSON;
+}
+
+// These methods are used by the legacy plugin return result method
 - (NSString*)toJSONString
 {
     NSDictionary* dict = [NSDictionary dictionaryWithObjectsAndKeys:

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVReachability.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVReachability.m b/lib/cordova-ios/CordovaLib/Classes/CDVReachability.m
index 3c5a48b..89f4ec9 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVReachability.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVReachability.m
@@ -61,18 +61,18 @@ static void CDVPrintReachabilityFlags(SCNetworkReachabilityFlags flags, const ch
 {
 #if kShouldPrintReachabilityFlags
         NSLog(@"Reachability Flag Status: %c%c %c%c%c%c%c%c%c %s\n",
-        (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
-        (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
-
-        (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
-        (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
-        (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
-        (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
-        (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
-        (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
-        (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
-        comment
-        );
+            (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
+            (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
+
+            (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
+            (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
+            (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
+            (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-',
+            comment
+            );
 #endif
 }
 
@@ -90,7 +90,7 @@ static void CDVReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRe
         return;
     }
 
-    if (![(__bridge NSObject*) info isKindOfClass:[CDVReachability class]]) {
+    if (![(__bridge NSObject*)info isKindOfClass :[CDVReachability class]]) {
         NSLog(@"info was wrong class in ReachabilityCallback");
         return;
     }
@@ -214,7 +214,7 @@ static void CDVReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkRe
     }
 
     if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand) != 0) ||
-            ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))) {
+        ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0))) {
         // ... and the connection is on-demand (or on-traffic) if the
         //     calling application is using the CFSocketStream or higher APIs
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVSound.m b/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
index 99515d7..88fbbd6 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
@@ -87,7 +87,7 @@
         NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
     } else {
         // if resourcePath is not from FileSystem put in tmp dir, else attempt to use provided resource path
-        NSString* tmpPath = [NSTemporaryDirectory ()stringByStandardizingPath];
+        NSString* tmpPath = [NSTemporaryDirectory()stringByStandardizingPath];
         BOOL isTmp = [resourcePath rangeOfString:tmpPath].location != NSNotFound;
         BOOL isDoc = [resourcePath rangeOfString:docsPath].location != NSNotFound;
         if (!isTmp && !isDoc) {
@@ -127,7 +127,7 @@
         filePath = [self.commandDelegate pathForResource:resourcePath];
         if (filePath == nil) {
             // see if this exists in the documents/temp directory from a previous recording
-            NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory ()stringByStandardizingPath], resourcePath];
+            NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory()stringByStandardizingPath], resourcePath];
             if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) {
                 // inefficient as existence will be checked again below but only way to determine if file exists from previous recording
                 filePath = testPath;
@@ -385,7 +385,7 @@
             // bug in AVAudioPlayer when playing downloaded data in NSData - we have to download the file and play from disk
             CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
             CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
-            NSString* filePath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory ()stringByStandardizingPath], uuidString];
+            NSString* filePath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory()stringByStandardizingPath], uuidString];
             CFRelease(uuidString);
             CFRelease(uuidRef);
 
@@ -458,9 +458,19 @@
     double position = [[command.arguments objectAtIndex:1] doubleValue];
 
     if ((audioFile != nil) && (audioFile.player != nil)) {
+        NSString* jsString;
         double posInSeconds = position / 1000;
-        audioFile.player.currentTime = posInSeconds;
-        NSString* jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%f);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, posInSeconds];
+        if (posInSeconds >= audioFile.player.duration) {
+            // The seek is past the end of file.  Stop media and reset to beginning instead of seeking past the end.
+            [audioFile.player stop];
+            audioFile.player.currentTime = 0;
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);\n%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, 0.0, @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+            // NSLog(@"seekToEndJsString=%@",jsString);
+        } else {
+            audioFile.player.currentTime = posInSeconds;
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%f);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, posInSeconds];
+            // NSLog(@"seekJsString=%@",jsString);
+        }
 
         [self.commandDelegate evalJs:jsString];
     }
@@ -625,6 +635,7 @@
         NSLog(@"Finished playing audio sample '%@'", audioFile.resourcePath);
     }
     if (flag) {
+        audioFile.player.currentTime = 0;
         jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
     } else {
         // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.h b/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.h
index a0868a0..704ab43 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.h
@@ -23,7 +23,8 @@
 @interface CDVSplashScreen : CDVPlugin {
     UIActivityIndicatorView* _activityView;
     UIImageView* _imageView;
-    UIView* _parentView;
+    NSString* _curImageName;
+    BOOL _visible;
 }
 
 - (void)show:(CDVInvokedUrlCommand*)command;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.m b/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.m
index cba1b53..efe3eaa 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVSplashScreen.m
@@ -19,29 +19,25 @@
 
 #import "CDVSplashScreen.h"
 
-#define kSplashScreenStateShow 0
-#define kSplashScreenStateHide 1
-
 #define kSplashScreenDurationDefault 0.25f
 
 @implementation CDVSplashScreen
 
 - (void)pluginInitialize
 {
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil];
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil];
+    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:self.webView];
 
-    [self show:nil];
+    [self setVisible:YES];
 }
 
 - (void)show:(CDVInvokedUrlCommand*)command
 {
-    [self updateSplashScreenWithState:kSplashScreenStateShow];
+    [self setVisible:YES];
 }
 
 - (void)hide:(CDVInvokedUrlCommand*)command
 {
-    [self updateSplashScreenWithState:kSplashScreenStateHide];
+    [self setVisible:NO];
 }
 
 - (void)pageDidLoad
@@ -50,16 +46,13 @@
 
     // if value is missing, default to yes
     if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
-        [self hide:nil];
+        [self setVisible:NO];
     }
 }
 
-- (void)onOrientationWillChange:(NSNotification*)notification
+- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
 {
-    if (_imageView != nil) {
-        UIInterfaceOrientation orientation = [notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] intValue];
-        [self updateSplashImageForOrientation:orientation];
-    }
+    [self updateImage];
 }
 
 - (void)createViews
@@ -83,43 +76,115 @@
         topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
     }
 
+    UIView* parentView = self.viewController.view;
+    parentView.userInteractionEnabled = NO;  // disable user interaction while splashscreen is shown
     _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
-    _activityView.tag = 2;
-    _activityView.center = self.viewController.view.center;
+    _activityView.center = CGPointMake(parentView.bounds.size.width / 2, parentView.bounds.size.height / 2);
+    _activityView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleLeftMargin
+        | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin;
     [_activityView startAnimating];
 
+    // Set the frame & image later.
     _imageView = [[UIImageView alloc] init];
-    [self.viewController.view addSubview:_imageView];
-    [self.viewController.view.superview addSubview:_activityView];
-    [self.viewController.view.superview layoutSubviews];
+    [parentView addSubview:_imageView];
+    [parentView addSubview:_activityView];
+
+    // Frame is required when launching in portrait mode.
+    // Bounds for landscape since it captures the rotation.
+    [parentView addObserver:self forKeyPath:@"frame" options:0 context:nil];
+    [parentView addObserver:self forKeyPath:@"bounds" options:0 context:nil];
+
+    [self updateImage];
 }
 
-- (void)updateSplashImageForOrientation:(UIInterfaceOrientation)orientation
+- (void)destroyViews
 {
-    // IPHONE (default)
-    NSString* imageName = @"Default";
+    [_imageView removeFromSuperview];
+    [_activityView removeFromSuperview];
+    _imageView = nil;
+    _activityView = nil;
+    _curImageName = nil;
+
+    self.viewController.view.userInteractionEnabled = YES;  // re-enable user interaction upon completion
+    [self.viewController.view removeObserver:self forKeyPath:@"frame"];
+    [self.viewController.view removeObserver:self forKeyPath:@"bounds"];
+}
+
+// Sets the view's frame and image.
+- (void)updateImage
+{
+    UIInterfaceOrientation orientation = self.viewController.interfaceOrientation;
+
+    // Use UILaunchImageFile if specified in plist.  Otherwise, use Default.
+    NSString* imageName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"];
+
+    if (imageName) {
+        imageName = [imageName stringByDeletingPathExtension];
+    } else {
+        imageName = @"Default";
+    }
 
     if (CDV_IsIPhone5()) {
         imageName = [imageName stringByAppendingString:@"-568h"];
     } else if (CDV_IsIPad()) {
-        // set default to portrait upside down
-        imageName = @"Default-Portrait"; // @"Default-PortraitUpsideDown.png";
+        switch (orientation) {
+            case UIInterfaceOrientationLandscapeLeft:
+            case UIInterfaceOrientationLandscapeRight:
+                imageName = [imageName stringByAppendingString:@"-Landscape"];
+                break;
+
+            case UIInterfaceOrientationPortrait:
+            case UIInterfaceOrientationPortraitUpsideDown:
+            default:
+                imageName = [imageName stringByAppendingString:@"-Portrait"];
+                break;
+        }
+    }
+
+    if (![imageName isEqualToString:_curImageName]) {
+        UIImage* img = [UIImage imageNamed:imageName];
+        _imageView.image = img;
+        _curImageName = imageName;
+    }
+
+    [self updateBounds];
+}
 
-        if (orientation == UIInterfaceOrientationLandscapeLeft) {
-            imageName = @"Default-Landscape.png"; // @"Default-LandscapeLeft.png";
-        } else if (orientation == UIInterfaceOrientationLandscapeRight) {
-            imageName = @"Default-Landscape.png"; // @"Default-LandscapeRight.png";
+- (void)updateBounds
+{
+    UIImage* img = _imageView.image;
+    CGRect imgBounds = CGRectMake(0, 0, img.size.width, img.size.height);
+
+    CGSize screenSize = [self.viewController.view convertRect:[UIScreen mainScreen].bounds fromView:nil].size;
+
+    // There's a special case when the image is the size of the screen.
+    if (CGSizeEqualToSize(screenSize, imgBounds.size)) {
+        CGRect statusFrame = [self.viewController.view convertRect:[UIApplication sharedApplication].statusBarFrame fromView:nil];
+        imgBounds.origin.y -= statusFrame.size.height;
+    } else {
+        CGRect viewBounds = self.viewController.view.bounds;
+        CGFloat imgAspect = imgBounds.size.width / imgBounds.size.height;
+        CGFloat viewAspect = viewBounds.size.width / viewBounds.size.height;
+        // This matches the behaviour of the native splash screen.
+        CGFloat ratio;
+        if (viewAspect > imgAspect) {
+            ratio = viewBounds.size.width / imgBounds.size.width;
+        } else {
+            ratio = viewBounds.size.height / imgBounds.size.height;
         }
+        imgBounds.size.height *= ratio;
+        imgBounds.size.width *= ratio;
     }
 
-    _imageView.image = [UIImage imageNamed:imageName];
-    _imageView.frame = CGRectMake(0, 0, _imageView.image.size.width, _imageView.image.size.height);
+    _imageView.frame = imgBounds;
 }
 
-- (void)updateSplashScreenWithState:(int)state
+- (void)setVisible:(BOOL)visible
 {
-    float toAlpha = state == kSplashScreenStateShow ? 1.0f : 0.0f;
-    BOOL hidden = state == kSplashScreenStateShow ? NO : YES;
+    if (visible == _visible) {
+        return;
+    }
+    _visible = visible;
 
     id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:@"FadeSplashScreen"];
     id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:@"FadeSplashScreenDuration"];
@@ -129,45 +194,26 @@
     if ((fadeSplashScreenValue == nil) || ![fadeSplashScreenValue boolValue]) {
         fadeDuration = 0;
     }
-    if (hidden && (_imageView == nil)) {
-        return;
-    } else if (_imageView == nil) {
-        [self createViews];
-        fadeDuration = 0;
-    }
-
-    if (!hidden) {
-        [self updateSplashImageForOrientation:self.viewController.interfaceOrientation];
-    }
 
-    if (fadeDuration == 0) {
-        [_imageView setHidden:hidden];
-        [_activityView setHidden:hidden];
-    } else {
-        if (state == kSplashScreenStateShow) {
-            // reset states
-            [_imageView setHidden:NO];
-            [_activityView setHidden:NO];
-            [_imageView setAlpha:0.0f];
-            [_activityView setAlpha:0.0f];
+    // Never animate the showing of the splash screen.
+    if (visible) {
+        if (_imageView == nil) {
+            [self createViews];
         }
-
+    } else if (fadeDuration == 0) {
+        [self destroyViews];
+    } else {
         [UIView transitionWithView:self.viewController.view
                           duration:fadeDuration
                            options:UIViewAnimationOptionTransitionNone
                         animations:^(void) {
-                [_imageView setAlpha:toAlpha];
-                [_activityView setAlpha:toAlpha];
-            }
+            [_imageView setAlpha:0];
+            [_activityView setAlpha:0];
+        }
+
                         completion:^(BOOL finished) {
-                if (state == kSplashScreenStateHide) {
-                    // Clean-up resources.
-                    [_imageView removeFromSuperview];
-                    [_activityView removeFromSuperview];
-                    _imageView = nil;
-                    _activityView = nil;
-                }
-            }];
+            [self destroyViews];
+        }];
     }
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVURLProtocol.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVURLProtocol.m b/lib/cordova-ios/CordovaLib/Classes/CDVURLProtocol.m
index 1959c77..afc10de 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVURLProtocol.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVURLProtocol.m
@@ -31,7 +31,7 @@
 @property (nonatomic) NSInteger statusCode;
 @end
 
-static CDVWhitelist * gWhitelist = nil;
+static CDVWhitelist* gWhitelist = nil;
 // Contains a set of NSNumbers of addresses of controllers. It doesn't store
 // the actual pointer to avoid retaining.
 static NSMutableSet* gRegisteredControllers = nil;
@@ -159,12 +159,12 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
         [self sendResponseWithResponseCode:200 data:nil mimeType:nil];
         return;
     } else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefix]) {
-        ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+        ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
             if (asset) {
                 // We have the asset!  Get the data and send it along.
                 ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-                NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass ((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
-                Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+                NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+                Byte* buffer = (Byte*)malloc([assetRepresentation size]);
                 NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
                 NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
                 [self sendResponseWithResponseCode:200 data:data mimeType:MIMEType];
@@ -173,7 +173,7 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
                 [self sendResponseWithResponseCode:404 data:nil mimeType:nil];
             }
         };
-        ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+        ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
             // Retrieving the asset failed for some reason.  Send an error.
             [self sendResponseWithResponseCode:401 data:nil mimeType:nil];
         };
@@ -202,7 +202,7 @@ static CDVViewController *viewControllerForRequest(NSURLRequest* request)
     if (mimeType == nil) {
         mimeType = @"text/plain";
     }
-    NSString* encodingName = [@"text/plain" isEqualToString:mimeType] ? @"UTF-8" : nil;
+    NSString* encodingName = [@"text/plain" isEqualToString : mimeType] ? @"UTF-8" : nil;
     CDVHTTPURLResponse* response =
         [[CDVHTTPURLResponse alloc] initWithURL:[[self request] URL]
                                        MIMEType:mimeType

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVUserAgentUtil.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVUserAgentUtil.m b/lib/cordova-ios/CordovaLib/Classes/CDVUserAgentUtil.m
index 5c43c51..9923d47 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVUserAgentUtil.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVUserAgentUtil.m
@@ -96,7 +96,7 @@ static NSMutableArray* gPendingSetUserAgentBlocks = nil;
         void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0];
         [gPendingSetUserAgentBlocks removeObjectAtIndex:0];
         gCurrentLockToken = ++gNextLockToken;
-        NSLog (@"Gave lock %d", gCurrentLockToken);
+        NSLog(@"Gave lock %d", gCurrentLockToken);
         block(gCurrentLockToken);
     } else {
         gCurrentLockToken = 0;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVViewController.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.h b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.h
index 82e22f6..2338baf 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.h
@@ -22,15 +22,14 @@
 #import "CDVAvailability.h"
 #import "CDVInvokedUrlCommand.h"
 #import "CDVCommandDelegate.h"
+#import "CDVCommandQueue.h"
 #import "CDVWhitelist.h"
 #import "CDVScreenOrientationDelegate.h"
-
-@class CDVCommandQueue;
-@class CDVCommandDelegateImpl;
+#import "CDVPlugin.h"
 
 @interface CDVViewController : UIViewController <UIWebViewDelegate, CDVScreenOrientationDelegate>{
     @protected
-    CDVCommandDelegateImpl* _commandDelegate;
+    id <CDVCommandDelegate> _commandDelegate;
     @protected
     CDVCommandQueue* _commandQueue;
     NSString* _userAgent;
@@ -49,7 +48,7 @@
 @property (nonatomic, readwrite, copy) NSString* wwwFolderName;
 @property (nonatomic, readwrite, copy) NSString* startPage;
 @property (nonatomic, readonly, strong) CDVCommandQueue* commandQueue;
-@property (nonatomic, readonly, strong) CDVCommandDelegateImpl* commandDelegate;
+@property (nonatomic, readonly, strong) id <CDVCommandDelegate> commandDelegate;
 @property (nonatomic, readonly) NSString* userAgent;
 
 + (NSDictionary*)getBundlePlist:(NSString*)plistName;
@@ -67,6 +66,7 @@
 
 - (id)getCommandInstance:(NSString*)pluginName;
 - (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className;
+- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName;
 
 - (BOOL)URLisAllowed:(NSURL*)url;
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
index bec716d..c7ad01b 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
@@ -19,7 +19,6 @@
 
 #import <objc/message.h>
 #import "CDV.h"
-#import "CDVCommandQueue.h"
 #import "CDVCommandDelegateImpl.h"
 #import "CDVConfigParser.h"
 #import "CDVUserAgentUtil.h"
@@ -103,6 +102,48 @@
     return self;
 }
 
+- (void)viewWillAppear:(BOOL)animated
+{
+    [super viewWillAppear:animated];
+
+    NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+    [nc addObserver:self
+           selector:@selector(keyboardWillShowOrHide:)
+               name:UIKeyboardWillShowNotification
+             object:nil];
+    [nc addObserver:self
+           selector:@selector(keyboardWillShowOrHide:)
+               name:UIKeyboardWillHideNotification
+             object:nil];
+}
+
+- (void)viewWillDisappear:(BOOL)animated
+{
+    [super viewWillDisappear:animated];
+
+    NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
+    [nc removeObserver:self name:UIKeyboardWillShowNotification object:nil];
+    [nc removeObserver:self name:UIKeyboardWillHideNotification object:nil];
+}
+
+- (void)keyboardWillShowOrHide:(NSNotification*)notif
+{
+    if (![@"true" isEqualToString:self.settings[@"KeyboardShrinksView"]]) {
+        return;
+    }
+    BOOL showEvent = [notif.name isEqualToString:UIKeyboardWillShowNotification];
+
+    CGRect keyboardFrame = [notif.userInfo[UIKeyboardFrameEndUserInfoKey] CGRectValue];
+    keyboardFrame = [self.view convertRect:keyboardFrame fromView:nil];
+
+    CGRect newFrame = self.view.bounds;
+    if (showEvent) {
+        newFrame.size.height -= keyboardFrame.size.height;
+    }
+    self.webView.frame = newFrame;
+    self.webView.scrollView.contentInset = UIEdgeInsetsMake(0, 0, -keyboardFrame.size.height, 0);
+}
+
 - (void)printDeprecationNotice
 {
     if (!IsAtLeastiOSVersion(@"5.0")) {
@@ -208,9 +249,6 @@
     id backupWebStorage = self.settings[@"BackupWebStorage"];
     if ([backupWebStorage isKindOfClass:[NSString class]]) {
         backupWebStorageType = backupWebStorage;
-    } else if ([backupWebStorage isKindOfClass:[NSNumber class]]) {
-        NSLog(@"Deprecated: BackupWebStorage boolean property is a string property now (none, local, cloud). A boolean value of 'true' will be mapped to 'cloud'. Consult the docs: http://docs.cordova.io/en/edge/guide_project-settings_ios_index.md.html#Project%%20Settings%%20for%%20iOS");
-        backupWebStorageType = [(NSNumber*) backupWebStorage boolValue] ? @"cloud" : @"none";
     }
     self.settings[@"BackupWebStorage"] = backupWebStorageType;
 
@@ -231,6 +269,10 @@
     if ([self.settings objectForKey:@"MediaPlaybackRequiresUserAction"]) {
         mediaPlaybackRequiresUserAction = [(NSNumber*)[settings objectForKey:@"MediaPlaybackRequiresUserAction"] boolValue];
     }
+    BOOL hideKeyboardFormAccessoryBar = NO;  // default value
+    if ([self.settings objectForKey:@"HideKeyboardFormAccessoryBar"]) {
+        hideKeyboardFormAccessoryBar = [(NSNumber*)[settings objectForKey:@"HideKeyboardFormAccessoryBar"] boolValue];
+    }
 
     self.webView.scalesPageToFit = [enableViewportScale boolValue];
 
@@ -239,15 +281,27 @@
      */
 
     if ([enableLocation boolValue]) {
+        NSLog(@"Deprecated: The 'EnableLocation' boolean property is deprecated in 2.5.0, and will be removed in 3.0.0. Use the 'onload' boolean attribute (of the CDVLocation plugin.");
         [[self.commandDelegate getCommandInstance:@"Geolocation"] getLocation:[CDVInvokedUrlCommand new]];
     }
 
+    if (hideKeyboardFormAccessoryBar) {
+        __weak CDVViewController* weakSelf = self;
+        [[NSNotificationCenter defaultCenter] addObserverForName:UIKeyboardWillShowNotification
+                                                          object:nil
+                                                           queue:[NSOperationQueue mainQueue]
+                                                      usingBlock:^(NSNotification * notification) {
+                // we can't hide it here because the accessory bar hasn't been created yet, so we delay on the queue
+                [weakSelf performSelector:@selector(hideKeyboardFormAccessoryBar) withObject:nil afterDelay:0];
+            }];
+    }
+
     /*
      * Fire up CDVLocalStorage to work-around WebKit storage limitations: on all iOS 5.1+ versions for local-only backups, but only needed on iOS 5.1 for cloud backup.
      */
-    if (IsAtLeastiOSVersion(@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
-            ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion(@"6.0")))) {
-        [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])];
+    if (IsAtLeastiOSVersion (@"5.1") && (([backupWebStorageType isEqualToString:@"local"]) ||
+            ([backupWebStorageType isEqualToString:@"cloud"] && !IsAtLeastiOSVersion (@"6.0")))) {
+        [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass ([CDVLocalStorage class])];
     }
 
     /*
@@ -260,12 +314,19 @@
         self.webView.mediaPlaybackRequiresUserAction = NO;
     }
 
-    // UIWebViewBounce property - defaults to true
-    NSNumber* bouncePreference = [self.settings objectForKey:@"UIWebViewBounce"];
-    BOOL bounceAllowed = (bouncePreference == nil || [bouncePreference boolValue]);
+    // By default, overscroll bouncing is allowed.
+    // UIWebViewBounce has been renamed to DisallowOverscroll, but both are checked.
+    BOOL bounceAllowed = YES;
+    NSNumber* disallowOverscroll = [self.settings objectForKey:@"DisallowOverscroll"];
+    if (disallowOverscroll == nil) {
+        NSNumber* bouncePreference = [self.settings objectForKey:@"UIWebViewBounce"];
+        bounceAllowed = (bouncePreference == nil || [bouncePreference boolValue]);
+    } else {
+        bounceAllowed = ![disallowOverscroll boolValue];
+    }
 
     // prevent webView from bouncing
-    // based on UIWebViewBounce key in config.xml
+    // based on the DisallowOverscroll/UIWebViewBounce key in config.xml
     if (!bounceAllowed) {
         if ([self.webView respondsToSelector:@selector(scrollView)]) {
             ((UIScrollView*)[self.webView scrollView]).bounces = NO;
@@ -281,7 +342,7 @@
     /*
      * iOS 6.0 UIWebView properties
      */
-    if (IsAtLeastiOSVersion(@"6.0")) {
+    if (IsAtLeastiOSVersion (@"6.0")) {
         BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES
         if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] != nil) {
             if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"]) {
@@ -330,6 +391,34 @@
         }];
 }
 
+- (void)hideKeyboardFormAccessoryBar
+{
+    NSArray* windows = [[UIApplication sharedApplication] windows];
+
+    for (UIWindow* window in windows) {
+        for (UIView* view in window.subviews) {
+            if ([[view description] hasPrefix:@"<UIPeripheralHostView"]) {
+                for (UIView* peripheralView in view.subviews) {
+                    // hides the accessory bar
+                    if ([[peripheralView description] hasPrefix:@"<UIWebFormAccessory"]) {
+                        // remove the extra scroll space for the form accessory bar
+                        CGRect newFrame = self.webView.scrollView.frame;
+                        newFrame.size.height += peripheralView.frame.size.height;
+                        self.webView.scrollView.frame = newFrame;
+
+                        // remove the form accessory bar
+                        [peripheralView removeFromSuperview];
+                    }
+                    // hides the thin grey line used to adorn the bar (iOS 6)
+                    if ([[peripheralView description] hasPrefix:@"<UIImageView"]) {
+                        [[peripheralView layer] setOpacity:0.0];
+                    }
+                }
+            }
+        }
+    }
+}
+
 - (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
 {
     NSMutableArray* result = [[NSMutableArray alloc] init];
@@ -531,7 +620,7 @@
 
     [self processOpenUrl];
 
-    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:nil]];
+    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:self.webView]];
 }
 
 - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
@@ -660,6 +749,22 @@
     [plugin pluginInitialize];
 }
 
+- (void)registerPlugin:(CDVPlugin*)plugin withPluginName:(NSString*)pluginName
+{
+    if ([plugin respondsToSelector:@selector(setViewController:)]) {
+        [plugin setViewController:self];
+    }
+
+    if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
+        [plugin setCommandDelegate:_commandDelegate];
+    }
+
+    NSString* className = NSStringFromClass([plugin class]);
+    [self.pluginObjects setObject:plugin forKey:className];
+    [self.pluginsMap setValue:className forKey:[pluginName lowercaseString]];
+    [plugin pluginInitialize];
+}
+
 /**
  Returns an instance of a CordovaCommand object, based on its name.  If one exists already, it is returned.
  */
@@ -814,13 +919,8 @@
 - (void)dealloc
 {
     [CDVURLProtocol unregisterViewController:self];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillTerminateNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillEnterForegroundNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidBecomeActiveNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationDidEnterBackgroundNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:NSCurrentLocaleDidChangeNotification object:nil];
-    [[NSNotificationCenter defaultCenter] removeObserver:self name:CDVPluginHandleOpenURLNotification object:nil];
+    [[NSNotificationCenter defaultCenter] removeObserver:self];
+
     self.webView.delegate = nil;
     self.webView = nil;
     [CDVUserAgentUtil releaseLock:&_userAgentLockToken];

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
index 9ee8186..a72bfb9 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
@@ -68,7 +68,9 @@ typedef enum {
     if (![self isJsLoadTokenSet:webView]) {
         _state = STATE_WAITING_FOR_FINISH;
         [self setLoadToken:webView];
-        [_delegate webViewDidStartLoad:webView];
+        if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+            [_delegate webViewDidStartLoad:webView];
+        }
         [self pollForPageLoadFinish:webView];
     }
 }
@@ -80,7 +82,9 @@ typedef enum {
     }
     if ([self isPageLoaded:webView]) {
         _state = STATE_SHOULD_LOAD_MISSING;
-        [_delegate webViewDidFinishLoad:webView];
+        if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+            [_delegate webViewDidFinishLoad:webView];
+        }
     } else {
         [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50];
     }
@@ -88,7 +92,11 @@ typedef enum {
 
 - (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
 {
-    BOOL shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
+    BOOL shouldLoad = YES;
+
+    if ([_delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)]) {
+        shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
+    }
 
     if (shouldLoad) {
         BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
@@ -104,7 +112,9 @@ typedef enum {
 {
     if (_state == STATE_NORMAL) {
         if (_loadCount == 0) {
-            [_delegate webViewDidStartLoad:webView];
+            if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+                [_delegate webViewDidStartLoad:webView];
+            }
             _loadCount += 1;
         } else if (_loadCount > 0) {
             _loadCount += 1;
@@ -128,8 +138,10 @@ typedef enum {
 {
     if (_state == STATE_NORMAL) {
         if (_loadCount == 1) {
-            [_delegate webViewDidFinishLoad:webView];
-            _loadCount -= 1;
+            if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+                [_delegate webViewDidFinishLoad:webView];
+            }
+            _loadCount = -1;
         } else if (_loadCount > 1) {
             _loadCount -= 1;
         }
@@ -143,8 +155,10 @@ typedef enum {
 {
     if (_state == STATE_NORMAL) {
         if (_loadCount == 1) {
-            [_delegate webView:webView didFailLoadWithError:error];
-            _loadCount -= 1;
+            if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+                [_delegate webView:webView didFailLoadWithError:error];
+            }
+            _loadCount = -1;
         } else if (_loadCount > 1) {
             _loadCount -= 1;
         }

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m b/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m
index 08c801b..d0f2189 100644
--- a/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m
+++ b/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m
@@ -249,13 +249,10 @@ char *CDVNewBase64Encode(
 //
 + (NSData*)dataFromBase64String:(NSString*)aString
 {
-    NSData* data = [aString dataUsingEncoding:NSASCIIStringEncoding];
-    size_t outputLength;
-    void* outputBuffer = CDVNewBase64Decode([data bytes], [data length], &outputLength);
-    NSData* result = [NSData dataWithBytes:outputBuffer length:outputLength];
+    size_t outputLength = 0;
+    void* outputBuffer = CDVNewBase64Decode([aString UTF8String], [aString length], &outputLength);
 
-    free(outputBuffer);
-    return result;
+    return [NSData dataWithBytesNoCopy:outputBuffer length:outputLength freeWhenDone:YES];
 }
 
 //
@@ -273,13 +270,11 @@ char *CDVNewBase64Encode(
     char* outputBuffer =
         CDVNewBase64Encode([self bytes], [self length], true, &outputLength);
 
-    NSString* result =
-        [[NSString alloc]
-        initWithBytes:outputBuffer
-               length:outputLength
-             encoding:NSASCIIStringEncoding];
+    NSString* result = [[NSString alloc] initWithBytesNoCopy:outputBuffer
+                                                      length:outputLength
+                                                    encoding:NSASCIIStringEncoding
+                                                freeWhenDone:YES];
 
-    free(outputBuffer);
     return result;
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m b/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m
index 80e9ac1..0361ff9 100644
--- a/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m
+++ b/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m
@@ -28,7 +28,7 @@
     bool exists = false;
 
     if (val != nil) {
-        exists = [(NSString*) val compare:expectedValue options:NSCaseInsensitiveSearch] == 0;
+        exists = [(NSString*)val compare : expectedValue options : NSCaseInsensitiveSearch] == 0;
     }
 
     return exists;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
index 4868020..e14f1f6 100644
--- a/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
+++ b/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
@@ -44,6 +44,9 @@
 		30F5EBAB14CA26E700987760 /* CDVCommandDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 30F5EBA914CA26E700987760 /* CDVCommandDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		3E76876D156A90EE00EB6FA3 /* CDVLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 3E76876B156A90EE00EB6FA3 /* CDVLogger.m */; };
 		3E76876F156A90EE00EB6FA3 /* CDVLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 3E76876C156A90EE00EB6FA3 /* CDVLogger.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		68B7516E16FD18190076A8B4 /* CDVJpegHeaderWriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B7516B16FD18190076A8B4 /* CDVJpegHeaderWriter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		68B7516F16FD18190076A8B4 /* CDVJpegHeaderWriter.m in Sources */ = {isa = PBXBuildFile; fileRef = 68B7516C16FD18190076A8B4 /* CDVJpegHeaderWriter.m */; };
+		68B7517016FD19F80076A8B4 /* CDVExif.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B7516A16FD18190076A8B4 /* CDVExif.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8852C43A14B65FD800F0E735 /* CDVViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 8852C43614B65FD800F0E735 /* CDVViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8852C43C14B65FD800F0E735 /* CDVViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 8852C43714B65FD800F0E735 /* CDVViewController.m */; };
 		8887FD661090FBE7009987E8 /* CDVCamera.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD261090FBE7009987E8 /* CDVCamera.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -141,6 +144,10 @@
 		686357DC14100B1600DF4CF2 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
 		68A32D7114102E1C006B237C /* libCordova.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCordova.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		68A32D7414103017006B237C /* AddressBook.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AddressBook.framework; path = System/Library/Frameworks/AddressBook.framework; sourceTree = SDKROOT; };
+		68B7516A16FD18190076A8B4 /* CDVExif.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVExif.h; path = Classes/CDVExif.h; sourceTree = "<group>"; };
+		68B7516B16FD18190076A8B4 /* CDVJpegHeaderWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVJpegHeaderWriter.h; path = Classes/CDVJpegHeaderWriter.h; sourceTree = "<group>"; };
+		68B7516C16FD18190076A8B4 /* CDVJpegHeaderWriter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVJpegHeaderWriter.m; path = Classes/CDVJpegHeaderWriter.m; sourceTree = "<group>"; };
+		8220B5C316D5427E00EC3921 /* AssetsLibrary.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AssetsLibrary.framework; path = System/Library/Frameworks/AssetsLibrary.framework; sourceTree = SDKROOT; };
 		8852C43614B65FD800F0E735 /* CDVViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVViewController.h; path = Classes/CDVViewController.h; sourceTree = "<group>"; };
 		8852C43714B65FD800F0E735 /* CDVViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVViewController.m; path = Classes/CDVViewController.m; sourceTree = "<group>"; };
 		8887FD261090FBE7009987E8 /* CDVCamera.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVCamera.h; path = Classes/CDVCamera.h; sourceTree = "<group>"; };
@@ -223,6 +230,7 @@
 			isa = PBXGroup;
 			children = (
 				68A32D7414103017006B237C /* AddressBook.framework */,
+				8220B5C316D5427E00EC3921 /* AssetsLibrary.framework */,
 				686357DC14100B1600DF4CF2 /* CoreMedia.framework */,
 				686357CE14100ADA00DF4CF2 /* AudioToolbox.framework */,
 				686357CF14100ADB00DF4CF2 /* AVFoundation.framework */,
@@ -292,6 +300,9 @@
 				8887FD271090FBE7009987E8 /* CDVCamera.m */,
 				1F584B991385A28900ED25E8 /* CDVCapture.h */,
 				1F584B9A1385A28900ED25E8 /* CDVCapture.m */,
+				68B7516A16FD18190076A8B4 /* CDVExif.h */,
+				68B7516B16FD18190076A8B4 /* CDVJpegHeaderWriter.h */,
+				68B7516C16FD18190076A8B4 /* CDVJpegHeaderWriter.m */,
 				1F3C04CC12BC247D004F9E10 /* CDVContact.h */,
 				1F3C04CD12BC247D004F9E10 /* CDVContact.m */,
 				8887FD2A1090FBE7009987E8 /* CDVContacts.h */,
@@ -364,6 +375,8 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				68B7517016FD19F80076A8B4 /* CDVExif.h in Headers */,
+				68B7516E16FD18190076A8B4 /* CDVJpegHeaderWriter.h in Headers */,
 				8887FD661090FBE7009987E8 /* CDVCamera.h in Headers */,
 				8887FD681090FBE7009987E8 /* NSDictionary+Extensions.h in Headers */,
 				8887FD6A1090FBE7009987E8 /* CDVContacts.h in Headers */,
@@ -502,6 +515,7 @@
 				30F3930C169F839700B22307 /* CDVJSON.m in Sources */,
 				EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */,
 				EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */,
+				68B7516F16FD18190076A8B4 /* CDVJpegHeaderWriter.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/VERSION b/lib/cordova-ios/CordovaLib/VERSION
index 437459c..f47de85 100644
--- a/lib/cordova-ios/CordovaLib/VERSION
+++ b/lib/cordova-ios/CordovaLib/VERSION
@@ -1 +1 @@
-2.5.0
+2.6.0rc1

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLib/cordova.ios.js
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/cordova.ios.js b/lib/cordova-ios/CordovaLib/cordova.ios.js
index 3d83df3..8ed1706 100644
--- a/lib/cordova-ios/CordovaLib/cordova.ios.js
+++ b/lib/cordova-ios/CordovaLib/cordova.ios.js
@@ -1,8 +1,8 @@
 // Platform: ios
 
-// commit f50d20a87431c79a54572263729461883f611a53
+// commit 47593b2bc1dba9bf46545b1da24577f937966e12
 
-// File generated at :: Tue Feb 26 2013 14:26:19 GMT-0800 (PST)
+// File generated at :: Thu Mar 21 2013 15:44:06 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -262,7 +262,7 @@ var cordova = {
      */
     callbackSuccess: function(callbackId, args) {
         try {
-            cordova.callbackFromNative(callbackId, true, args.status, args.message, args.keepCallback);
+            cordova.callbackFromNative(callbackId, true, args.status, [args.message], args.keepCallback);
         } catch (e) {
             console.log("Error in error callback: " + callbackId + " = "+e);
         }
@@ -275,7 +275,7 @@ var cordova = {
         // TODO: Deprecate callbackSuccess and callbackError in favour of callbackFromNative.
         // Derive success from status.
         try {
-            cordova.callbackFromNative(callbackId, false, args.status, args.message, args.keepCallback);
+            cordova.callbackFromNative(callbackId, false, args.status, [args.message], args.keepCallback);
         } catch (e) {
             console.log("Error in error callback: " + callbackId + " = "+e);
         }
@@ -284,13 +284,13 @@ var cordova = {
     /**
      * Called by native code when returning the result from an action.
      */
-    callbackFromNative: function(callbackId, success, status, message, keepCallback) {
+    callbackFromNative: function(callbackId, success, status, args, keepCallback) {
         var callback = cordova.callbacks[callbackId];
         if (callback) {
             if (success && status == cordova.callbackStatus.OK) {
-                callback.success && callback.success(message);
+                callback.success && callback.success.apply(null, args);
             } else if (!success) {
-                callback.fail && callback.fail(message);
+                callback.fail && callback.fail.apply(null, args);
             }
 
             // Clear callback if not expecting any more results
@@ -724,6 +724,9 @@ channel.createSticky('onCordovaInfoReady');
 // Event to indicate that the connection property has been set.
 channel.createSticky('onCordovaConnectionReady');
 
+// Event to indicate that all automatically loaded JS plugins are loaded and ready.
+channel.createSticky('onPluginsReady');
+
 // Event to indicate that Cordova is ready
 channel.createSticky('onDeviceReady');
 
@@ -826,6 +829,7 @@ function massageArgsJsToNative(args) {
     if (!args || utils.typeName(args) != 'Array') {
        return args;
     }
+    var ret = [];
     var encodeArrayBufferAs8bitString = function(ab) {
         return String.fromCharCode.apply(null, new Uint8Array(ab));
     };
@@ -834,17 +838,19 @@ function massageArgsJsToNative(args) {
     };
     args.forEach(function(arg, i) {
         if (utils.typeName(arg) == 'ArrayBuffer') {
-            args[i] = {
+            ret.push({
                 'CDVType': 'ArrayBuffer',
                 'data': encodeArrayBufferAsBase64(arg)
-            };
+            });
+        } else {
+            ret.push(arg);
         }
     });
-    return args;
+    return ret;
 }
 
-function massagePayloadNativeToJs(payload) {
-    if (payload && payload.hasOwnProperty('CDVType') && payload.CDVType == 'ArrayBuffer') {
+function massageMessageNativeToJs(message) {
+    if (message.CDVType == 'ArrayBuffer') {
         var stringToArrayBuffer = function(str) {
             var ret = new Uint8Array(str.length);
             for (var i = 0; i < str.length; i++) {
@@ -855,9 +861,23 @@ function massagePayloadNativeToJs(payload) {
         var base64ToArrayBuffer = function(b64) {
             return stringToArrayBuffer(atob(b64));
         };
-        payload = base64ToArrayBuffer(payload.data);
+        message = base64ToArrayBuffer(message.data);
+    }
+    return message;
+}
+
+function convertMessageToArgsNativeToJs(message) {
+    var args = [];
+    if (!message || !message.hasOwnProperty('CDVType')) {
+        args.push(message);
+    } else if (message.CDVType == 'MultiPart') {
+        message.messages.forEach(function(e) {
+            args.push(massageMessageNativeToJs(e));
+        });
+    } else {
+        args.push(massageMessageNativeToJs(message));
     }
-    return payload;
+    return args;
 }
 
 function iOSExec() {
@@ -964,11 +984,11 @@ iOSExec.nativeFetchMessages = function() {
     return json;
 };
 
-iOSExec.nativeCallback = function(callbackId, status, payload, keepCallback) {
+iOSExec.nativeCallback = function(callbackId, status, message, keepCallback) {
     return iOSExec.nativeEvalAndFetch(function() {
         var success = status === 0 || status === 1;
-        payload = massagePayloadNativeToJs(payload);
-        cordova.callbackFromNative(callbackId, success, status, payload, keepCallback);
+        var args = convertMessageToArgsNativeToJs(message);
+        cordova.callbackFromNative(callbackId, success, status, args, keepCallback);
     });
 };
 
@@ -1161,9 +1181,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
     var correctOrientation = !!options.correctOrientation;
     var saveToPhotoAlbum = !!options.saveToPhotoAlbum;
     var popoverOptions = getValue(options.popoverOptions, null);
+    var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
 
     var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
-                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
+                mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
 
     exec(successCallback, errorCallback, "Camera", "takePicture", args);
     return new CameraPopoverHandle();
@@ -1206,6 +1227,10 @@ module.exports = {
       ARROW_LEFT : 4,
       ARROW_RIGHT : 8,
       ARROW_ANY : 15
+  },
+  Direction:{
+      BACK: 0,
+      FRONT: 1
   }
 };
 
@@ -2398,14 +2423,7 @@ FileReader.prototype.readAsText = function(file, encoding) {
     // Default encoding is UTF-8
     var enc = encoding ? encoding : "UTF-8";
     var me = this;
-    var execArgs = [this._fileName, enc];
-
-    // Maybe add slice parameters.
-    if (file.end < file.size) {
-        execArgs.push(file.start, file.end);
-    } else if (file.start > 0) {
-        execArgs.push(file.start);
-    }
+    var execArgs = [this._fileName, enc, file.start, file.end];
 
     // Read file
     exec(
@@ -2474,14 +2492,7 @@ FileReader.prototype.readAsDataURL = function(file) {
     }
 
     var me = this;
-    var execArgs = [this._fileName];
-
-    // Maybe add slice parameters.
-    if (file.end < file.size) {
-        execArgs.push(file.start, file.end);
-    } else if (file.start > 0) {
-        execArgs.push(file.start);
-    }
+    var execArgs = [this._fileName, file.start, file.end];
 
     // Read file
     exec(
@@ -2544,9 +2555,59 @@ FileReader.prototype.readAsBinaryString = function(file) {
     if (initRead(this, file)) {
         return this._realReader.readAsBinaryString(file);
     }
-    // TODO - Can't return binary data to browser.
-    console.log('method "readAsBinaryString" is not supported at this time.');
-    this.abort();
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsBinaryString", execArgs);
 };
 
 /**
@@ -2558,9 +2619,59 @@ FileReader.prototype.readAsArrayBuffer = function(file) {
     if (initRead(this, file)) {
         return this._realReader.readAsArrayBuffer(file);
     }
-    // TODO - Can't return binary data to browser.
-    console.log('This method is not supported at this time.');
-    this.abort();
+
+    var me = this;
+    var execArgs = [this._fileName, file.start, file.end];
+
+    // Read file
+    exec(
+        // Success callback
+        function(r) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = r;
+
+            // If onload callback
+            if (typeof me.onload === "function") {
+                me.onload(new ProgressEvent("load", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        },
+        // Error callback
+        function(e) {
+            // If DONE (cancelled), then don't do anything
+            if (me._readyState === FileReader.DONE) {
+                return;
+            }
+
+            // DONE state
+            me._readyState = FileReader.DONE;
+
+            me._result = null;
+
+            // Save error
+            me._error = new FileError(e);
+
+            // If onerror callback
+            if (typeof me.onerror === "function") {
+                me.onerror(new ProgressEvent("error", {target:me}));
+            }
+
+            // If onloadend callback
+            if (typeof me.onloadend === "function") {
+                me.onloadend(new ProgressEvent("loadend", {target:me}));
+            }
+        }, "File", "readAsArrayBuffer", execArgs);
 };
 
 module.exports = FileReader;
@@ -2606,6 +2717,38 @@ function newProgressEvent(result) {
     return pe;
 }
 
+function getBasicAuthHeader(urlString) {
+    var header =  null;
+
+    if (window.btoa) {
+        // parse the url using the Location object
+        var url = document.createElement('a');
+        url.href = urlString;
+
+        var credentials = null;
+        var protocol = url.protocol + "//";
+        var origin = protocol + url.host;
+
+        // check whether there are the username:password credentials in the url
+        if (url.href.indexOf(origin) != 0) { // credentials found
+            var atIndex = url.href.indexOf("@");
+            credentials = url.href.substring(protocol.length, atIndex);
+        }
+
+        if (credentials) {
+            var authHeader = "Authorization";
+            var authHeaderValue = "Basic " + window.btoa(credentials);
+
+            header = {
+                name : authHeader,
+                value : authHeaderValue
+            };
+        }
+    }
+
+    return header;
+}
+
 var idCounter = 0;
 
 /**
@@ -2636,6 +2779,18 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
     var params = null;
     var chunkedMode = true;
     var headers = null;
+
+    var basicAuthHeader = getBasicAuthHeader(server);
+    if (basicAuthHeader) {
+        if (!options) {
+            options = new FileUploadOptions();
+        }
+        if (!options.headers) {
+            options.headers = {};
+        }
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
     if (options) {
         fileKey = options.fileKey;
         fileName = options.fileName;
@@ -2653,7 +2808,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
     }
 
     var fail = errorCallback && function(e) {
-        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
         errorCallback(error);
     };
 
@@ -2677,10 +2832,28 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
  * @param successCallback (Function}  Callback to be invoked when upload has completed
  * @param errorCallback {Function}    Callback to be invoked upon error
  * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
+ * @param options {FileDownloadOptions} Optional parameters such as headers
  */
-FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts, options) {
     argscheck.checkArgs('ssFF*', 'FileTransfer.download', arguments);
     var self = this;
+
+    var basicAuthHeader = getBasicAuthHeader(source);
+    if (basicAuthHeader) {
+        if (!options) {
+            options = {};
+        }
+        if (!options.headers) {
+            options.headers = {};
+        }
+        options.headers[basicAuthHeader.name] = basicAuthHeader.value;
+    }
+
+    var headers = null;
+    if (options) {
+        headers = options.headers || null;
+    }
+
     var win = function(result) {
         if (typeof result.lengthComputable != "undefined") {
             if (self.onprogress) {
@@ -2703,11 +2876,11 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro
     };
 
     var fail = errorCallback && function(e) {
-        var error = new FileTransferError(e.code, e.source, e.target, e.http_status);
+        var error = new FileTransferError(e.code, e.source, e.target, e.http_status, e.body);
         errorCallback(error);
     };
 
-    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
+    exec(win, fail, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id, headers]);
 };
 
 /**
@@ -3105,6 +3278,7 @@ function InAppBrowser() {
    this.channels = {
         'loadstart': channel.create('loadstart'),
         'loadstop' : channel.create('loadstop'),
+        'loaderror' : channel.create('loaderror'),
         'exit' : channel.create('exit')
    };
 }
@@ -3135,7 +3309,7 @@ module.exports = function(strUrl, strWindowName, strWindowFeatures) {
     var cb = function(eventname) {
        iab._eventHandler(eventname);
     };
-    exec(cb, null, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
+    exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
     return iab;
 };
 
@@ -4341,7 +4515,8 @@ modulemapper.clobbers('cordova/plugin/device', 'device');
 // file: lib/common/plugin/echo.js
 define("cordova/plugin/echo", function(require, exports, module) {
 
-var exec = require('cordova/exec');
+var exec = require('cordova/exec'),
+    utils = require('cordova/utils');
 
 /**
  * Sends the given message through exec() to the Echo plugin, which sends it back to the successCallback.
@@ -4351,11 +4526,25 @@ var exec = require('cordova/exec');
  * @param forceAsync  Whether to force an async return value (for testing native->js bridge).
  */
 module.exports = function(successCallback, errorCallback, message, forceAsync) {
-    var action = forceAsync ? 'echoAsync' : 'echo';
-    if (!forceAsync && message.constructor == ArrayBuffer) {
-        action = 'echoArrayBuffer';
+    var action = 'echo';
+    var messageIsMultipart = (utils.typeName(message) == "Array");
+    var args = messageIsMultipart ? message : [message];
+
+    if (utils.typeName(message) == 'ArrayBuffer') {
+        if (forceAsync) {
+            console.warn('Cannot echo ArrayBuffer with forced async, falling back to sync.');
+        }
+        action += 'ArrayBuffer';
+    } else if (messageIsMultipart) {
+        if (forceAsync) {
+            console.warn('Cannot echo MultiPart Array with forced async, falling back to sync.');
+        }
+        action += 'MultiPart';
+    } else if (forceAsync) {
+        action += 'Async';
     }
-    exec(successCallback, errorCallback, "Echo", action, [message]);
+
+    exec(successCallback, errorCallback, "Echo", action, args);
 };
 
 
@@ -5086,7 +5275,7 @@ function stringify(message) {
                 return "error JSON.stringify()ing argument: " + e;
             }
         } else {
-            return message.toString();
+            return (typeof message === "undefined") ? "undefined" : message.toString();
         }
     } catch (e) {
         return e.toString();
@@ -5100,10 +5289,20 @@ function stringify(message) {
 function wrappedMethod(console, method) {
     var origMethod = console[method];
 
-    return function(message) {
+    return function() {
+
+        var args = [].slice.call(arguments),
+            len = args.length,
+            i = 0,
+            res = [];
+
+        for ( ; i < len; i++) {
+            res.push(stringify(args[i]));
+        }
+
         exec(null, null,
             'Debug Console', 'log',
-            [ stringify(message), { logLevel: method.toUpperCase() } ]
+            [ res.join(' '), { logLevel: method.toUpperCase() } ]
         );
 
         if (!origMethod) return;
@@ -5572,6 +5771,7 @@ modulemapper.defaults('cordova/plugin/Connection', 'Connection');
 define("cordova/plugin/notification", function(require, exports, module) {
 
 var exec = require('cordova/exec');
+var platform = require('cordova/platform');
 
 /**
  * Provides access to notifications on the device.
@@ -5600,15 +5800,53 @@ module.exports = {
      * @param {String} message              Message to print in the body of the alert
      * @param {Function} resultCallback     The callback that is called when user clicks on a button.
      * @param {String} title                Title of the alert dialog (default: Confirm)
-     * @param {String} buttonLabels         Comma separated list of the labels of the buttons (default: 'OK,Cancel')
+     * @param {Array} buttonLabels          Array of the labels of the buttons (default: ['OK', 'Cancel'])
      */
     confirm: function(message, resultCallback, title, buttonLabels) {
         var _title = (title || "Confirm");
-        var _buttonLabels = (buttonLabels || "OK,Cancel");
+        var _buttonLabels = (buttonLabels || ["OK", "Cancel"]);
+
+        // Strings are deprecated!
+        if (typeof _buttonLabels === 'string') {
+            console.log("Notification.confirm(string, function, string, string) is deprecated.  Use Notification.confirm(string, function, string, array).");
+        }
+
+        // Android and iOS take an array of button label names.
+        // Other platforms take a comma separated list.
+        // For compatibility, we convert to the desired type based on the platform.
+        if (platform.id == "android" || platform.id == "ios") {
+            if (typeof _buttonLabels === 'string') {
+                var buttonLabelString = _buttonLabels;
+                _buttonLabels = buttonLabelString.split(",");
+            }
+        } else {
+            if (Array.isArray(_buttonLabels)) {
+                var buttonLabelArray = _buttonLabels;
+                _buttonLabels = buttonLabelArray.toString();
+            }
+        }
         exec(resultCallback, null, "Notification", "confirm", [message, _title, _buttonLabels]);
     },
 
     /**
+     * Open a native prompt dialog, with a customizable title and button text.
+     * The following results are returned to the result callback:
+     *  buttonIndex     Index number of the button selected.
+     *  input1          The text entered in the prompt dialog box.
+     *
+     * @param {String} message              Dialog message to display (default: "Prompt message")
+     * @param {Function} resultCallback     The callback that is called when user clicks on a button.
+     * @param {String} title                Title of the dialog (default: "Prompt")
+     * @param {Array} buttonLabels          Array of strings for the button labels (default: ["OK","Cancel"])
+     */
+    prompt: function(message, resultCallback, title, buttonLabels) {
+        var _message = (message || "Prompt message");
+        var _title = (title || "Prompt");
+        var _buttonLabels = (buttonLabels || ["OK","Cancel"]);
+        exec(resultCallback, null, "Notification", "prompt", [_message, _title, _buttonLabels]);
+    },
+
+    /**
      * Causes the device to vibrate.
      *
      * @param {Integer} mills       The number of milliseconds to vibrate for.
@@ -6031,53 +6269,160 @@ window.cordova = require('cordova');
 (function (context) {
     // Replace navigator before any modules are required(), to ensure it happens as soon as possible.
     // We replace it so that properties that can't be clobbered can instead be overridden.
-    if (context.navigator) {
+    function replaceNavigator(origNavigator) {
         var CordovaNavigator = function() {};
-        CordovaNavigator.prototype = context.navigator;
-        context.navigator = new CordovaNavigator();
+        CordovaNavigator.prototype = origNavigator;
+        var newNavigator = new CordovaNavigator();
+        // This work-around really only applies to new APIs that are newer than Function.bind.
+        // Without it, APIs such as getGamepads() break.
+        if (CordovaNavigator.bind) {
+            for (var key in origNavigator) {
+                if (typeof origNavigator[key] == 'function') {
+                    newNavigator[key] = origNavigator[key].bind(origNavigator);
+                }
+            }
+        }
+        return newNavigator;
+    }
+    if (context.navigator) {
+        context.navigator = replaceNavigator(context.navigator);
     }
 
-    var channel = require("cordova/channel"),
-        _self = {
-            boot: function () {
-                /**
-                 * Create all cordova objects once page has fully loaded and native side is ready.
-                 */
-                channel.join(function() {
-                    var builder = require('cordova/builder'),
-                        platform = require('cordova/platform');
+    var channel = require("cordova/channel");
+
+    // _nativeReady is global variable that the native side can set
+    // to signify that the native code is ready. It is a global since
+    // it may be called before any cordova JS is ready.
+    if (window._nativeReady) {
+        channel.onNativeReady.fire();
+    }
+
+    /**
+     * Create all cordova objects once page has fully loaded and native side is ready.
+     */
+    channel.join(function() {
+        var builder = require('cordova/builder'),
+            platform = require('cordova/platform');
+
+        builder.buildIntoButDoNotClobber(platform.defaults, context);
+        builder.buildIntoAndClobber(platform.clobbers, context);
+        builder.buildIntoAndMerge(platform.merges, context);
+
+        // Call the platform-specific initialization
+        platform.initialize();
+
+        // Fire event to notify that all objects are created
+        channel.onCordovaReady.fire();
+
+        // Fire onDeviceReady event once all constructors have run and
+        // cordova info has been received from native side.
+        channel.join(function() {
+            require('cordova').fireDocumentEvent('deviceready');
+        }, channel.deviceReadyChannelsArray);
+
+    }, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
+
+}(window));
+
+// file: lib/scripts/plugin_loader.js
 
-                    builder.buildIntoButDoNotClobber(platform.defaults, context);
-                    builder.buildIntoAndClobber(platform.clobbers, context);
-                    builder.buildIntoAndMerge(platform.merges, context);
+// Tries to load all plugins' js-modules.
+// This is an async process, but onDeviceReady is blocked on onPluginsReady.
+// onPluginsReady is fired when there are no plugins to load, or they are all done.
+(function (context) {
+    // To be populated with the handler by handlePluginsObject.
+    var onScriptLoadingComplete;
+
+    var scriptCounter = 0;
+    function scriptLoadedCallback() {
+        scriptCounter--;
+        if (scriptCounter === 0) {
+            onScriptLoadingComplete && onScriptLoadingComplete();
+        }
+    }
+
+    // Helper function to inject a <script> tag.
+    function injectScript(path) {
+        scriptCounter++;
+        var script = document.createElement("script");
+        script.onload = scriptLoadedCallback;
+        script.src = path;
+        document.head.appendChild(script);
+    }
 
-                    // Call the platform-specific initialization
-                    platform.initialize();
+    // Called when:
+    // * There are plugins defined and all plugins are finished loading.
+    // * There are no plugins to load.
+    function finishPluginLoading() {
+        context.cordova.require('cordova/channel').onPluginsReady.fire();
+    }
 
-                    // Fire event to notify that all objects are created
-                    channel.onCordovaReady.fire();
+    // Handler for the cordova_plugins.json content.
+    // See plugman's plugin_loader.js for the details of this object.
+    // This function is only called if the really is a plugins array that isn't empty.
+    // Otherwise the XHR response handler will just call finishPluginLoading().
+    function handlePluginsObject(modules) {
+        // First create the callback for when all plugins are loaded.
+        var mapper = context.cordova.require('cordova/modulemapper');
+        onScriptLoadingComplete = function() {
+            // Loop through all the plugins and then through their clobbers and merges.
+            for (var i = 0; i < modules.length; i++) {
+                var module = modules[i];
+                if (!module) continue;
+
+                if (module.clobbers && module.clobbers.length) {
+                    for (var j = 0; j < module.clobbers.length; j++) {
+                        mapper.clobbers(module.id, module.clobbers[j]);
+                    }
+                }
 
-                    // Fire onDeviceReady event once all constructors have run and
-                    // cordova info has been received from native side.
-                    channel.join(function() {
-                        require('cordova').fireDocumentEvent('deviceready');
-                    }, channel.deviceReadyChannelsArray);
+                if (module.merges && module.merges.length) {
+                    for (var k = 0; k < module.merges.length; k++) {
+                        mapper.merges(module.id, module.merges[k]);
+                    }
+                }
 
-                }, [ channel.onDOMContentLoaded, channel.onNativeReady ]);
+                // Finally, if runs is truthy we want to simply require() the module.
+                // This can be skipped if it had any merges or clobbers, though,
+                // since the mapper will already have required the module.
+                if (module.runs && !(module.clobbers && module.clobbers.length) && !(module.merges && module.merges.length)) {
+                    context.cordova.require(module.id);
+                }
             }
-        };
 
-    // boot up once native side is ready
-    channel.onNativeReady.subscribe(_self.boot);
+            finishPluginLoading();
+        };
 
-    // _nativeReady is global variable that the native side can set
-    // to signify that the native code is ready. It is a global since
-    // it may be called before any cordova JS is ready.
-    if (window._nativeReady) {
-        channel.onNativeReady.fire();
+        // Now inject the scripts.
+        for (var i = 0; i < modules.length; i++) {
+            injectScript(modules[i].file);
+        }
     }
 
+    // Try to XHR the cordova_plugins.json file asynchronously.
+    var xhr = new context.XMLHttpRequest();
+    xhr.onreadystatechange = function() {
+        if (this.readyState != 4) { // not DONE
+            return;
+        }
+
+        // If the response is a JSON string which composes an array, call handlePluginsObject.
+        // If the request fails, or the response is not a JSON array, just call finishPluginLoading.
+        if (this.status == 200) {
+            var obj = JSON.parse(this.responseText);
+            if (obj && obj instanceof Array && obj.length > 0) {
+                handlePluginsObject(obj);
+            } else {
+                finishPluginLoading();
+            }
+        } else {
+            finishPluginLoading();
+        }
+    };
+    xhr.open('GET', 'cordova_plugins.json', true); // Async
+    xhr.send();
 }(window));
 
 
+
 })();
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLibTests/CDVFakeFileManager.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLibTests/CDVFakeFileManager.h b/lib/cordova-ios/CordovaLibTests/CDVFakeFileManager.h
index 41e5523..f9300a0 100644
--- a/lib/cordova-ios/CordovaLibTests/CDVFakeFileManager.h
+++ b/lib/cordova-ios/CordovaLibTests/CDVFakeFileManager.h
@@ -19,7 +19,7 @@
 
 #import <Foundation/Foundation.h>
 
-typedef BOOL (^CDVFileExistsBlock)(NSString*);
+typedef BOOL (^ CDVFileExistsBlock)(NSString*);
 
 // Used in place of an NSFileManager for unit tests. It implements only those functions
 // which are required by the tests that use it.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/37b92ff4/lib/cordova-ios/CordovaLibTests/CDVFileTransferTests.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLibTests/CDVFileTransferTests.m b/lib/cordova-ios/CordovaLibTests/CDVFileTransferTests.m
index 7488a5a..2effc98 100644
--- a/lib/cordova-ios/CordovaLibTests/CDVFileTransferTests.m
+++ b/lib/cordova-ios/CordovaLibTests/CDVFileTransferTests.m
@@ -140,15 +140,15 @@ static NSData *readStream(NSInputStream* stream)
 - (void)testEscapePathComponentForUrlString
 {
     STAssertTrue([@"" isEqualToString:
-            [_fileTransfer escapePathComponentForUrlString:@""]], nil);
+        [_fileTransfer escapePathComponentForUrlString:@""]], nil);
     STAssertTrue([@"foo" isEqualToString:
-            [_fileTransfer escapePathComponentForUrlString:@"foo"]], nil);
+        [_fileTransfer escapePathComponentForUrlString:@"foo"]], nil);
     STAssertTrue([@"http://a.org/spa%20ce%25" isEqualToString:
-            [_fileTransfer escapePathComponentForUrlString:@"http://a.org/spa ce%"]], nil);
+        [_fileTransfer escapePathComponentForUrlString:@"http://a.org/spa ce%"]], nil);
     STAssertTrue([@"http://a.org/spa%20ce%25/" isEqualToString:
-            [_fileTransfer escapePathComponentForUrlString:@"http://a.org/spa ce%/"]], nil);
+        [_fileTransfer escapePathComponentForUrlString:@"http://a.org/spa ce%/"]], nil);
     STAssertTrue([@"http://a.org/%25/%25/" isEqualToString:
-            [_fileTransfer escapePathComponentForUrlString:@"http://a.org/%/%/"]], nil);
+        [_fileTransfer escapePathComponentForUrlString:@"http://a.org/%/%/"]], nil);
 }
 
 - (void)testUpload_invalidServerUrl
@@ -208,7 +208,7 @@ static NSData *readStream(NSInputStream* stream)
 {
     [self setChunkedModeArg:NO];
     [self setHeaders:[NSDictionary dictionaryWithObjectsAndKeys:@"val1", @"key1",
-            [NSArray arrayWithObjects:@"val2a", @"val2b", nil], @"key2", [NSNull null], @"X-Requested-With", nil]];
+    [NSArray arrayWithObjects:@"val2a", @"val2b", nil], @"key2", [NSNull null], @"X-Requested-With", nil]];
     NSURLRequest* request = [self requestForUpload];
     STAssertTrue([@"val1" isEqualToString:[request valueForHTTPHeaderField:@"key1"]], nil);
     STAssertTrue([@"val2a,val2b" isEqualToString:[request valueForHTTPHeaderField:@"key2"]], nil);