cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mwbro...@apache.org
Subject [2/5] Version 2.7.0-rc.1
Date Tue, 23 Apr 2013 16:56:41 GMT
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m b/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
index 90c96d2..93cafb8 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVJpegHeaderWriter.m
@@ -20,8 +20,15 @@
 #import "CDVJpegHeaderWriter.h"
 #include "CDVExif.h"
 
-// tag info shorthand, tagno: tag number, typecode: data type:, components: number of components
+/* macros for tag info shorthand:
+   tagno        : tag number
+   typecode     : data type
+   components   : number of components
+   appendString (TAGINF_W_APPEND only) : string to append to data
+      Exif date data format include an extra 0x00 to the end of the data
+ */
 #define TAGINF(tagno, typecode, components) [NSArray arrayWithObjects: tagno, typecode, components, nil]
+#define TAGINF_W_APPEND(tagno, typecode, components, appendString) [NSArray arrayWithObjects: tagno, typecode, components, appendString, nil]
 
 const uint mJpegId = 0xffd8; // JPEG format marker
 const uint mExifMarker = 0xffe1; // APP1 jpeg header marker
@@ -38,7 +45,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
     // supported tags for exif IFD
     IFD0TagFormatDict = [[NSDictionary alloc] initWithObjectsAndKeys:
                   //      TAGINF(@"010e", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"ImageDescription",
-                        TAGINF(@"0132", [NSNumber numberWithInt:EDT_ASCII_STRING], @20), @"DateTime",
+                        TAGINF_W_APPEND(@"0132", [NSNumber numberWithInt:EDT_ASCII_STRING], @20, @"00"), @"DateTime",
                         TAGINF(@"010f", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Make",
                         TAGINF(@"0110", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Model",
                         TAGINF(@"0131", [NSNumber numberWithInt:EDT_ASCII_STRING], @0), @"Software",
@@ -68,8 +75,8 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
                            //TAGINF(@"9202",[NSNumber numberWithInt:EDT_URATIONAL],@1), @"ApertureValue",
                            //TAGINF(@"9203",[NSNumber numberWithInt:EDT_SRATIONAL],@1), @"BrightnessValue",
                            TAGINF(@"a001",[NSNumber numberWithInt:EDT_USHORT],@1), @"ColorSpace",
-                           TAGINF(@"9004",[NSNumber numberWithInt:EDT_ASCII_STRING],@20), @"DateTimeDigitized",
-                           TAGINF(@"9003",[NSNumber numberWithInt:EDT_ASCII_STRING],@20), @"DateTimeOriginal",
+                           TAGINF_W_APPEND(@"9004",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeDigitized",
+                           TAGINF_W_APPEND(@"9003",[NSNumber numberWithInt:EDT_ASCII_STRING],@20,@"00"), @"DateTimeOriginal",
                            TAGINF(@"a402", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureMode",
                            TAGINF(@"8822", [NSNumber numberWithInt:EDT_USHORT], @1), @"ExposureProgram",
                            //TAGINF(@"829a", [NSNumber numberWithInt:EDT_URATIONAL], @1), @"ExposureTime",
@@ -167,12 +174,14 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
     NSString * tiffheader = @"4d4d002a";
     //first IFD offset from the Tiff header to IFD0. Since we are writing it, we know it's address 0x08
     NSString * ifd0offset = @"00000008";
+    // current offset to next data area
+    int currentDataOffset = 0;
     
     //data labeled as TIFF in UIImagePickerControllerMediaMetaData is part of the EXIF IFD0 portion of APP1
-    exifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{TIFF}"] withFormatDict: IFD0TagFormatDict isIFD0:YES];
+    exifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{TIFF}"] withFormatDict: IFD0TagFormatDict isIFD0:YES currentDataOffset:&currentDataOffset];
 
     //data labeled as EXIF in UIImagePickerControllerMediaMetaData is part of the EXIF Sub IFD portion of APP1
-    subExifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{Exif}"] withFormatDict: SubIFDTagFormatDict isIFD0:NO];
+    subExifIFD = [self createExifIFDFromDict: [datadict objectForKey:@"{Exif}"] withFormatDict: SubIFDTagFormatDict isIFD0:NO currentDataOffset:&currentDataOffset];
     /*
     NSLog(@"SUB EXIF IFD %@  WITH SIZE: %d",exifIFD,[exifIFD length]);
     
@@ -192,8 +201,11 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
 }
 
 // returns hex string representing a valid exif information file directory constructed from the datadict and formatdict
-- (NSString*) createExifIFDFromDict : (NSDictionary*) datadict withFormatDict : (NSDictionary*) formatdict isIFD0 : (BOOL) ifd0flag {
-    NSArray * datakeys = [datadict allKeys]; // all known data keys 
+- (NSString*) createExifIFDFromDict : (NSDictionary*) datadict
+                     withFormatDict : (NSDictionary*) formatdict
+                             isIFD0 : (BOOL) ifd0flag
+                  currentDataOffset : (int*) dataoffset {
+    NSArray * datakeys = [datadict allKeys]; // all known data keys
     NSArray * knownkeys = [formatdict  allKeys]; // only keys in knowkeys are considered for entry in this IFD
     NSMutableArray * ifdblock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // all ifd entries
     NSMutableArray * ifddatablock = [[NSMutableArray alloc] initWithCapacity: [datadict count]]; // data block entries
@@ -225,13 +237,13 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
     NSMutableString * exifstr = [[NSMutableString alloc] initWithCapacity: [ifdblock count] * 24];
     NSMutableString * dbstr = [[NSMutableString alloc] initWithCapacity: 100];
     
-    int addr=0; // current offset/address in datablock
+    int addr=*dataoffset; // current offset/address in datablock
     if (ifd0flag) {
         // calculate offset to datablock based on ifd file entry count
-        addr = 14+(12*([ifddatablock count]+1)); // +1 for tag 0x8769, exifsubifd offset
+        addr += 14+(12*([ifddatablock count]+1)); // +1 for tag 0x8769, exifsubifd offset
     } else {
-        // same calculation as above, but no exifsubifd offset
-        addr = 14+12*[ifddatablock count];
+        // current offset + numSubIFDs (2-bytes) + 12*numSubIFDs + endMarker (4-bytes)
+        addr += 2+(12*[ifddatablock count])+4;
     }
     
     for (int i = 0; i < [ifdblock count]; i++) {
@@ -244,8 +256,14 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
             [exifstr appendFormat : @"%@%@", entry, data];
         } else {
             [exifstr appendFormat : @"%@%08x", entry, addr];
-            [dbstr appendFormat: @"%@", data]; 
+            [dbstr appendFormat: @"%@", data];
             addr+= [data length] / 2;
+            /*
+            NSLog(@"=====data-length[%i]=======",[data length]);
+            NSLog(@"addr-offset[%i]",addr);
+            NSLog(@"entry[%@]",entry);
+            NSLog(@"data[%@]",data);
+             */
         }
     }
     
@@ -258,7 +276,8 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
         [self appendExifOffsetTagTo: exifstr
                         withOffset : offset];
         entrycount++;
-    } 
+    }
+    *dataoffset = addr;
     return [[NSString alloc] initWithFormat: @"%04x%@%@%@",
             entrycount,
             exifstr,
@@ -307,6 +326,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
     NSMutableString * datastr = nil;
     NSNumber * tmp = nil;
     NSNumber * formatcode = [dataformat objectAtIndex:1];
+    NSUInteger formatItemsCount = [dataformat count];
     NSNumber * num = @0;
     NSNumber * denom = @0;
     
@@ -318,6 +338,11 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
             for (int i = 0; i < [data length]; i++) {
                 [datastr appendFormat:@"%02x",[data characterAtIndex:i]];
             }
+            if (formatItemsCount > 3) {
+                // We have additional data to append.
+                // currently used by Date format to append final 0x00 but can be used by other data types as well in the future
+                [datastr appendString:[dataformat objectAtIndex:3]];
+            }
             if ([datastr length] < 8) {
                 NSString * format = [NSString stringWithFormat:@"%%0%dd", 8 - [datastr length]];
                 [datastr appendFormat:format,0];

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 6e25a27..33ba1c4 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"
 
-NSString* const CDVPageDidLoadNotification;
-NSString* const CDVPluginHandleOpenURLNotification;
-NSString* const CDVPluginResetNotification;
-NSString* const CDVLocalNotification;
+extern NSString* const CDVPageDidLoadNotification;
+extern NSString* const CDVPluginHandleOpenURLNotification;
+extern NSString* const CDVPluginResetNotification;
+extern NSString* const CDVLocalNotification;
 
 @interface CDVPlugin : NSObject {}
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 88fbbd6..71eab59 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVSound.m
@@ -273,6 +273,9 @@
     } else {
         audioFile = [[self soundCache] objectForKey:mediaId];
         audioFile.volume = volume;
+        if (audioFile.player) {
+            audioFile.player.volume = [volume floatValue];
+        }
         [[self soundCache] setObject:audioFile forKey:mediaId];
     }
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
new file mode 100644
index 0000000..6d31593
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.h
@@ -0,0 +1,27 @@
+/*
+ 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>
+
+@interface CDVTimer : NSObject
+
++ (void)start:(NSString*)name;
++ (void)stop:(NSString*)name;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m
new file mode 100644
index 0000000..784e94d
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVTimer.m
@@ -0,0 +1,123 @@
+/*
+ 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 "CDVTimer.h"
+
+#pragma mark CDVTimerItem
+
+@interface CDVTimerItem : NSObject
+
+@property (nonatomic, strong) NSString* name;
+@property (nonatomic, strong) NSDate* started;
+@property (nonatomic, strong) NSDate* ended;
+
+- (void)log;
+
+@end
+
+@implementation CDVTimerItem
+
+- (void)log
+{
+    NSLog(@"[CDVTimer][%@] %fms", self.name, [self.ended timeIntervalSinceDate:self.started] * 1000.0);
+}
+
+@end
+
+#pragma mark CDVTimer
+
+@interface CDVTimer ()
+
+@property (nonatomic, strong) NSMutableDictionary* items;
+
+@end
+
+@implementation CDVTimer
+
+#pragma mark object methods
+
+- (id)init
+{
+    if (self = [super init]) {
+        self.items = [NSMutableDictionary dictionaryWithCapacity:6];
+    }
+
+    return self;
+}
+
+- (void)add:(NSString*)name
+{
+    if ([self.items objectForKey:[name lowercaseString]] == nil) {
+        CDVTimerItem* item = [CDVTimerItem new];
+        item.name = name;
+        item.started = [NSDate new];
+        [self.items setObject:item forKey:[name lowercaseString]];
+    } else {
+        NSLog(@"Timer called '%@' already exists.", name);
+    }
+}
+
+- (void)remove:(NSString*)name
+{
+    CDVTimerItem* item = [self.items objectForKey:[name lowercaseString]];
+
+    if (item != nil) {
+        item.ended = [NSDate new];
+        [item log];
+        [self.items removeObjectForKey:[name lowercaseString]];
+    } else {
+        NSLog(@"Timer called '%@' does not exist.", name);
+    }
+}
+
+- (void)removeAll
+{
+    [self.items removeAllObjects];
+}
+
+#pragma mark class methods
+
++ (void)start:(NSString*)name
+{
+    [[CDVTimer sharedInstance] add:name];
+}
+
++ (void)stop:(NSString*)name
+{
+    [[CDVTimer sharedInstance] remove:name];
+}
+
++ (void)clearAll
+{
+    [[CDVTimer sharedInstance] removeAll];
+}
+
++ (CDVTimer*)sharedInstance
+{
+    static dispatch_once_t pred = 0;
+    __strong static CDVTimer* _sharedObject = nil;
+
+    dispatch_once(&pred, ^{
+            _sharedObject = [[self alloc] init];
+        });
+
+    return _sharedObject;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 c7ad01b..94f4552 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
@@ -128,7 +128,7 @@
 
 - (void)keyboardWillShowOrHide:(NSNotification*)notif
 {
-    if (![@"true" isEqualToString:self.settings[@"KeyboardShrinksView"]]) {
+    if (![@"true" isEqualToString : self.settings[@"KeyboardShrinksView"]]) {
         return;
     }
     BOOL showEvent = [notif.name isEqualToString:UIKeyboardWillShowNotification];
@@ -290,18 +290,18 @@
         [[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];
-            }];
+                                                      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])];
     }
 
     /*
@@ -342,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"]) {
@@ -368,8 +368,16 @@
         }
     }
 
-    for (NSString* pluginName in self.startupPluginNames) {
-        [self getCommandInstance:pluginName];
+    if ([self.startupPluginNames count] > 0) {
+        [CDVTimer start:@"TotalPluginStartup"];
+
+        for (NSString* pluginName in self.startupPluginNames) {
+            [CDVTimer start:pluginName];
+            [self getCommandInstance:pluginName];
+            [CDVTimer stop:pluginName];
+        }
+
+        [CDVTimer stop:@"TotalPluginStartup"];
     }
 
     // TODO: Remove this explicit instantiation once we move to cordova-CLI.
@@ -379,16 +387,16 @@
 
     // /////////////////
     [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
-            _userAgentLockToken = lockToken;
-            [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
-            if (!loadErr) {
-                NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
-                [self.webView loadRequest:appReq];
-            } else {
-                NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
-                [self.webView loadHTMLString:html baseURL:nil];
-            }
-        }];
+        _userAgentLockToken = lockToken;
+        [CDVUserAgentUtil setUserAgent:self.userAgent lockToken:lockToken];
+        if (!loadErr) {
+            NSURLRequest* appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:20.0];
+            [self.webView loadRequest:appReq];
+        } else {
+            NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr];
+            [self.webView loadHTMLString:html baseURL:nil];
+        }
+    }];
 }
 
 - (void)hideKeyboardFormAccessoryBar
@@ -607,12 +615,6 @@
     // It's safe to release the lock even if this is just a sub-frame that's finished loading.
     [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
 
-    // The .onNativeReady().fire() will work when cordova.js is already loaded.
-    // The _nativeReady = true; is used when this is run before cordova.js is loaded.
-    NSString* nativeReady = @"try{cordova.require('cordova/channel').onNativeReady.fire();}catch(e){window._nativeReady = true;}";
-    // Don't use [commandDelegate evalJs] here since it relies on cordova.js being loaded already.
-    [self.webView stringByEvaluatingJavaScriptFromString:nativeReady];
-
     /*
      * Hide the Top Activity THROBBER in the Battery Bar
      */
@@ -783,7 +785,7 @@
 
     id obj = [self.pluginObjects objectForKey:className];
     if (!obj) {
-        obj = [[NSClassFromString (className)alloc] initWithWebView:webView];
+        obj = [[NSClassFromString(className)alloc] initWithWebView:webView];
 
         if (obj != nil) {
             [self registerPlugin:obj withClassName:className];

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
index 8a89a22..a4d78bd 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.h
@@ -30,6 +30,7 @@
     NSInteger _loadCount;
     NSInteger _state;
     NSInteger _curLoadToken;
+    NSInteger _loadStartPollCount;
 }
 
 - (id)initWithDelegate:(NSObject <UIWebViewDelegate>*)delegate;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 a72bfb9..fd9c032 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWebViewDelegate.m
@@ -17,14 +17,77 @@
  under the License.
  */
 
+//
+// Testing shows:
+//
+// In all cases, webView.request.URL is the previous page's URL (or empty) during the didStartLoad callback.
+// When loading a page with a redirect:
+// 1. shouldStartLoading (requestURL is target page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is redirect target)
+// 4. didFinishLoad (request.URL is redirect target)
+//
+// Note the lack of a second didStartLoading **
+//
+// When loading a page with iframes:
+// 1. shouldStartLoading (requestURL is main page)
+// 2. didStartLoading
+// 3. shouldStartLoading (requestURL is one of the iframes)
+// 4. didStartLoading
+// 5. didFinishLoad
+// 6. didFinishLoad
+//
+// Note there is no way to distinguish which didFinishLoad maps to which didStartLoad **
+//
+// Loading a page by calling window.history.go(-1):
+// 1. didStartLoading
+// 2. didFinishLoad
+//
+// Note the lack of a shouldStartLoading call **
+// Actually - this is fixed on iOS6. iOS6 has a shouldStart. **
+//
+// Loading a page by calling location.reload()
+// 1. shouldStartLoading
+// 2. didStartLoading
+// 3. didFinishLoad
+//
+// Loading a page with an iframe that fails to load:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 4. didStart
+// 5. didFailWithError
+// 6. didFinish
+//
+// Loading a page with an iframe that fails to load due to an invalid URL:
+// 1. shouldStart (main page)
+// 2. didStart
+// 3. shouldStart (iframe)
+// 5. didFailWithError
+// 6. didFinish
+//
+// This case breaks our logic since there is a missing didStart. To prevent this,
+// we check URLs in shouldStart and return NO if they are invalid.
+//
+// Loading a page with an invalid URL
+// 1. shouldStart (main page)
+// 2. didFailWithError
+//
+// TODO: Record order when page is re-navigated before the first navigation finishes.
+//
+
 #import "CDVWebViewDelegate.h"
 #import "CDVAvailability.h"
 
+// #define VerboseLog NSLog
+#define VerboseLog(...) do {} while (0)
+
 typedef enum {
-    STATE_NORMAL,
-    STATE_SHOULD_LOAD_MISSING,
-    STATE_WAITING_FOR_START,
-    STATE_WAITING_FOR_FINISH
+    STATE_IDLE,
+    STATE_WAITING_FOR_LOAD_START,
+    STATE_WAITING_FOR_LOAD_FINISH,
+    STATE_IOS5_POLLING_FOR_LOAD_START,
+    STATE_IOS5_POLLING_FOR_LOAD_FINISH
 } State;
 
 @implementation CDVWebViewDelegate
@@ -35,7 +98,7 @@ typedef enum {
     if (self != nil) {
         _delegate = delegate;
         _loadCount = -1;
-        _state = STATE_NORMAL;
+        _state = STATE_IDLE;
     }
     return self;
 }
@@ -62,31 +125,41 @@ typedef enum {
 
 - (void)pollForPageLoadStart:(UIWebView*)webView
 {
-    if ((_state != STATE_WAITING_FOR_START) && (_state != STATE_SHOULD_LOAD_MISSING)) {
+    if (_state != STATE_IOS5_POLLING_FOR_LOAD_START) {
         return;
     }
     if (![self isJsLoadTokenSet:webView]) {
-        _state = STATE_WAITING_FOR_FINISH;
+        VerboseLog(@"Polled for page load start. result = YES!");
+        _state = STATE_IOS5_POLLING_FOR_LOAD_FINISH;
         [self setLoadToken:webView];
         if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
             [_delegate webViewDidStartLoad:webView];
         }
         [self pollForPageLoadFinish:webView];
+    } else {
+        VerboseLog(@"Polled for page load start. result = NO");
+        // Poll only for 1 second, and then fall back on checking only when delegate methods are called.
+        ++_loadStartPollCount;
+        if (_loadStartPollCount < (1000 * .05)) {
+            [self performSelector:@selector(pollForPageLoadStart:) withObject:webView afterDelay:.05];
+        }
     }
 }
 
 - (void)pollForPageLoadFinish:(UIWebView*)webView
 {
-    if (_state != STATE_WAITING_FOR_FINISH) {
+    if (_state != STATE_IOS5_POLLING_FOR_LOAD_FINISH) {
         return;
     }
     if ([self isPageLoaded:webView]) {
-        _state = STATE_SHOULD_LOAD_MISSING;
+        VerboseLog(@"Polled for page load finish. result = YES!");
+        _state = STATE_IDLE;
         if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
             [_delegate webViewDidFinishLoad:webView];
         }
     } else {
-        [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50];
+        VerboseLog(@"Polled for page load finish. result = NO");
+        [self performSelector:@selector(pollForPageLoadFinish:) withObject:webView afterDelay:.05];
     }
 }
 
@@ -98,73 +171,163 @@ typedef enum {
         shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType];
     }
 
+    VerboseLog(@"webView shouldLoad=%d (before) state=%d loadCount=%d URL=%@", shouldLoad, _state, _loadCount, request.URL);
+
     if (shouldLoad) {
         BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
         if (isTopLevelNavigation) {
-            _loadCount = 0;
-            _state = STATE_NORMAL;
+            switch (_state) {
+                case STATE_WAITING_FOR_LOAD_FINISH:
+                    // Redirect case.
+                    // We expect loadCount == 1.
+                    if (_loadCount != 1) {
+                        NSLog(@"CDVWebViewDelegate: Detected redirect when loadCount=%d", _loadCount);
+                    }
+                    break;
+
+                case STATE_IDLE:
+                case STATE_IOS5_POLLING_FOR_LOAD_START:
+                    // Page navigation start.
+                    _loadCount = 0;
+                    _state = STATE_WAITING_FOR_LOAD_START;
+                    break;
+
+                default:
+                    NSLog(@"CDVWebViewDelegate: Navigation started when state=%d", _state);
+                    _loadCount = 0;
+                    _state = STATE_WAITING_FOR_LOAD_START;
+                    if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+                        [_delegate webView:webView didFailLoadWithError:nil];
+                    }
+            }
+        } else {
+            // Deny invalid URLs so that we don't get the case where we go straight from
+            // webViewShouldLoad -> webViewDidFailLoad (messes up _loadCount).
+            shouldLoad = shouldLoad && [NSURLConnection canHandleRequest:request];
         }
+        VerboseLog(@"webView shouldLoad=%d (after) isTopLevelNavigation=%d state=%d loadCount=%d", shouldLoad, isTopLevelNavigation, _state, _loadCount);
     }
     return shouldLoad;
 }
 
 - (void)webViewDidStartLoad:(UIWebView*)webView
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 0) {
-            if ([_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
-                [_delegate webViewDidStartLoad:webView];
+    VerboseLog(@"webView didStartLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+    switch (_state) {
+        case STATE_IDLE:
+            if (IsAtLeastiOSVersion(@"6.0")) {
+                break;
             }
-            _loadCount += 1;
-        } else if (_loadCount > 0) {
-            _loadCount += 1;
-        } else if (!IsAtLeastiOSVersion(@"6.0")) {
             // If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called.
             // Without shouldLoad, we can't distinguish an iframe from a top-level navigation.
             // We could try to distinguish using [UIWebView canGoForward], but that's too much complexity,
             // and would work only on the first time it was used.
 
             // Our work-around is to set a JS variable and poll until it disappears (from a naviagtion).
-            _state = STATE_WAITING_FOR_START;
+            _state = STATE_IOS5_POLLING_FOR_LOAD_START;
+            _loadStartPollCount = 0;
             [self setLoadToken:webView];
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            if (_loadCount != 0) {
+                NSLog(@"CDVWebViewDelegate: Unexpected loadCount in didStart. count=%d", _loadCount);
+            }
+            fireCallback = YES;
+            _state = STATE_WAITING_FOR_LOAD_FINISH;
+            _loadCount = 1;
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            _loadCount += 1;
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+
+        default:
+            NSLog(@"CDVWebViewDelegate: Unexpected didStart with state=%d loadCount=%d", _state, _loadCount);
+    }
+    VerboseLog(@"webView didStartLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidStartLoad:)]) {
+        [_delegate webViewDidStartLoad:webView];
     }
 }
 
 - (void)webViewDidFinishLoad:(UIWebView*)webView
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 1) {
-            if ([_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
-                [_delegate webViewDidFinishLoad:webView];
+    VerboseLog(@"webView didFinishLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+    switch (_state) {
+        case STATE_IDLE:
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            NSLog(@"CDVWebViewDelegate: Unexpected didFinish while waiting for load start.");
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            if (_loadCount == 1) {
+                fireCallback = YES;
+                _state = STATE_IDLE;
             }
-            _loadCount = -1;
-        } else if (_loadCount > 1) {
             _loadCount -= 1;
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+    }
+    VerboseLog(@"webView didFinishLoad (after). state=%d loadCount=%d fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webViewDidFinishLoad:)]) {
+        [_delegate webViewDidFinishLoad:webView];
     }
 }
 
 - (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
 {
-    if (_state == STATE_NORMAL) {
-        if (_loadCount == 1) {
-            if ([_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
-                [_delegate webView:webView didFailLoadWithError:error];
+    VerboseLog(@"webView didFailLoad (before). state=%d loadCount=%d", _state, _loadCount);
+    BOOL fireCallback = NO;
+
+    switch (_state) {
+        case STATE_IDLE:
+            break;
+
+        case STATE_WAITING_FOR_LOAD_START:
+            _state = STATE_IDLE;
+            fireCallback = YES;
+            break;
+
+        case STATE_WAITING_FOR_LOAD_FINISH:
+            if (_loadCount == 1) {
+                _state = STATE_IDLE;
+                fireCallback = YES;
             }
             _loadCount = -1;
-        } else if (_loadCount > 1) {
-            _loadCount -= 1;
-        }
-    } else {
-        [self pollForPageLoadStart:webView];
-        [self pollForPageLoadFinish:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_START:
+            [self pollForPageLoadStart:webView];
+            break;
+
+        case STATE_IOS5_POLLING_FOR_LOAD_FINISH:
+            [self pollForPageLoadFinish:webView];
+            break;
+    }
+    VerboseLog(@"webView didFailLoad (after). state=%d loadCount=%d, fireCallback=%d", _state, _loadCount, fireCallback);
+    if (fireCallback && [_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)]) {
+        [_delegate webView:webView didFailLoadWithError:error];
     }
 }
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
index 3741e94..e339dd0 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
@@ -23,14 +23,11 @@ extern NSString* const kCDVDefaultWhitelistRejectionString;
 
 @interface CDVWhitelist : NSObject
 
-@property (nonatomic, readonly, strong) NSArray* whitelist;
-@property (nonatomic, readonly, strong) NSArray* expandedWhitelist;
-@property (nonatomic, readonly, assign) BOOL allowAll;
 @property (nonatomic, copy) NSString* whitelistRejectionFormatString;
 
 - (id)initWithArray:(NSArray*)array;
-- (BOOL)URLIsAllowed:(NSURL*)url;
 - (BOOL)schemeIsAllowed:(NSString*)scheme;
+- (BOOL)URLIsAllowed:(NSURL*)url;
 - (NSString*)errorStringForURL:(NSURL*)url;
 
 @end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
index 77e20ac..db7aa32 100644
--- a/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
@@ -20,12 +20,12 @@
 #import "CDVWhitelist.h"
 
 NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
+NSString* const kCDVDefaultSchemeName = @"cdv-default-scheme";
 
 @interface CDVWhitelist ()
 
 @property (nonatomic, readwrite, strong) NSArray* whitelist;
-@property (nonatomic, readwrite, strong) NSArray* expandedWhitelist;
-@property (nonatomic, readwrite, assign) BOOL allowAll;
+@property (nonatomic, readwrite, strong) NSDictionary* expandedWhitelists;
 
 - (void)processWhitelist;
 
@@ -33,15 +33,14 @@ NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejectio
 
 @implementation CDVWhitelist
 
-@synthesize whitelist, expandedWhitelist, allowAll, whitelistRejectionFormatString;
+@synthesize whitelist, expandedWhitelists, whitelistRejectionFormatString;
 
 - (id)initWithArray:(NSArray*)array
 {
     self = [super init];
     if (self) {
         self.whitelist = array;
-        self.expandedWhitelist = nil;
-        self.allowAll = NO;
+        self.expandedWhitelists = nil;
         self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
         [self processWhitelist];
     }
@@ -94,6 +93,17 @@ NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejectio
     }
 }
 
+- (NSString*)extractSchemeFromUrlString:(NSString*)url
+{
+    NSURL* aUrl = [NSURL URLWithString:url];
+
+    if ((aUrl != nil) && ([aUrl scheme] != nil)) { // found scheme
+        return [aUrl scheme];
+    } else {
+        return kCDVDefaultSchemeName;
+    }
+}
+
 - (void)processWhitelist
 {
     if (self.whitelist == nil) {
@@ -101,70 +111,91 @@ NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejectio
         return;
     }
 
-    NSMutableArray* expanded = [NSMutableArray arrayWithCapacity:[self.whitelist count]];
-
-    // iterate through settings ExternalHosts, check for equality
-    NSEnumerator* enumerator = [self.whitelist objectEnumerator];
-    id externalHost = nil;
+    NSMutableDictionary* _expandedWhitelists = [@{kCDVDefaultSchemeName: [NSMutableArray array]} mutableCopy];
 
     // only allow known TLDs (since Aug 23rd 2011), and two character country codes
     // does not match internationalized domain names with non-ASCII characters
     NSString* tld_match = @"(aero|asia|arpa|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx|[a-z][a-z])";
 
-    while (externalHost = [enumerator nextObject]) {
-        NSString* regex = [self extractHostFromUrlString:externalHost];
-        BOOL is_ip = [self isIPv4Address:regex];
+    // iterate through settings ExternalHosts, check for equality
+    for (NSString* externalHost in self.whitelist) {
+        NSString* host = [self extractHostFromUrlString:externalHost];
+        NSString* scheme = [self extractSchemeFromUrlString:externalHost];
 
         // check for single wildcard '*', if found set allowAll to YES
-        if ([regex isEqualToString:@"*"]) {
-            self.allowAll = YES;
-            self.expandedWhitelist = [NSArray arrayWithObject:regex];
-            break;
+        if ([host isEqualToString:@"*"]) {
+            [_expandedWhitelists setObject:[NSArray arrayWithObject:host] forKey:scheme];
+            continue;
+        }
+
+        // if this is the first value for this scheme, create a new entry
+        if ([_expandedWhitelists objectForKey:scheme] == nil) {
+            [_expandedWhitelists setObject:[NSMutableArray array] forKey:scheme];
         }
 
         // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova')
         NSString* prefix = @"*.";
-        if ([regex hasPrefix:prefix]) {
+        if ([host hasPrefix:prefix]) {
             // replace the first two characters '*.' with our regex
-            regex = [regex stringByReplacingCharactersInRange:NSMakeRange(0, [prefix length]) withString:@"(\\s{0}|*.)"]; // the '*' and '.' will be substituted later
+            host = [host stringByReplacingCharactersInRange:NSMakeRange(0, [prefix length]) withString:@"(\\s{0}|*.)"]; // the '*' and '.' will be substituted later
         }
 
         // ends with wildcard match for TLD
-        if (!is_ip && [regex hasSuffix:@".*"]) {
+        if (![self isIPv4Address:host] && [host hasSuffix:@".*"]) {
             // replace * with tld_match
-            regex = [regex stringByReplacingCharactersInRange:NSMakeRange([regex length] - 1, 1) withString:tld_match];
+            host = [host stringByReplacingCharactersInRange:NSMakeRange([host length] - 1, 1) withString:tld_match];
         }
         // escape periods - since '.' means any character in regex
-        regex = [regex stringByReplacingOccurrencesOfString:@"." withString:@"\\."];
+        host = [host stringByReplacingOccurrencesOfString:@"." withString:@"\\."];
         // wildcard is match 1 or more characters (to make it simple, since we are not doing verification whether the hostname is valid)
-        regex = [regex stringByReplacingOccurrencesOfString:@"*" withString:@".*"];
+        host = [host stringByReplacingOccurrencesOfString:@"*" withString:@".*"];
 
-        [expanded addObject:regex];
+        [[_expandedWhitelists objectForKey:scheme] addObject:host];
     }
 
-    self.expandedWhitelist = expanded;
+    self.expandedWhitelists = _expandedWhitelists;
 }
 
 - (BOOL)schemeIsAllowed:(NSString*)scheme
 {
-    return [scheme isEqualToString:@"http"] ||
-           [scheme isEqualToString:@"https"] ||
-           [scheme isEqualToString:@"ftp"] ||
-           [scheme isEqualToString:@"ftps"];
+    if ([scheme isEqualToString:@"http"] ||
+        [scheme isEqualToString:@"https"] ||
+        [scheme isEqualToString:@"ftp"] ||
+        [scheme isEqualToString:@"ftps"]) {
+        return YES;
+    }
+
+    return (self.expandedWhitelists != nil) && ([self.expandedWhitelists objectForKey:scheme] != nil);
 }
 
 - (BOOL)URLIsAllowed:(NSURL*)url
 {
-    if (self.expandedWhitelist == nil) {
+    NSString* scheme = [url scheme];
+
+    // http[s] and ftp[s] should also validate against the common set in the kCDVDefaultSchemeName list
+    if ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"] || [scheme isEqualToString:@"ftp"] || [scheme isEqualToString:@"ftps"]) {
+        NSURL* newUrl = [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@", kCDVDefaultSchemeName, [url host]]];
+        // If it is allowed, we are done.  If not, continue to check for the actual scheme-specific list
+        if ([self URLIsAllowed:newUrl]) {
+            return YES;
+        }
+    }
+
+    // Check that the scheme is supported
+    if (![self schemeIsAllowed:scheme]) {
         return NO;
     }
 
-    if (self.allowAll) {
+    NSArray* expandedWhitelist = [self.expandedWhitelists objectForKey:scheme];
+
+    // Are we allowing everything for this scheme?
+    // TODO: consider just having a static sentinel value for the "allow all" list, so we can use object equality
+    if (([expandedWhitelist count] == 1) && [[expandedWhitelist objectAtIndex:0] isEqualToString:@"*"]) {
         return YES;
     }
 
     // iterate through settings ExternalHosts, check for equality
-    NSEnumerator* enumerator = [self.expandedWhitelist objectEnumerator];
+    NSEnumerator* enumerator = [expandedWhitelist objectEnumerator];
     id regex = nil;
     NSString* urlHost = [url host];
 

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 e14f1f6..c637d60 100644
--- a/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
+++ b/lib/cordova-ios/CordovaLib/CordovaLib.xcodeproj/project.pbxproj
@@ -47,6 +47,8 @@
 		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, ); }; };
+		7E14B5A81705050A0032169E /* CDVTimer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7E14B5A61705050A0032169E /* CDVTimer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		7E14B5A91705050A0032169E /* CDVTimer.m in Sources */ = {isa = PBXBuildFile; fileRef = 7E14B5A71705050A0032169E /* CDVTimer.m */; };
 		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, ); }; };
@@ -55,8 +57,6 @@
 		8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */; };
 		8887FD6A1090FBE7009987E8 /* CDVContacts.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD2A1090FBE7009987E8 /* CDVContacts.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8887FD6B1090FBE7009987E8 /* CDVContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD2B1090FBE7009987E8 /* CDVContacts.m */; };
-		8887FD6C1090FBE7009987E8 /* CDVDebugConsole.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		8887FD6D1090FBE7009987E8 /* CDVDebugConsole.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */; };
 		8887FD701090FBE7009987E8 /* CDVFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD301090FBE7009987E8 /* CDVFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		8887FD711090FBE7009987E8 /* CDVFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8887FD311090FBE7009987E8 /* CDVFile.m */; };
 		8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -147,6 +147,8 @@
 		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>"; };
+		7E14B5A61705050A0032169E /* CDVTimer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVTimer.h; path = Classes/CDVTimer.h; sourceTree = "<group>"; };
+		7E14B5A71705050A0032169E /* CDVTimer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVTimer.m; path = Classes/CDVTimer.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>"; };
@@ -156,8 +158,6 @@
 		8887FD291090FBE7009987E8 /* NSDictionary+Extensions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSDictionary+Extensions.m"; path = "Classes/NSDictionary+Extensions.m"; sourceTree = "<group>"; };
 		8887FD2A1090FBE7009987E8 /* CDVContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVContacts.h; path = Classes/CDVContacts.h; sourceTree = "<group>"; };
 		8887FD2B1090FBE7009987E8 /* CDVContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVContacts.m; path = Classes/CDVContacts.m; sourceTree = "<group>"; };
-		8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDebugConsole.h; path = Classes/CDVDebugConsole.h; sourceTree = "<group>"; };
-		8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVDebugConsole.m; path = Classes/CDVDebugConsole.m; sourceTree = "<group>"; };
 		8887FD301090FBE7009987E8 /* CDVFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVFile.h; path = Classes/CDVFile.h; sourceTree = "<group>"; };
 		8887FD311090FBE7009987E8 /* CDVFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVFile.m; path = Classes/CDVFile.m; sourceTree = "<group>"; };
 		8887FD341090FBE7009987E8 /* CDVInvokedUrlCommand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVInvokedUrlCommand.h; path = Classes/CDVInvokedUrlCommand.h; sourceTree = "<group>"; };
@@ -307,8 +307,6 @@
 				1F3C04CD12BC247D004F9E10 /* CDVContact.m */,
 				8887FD2A1090FBE7009987E8 /* CDVContacts.h */,
 				8887FD2B1090FBE7009987E8 /* CDVContacts.m */,
-				8887FD2C1090FBE7009987E8 /* CDVDebugConsole.h */,
-				8887FD2D1090FBE7009987E8 /* CDVDebugConsole.m */,
 				EB80C2AA15DEA63D004D9E7B /* CDVEcho.h */,
 				EB80C2AB15DEA63D004D9E7B /* CDVEcho.m */,
 				8887FD301090FBE7009987E8 /* CDVFile.h */,
@@ -354,6 +352,8 @@
 				30E563CE13E217EC00C949AA /* NSMutableArray+QueueAdditions.m */,
 				8887FD501090FBE7009987E8 /* NSData+Base64.h */,
 				8887FD511090FBE7009987E8 /* NSData+Base64.m */,
+				7E14B5A61705050A0032169E /* CDVTimer.h */,
+				7E14B5A71705050A0032169E /* CDVTimer.m */,
 			);
 			name = Util;
 			sourceTree = "<group>";
@@ -380,7 +380,6 @@
 				8887FD661090FBE7009987E8 /* CDVCamera.h in Headers */,
 				8887FD681090FBE7009987E8 /* NSDictionary+Extensions.h in Headers */,
 				8887FD6A1090FBE7009987E8 /* CDVContacts.h in Headers */,
-				8887FD6C1090FBE7009987E8 /* CDVDebugConsole.h in Headers */,
 				8887FD701090FBE7009987E8 /* CDVFile.h in Headers */,
 				8887FD741090FBE7009987E8 /* CDVInvokedUrlCommand.h in Headers */,
 				8887FD851090FBE7009987E8 /* CDVLocation.h in Headers */,
@@ -420,6 +419,7 @@
 				30F3930B169F839700B22307 /* CDVJSON.h in Headers */,
 				EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */,
 				EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */,
+				7E14B5A81705050A0032169E /* CDVTimer.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -480,7 +480,6 @@
 				8887FD671090FBE7009987E8 /* CDVCamera.m in Sources */,
 				8887FD691090FBE7009987E8 /* NSDictionary+Extensions.m in Sources */,
 				8887FD6B1090FBE7009987E8 /* CDVContacts.m in Sources */,
-				8887FD6D1090FBE7009987E8 /* CDVDebugConsole.m in Sources */,
 				8887FD711090FBE7009987E8 /* CDVFile.m in Sources */,
 				8887FD751090FBE7009987E8 /* CDVInvokedUrlCommand.m in Sources */,
 				8887FD861090FBE7009987E8 /* CDVLocation.m in Sources */,
@@ -516,6 +515,7 @@
 				EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */,
 				EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */,
 				68B7516F16FD18190076A8B4 /* CDVJpegHeaderWriter.m in Sources */,
+				7E14B5A91705050A0032169E /* CDVTimer.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLib/VERSION
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/VERSION b/lib/cordova-ios/CordovaLib/VERSION
index e70b452..59b7056 100644
--- a/lib/cordova-ios/CordovaLib/VERSION
+++ b/lib/cordova-ios/CordovaLib/VERSION
@@ -1 +1 @@
-2.6.0
+2.7.0rc1

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/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 4df7891..844816f 100644
--- a/lib/cordova-ios/CordovaLib/cordova.ios.js
+++ b/lib/cordova-ios/CordovaLib/cordova.ios.js
@@ -1,8 +1,8 @@
 // Platform: ios
 
-// commit 125dca530923a44a8f44f68f5e1970cbdd4e7faf
+// commit 360bd3e65c33ce4f01e2efb82d641a565ef3c333
 
-// File generated at :: Thu Apr 04 2013 10:21:55 GMT-0700 (PDT)
+// File generated at :: Fri Apr 19 2013 18:36:09 GMT-0700 (PDT)
 
 /*
  Licensed to the Apache Software Foundation (ASF) under one
@@ -219,6 +219,10 @@ var cordova = {
             }
             else {
               setTimeout(function() {
+                  // Fire deviceready on listeners that were registered before cordova.js was loaded.
+                  if (type == 'deviceready') {
+                      document.dispatchEvent(evt);
+                  }
                   documentEventHandlers[type].fire(evt);
               }, 0);
             }
@@ -742,6 +746,7 @@ channel.createSticky('onDestroy');
 // Channels that must fire before "deviceready" is fired.
 channel.waitForInitialization('onCordovaReady');
 channel.waitForInitialization('onCordovaConnectionReady');
+channel.waitForInitialization('onDOMContentLoaded');
 
 module.exports = channel;
 
@@ -2372,7 +2377,7 @@ function initRead(reader, file) {
 
     if (typeof file == 'string') {
         // Deprecated in Cordova 2.4.
-        console.warning('Using a string argument with FileReader.readAs functions is deprecated.');
+        console.warn('Using a string argument with FileReader.readAs functions is deprecated.');
         reader._fileName = file;
     } else if (typeof file.fullPath == 'string') {
         reader._fileName = file.fullPath;
@@ -2730,7 +2735,7 @@ function getBasicAuthHeader(urlString) {
         var origin = protocol + url.host;
 
         // check whether there are the username:password credentials in the url
-        if (url.href.indexOf(origin) != 0) { // credentials found
+        if (url.href.indexOf(origin) !== 0) { // credentials found
             var atIndex = url.href.indexOf("@");
             credentials = url.href.substring(protocol.length, atIndex);
         }
@@ -2779,15 +2784,11 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
     var params = null;
     var chunkedMode = true;
     var headers = null;
-
+    var httpMethod = null;
     var basicAuthHeader = getBasicAuthHeader(server);
     if (basicAuthHeader) {
-        if (!options) {
-            options = new FileUploadOptions();
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2796,6 +2797,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
         fileName = options.fileName;
         mimeType = options.mimeType;
         headers = options.headers;
+        httpMethod = options.httpMethod || "POST";
+        if (httpMethod.toUpperCase() == "PUT"){
+            httpMethod = "PUT";
+        } else {
+            httpMethod = "POST";
+        }
         if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
             chunkedMode = options.chunkedMode;
         }
@@ -2822,7 +2829,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
             successCallback && successCallback(result);
         }
     };
-    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
+    exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id, httpMethod]);
 };
 
 /**
@@ -2840,12 +2847,8 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro
 
     var basicAuthHeader = getBasicAuthHeader(source);
     if (basicAuthHeader) {
-        if (!options) {
-            options = {};
-        }
-        if (!options.headers) {
-            options.headers = {};
-        }
+        options = options || {};
+        options.headers = options.headers || {};
         options.headers[basicAuthHeader.name] = basicAuthHeader.value;
     }
 
@@ -2884,12 +2887,11 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro
 };
 
 /**
- * Aborts the ongoing file transfer on this object
- * @param successCallback {Function}  Callback to be invoked upon success
- * @param errorCallback {Function}    Callback to be invoked upon error
+ * Aborts the ongoing file transfer on this object. The original error
+ * callback for the file transfer will be called if necessary.
  */
-FileTransfer.prototype.abort = function(successCallback, errorCallback) {
-    exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+FileTransfer.prototype.abort = function() {
+    exec(null, null, 'FileTransfer', 'abort', [this._id]);
 };
 
 module.exports = FileTransfer;
@@ -2933,12 +2935,13 @@ define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
  * @param headers {Object}   Keys are header names, values are header values. Multiple
  *                           headers of the same name are not supported.
  */
-var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) {
+var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers, httpMethod) {
     this.fileKey = fileKey || null;
     this.fileName = fileName || null;
     this.mimeType = mimeType || null;
     this.params = params || null;
     this.headers = headers || null;
+    this.httpMethod = httpMethod || null;
 };
 
 module.exports = FileUploadOptions;
@@ -3273,6 +3276,7 @@ define("cordova/plugin/InAppBrowser", function(require, exports, module) {
 
 var exec = require('cordova/exec');
 var channel = require('cordova/channel');
+var modulemapper = require('cordova/modulemapper');
 
 function InAppBrowser() {
    this.channels = {
@@ -3301,6 +3305,26 @@ InAppBrowser.prototype = {
         if (eventname in this.channels) {
             this.channels[eventname].unsubscribe(f);
         }
+    },
+
+    executeScript: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectScriptCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectScriptFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('executeScript requires exactly one of code or file to be specified');
+        }
+    },
+
+    insertCSS: function(injectDetails, cb) {
+        if (injectDetails.code) {
+            exec(cb, null, "InAppBrowser", "injectStyleCode", [injectDetails.code, !!cb]);
+        } else if (injectDetails.file) {
+            exec(cb, null, "InAppBrowser", "injectStyleFile", [injectDetails.file, !!cb]);
+        } else {
+            throw new Error('insertCSS requires exactly one of code or file to be specified');
+        }
     }
 };
 
@@ -3309,6 +3333,13 @@ module.exports = function(strUrl, strWindowName, strWindowFeatures) {
     var cb = function(eventname) {
        iab._eventHandler(eventname);
     };
+
+    // Don't catch calls that write to existing frames (e.g. named iframes).
+    if (window.frames && window.frames[strWindowName]) {
+        var origOpenFunc = modulemapper.getOriginalSymbol(window, 'open');
+        return origOpenFunc.apply(window, arguments);
+    }
+
     exec(cb, cb, "InAppBrowser", "open", [strUrl, strWindowName, strWindowFeatures]);
     return iab;
 };
@@ -4275,7 +4306,7 @@ console.debug = function() {
 console.assert = function(expression) {
     if (expression) return;
 
-    var message = utils.vformat(arguments[1], [].slice.call(arguments, 2));
+    var message = logger.format.apply(logger.format, [].slice.call(arguments, 1));
     console.log("ASSERT: " + message);
 };
 
@@ -5257,93 +5288,6 @@ module.exports = {
 
 });
 
-// file: lib/ios/plugin/ios/console.js
-define("cordova/plugin/ios/console", function(require, exports, module) {
-
-var exec = require('cordova/exec');
-
-/**
- * create a nice string for an object
- */
-function stringify(message) {
-    try {
-        if (typeof message === "object" && JSON && JSON.stringify) {
-            try {
-                return JSON.stringify(message);
-            }
-            catch (e) {
-                return "error JSON.stringify()ing argument: " + e;
-            }
-        } else {
-            return (typeof message === "undefined") ? "undefined" : message.toString();
-        }
-    } catch (e) {
-        return e.toString();
-    }
-}
-
-/**
- * Wrapper one of the console logging methods, so that
- * the Cordova logging native is called, then the original.
- */
-function wrappedMethod(console, method) {
-    var origMethod = console[method];
-
-    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',
-            [ res.join(' '), { logLevel: method.toUpperCase() } ]
-        );
-
-        if (!origMethod) return;
-
-        origMethod.apply(console, arguments);
-    };
-}
-
-var console = window.console || {};
-
-// 2012-10-06 pmuellr - marking setLevel() method and logLevel property
-// on console as deprecated;
-// it didn't do anything useful, since the level constants weren't accessible
-// to anyone
-
-console.setLevel = function() {};
-console.logLevel = 0;
-
-// wrapper the logging messages
-
-var methods = ["log", "debug", "info", "warn", "error"];
-
-for (var i=0; i<methods.length; i++) {
-    var method = methods[i];
-
-    console[method] = wrappedMethod(console, method);
-}
-
-module.exports = console;
-
-});
-
-// file: lib/ios/plugin/ios/console/symbols.js
-define("cordova/plugin/ios/console/symbols", function(require, exports, module) {
-
-var modulemapper = require('cordova/modulemapper');
-
-modulemapper.clobbers('cordova/plugin/ios/console', 'console');
-
-});
-
 // file: lib/ios/plugin/ios/contacts.js
 define("cordova/plugin/ios/contacts", function(require, exports, module) {
 
@@ -5421,6 +5365,16 @@ logger.useConsole(false);
 
 });
 
+// file: lib/ios/plugin/ios/logger/symbols.js
+define("cordova/plugin/ios/logger/symbols", function(require, exports, module) {
+
+
+var modulemapper = require('cordova/modulemapper');
+
+modulemapper.clobbers('cordova/plugin/logger', 'console');
+
+});
+
 // file: lib/ios/plugin/ios/notification.js
 define("cordova/plugin/ios/notification", function(require, exports, module) {
 
@@ -5605,10 +5559,10 @@ function logWithArgs(level, args) {
  * Parameters passed after message are used applied to
  * the message with utils.format()
  */
-logger.logLevel = function(level, message /* , ... */) {
+logger.logLevel = function(level /* , ... */) {
     // format the message with the parameters
-    var formatArgs = [].slice.call(arguments, 2);
-    message    = utils.vformat(message, formatArgs);
+    var formatArgs = [].slice.call(arguments, 1);
+    var message    = logger.format.apply(logger.format, formatArgs);
 
     if (LevelsMap[level] === null) {
         throw new Error("invalid logging level: " + level);
@@ -5643,6 +5597,92 @@ logger.logLevel = function(level, message /* , ... */) {
     }
 };
 
+
+/**
+ * Formats a string and arguments following it ala console.log()
+ *
+ * Any remaining arguments will be appended to the formatted string.
+ *
+ * for rationale, see FireBug's Console API:
+ *    http://getfirebug.com/wiki/index.php/Console_API
+ */
+logger.format = function(formatString, args) {
+    return __format(arguments[0], [].slice.call(arguments,1)).join(' ');
+};
+
+
+//------------------------------------------------------------------------------
+/**
+ * Formats a string and arguments following it ala vsprintf()
+ *
+ * format chars:
+ *   %j - format arg as JSON
+ *   %o - format arg as JSON
+ *   %c - format arg as ''
+ *   %% - replace with '%'
+ * any other char following % will format it's
+ * arg via toString().
+ *
+ * Returns an array containing the formatted string and any remaining
+ * arguments.
+ */
+function __format(formatString, args) {
+    if (formatString === null || formatString === undefined) return [""];
+    if (arguments.length == 1) return [formatString.toString()];
+
+    if (typeof formatString != "string")
+        formatString = formatString.toString();
+
+    var pattern = /(.*?)%(.)(.*)/;
+    var rest    = formatString;
+    var result  = [];
+
+    while (args.length) {
+        var match = pattern.exec(rest);
+        if (!match) break;
+
+        var arg   = args.shift();
+        rest = match[3];
+        result.push(match[1]);
+
+        if (match[2] == '%') {
+            result.push('%');
+            args.unshift(arg);
+            continue;
+        }
+
+        result.push(__formatted(arg, match[2]));
+    }
+
+    result.push(rest);
+
+    var remainingArgs = [].slice.call(args);
+    remainingArgs.unshift(result.join(''));
+    return remainingArgs;
+}
+
+function __formatted(object, formatChar) {
+
+    try {
+        switch(formatChar) {
+            case 'j':
+            case 'o': return JSON.stringify(object);
+            case 'c': return '';
+        }
+    }
+    catch (e) {
+        return "error JSON.stringify()ing argument: " + e;
+    }
+
+    if ((object === null) || (object === undefined)) {
+        return Object.prototype.toString.call(object);
+    }
+
+    return object.toString();
+}
+
+
+//------------------------------------------------------------------------------
 // when deviceready fires, log queued messages
 logger.__onDeviceReady = function() {
     if (DeviceReady) return;
@@ -5811,13 +5851,13 @@ module.exports = {
             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.
+        // Some platforms 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 (platform.id == "android" || platform.id == "ios" || platform.id == "windowsphone") {
             if (typeof _buttonLabels === 'string') {
                 var buttonLabelString = _buttonLabels;
-                _buttonLabels = buttonLabelString.split(",");
+                _buttonLabels = _buttonLabels.split(","); // not crazy about changing the var type here
             }
         } else {
             if (Array.isArray(_buttonLabels)) {
@@ -6168,62 +6208,6 @@ utils.alert = function(msg) {
     }
 };
 
-/**
- * Formats a string and arguments following it ala sprintf()
- *
- * see utils.vformat() for more information
- */
-utils.format = function(formatString /* ,... */) {
-    var args = [].slice.call(arguments, 1);
-    return utils.vformat(formatString, args);
-};
-
-/**
- * Formats a string and arguments following it ala vsprintf()
- *
- * format chars:
- *   %j - format arg as JSON
- *   %o - format arg as JSON
- *   %c - format arg as ''
- *   %% - replace with '%'
- * any other char following % will format it's
- * arg via toString().
- *
- * for rationale, see FireBug's Console API:
- *    http://getfirebug.com/wiki/index.php/Console_API
- */
-utils.vformat = function(formatString, args) {
-    if (formatString === null || formatString === undefined) return "";
-    if (arguments.length == 1) return formatString.toString();
-    if (typeof formatString != "string") return formatString.toString();
-
-    var pattern = /(.*?)%(.)(.*)/;
-    var rest    = formatString;
-    var result  = [];
-
-    while (args.length) {
-        var arg   = args.shift();
-        var match = pattern.exec(rest);
-
-        if (!match) break;
-
-        rest = match[3];
-
-        result.push(match[1]);
-
-        if (match[2] == '%') {
-            result.push('%');
-            args.unshift(arg);
-            continue;
-        }
-
-        result.push(formatted(arg, match[2]));
-    }
-
-    result.push(rest);
-
-    return result.join('');
-};
 
 //------------------------------------------------------------------------------
 function UUIDcreatePart(length) {
@@ -6238,26 +6222,6 @@ function UUIDcreatePart(length) {
     return uuidpart;
 }
 
-//------------------------------------------------------------------------------
-function formatted(object, formatChar) {
-
-    try {
-        switch(formatChar) {
-            case 'j':
-            case 'o': return JSON.stringify(object);
-            case 'c': return '';
-        }
-    }
-    catch (e) {
-        return "error JSON.stringify()ing argument: " + e;
-    }
-
-    if ((object === null) || (object === undefined)) {
-        return Object.prototype.toString.call(object);
-    }
-
-    return object.toString();
-}
 
 });
 
@@ -6267,6 +6231,25 @@ window.cordova = require('cordova');
 // file: lib/scripts/bootstrap.js
 
 (function (context) {
+    var channel = require('cordova/channel');
+    var platformInitChannelsArray = [channel.onNativeReady, channel.onPluginsReady];
+
+    function logUnfiredChannels(arr) {
+        for (var i = 0; i < arr.length; ++i) {
+            if (arr[i].state != 2) {
+                console.log('Channel not fired: ' + arr[i].type);
+            }
+        }
+    }
+
+    window.setTimeout(function() {
+        if (channel.onDeviceReady.state != 2) {
+            console.log('deviceready has not fired after 5 seconds.');
+            logUnfiredChannels(platformInitChannelsArray);
+            logUnfiredChannels(channel.deviceReadyChannelsArray);
+        }
+    }, 5000);
+
     // 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.
     function replaceNavigator(origNavigator) {
@@ -6288,8 +6271,6 @@ window.cordova = require('cordova');
         context.navigator = replaceNavigator(context.navigator);
     }
 
-    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.
@@ -6298,32 +6279,33 @@ window.cordova = require('cordova');
     }
 
     /**
-     * Create all cordova objects once page has fully loaded and native side is ready.
+     * Create all cordova objects once 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();
+        require('cordova/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.
+        // Fire onDeviceReady event once page has fully loaded, all
+        // constructors have run and cordova info has been received from native
+        // side.
+        // This join call is deliberately made after platform.initialize() in
+        // order that plugins may manipulate channel.deviceReadyChannelsArray
+        // if necessary.
         channel.join(function() {
             require('cordova').fireDocumentEvent('deviceready');
         }, channel.deviceReadyChannelsArray);
 
-    }, [ channel.onDOMContentLoaded, channel.onNativeReady, channel.onPluginsReady ]);
+    }, platformInitChannelsArray);
 
 }(window));
 
+// file: lib/scripts/bootstrap-ios.js
+
+require('cordova/channel').onNativeReady.fire();
+
 // file: lib/scripts/plugin_loader.js
 
 // Tries to load all plugins' js-modules.
@@ -6399,31 +6381,27 @@ window.cordova = require('cordova');
         }
     }
 
+
     // Try to XHR the cordova_plugins.json file asynchronously.
-    try { // we commented we were going to try, so let us actually try and catch 
+    try { // we commented we were going to try, so let us actually try and catch
         var xhr = new context.XMLHttpRequest();
-        xhr.onreadystatechange = function() {
-            if (this.readyState != 4) { // not DONE
-                return;
-            }
-
+        xhr.onload = function() {
             // 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();
-                }
+            var obj = JSON.parse(this.responseText);
+            if (obj && obj instanceof Array && obj.length > 0) {
+                handlePluginsObject(obj);
             } else {
                 finishPluginLoading();
             }
         };
+        xhr.onerror = function() {
+            finishPluginLoading();
+        };
         xhr.open('GET', 'cordova_plugins.json', true); // Async
         xhr.send();
     }
-    catch(err) {
+    catch(err){
         finishPluginLoading();
     }
 }(window));

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m b/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
index 172b62e..21576a3 100644
--- a/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
+++ b/lib/cordova-ios/CordovaLibTests/CDVWhitelistTests.m
@@ -149,9 +149,10 @@
     CDVWhitelist* whitelist = [[CDVWhitelist alloc] initWithArray:allowedHosts];
 
     STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://apache.org"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://build.apache.prg"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://MyDangerousSite.org"]], nil);
-    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://apache.org.SuspiciousSite.com"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"https://build.apache.prg"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"ftp://MyDangerousSite.org"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"ftps://apache.org.SuspiciousSite.com"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"gopher://apache.org"]], nil);
 }
 
 - (void)testWildcardInHostname
@@ -274,4 +275,19 @@
     STAssertTrue([expectedErrorString isEqualToString:errorString], @"Customized whitelist rejection string has unexpected value.");
 }
 
+- (void)testSpecificProtocol
+{
+    NSArray* allowedHosts = [NSArray arrayWithObjects:
+        @"http://www.apache.org",
+        @"cordova://www.google.com",
+        nil];
+
+    CDVWhitelist* whitelist = [[CDVWhitelist alloc] initWithArray:allowedHosts];
+
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"http://www.apache.org"]], nil);
+    STAssertTrue([whitelist URLIsAllowed:[NSURL URLWithString:@"cordova://www.google.com"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"cordova://www.apache.org"]], nil);
+    STAssertFalse([whitelist URLIsAllowed:[NSURL URLWithString:@"http://www.google.com"]], nil);
+}
+
 @end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml b/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
index 5610e5e..f8a2fad 100644
--- a/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
+++ b/lib/cordova-ios/CordovaLibTests/CordovaLibApp/config.xml
@@ -25,7 +25,6 @@
         <plugin name="Camera" value="CDVCamera" />
         <plugin name="NetworkStatus" value="CDVConnection" />
         <plugin name="Contacts" value="CDVContacts" />
-        <plugin name="Debug Console" value="CDVDebugConsole" />
         <plugin name="File" value="CDVFile" />
         <plugin name="FileTransfer" value="CDVFileTransfer" />
         <plugin name="Geolocation" value="CDVLocation" />

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/RELEASENOTES.md
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/RELEASENOTES.md b/lib/cordova-ios/RELEASENOTES.md
index aa501b0..ffe0c0b 100644
--- a/lib/cordova-ios/RELEASENOTES.md
+++ b/lib/cordova-ios/RELEASENOTES.md
@@ -22,6 +22,45 @@
  
  Cordova is a static library that enables developers to include the Cordova API in their iOS application projects easily, and also create new Cordova-based iOS application projects through the command-line.
 
+### 2.7.0 (201304XX) ###
+
+* Fix NPE in InAppBrowser's error callback.
+* [CB-2849] Fix bin/create when CordovaLib parent dir has a space
+* [CB-3069] Fix InAppBrowser load events (for non-redirecting pages)
+* InAppBrowser: Don't inject iframe bridge until necessary.
+* Fix FileTransfer unit test. HTTP Method was being set to null.
+* [CB-2305] Add InAppBrowser injectSriptCode command to support InAppBrowser.executeScript and InAppBrowser.insertCSS APIs
+* [CB-2653] Simplify InAppBrowser.injectScriptCode.
+* [CB-2537] Implement streaming downloads for FileTransfer
+* [CB-2190] Allow FileTransfer uploads to continue in background
+* [CB-1518] Request content length in parallel with download for gzipped content
+* [CB-2653] Delay executeScript/insertCSS callback until resources have loaded; pass JS results to callback
+* [CB-2824] Remove DebugConsole plugin
+* [CB-3066] Fire onNativeReady from JS, as bridge is available immediately
+* [CB-2725] Fix www deploy issues with symlinks
+* [CB-2725] follow links in www copy script
+* [CB-3039] iOS Exif date length mismtach
+* [CB-3052] iOS Exif SubIFD offsets incorrect
+* [CB-51] Added httpMethod for file transfer options (defaults to POST)
+* [CB-2732] Only set camera device when allowed.
+* [CB-2911] Updated resolveLocalFileSystemURI.
+* [CB-3032] Add whitelist support for custom schemes.
+* [CB-3048] Add --arc flag to create script, support arc in template.
+* [CB-3067]: fixing ios5 whitelist of file url
+* [CB-3067] Revert CDVURLProtocol to not whitelist file urls
+* [CB-2788] add ./bin/check_reqs script to iOS
+* [CB-2587] Added plugin timing for plugins that are loaded on startup (plugin 'onload' attribute)
+* [CB-2848] ShowSplashScreenSpinner not used
+* [CB-2960] Changing the volume of a sound already playing
+* [CB-3021] Can no longer import CDVPlugin.h from plugin Objective-C++ code
+* [CB-2790] added splice function to header writer: accepts jpeg as NSData, and splices in exif data specified by a string
+* [CB-2790] removed old splice code, replaced with JpegHeaderWriter api calls
+* [CB-2896] split writing of working tags off here, multipart tags not supported
+* [CB-2896] fixed error in exif subifd offset calculation for tag 8769
+* [CB-2902] re-added long/short tags to template dict, fixed subExifIFD offset
+
+<br />
+
 ### 2.6.0 (20130401) ###
 
 * [CB-2732] Only set camera device when allowed.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/bin/check_reqs
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/bin/check_reqs b/lib/cordova-ios/bin/check_reqs
new file mode 100755
index 0000000..27d00c3
--- /dev/null
+++ b/lib/cordova-ios/bin/check_reqs
@@ -0,0 +1,34 @@
+#! /bin/sh
+
+#
+# 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.
+#
+
+XCODEBUILD_LOCATION=$(which xcodebuild)
+if [ $? != 0 ]; then
+	echo "Xcode is (probably) not installed, specifically the command 'xcodebuild' is unavailable."
+	exit 1
+fi
+
+XCODEBUILD_MIN_VERSION="4.5"
+XCODEBUILD_VERSION=$(xcodebuild -version | head -n 1 | sed -e 's/Xcode //')
+
+if [[ "$XCODEBUILD_VERSION" < "$XCODEBUILD_MIN_VERSION" ]]; then
+	echo "Cordova can only run in Xcode version $XCODEBUILD_MIN_VERSION or greater."
+	exit 1
+fi

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/bin/create
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/bin/create b/lib/cordova-ios/bin/create
index bef0f57..d617f09 100755
--- a/lib/cordova-ios/bin/create
+++ b/lib/cordova-ios/bin/create
@@ -31,8 +31,9 @@
 set -e
 
 function usage() {
-  echo "Usage: $0 [--shared] <path_to_new_project> <package_name> <project_name>"
+  echo "Usage: $0 [--shared] [--arc] <path_to_new_project> <package_name> <project_name>"
   echo "	--shared (optional): Link directly against the shared copy of the CordovaLib instead of a copy of it."
+  echo "	--arc (optional): Enable ARC."
   echo "	<path_to_new_project>: Path to your new Cordova iOS project"
   echo "	<package_name>: Package name, following reverse-domain style convention"
   echo "	<project_name>: Project name"
@@ -40,10 +41,15 @@ function usage() {
 }
 
 USE_SHARED=0
+USE_ARC=0
 if [[ $1 == "--shared" ]]; then
     USE_SHARED=1
     shift;
 fi
+if [[ $1 == "--arc" ]]; then
+    USE_ARC=1
+    shift;
+fi
 
 # check whether it is a proper create command (at least 3 arguments)
 if [ $# -lt 3 ]; then
@@ -59,7 +65,7 @@ done
 
 BINDIR=$( cd "$( dirname "$SCRIPT" )" && pwd )
 CORDOVALIB_DIR="$BINDIR/../CordovaLib"
-CDV_VER=$(cat $CORDOVALIB_DIR/VERSION)
+CDV_VER=$(cat "$CORDOVALIB_DIR/VERSION")
 
 PROJECT_PATH=$1
 PACKAGE=$2
@@ -134,3 +140,6 @@ else
     "$BINDIR/update_cordova_subproject" "$R.xcodeproj/project.pbxproj" "$PROJECT_PATH/CordovaLib/CordovaLib.xcodeproj/project.pbxproj" > /dev/null
 fi
 
+if [[ $USE_ARC = 1 ]]; then
+    /usr/bin/sed -i '' 's/CLANG_ENABLE_OBJC_ARC = NO/CLANG_ENABLE_OBJC_ARC = YES/' "$R.xcodeproj/project.pbxproj" > /dev/null
+fi

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d9a153c4/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj b/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
index f5ed342..0af952e 100755
--- a/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
+++ b/lib/cordova-ios/bin/templates/project/__TESTING__.xcodeproj/project.pbxproj
@@ -404,7 +404,7 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
-			shellScript = "SRC_DIR=\"www\"\nDST_DIR=\"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/www\"\nCOPY_HIDDEN=\nORIG_IFS=$IFS\nIFS=$(echo -en \"\\n\\b\")\n\nif [[ ! -e \"$SRC_DIR\" ]]; then\n  echo \"Path does not exist: $SRC_DIR\"\n  exit 1\nfi\n\nif [[ -n $COPY_HIDDEN ]]; then\n  alias do_find='find \"$SRC_DIR\"'\nelse\n  alias do_find='find \"$SRC_DIR\" -name \".*\" -prune -o'\nfi\n\ntime (\n# Code signing files must be removed or else there are\n# resource signing errors.\nrm -rf \"$DST_DIR\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/_CodeSignature\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/PkgInfo\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/embedded.mobileprovision\"\n\n# Directories\nfor p in $(do_find -type d -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  mkdir \"$DST_DIR$subpath\" || exit 1\ndone\n\n# Symlinks\nfor p in $(do_find -type l -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 2\ndone\n\n# Fi
 les\nfor p in $(do_find -type f -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  if ! ln \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" 2>/dev/null; then\n    rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 3\n  fi\ndone\n\n)\nIFS=$ORIG_IFS";
+			shellScript = "SRC_DIR=\"www/\"\nDST_DIR=\"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/www\"\nCOPY_HIDDEN=\nORIG_IFS=$IFS\nIFS=$(echo -en \"\\n\\b\")\n\nif [[ ! -e \"$SRC_DIR\" ]]; then\n  echo \"Path does not exist: $SRC_DIR\"\n  exit 1\nfi\n\nif [[ -n $COPY_HIDDEN ]]; then\n  alias do_find='find \"$SRC_DIR\"'\nelse\n  alias do_find='find -L \"$SRC_DIR\" -name \".*\" -prune -o'\nfi\n\ntime (\n# Code signing files must be removed or else there are\n# resource signing errors.\nrm -rf \"$DST_DIR\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/_CodeSignature\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/PkgInfo\" \\\n       \"$BUILT_PRODUCTS_DIR/$FULL_PRODUCT_NAME/embedded.mobileprovision\"\n\n# Directories\nfor p in $(do_find -type d -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  mkdir \"$DST_DIR$subpath\" || exit 1\ndone\n\n# Symlinks\nfor p in $(do_find -type l -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  source=$(readlink $SRC_DIR$subpath)\n  sourcetype=$(stat -f \"%HT%SY\"
  $source)\n  if [ \"$sourcetype\" = \"Directory\" ]; then\n    mkdir \"$DST_DIR$subpath\" || exit 2\n  else\n    rsync -a \"$source\" \"$DST_DIR$subpath\" || exit 3\n  fi\ndone\n\n# Files\nfor p in $(do_find -type f -print); do\n  subpath=\"${p#$SRC_DIR}\"\n  if ! ln \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" 2>/dev/null; then\n    rsync -a \"$SRC_DIR$subpath\" \"$DST_DIR$subpath\" || exit 4\n  fi\ndone\n\n)\nIFS=$ORIG_IFS";
 		};
 /* End PBXShellScriptBuildPhase section */
 


Mime
View raw message