You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2013/01/22 02:57:59 UTC

[8/52] [partial] support for 2.4.0rc1. "vendored" the platform libs in. added Gord and Braden as contributors. removed dependency on unzip and axed the old download-cordova code.

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
new file mode 100644
index 0000000..ef91f5f
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVViewController.m
@@ -0,0 +1,952 @@
+/*
+ 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 <objc/message.h>
+#import "CDV.h"
+#import "CDVCommandQueue.h"
+#import "CDVCommandDelegateImpl.h"
+#import "CDVConfigParser.h"
+#import "CDVUserAgentUtil.h"
+
+#define degreesToRadian(x) (M_PI * (x) / 180.0)
+
+@interface CDVViewController ()
+
+@property (nonatomic, readwrite, strong) NSXMLParser* configParser;
+@property (nonatomic, readwrite, strong) NSDictionary* settings;
+@property (nonatomic, readwrite, strong) CDVWhitelist* whitelist;
+@property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects;
+@property (nonatomic, readwrite, strong) NSDictionary* pluginsMap;
+@property (nonatomic, readwrite, strong) NSArray* supportedOrientations;
+@property (nonatomic, readwrite, assign) BOOL loadFromString;
+
+@property (nonatomic, readwrite, strong) IBOutlet UIActivityIndicatorView* activityView;
+@property (nonatomic, readwrite, strong) UIImageView* imageView;
+@property (readwrite, assign) BOOL initialized;
+
+@property (atomic, strong) NSURL* openURL;
+
+@end
+
+@implementation CDVViewController
+
+@synthesize webView, supportedOrientations;
+@synthesize pluginObjects, pluginsMap, whitelist;
+@synthesize configParser, settings, loadFromString;
+@synthesize imageView, activityView, useSplashScreen;
+@synthesize wwwFolderName, startPage, initialized, openURL;
+@synthesize commandDelegate = _commandDelegate;
+@synthesize commandQueue = _commandQueue;
+
+- (void)__init
+{
+    if ((self != nil) && !self.initialized) {
+        _commandQueue = [[CDVCommandQueue alloc] initWithViewController:self];
+        _commandDelegate = [[CDVCommandDelegateImpl alloc] initWithViewController:self];
+        [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receivedOrientationChange)
+                                                     name:UIDeviceOrientationDidChangeNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillTerminate:)
+                                                     name:UIApplicationWillTerminateNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillResignActive:)
+                                                     name:UIApplicationWillResignActiveNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidBecomeActive:)
+                                                     name:UIApplicationDidBecomeActiveNotification object:nil];
+
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppWillEnterForeground:)
+                                                     name:UIApplicationWillEnterForegroundNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:)
+                                                     name:UIApplicationDidEnterBackgroundNotification object:nil];
+        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleOpenURL:) name:CDVPluginHandleOpenURLNotification object:nil];
+
+        // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist
+        self.supportedOrientations = [self parseInterfaceOrientations:
+            [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]];
+
+        [self printMultitaskingInfo];
+        [self printDeprecationNotice];
+        self.initialized = YES;
+
+        // load config.xml settings
+        [self loadSettings];
+    }
+}
+
+- (id)initWithNibName:(NSString*)nibNameOrNil bundle:(NSBundle*)nibBundleOrNil
+{
+    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
+    [self __init];
+    return self;
+}
+
+- (id)init
+{
+    self = [super init];
+    [self __init];
+    return self;
+}
+
+- (void)printDeprecationNotice
+{
+    if (!IsAtLeastiOSVersion(@"5.0")) {
+        NSLog(@"CRITICAL: For Cordova 2.0, you will need to upgrade to at least iOS 5.0 or greater. Your current version of iOS is %@.",
+            [[UIDevice currentDevice] systemVersion]
+            );
+    }
+}
+
+- (void)printMultitaskingInfo
+{
+    UIDevice* device = [UIDevice currentDevice];
+    BOOL backgroundSupported = NO;
+
+    if ([device respondsToSelector:@selector(isMultitaskingSupported)]) {
+        backgroundSupported = device.multitaskingSupported;
+    }
+
+    NSNumber* exitsOnSuspend = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIApplicationExitsOnSuspend"];
+    if (exitsOnSuspend == nil) { // if it's missing, it should be NO (i.e. multi-tasking on by default)
+        exitsOnSuspend = [NSNumber numberWithBool:NO];
+    }
+
+    NSLog(@"Multi-tasking -> Device: %@, App: %@", (backgroundSupported ? @"YES" : @"NO"), (![exitsOnSuspend intValue]) ? @"YES" : @"NO");
+}
+
+- (BOOL)URLisAllowed:(NSURL*)url
+{
+    if (self.whitelist == nil) {
+        return YES;
+    }
+
+    return [self.whitelist URLIsAllowed:url];
+}
+
+- (void)loadSettings
+{
+    CDVConfigParser* delegate = [[CDVConfigParser alloc] init];
+
+    // read from config.xml in the app bundle
+    NSString* path = [[NSBundle mainBundle] pathForResource:@"config" ofType:@"xml"];
+
+    if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
+        NSAssert(NO, @"ERROR: config.xml does not exist. Please run cordova-ios/bin/cordova_plist_to_config_xml path/to/project.");
+        return;
+    }
+
+    NSURL* url = [NSURL fileURLWithPath:path];
+
+    configParser = [[NSXMLParser alloc] initWithContentsOfURL:url];
+    if (configParser == nil) {
+        NSLog(@"Failed to initialize XML parser.");
+        return;
+    }
+    [configParser setDelegate:((id < NSXMLParserDelegate >)delegate)];
+    [configParser parse];
+
+    // Get the plugin dictionary, whitelist and settings from the delegate.
+    self.pluginsMap = [delegate.pluginsDict dictionaryWithLowercaseKeys];
+    self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts];
+    self.settings = delegate.settings;
+
+    // And the start folder/page.
+    self.wwwFolderName = @"www";
+    self.startPage = [delegate getStartPage];
+
+    // Initialize the plugin objects dict.
+    self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:4];
+}
+
+// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+
+    NSURL* appURL = nil;
+    NSString* loadErr = nil;
+
+    if ([self.startPage rangeOfString:@"://"].location != NSNotFound) {
+        appURL = [NSURL URLWithString:self.startPage];
+    } else if ([self.wwwFolderName rangeOfString:@"://"].location != NSNotFound) {
+        appURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@/%@", self.wwwFolderName, self.startPage]];
+    } else {
+        NSString* startFilePath = [self.commandDelegate pathForResource:self.startPage];
+        if (startFilePath == nil) {
+            loadErr = [NSString stringWithFormat:@"ERROR: Start Page at '%@/%@' was not found.", self.wwwFolderName, self.startPage];
+            NSLog(@"%@", loadErr);
+            self.loadFromString = YES;
+            appURL = nil;
+        } else {
+            appURL = [NSURL fileURLWithPath:startFilePath];
+        }
+    }
+
+    // // Fix the iOS 5.1 SECURITY_ERR bug (CB-347), this must be before the webView is instantiated ////
+
+    NSString* backupWebStorageType = @"cloud"; // default value
+
+    id backupWebStorage = [self.settings objectForKey:@"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";
+    }
+
+    if (IsAtLeastiOSVersion(@"5.1")) {
+        [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType];
+    }
+
+    // // Instantiate the WebView ///////////////
+
+    [self createGapView];
+
+    // /////////////////
+
+    NSNumber* enableLocation = [self.settings objectForKey:@"EnableLocation"];
+    NSString* enableViewportScale = [self.settings objectForKey:@"EnableViewportScale"];
+    NSNumber* allowInlineMediaPlayback = [self.settings objectForKey:@"AllowInlineMediaPlayback"];
+    BOOL mediaPlaybackRequiresUserAction = YES;  // default value
+    if ([self.settings objectForKey:@"MediaPlaybackRequiresUserAction"]) {
+        mediaPlaybackRequiresUserAction = [(NSNumber*)[settings objectForKey:@"MediaPlaybackRequiresUserAction"] boolValue];
+    }
+
+    self.webView.scalesPageToFit = [enableViewportScale boolValue];
+
+    /*
+     * Fire up the GPS Service right away as it takes a moment for data to come back.
+     */
+
+    if ([enableLocation boolValue]) {
+        [[self.commandDelegate getCommandInstance:@"Geolocation"] getLocation:[CDVInvokedUrlCommand new]];
+    }
+
+    /*
+     * 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 settings:[NSDictionary dictionaryWithObjectsAndKeys:
+                    @"backupType", backupWebStorageType, nil]] withClassName:NSStringFromClass([CDVLocalStorage class])];
+    }
+
+    /*
+     * This is for iOS 4.x, where you can allow inline <video> and <audio>, and also autoplay them
+     */
+    if ([allowInlineMediaPlayback boolValue] && [self.webView respondsToSelector:@selector(allowsInlineMediaPlayback)]) {
+        self.webView.allowsInlineMediaPlayback = YES;
+    }
+    if ((mediaPlaybackRequiresUserAction == NO) && [self.webView respondsToSelector:@selector(mediaPlaybackRequiresUserAction)]) {
+        self.webView.mediaPlaybackRequiresUserAction = NO;
+    }
+
+    // UIWebViewBounce property - defaults to true
+    NSNumber* bouncePreference = [self.settings objectForKey:@"UIWebViewBounce"];
+    BOOL bounceAllowed = (bouncePreference == nil || [bouncePreference boolValue]);
+
+    // prevent webView from bouncing
+    // based on UIWebViewBounce key in config.xml
+    if (!bounceAllowed) {
+        if ([self.webView respondsToSelector:@selector(scrollView)]) {
+            ((UIScrollView*)[self.webView scrollView]).bounces = NO;
+        } else {
+            for (id subview in self.webView.subviews) {
+                if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
+                    ((UIScrollView*)subview).bounces = NO;
+                }
+            }
+        }
+    }
+
+    /*
+     * iOS 6.0 UIWebView properties
+     */
+    if (IsAtLeastiOSVersion(@"6.0")) {
+        BOOL keyboardDisplayRequiresUserAction = YES; // KeyboardDisplayRequiresUserAction - defaults to YES
+        if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] != nil) {
+            if ([self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"]) {
+                keyboardDisplayRequiresUserAction = [(NSNumber*)[self.settings objectForKey:@"KeyboardDisplayRequiresUserAction"] boolValue];
+            }
+        }
+
+        // property check for compiling under iOS < 6
+        if ([self.webView respondsToSelector:@selector(setKeyboardDisplayRequiresUserAction:)]) {
+            [self.webView setValue:[NSNumber numberWithBool:keyboardDisplayRequiresUserAction] forKey:@"keyboardDisplayRequiresUserAction"];
+        }
+
+        BOOL suppressesIncrementalRendering = NO; // SuppressesIncrementalRendering - defaults to NO
+        if ([self.settings objectForKey:@"SuppressesIncrementalRendering"] != nil) {
+            if ([self.settings objectForKey:@"SuppressesIncrementalRendering"]) {
+                suppressesIncrementalRendering = [(NSNumber*)[self.settings objectForKey:@"SuppressesIncrementalRendering"] boolValue];
+            }
+        }
+
+        // property check for compiling under iOS < 6
+        if ([self.webView respondsToSelector:@selector(setSuppressesIncrementalRendering:)]) {
+            [self.webView setValue:[NSNumber numberWithBool:suppressesIncrementalRendering] forKey:@"suppressesIncrementalRendering"];
+        }
+    }
+
+    // /////////////////
+
+    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];
+    }
+}
+
+- (NSArray*)parseInterfaceOrientations:(NSArray*)orientations
+{
+    NSMutableArray* result = [[NSMutableArray alloc] init];
+
+    if (orientations != nil) {
+        NSEnumerator* enumerator = [orientations objectEnumerator];
+        NSString* orientationString;
+
+        while (orientationString = [enumerator nextObject]) {
+            if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) {
+                [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
+            } else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) {
+                [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]];
+            } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) {
+                [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]];
+            } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) {
+                [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]];
+            }
+        }
+    }
+
+    // default
+    if ([result count] == 0) {
+        [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]];
+    }
+
+    return result;
+}
+
+- (NSInteger)mapIosOrientationToJsOrientation:(UIInterfaceOrientation)orientation
+{
+    switch (orientation) {
+        case UIInterfaceOrientationPortraitUpsideDown:
+            return 180;
+
+        case UIInterfaceOrientationLandscapeLeft:
+            return -90;
+
+        case UIInterfaceOrientationLandscapeRight:
+            return 90;
+
+        case UIInterfaceOrientationPortrait:
+            return 0;
+
+        default:
+            return 0;
+    }
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+    // First, ask the webview via JS if it supports the new orientation
+    NSString* jsCall = [NSString stringWithFormat:
+        @"window.shouldRotateToOrientation && window.shouldRotateToOrientation(%d);"
+        , [self mapIosOrientationToJsOrientation:interfaceOrientation]];
+    NSString* res = [webView stringByEvaluatingJavaScriptFromString:jsCall];
+
+    if ([res length] > 0) {
+        return [res boolValue];
+    }
+
+    // if js did not handle the new orientation (no return value), use values from the plist (via supportedOrientations)
+    return [self supportsOrientation:interfaceOrientation];
+}
+
+- (BOOL)shouldAutorotate
+{
+    return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+    NSUInteger ret = 0;
+
+    if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortrait]) {
+        ret = ret | (1 << UIInterfaceOrientationPortrait);
+    }
+    if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
+        ret = ret | (1 << UIInterfaceOrientationPortraitUpsideDown);
+    }
+    if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeRight]) {
+        ret = ret | (1 << UIInterfaceOrientationLandscapeRight);
+    }
+    if ([self shouldAutorotateToInterfaceOrientation:UIInterfaceOrientationLandscapeLeft]) {
+        ret = ret | (1 << UIInterfaceOrientationLandscapeLeft);
+    }
+
+    return ret;
+}
+
+- (BOOL)supportsOrientation:(UIInterfaceOrientation)orientation
+{
+    return [self.supportedOrientations containsObject:[NSNumber numberWithInt:orientation]];
+}
+
+- (UIWebView*)newCordovaViewWithFrame:(CGRect)bounds
+{
+    return [[UIWebView alloc] initWithFrame:bounds];
+}
+
+- (NSString*)userAgent
+{
+    if (_userAgent == nil) {
+        NSString* originalUserAgent = [CDVUserAgentUtil originalUserAgent];
+        // Use our address as a unique number to append to the User-Agent.
+        _userAgent = [NSString stringWithFormat:@"%@ (%lld)", originalUserAgent, (long long)self];
+    }
+    return _userAgent;
+}
+
+- (void)createGapView
+{
+    CGRect webViewBounds = self.view.bounds;
+
+    webViewBounds.origin = self.view.bounds.origin;
+
+    if (!self.webView) {
+        [CDVUserAgentUtil setUserAgent:self.userAgent];
+
+        self.webView = [self newCordovaViewWithFrame:webViewBounds];
+        self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+
+        [self.view addSubview:self.webView];
+        [self.view sendSubviewToBack:self.webView];
+
+        self.webView.delegate = self;
+
+        // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set
+        [CDVURLProtocol registerViewController:self];
+    }
+}
+
+- (void)didReceiveMemoryWarning
+{
+    // iterate through all the plugin objects, and call hasPendingOperation
+    // if at least one has a pending operation, we don't call [super didReceiveMemoryWarning]
+
+    NSEnumerator* enumerator = [self.pluginObjects objectEnumerator];
+    CDVPlugin* plugin;
+
+    BOOL doPurge = YES;
+
+    while ((plugin = [enumerator nextObject])) {
+        if (plugin.hasPendingOperation) {
+            NSLog(@"Plugin '%@' has a pending operation, memory purge is delayed for didReceiveMemoryWarning.", NSStringFromClass([plugin class]));
+            doPurge = NO;
+        }
+    }
+
+    if (doPurge) {
+        // Releases the view if it doesn't have a superview.
+        [super didReceiveMemoryWarning];
+    }
+
+    // Release any cached data, images, etc. that aren't in use.
+}
+
+- (void)viewDidUnload
+{
+    // Release any retained subviews of the main view.
+    // e.g. self.myOutlet = nil;
+
+    self.webView.delegate = nil;
+    self.webView = nil;
+}
+
+#pragma mark UIWebViewDelegate
+
+/**
+ When web application loads Add stuff to the DOM, mainly the user-defined settings from the Settings.plist file, and
+ the device's data such as device ID, platform version, etc.
+ */
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+    [_commandQueue resetRequestId];
+    [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:nil]];
+}
+
+/**
+ Called when the webview finishes loading.  This stops the activity view and closes the imageview
+ */
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+    /*
+     * Hide the Top Activity THROBBER in the Battery Bar
+     */
+    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
+
+    id autoHideSplashScreenValue = [self.settings objectForKey:@"AutoHideSplashScreen"];
+    // if value is missing, default to yes
+    if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) {
+        self.imageView.hidden = YES;
+        self.activityView.hidden = YES;
+        [self.view.superview bringSubviewToFront:self.webView];
+    }
+    [self didRotateFromInterfaceOrientation:(UIInterfaceOrientation)[[UIDevice currentDevice] orientation]];
+
+    // 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];
+
+    [self processOpenUrl];
+}
+
+- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error
+{
+    NSLog(@"Failed to load webpage with error: %@", [error localizedDescription]);
+
+    /*
+     if ([error code] != NSURLErrorCancelled)
+     alert([error localizedDescription]);
+     */
+}
+
+- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
+{
+    NSURL* url = [request URL];
+
+    /*
+     * Execute any commands queued with cordova.exec() on the JS side.
+     * The part of the URL after gap:// is irrelevant.
+     */
+    if ([[url scheme] isEqualToString:@"gap"]) {
+        [_commandQueue fetchCommandsFromJs];
+        return NO;
+    }
+
+    /*
+     * If a URL is being loaded that's a file/http/https URL, just load it internally
+     */
+    else if ([url isFileURL]) {
+        return YES;
+    }
+
+    /*
+     *    If we loaded the HTML from a string, we let the app handle it
+     */
+    else if (self.loadFromString == YES) {
+        self.loadFromString = NO;
+        return YES;
+    }
+
+    /*
+     * all tel: scheme urls we let the UIWebview handle it using the default behavior
+     */
+    else if ([[url scheme] isEqualToString:@"tel"]) {
+        return YES;
+    }
+
+    /*
+     * all about: scheme urls are not handled
+     */
+    else if ([[url scheme] isEqualToString:@"about"]) {
+        return NO;
+    }
+
+    /*
+     * all data: scheme urls are handled
+     */
+    else if ([[url scheme] isEqualToString:@"data"]) {
+        return YES;
+    }
+
+    /*
+     * Handle all other types of urls (tel:, sms:), and requests to load a url in the main webview.
+     */
+    else {
+        // BOOL isIFrame = ([theWebView.request.mainDocumentURL absoluteString] == nil);
+
+        if ([self.whitelist schemeIsAllowed:[url scheme]]) {
+            return [self.whitelist URLIsAllowed:url];
+        } else {
+            if ([[UIApplication sharedApplication] canOpenURL:url]) {
+                [[UIApplication sharedApplication] openURL:url];
+            } else { // handle any custom schemes to plugins
+                [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
+            }
+        }
+
+        return NO;
+    }
+
+    return YES;
+}
+
+#pragma mark GapHelpers
+
+- (void)javascriptAlert:(NSString*)text
+{
+    NSString* jsString = [NSString stringWithFormat:@"alert('%@');", text];
+
+    [self.commandDelegate evalJs:jsString];
+}
+
++ (NSString*)resolveImageResource:(NSString*)resource
+{
+    NSString* systemVersion = [[UIDevice currentDevice] systemVersion];
+    BOOL isLessThaniOS4 = ([systemVersion compare:@"4.0" options:NSNumericSearch] == NSOrderedAscending);
+
+    // the iPad image (nor retina) differentiation code was not in 3.x, and we have to explicitly set the path
+    if (isLessThaniOS4) {
+        if (CDV_IsIPad()) {
+            return [NSString stringWithFormat:@"%@~ipad.png", resource];
+        } else {
+            return [NSString stringWithFormat:@"%@.png", resource];
+        }
+    }
+
+    return resource;
+}
+
++ (NSString*)applicationDocumentsDirectory
+{
+    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+    NSString* basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
+
+    return basePath;
+}
+
+- (void)showSplashScreen
+{
+    CGRect screenBounds = [[UIScreen mainScreen] bounds];
+    NSString* launchImageFile = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UILaunchImageFile"];
+
+    if (launchImageFile == nil) { // fallback if no launch image was specified
+        if (CDV_IsIPhone5()) {
+            // iPhone 5 or iPod Touch 6th-gen
+            launchImageFile = @"Default-568h";
+        } else {
+            launchImageFile = @"Default";
+        }
+    }
+
+    NSString* orientedLaunchImageFile = nil;
+    CGAffineTransform startupImageTransform = CGAffineTransformIdentity;
+    UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
+    CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
+    UIInterfaceOrientation statusBarOrientation = [UIApplication sharedApplication].statusBarOrientation;
+    UIImage* launchImage = nil;
+
+    // default to center of screen as in the original implementation. This will produce the 20px jump
+    CGPoint center = CGPointMake((screenBounds.size.width / 2), (screenBounds.size.height / 2));
+
+    if (CDV_IsIPad()) {
+        if (!UIDeviceOrientationIsValidInterfaceOrientation(deviceOrientation)) {
+            deviceOrientation = (UIDeviceOrientation)statusBarOrientation;
+        }
+
+        switch (deviceOrientation) {
+            case UIDeviceOrientationLandscapeLeft: // this is where the home button is on the right (yeah, I know, confusing)
+                {
+                    orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Landscape", launchImageFile];
+                    startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(90));
+                    center.x -= MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
+                }
+                break;
+
+            case UIDeviceOrientationLandscapeRight: // this is where the home button is on the left (yeah, I know, confusing)
+                {
+                    orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Landscape", launchImageFile];
+                    startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(-90));
+                    center.x += MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
+                }
+                break;
+
+            case UIDeviceOrientationPortraitUpsideDown:
+                {
+                    orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Portrait", launchImageFile];
+                    startupImageTransform = CGAffineTransformMakeRotation(degreesToRadian(180));
+                    center.y -= MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
+                }
+                break;
+
+            case UIDeviceOrientationPortrait:
+            default:
+                {
+                    orientedLaunchImageFile = [NSString stringWithFormat:@"%@-Portrait", launchImageFile];
+                    startupImageTransform = CGAffineTransformIdentity;
+                    center.y += MIN(statusBarFrame.size.width, statusBarFrame.size.height) / 2;
+                }
+                break;
+        }
+    } else { // not iPad
+        orientedLaunchImageFile = launchImageFile;
+    }
+
+    launchImage = [UIImage imageNamed:[[self class] resolveImageResource:orientedLaunchImageFile]];
+    if (launchImage == nil) {
+        NSLog(@"WARNING: Splash-screen image '%@' was not found. Orientation: %d, iPad: %d", orientedLaunchImageFile, deviceOrientation, CDV_IsIPad());
+    }
+
+    self.imageView = [[UIImageView alloc] initWithImage:launchImage];
+    self.imageView.tag = 1;
+    self.imageView.center = center;
+
+    self.imageView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin);
+    [self.imageView setTransform:startupImageTransform];
+    [self.view.superview addSubview:self.imageView];
+
+    /*
+     * The Activity View is the top spinning throbber in the status/battery bar. We init it with the default Grey Style.
+     *
+     *     whiteLarge = UIActivityIndicatorViewStyleWhiteLarge
+     *     white      = UIActivityIndicatorViewStyleWhite
+     *     gray       = UIActivityIndicatorViewStyleGray
+     *
+     */
+    NSString* topActivityIndicator = [self.settings objectForKey:@"TopActivityIndicator"];
+    UIActivityIndicatorViewStyle topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+
+    if ([topActivityIndicator isEqualToString:@"whiteLarge"]) {
+        topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhiteLarge;
+    } else if ([topActivityIndicator isEqualToString:@"white"]) {
+        topActivityIndicatorStyle = UIActivityIndicatorViewStyleWhite;
+    } else if ([topActivityIndicator isEqualToString:@"gray"]) {
+        topActivityIndicatorStyle = UIActivityIndicatorViewStyleGray;
+    }
+
+    self.activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle];
+    self.activityView.tag = 2;
+
+    id showSplashScreenSpinnerValue = [self.settings objectForKey:@"ShowSplashScreenSpinner"];
+    // backwards compatibility - if key is missing, default to true
+    if ((showSplashScreenSpinnerValue == nil) || [showSplashScreenSpinnerValue boolValue]) {
+        [self.view.superview addSubview:self.activityView];
+    }
+
+    self.activityView.center = self.view.center;
+    [self.activityView startAnimating];
+
+    [self.view.superview layoutSubviews];
+}
+
+BOOL gSplashScreenShown = NO;
+- (void)receivedOrientationChange
+{
+    if (self.imageView == nil) {
+        gSplashScreenShown = YES;
+        if (self.useSplashScreen) {
+            [self showSplashScreen];
+        }
+    }
+}
+
+#pragma mark CordovaCommands
+
+- (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className
+{
+    if ([plugin respondsToSelector:@selector(setViewController:)]) {
+        [plugin setViewController:self];
+    }
+
+    if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) {
+        [plugin setCommandDelegate:_commandDelegate];
+    }
+
+    [self.pluginObjects setObject:plugin forKey:className];
+}
+
+/**
+ Returns an instance of a CordovaCommand object, based on its name.  If one exists already, it is returned.
+ */
+- (id)getCommandInstance:(NSString*)pluginName
+{
+    // first, we try to find the pluginName in the pluginsMap
+    // (acts as a whitelist as well) if it does not exist, we return nil
+    // NOTE: plugin names are matched as lowercase to avoid problems - however, a
+    // possible issue is there can be duplicates possible if you had:
+    // "org.apache.cordova.Foo" and "org.apache.cordova.foo" - only the lower-cased entry will match
+    NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]];
+
+    if (className == nil) {
+        return nil;
+    }
+
+    id obj = [self.pluginObjects objectForKey:className];
+    if (!obj) {
+        // attempt to load the settings for this command class
+        NSDictionary* classSettings = [self.settings objectForKey:className];
+
+        if (classSettings) {
+            obj = [[NSClassFromString (className)alloc] initWithWebView:webView settings:classSettings];
+        } else {
+            obj = [[NSClassFromString (className)alloc] initWithWebView:webView];
+        }
+
+        if ((obj != nil) && [obj isKindOfClass:[CDVPlugin class]]) {
+            [self registerPlugin:obj withClassName:className];
+        } else {
+            NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName);
+        }
+    }
+    return obj;
+}
+
+#pragma mark -
+
+- (NSString*)appURLScheme
+{
+    NSString* URLScheme = nil;
+
+    NSArray* URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"];
+
+    if (URLTypes != nil) {
+        NSDictionary* dict = [URLTypes objectAtIndex:0];
+        if (dict != nil) {
+            NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"];
+            if (URLSchemes != nil) {
+                URLScheme = [URLSchemes objectAtIndex:0];
+            }
+        }
+    }
+
+    return URLScheme;
+}
+
+/**
+ Returns the contents of the named plist bundle, loaded as a dictionary object
+ */
++ (NSDictionary*)getBundlePlist:(NSString*)plistName
+{
+    NSString* errorDesc = nil;
+    NSPropertyListFormat format;
+    NSString* plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"];
+    NSData* plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
+    NSDictionary* temp = (NSDictionary*)[NSPropertyListSerialization
+        propertyListFromData:plistXML
+            mutabilityOption:NSPropertyListMutableContainersAndLeaves
+                      format:&format errorDescription:&errorDesc];
+
+    return temp;
+}
+
+#pragma mark -
+#pragma mark UIApplicationDelegate impl
+
+/*
+ This method lets your application know that it is about to be terminated and purged from memory entirely
+ */
+- (void)onAppWillTerminate:(NSNotification*)notification
+{
+    // empty the tmp directory
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing err = nil;
+
+    // clear contents of NSTemporaryDirectory
+    NSString* tempDirectoryPath = NSTemporaryDirectory();
+    NSDirectoryEnumerator* directoryEnumerator = [fileMgr enumeratorAtPath:tempDirectoryPath];
+    NSString* fileName = nil;
+    BOOL result;
+
+    while ((fileName = [directoryEnumerator nextObject])) {
+        NSString* filePath = [tempDirectoryPath stringByAppendingPathComponent:fileName];
+        result = [fileMgr removeItemAtPath:filePath error:&err];
+        if (!result && err) {
+            NSLog(@"Failed to delete: %@ (error: %@)", filePath, err);
+        }
+    }
+}
+
+/*
+ This method is called to let your application know that it is about to move from the active to inactive state.
+ You should use this method to pause ongoing tasks, disable timer, ...
+ */
+- (void)onAppWillResignActive:(NSNotification*)notification
+{
+    // NSLog(@"%@",@"applicationWillResignActive");
+    [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resign');" scheduledOnRunLoop:NO];
+}
+
+/*
+ In iOS 4.0 and later, this method is called as part of the transition from the background to the inactive state.
+ You can use this method to undo many of the changes you made to your application upon entering the background.
+ invariably followed by applicationDidBecomeActive
+ */
+- (void)onAppWillEnterForeground:(NSNotification*)notification
+{
+    // NSLog(@"%@",@"applicationWillEnterForeground");
+    [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('resume');"];
+}
+
+// This method is called to let your application know that it moved from the inactive to active state.
+- (void)onAppDidBecomeActive:(NSNotification*)notification
+{
+    // NSLog(@"%@",@"applicationDidBecomeActive");
+    [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('active');"];
+}
+
+/*
+ In iOS 4.0 and later, this method is called instead of the applicationWillTerminate: method
+ when the user quits an application that supports background execution.
+ */
+- (void)onAppDidEnterBackground:(NSNotification*)notification
+{
+    // NSLog(@"%@",@"applicationDidEnterBackground");
+    [self.commandDelegate evalJs:@"cordova.fireDocumentEvent('pause', null, true);" scheduledOnRunLoop:NO];
+}
+
+// ///////////////////////
+
+- (void)handleOpenURL:(NSNotification*)notification
+{
+    self.openURL = notification.object;
+}
+
+- (void)processOpenUrl
+{
+    if (self.openURL) {
+        // calls into javascript global function 'handleOpenURL'
+        NSString* jsString = [NSString stringWithFormat:@"handleOpenURL(\"%@\");", [self.openURL description]];
+        [self.webView stringByEvaluatingJavaScriptFromString:jsString];
+        self.openURL = nil;
+    }
+}
+
+// ///////////////////////
+
+- (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];
+
+    self.webView.delegate = nil;
+    self.webView = nil;
+    [_commandQueue dispose];
+    [[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/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
new file mode 100644
index 0000000..3741e94
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.h
@@ -0,0 +1,36 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+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;
+- (NSString*)errorStringForURL:(NSURL*)url;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/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
new file mode 100644
index 0000000..77e20ac
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVWhitelist.m
@@ -0,0 +1,192 @@
+/*
+ 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 "CDVWhitelist.h"
+
+NSString* const kCDVDefaultWhitelistRejectionString = @"ERROR whitelist rejection: url='%@'";
+
+@interface CDVWhitelist ()
+
+@property (nonatomic, readwrite, strong) NSArray* whitelist;
+@property (nonatomic, readwrite, strong) NSArray* expandedWhitelist;
+@property (nonatomic, readwrite, assign) BOOL allowAll;
+
+- (void)processWhitelist;
+
+@end
+
+@implementation CDVWhitelist
+
+@synthesize whitelist, expandedWhitelist, allowAll, whitelistRejectionFormatString;
+
+- (id)initWithArray:(NSArray*)array
+{
+    self = [super init];
+    if (self) {
+        self.whitelist = array;
+        self.expandedWhitelist = nil;
+        self.allowAll = NO;
+        self.whitelistRejectionFormatString = kCDVDefaultWhitelistRejectionString;
+        [self processWhitelist];
+    }
+
+    return self;
+}
+
+- (BOOL)isIPv4Address:(NSString*)externalHost
+{
+    // an IPv4 address has 4 octets b.b.b.b where b is a number between 0 and 255.
+    // for our purposes, b can also be the wildcard character '*'
+
+    // we could use a regex to solve this problem but then I would have two problems
+    // anyways, this is much clearer and maintainable
+    NSArray* octets = [externalHost componentsSeparatedByString:@"."];
+    NSUInteger num_octets = [octets count];
+
+    // quick check
+    if (num_octets != 4) {
+        return NO;
+    }
+
+    // restrict number parsing to 0-255
+    NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
+    [numberFormatter setMinimum:[NSNumber numberWithUnsignedInteger:0]];
+    [numberFormatter setMaximum:[NSNumber numberWithUnsignedInteger:255]];
+
+    // iterate through each octet, and test for a number between 0-255 or if it equals '*'
+    for (NSUInteger i = 0; i < num_octets; ++i) {
+        NSString* octet = [octets objectAtIndex:i];
+
+        if ([octet isEqualToString:@"*"]) { // passes - check next octet
+            continue;
+        } else if ([numberFormatter numberFromString:octet] == nil) { // fails - not a number and not within our range, return
+            return NO;
+        }
+    }
+
+    return YES;
+}
+
+- (NSString*)extractHostFromUrlString:(NSString*)url
+{
+    NSURL* aUrl = [NSURL URLWithString:url];
+
+    if ((aUrl != nil) && ([aUrl scheme] != nil)) { // found scheme
+        return [aUrl host];
+    } else {
+        return url;
+    }
+}
+
+- (void)processWhitelist
+{
+    if (self.whitelist == nil) {
+        NSLog(@"ERROR: CDVWhitelist was not initialized properly, all urls will be disallowed.");
+        return;
+    }
+
+    NSMutableArray* expanded = [NSMutableArray arrayWithCapacity:[self.whitelist count]];
+
+    // iterate through settings ExternalHosts, check for equality
+    NSEnumerator* enumerator = [self.whitelist objectEnumerator];
+    id externalHost = nil;
+
+    // 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];
+
+        // check for single wildcard '*', if found set allowAll to YES
+        if ([regex isEqualToString:@"*"]) {
+            self.allowAll = YES;
+            self.expandedWhitelist = [NSArray arrayWithObject:regex];
+            break;
+        }
+
+        // starts with wildcard match - we make the first '.' optional (so '*.org.apache.cordova' will match 'org.apache.cordova')
+        NSString* prefix = @"*.";
+        if ([regex 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
+        }
+
+        // ends with wildcard match for TLD
+        if (!is_ip && [regex hasSuffix:@".*"]) {
+            // replace * with tld_match
+            regex = [regex stringByReplacingCharactersInRange:NSMakeRange([regex length] - 1, 1) withString:tld_match];
+        }
+        // escape periods - since '.' means any character in regex
+        regex = [regex 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:@".*"];
+
+        [expanded addObject:regex];
+    }
+
+    self.expandedWhitelist = expanded;
+}
+
+- (BOOL)schemeIsAllowed:(NSString*)scheme
+{
+    return [scheme isEqualToString:@"http"] ||
+           [scheme isEqualToString:@"https"] ||
+           [scheme isEqualToString:@"ftp"] ||
+           [scheme isEqualToString:@"ftps"];
+}
+
+- (BOOL)URLIsAllowed:(NSURL*)url
+{
+    if (self.expandedWhitelist == nil) {
+        return NO;
+    }
+
+    if (self.allowAll) {
+        return YES;
+    }
+
+    // iterate through settings ExternalHosts, check for equality
+    NSEnumerator* enumerator = [self.expandedWhitelist objectEnumerator];
+    id regex = nil;
+    NSString* urlHost = [url host];
+
+    // if the url host IS found in the whitelist, load it in the app (however UIWebViewNavigationTypeOther kicks it out to Safari)
+    // if the url host IS NOT found in the whitelist, we do nothing
+    while (regex = [enumerator nextObject]) {
+        NSPredicate* regex_test = [NSPredicate predicateWithFormat:@"SELF MATCHES %@", regex];
+
+        if ([regex_test evaluateWithObject:urlHost] == YES) {
+            // if it matches at least one rule, return
+            return YES;
+        }
+    }
+
+    NSLog(@"%@", [self errorStringForURL:url]);
+    // if we got here, the url host is not in the white-list, do nothing
+    return NO;
+}
+
+- (NSString*)errorStringForURL:(NSURL*)url
+{
+    return [NSString stringWithFormat:self.whitelistRejectionFormatString, [url absoluteString]];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.h b/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.h
new file mode 100644
index 0000000..cd02b71
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.h
@@ -0,0 +1,26 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface NSArray (Comparisons)
+
+- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.m b/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.m
new file mode 100644
index 0000000..485e3c8
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSArray+Comparisons.m
@@ -0,0 +1,41 @@
+/*
+ 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 "NSArray+Comparisons.h"
+
+@implementation NSArray (Comparisons)
+
+- (id)objectAtIndex:(NSUInteger)index withDefault:(id)aDefault
+{
+    id obj = nil;
+
+    @try {
+        obj = [self objectAtIndex:index];
+        if ((obj == [NSNull null]) || (obj == nil)) {
+            return aDefault;
+        }
+    }
+    @catch(NSException* exception) {
+        NSLog(@"Exception - Name: %@ Reason: %@", [exception name], [exception reason]);
+    }
+
+    return obj;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.h b/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.h
new file mode 100644
index 0000000..ffe9c83
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.h
@@ -0,0 +1,33 @@
+//
+//  NSData+Base64.h
+//  base64
+//
+//  Created by Matt Gallagher on 2009/06/03.
+//  Copyright 2009 Matt Gallagher. All rights reserved.
+//
+//  Permission is given to use this source code file, free of charge, in any
+//  project, commercial or otherwise, entirely at your risk, with the condition
+//  that any redistribution (in part or whole) of source code must retain
+//  this copyright and permission notice. Attribution in compiled projects is
+//  appreciated but not required.
+//
+
+#import <Foundation/Foundation.h>
+
+void *CDVNewBase64Decode(
+    const char* inputBuffer,
+    size_t    length,
+    size_t    * outputLength);
+
+char *CDVNewBase64Encode(
+    const void* inputBuffer,
+    size_t    length,
+    bool      separateLines,
+    size_t    * outputLength);
+
+@interface NSData (CDVBase64)
+
++ (NSData*)dataFromBase64String:(NSString*)aString;
+- (NSString*)base64EncodedString;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/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
new file mode 100644
index 0000000..08c801b
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSData+Base64.m
@@ -0,0 +1,286 @@
+//
+//  NSData+Base64.m
+//  base64
+//
+//  Created by Matt Gallagher on 2009/06/03.
+//  Copyright 2009 Matt Gallagher. All rights reserved.
+//
+//  Permission is given to use this source code file, free of charge, in any
+//  project, commercial or otherwise, entirely at your risk, with the condition
+//  that any redistribution (in part or whole) of source code must retain
+//  this copyright and permission notice. Attribution in compiled projects is
+//  appreciated but not required.
+//
+
+#import "NSData+Base64.h"
+
+//
+// Mapping from 6 bit pattern to ASCII character.
+//
+static unsigned char cdvbase64EncodeLookup[65] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+//
+// Definition for "masked-out" areas of the base64DecodeLookup mapping
+//
+#define xx 65
+
+//
+// Mapping from ASCII character to 6 bit pattern.
+//
+static unsigned char cdvbase64DecodeLookup[256] =
+{
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, 62, xx, xx, xx, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, xx, xx, xx, xx, xx, xx,
+    xx, 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,  10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, xx, xx, xx, xx, xx,
+    xx, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+    xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx,
+};
+
+//
+// Fundamental sizes of the binary and base64 encode/decode units in bytes
+//
+#define CDV_BINARY_UNIT_SIZE 3
+#define CDV_BASE64_UNIT_SIZE 4
+
+//
+// NewBase64Decode
+//
+// Decodes the base64 ASCII string in the inputBuffer to a newly malloced
+// output buffer.
+//
+//  inputBuffer - the source ASCII string for the decode
+//	length - the length of the string or -1 (to specify strlen should be used)
+//	outputLength - if not-NULL, on output will contain the decoded length
+//
+// returns the decoded buffer. Must be freed by caller. Length is given by
+//	outputLength.
+//
+void *CDVNewBase64Decode(
+    const char* inputBuffer,
+    size_t    length,
+    size_t    * outputLength)
+{
+    if (length == -1) {
+        length = strlen(inputBuffer);
+    }
+
+    size_t outputBufferSize = (length / CDV_BASE64_UNIT_SIZE) * CDV_BINARY_UNIT_SIZE;
+    unsigned char* outputBuffer = (unsigned char*)malloc(outputBufferSize);
+
+    size_t i = 0;
+    size_t j = 0;
+
+    while (i < length) {
+        //
+        // Accumulate 4 valid characters (ignore everything else)
+        //
+        unsigned char accumulated[CDV_BASE64_UNIT_SIZE];
+        bzero(accumulated, sizeof(unsigned char) * CDV_BASE64_UNIT_SIZE);
+        size_t accumulateIndex = 0;
+
+        while (i < length) {
+            unsigned char decode = cdvbase64DecodeLookup[inputBuffer[i++]];
+            if (decode != xx) {
+                accumulated[accumulateIndex] = decode;
+                accumulateIndex++;
+
+                if (accumulateIndex == CDV_BASE64_UNIT_SIZE) {
+                    break;
+                }
+            }
+        }
+
+        //
+        // Store the 6 bits from each of the 4 characters as 3 bytes
+        //
+        outputBuffer[j] = (accumulated[0] << 2) | (accumulated[1] >> 4);
+        outputBuffer[j + 1] = (accumulated[1] << 4) | (accumulated[2] >> 2);
+        outputBuffer[j + 2] = (accumulated[2] << 6) | accumulated[3];
+        j += accumulateIndex - 1;
+    }
+
+    if (outputLength) {
+        *outputLength = j;
+    }
+    return outputBuffer;
+}
+
+//
+// NewBase64Decode
+//
+// Encodes the arbitrary data in the inputBuffer as base64 into a newly malloced
+// output buffer.
+//
+//  inputBuffer - the source data for the encode
+//	length - the length of the input in bytes
+//  separateLines - if zero, no CR/LF characters will be added. Otherwise
+//		a CR/LF pair will be added every 64 encoded chars.
+//	outputLength - if not-NULL, on output will contain the encoded length
+//		(not including terminating 0 char)
+//
+// returns the encoded buffer. Must be freed by caller. Length is given by
+//	outputLength.
+//
+char *CDVNewBase64Encode(
+    const void* buffer,
+    size_t    length,
+    bool      separateLines,
+    size_t    * outputLength)
+{
+    const unsigned char* inputBuffer = (const unsigned char*)buffer;
+
+#define MAX_NUM_PADDING_CHARS 2
+#define OUTPUT_LINE_LENGTH 64
+#define INPUT_LINE_LENGTH ((OUTPUT_LINE_LENGTH / CDV_BASE64_UNIT_SIZE) * CDV_BINARY_UNIT_SIZE)
+#define CR_LF_SIZE 0
+
+    //
+    // Byte accurate calculation of final buffer size
+    //
+    size_t outputBufferSize =
+        ((length / CDV_BINARY_UNIT_SIZE)
+        + ((length % CDV_BINARY_UNIT_SIZE) ? 1 : 0))
+        * CDV_BASE64_UNIT_SIZE;
+    if (separateLines) {
+        outputBufferSize +=
+            (outputBufferSize / OUTPUT_LINE_LENGTH) * CR_LF_SIZE;
+    }
+
+    //
+    // Include space for a terminating zero
+    //
+    outputBufferSize += 1;
+
+    //
+    // Allocate the output buffer
+    //
+    char* outputBuffer = (char*)malloc(outputBufferSize);
+    if (!outputBuffer) {
+        return NULL;
+    }
+
+    size_t i = 0;
+    size_t j = 0;
+    const size_t lineLength = separateLines ? INPUT_LINE_LENGTH : length;
+    size_t lineEnd = lineLength;
+
+    while (true) {
+        if (lineEnd > length) {
+            lineEnd = length;
+        }
+
+        for (; i + CDV_BINARY_UNIT_SIZE - 1 < lineEnd; i += CDV_BINARY_UNIT_SIZE) {
+            //
+            // Inner loop: turn 48 bytes into 64 base64 characters
+            //
+            outputBuffer[j++] = cdvbase64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+            outputBuffer[j++] = cdvbase64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
+                | ((inputBuffer[i + 1] & 0xF0) >> 4)];
+            outputBuffer[j++] = cdvbase64EncodeLookup[((inputBuffer[i + 1] & 0x0F) << 2)
+                | ((inputBuffer[i + 2] & 0xC0) >> 6)];
+            outputBuffer[j++] = cdvbase64EncodeLookup[inputBuffer[i + 2] & 0x3F];
+        }
+
+        if (lineEnd == length) {
+            break;
+        }
+
+        //
+        // Add the newline
+        //
+        // outputBuffer[j++] = '\r';
+        // outputBuffer[j++] = '\n';
+        lineEnd += lineLength;
+    }
+
+    if (i + 1 < length) {
+        //
+        // Handle the single '=' case
+        //
+        outputBuffer[j++] = cdvbase64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+        outputBuffer[j++] = cdvbase64EncodeLookup[((inputBuffer[i] & 0x03) << 4)
+            | ((inputBuffer[i + 1] & 0xF0) >> 4)];
+        outputBuffer[j++] = cdvbase64EncodeLookup[(inputBuffer[i + 1] & 0x0F) << 2];
+        outputBuffer[j++] = '=';
+    } else if (i < length) {
+        //
+        // Handle the double '=' case
+        //
+        outputBuffer[j++] = cdvbase64EncodeLookup[(inputBuffer[i] & 0xFC) >> 2];
+        outputBuffer[j++] = cdvbase64EncodeLookup[(inputBuffer[i] & 0x03) << 4];
+        outputBuffer[j++] = '=';
+        outputBuffer[j++] = '=';
+    }
+    outputBuffer[j] = 0;
+
+    //
+    // Set the output length and return the buffer
+    //
+    if (outputLength) {
+        *outputLength = j;
+    }
+    return outputBuffer;
+}
+
+@implementation NSData (CDVBase64)
+
+//
+// dataFromBase64String:
+//
+// Creates an NSData object containing the base64 decoded representation of
+// the base64 string 'aString'
+//
+// Parameters:
+//    aString - the base64 string to decode
+//
+// returns the autoreleased NSData representation of the base64 string
+//
++ (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];
+
+    free(outputBuffer);
+    return result;
+}
+
+//
+// base64EncodedString
+//
+// Creates an NSString object that contains the base 64 encoding of the
+// receiver's data. Lines are broken at 64 characters long.
+//
+// returns an autoreleased NSString being the base 64 representation of the
+//	receiver.
+//
+- (NSString*)base64EncodedString
+{
+    size_t outputLength = 0;
+    char* outputBuffer =
+        CDVNewBase64Encode([self bytes], [self length], true, &outputLength);
+
+    NSString* result =
+        [[NSString alloc]
+        initWithBytes:outputBuffer
+               length:outputLength
+             encoding:NSASCIIStringEncoding];
+
+    free(outputBuffer);
+    return result;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.h b/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.h
new file mode 100644
index 0000000..d38a6fc
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.h
@@ -0,0 +1,35 @@
+/*
+ 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 NSDictionary (org_apache_cordova_NSDictionary_Extension)
+
+- (bool)existsValue:(NSString*)expectedValue forKey:(NSString*)key;
+- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue withRange:(NSRange)range;
+- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue;
+- (BOOL)typeValueForKey:(NSString*)key isArray:(BOOL*)bArray isNull:(BOOL*)bNull isNumber:(BOOL*)bNumber isString:(BOOL*)bString;
+- (BOOL)valueForKeyIsArray:(NSString*)key;
+- (BOOL)valueForKeyIsNull:(NSString*)key;
+- (BOOL)valueForKeyIsString:(NSString*)key;
+- (BOOL)valueForKeyIsNumber:(NSString*)key;
+
+- (NSDictionary*)dictionaryWithLowercaseKeys;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/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
new file mode 100644
index 0000000..80e9ac1
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSDictionary+Extensions.m
@@ -0,0 +1,159 @@
+/*
+ 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 "NSDictionary+Extensions.h"
+#import <math.h>
+
+@implementation NSDictionary (org_apache_cordova_NSDictionary_Extension)
+
+- (bool)existsValue:(NSString*)expectedValue forKey:(NSString*)key
+{
+    id val = [self valueForKey:key];
+    bool exists = false;
+
+    if (val != nil) {
+        exists = [(NSString*) val compare:expectedValue options:NSCaseInsensitiveSearch] == 0;
+    }
+
+    return exists;
+}
+
+- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue withRange:(NSRange)range
+{
+    NSInteger value = defaultValue;
+
+    NSNumber* val = [self valueForKey:key];  // value is an NSNumber
+
+    if (val != nil) {
+        value = [val integerValue];
+    }
+
+    // min, max checks
+    value = MAX(range.location, value);
+    value = MIN(range.length, value);
+
+    return value;
+}
+
+- (NSInteger)integerValueForKey:(NSString*)key defaultValue:(NSInteger)defaultValue
+{
+    NSInteger value = defaultValue;
+
+    NSNumber* val = [self valueForKey:key];  // value is an NSNumber
+
+    if (val != nil) {
+        value = [val integerValue];
+    }
+    return value;
+}
+
+/*
+ *	Determine the type of object stored in a dictionary
+ *	IN:
+ *	(BOOL*) bString - if exists will be set to YES if object is an NSString, NO if not
+ *	(BOOL*) bNull - if exists will be set to YES if object is an NSNull, NO if not
+ *	(BOOL*) bArray - if exists will be set to YES if object is an NSArray, NO if not
+ *	(BOOL*) bNumber - if exists will be set to YES if object is an NSNumber, NO if not
+ *
+ *	OUT:
+ *	YES if key exists
+ *  NO if key does not exist.  Input parameters remain untouched
+ *
+ */
+
+- (BOOL)typeValueForKey:(NSString*)key isArray:(BOOL*)bArray isNull:(BOOL*)bNull isNumber:(BOOL*)bNumber isString:(BOOL*)bString
+{
+    BOOL bExists = YES;
+    NSObject* value = [self objectForKey:key];
+
+    if (value) {
+        bExists = YES;
+        if (bString) {
+            *bString = [value isKindOfClass:[NSString class]];
+        }
+        if (bNull) {
+            *bNull = [value isKindOfClass:[NSNull class]];
+        }
+        if (bArray) {
+            *bArray = [value isKindOfClass:[NSArray class]];
+        }
+        if (bNumber) {
+            *bNumber = [value isKindOfClass:[NSNumber class]];
+        }
+    }
+    return bExists;
+}
+
+- (BOOL)valueForKeyIsArray:(NSString*)key
+{
+    BOOL bArray = NO;
+    NSObject* value = [self objectForKey:key];
+
+    if (value) {
+        bArray = [value isKindOfClass:[NSArray class]];
+    }
+    return bArray;
+}
+
+- (BOOL)valueForKeyIsNull:(NSString*)key
+{
+    BOOL bNull = NO;
+    NSObject* value = [self objectForKey:key];
+
+    if (value) {
+        bNull = [value isKindOfClass:[NSNull class]];
+    }
+    return bNull;
+}
+
+- (BOOL)valueForKeyIsString:(NSString*)key
+{
+    BOOL bString = NO;
+    NSObject* value = [self objectForKey:key];
+
+    if (value) {
+        bString = [value isKindOfClass:[NSString class]];
+    }
+    return bString;
+}
+
+- (BOOL)valueForKeyIsNumber:(NSString*)key
+{
+    BOOL bNumber = NO;
+    NSObject* value = [self objectForKey:key];
+
+    if (value) {
+        bNumber = [value isKindOfClass:[NSNumber class]];
+    }
+    return bNumber;
+}
+
+- (NSDictionary*)dictionaryWithLowercaseKeys
+{
+    NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:self.count];
+    NSString* key;
+
+    for (key in self) {
+        [result setObject:[self objectForKey:key] forKey:[key lowercaseString]];
+    }
+
+    return result;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.h b/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.h
new file mode 100644
index 0000000..3194094
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.h
@@ -0,0 +1,29 @@
+/*
+ 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 NSMutableArray (QueueAdditions)
+
+- (id)pop;
+- (id)queueHead;
+- (id)dequeue;
+- (void)enqueue:(id)obj;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.m b/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.m
new file mode 100644
index 0000000..9e67ede
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/NSMutableArray+QueueAdditions.m
@@ -0,0 +1,58 @@
+/*
+ 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 "NSMutableArray+QueueAdditions.h"
+
+@implementation NSMutableArray (QueueAdditions)
+
+- (id)queueHead
+{
+    if ([self count] == 0) {
+        return nil;
+    }
+
+    return [self objectAtIndex:0];
+}
+
+- (__autoreleasing id)dequeue
+{
+    if ([self count] == 0) {
+        return nil;
+    }
+
+    id head = [self objectAtIndex:0];
+    if (head != nil) {
+        // [[head retain] autorelease]; ARC - the __autoreleasing on the return value should so the same thing
+        [self removeObjectAtIndex:0];
+    }
+
+    return head;
+}
+
+- (id)pop
+{
+    return [self dequeue];
+}
+
+- (void)enqueue:(id)object
+{
+    [self addObject:object];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.h b/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.h
new file mode 100644
index 0000000..dab73e6
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.h
@@ -0,0 +1,31 @@
+/*
+ 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 UIDevice (org_apache_cordova_UIDevice_Extension)
+
+/*
+    Get the unique identifier from the app bundle's folder, which is already a GUID
+    Upgrading and/or deleting the app and re-installing will get you a new GUID, so
+    this is only unique per install per device.
+ */
+- (NSString*)uniqueAppInstanceIdentifier;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.m b/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.m
new file mode 100644
index 0000000..c0c91af
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/UIDevice+Extensions.m
@@ -0,0 +1,47 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <UIKit/UIKit.h>
+#import "UIDevice+Extensions.h"
+
+@implementation UIDevice (org_apache_cordova_UIDevice_Extension)
+
+- (NSString*)uniqueAppInstanceIdentifier
+{
+    NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
+    static NSString* UUID_KEY = @"CDVUUID";
+
+    NSString* app_uuid = [userDefaults stringForKey:UUID_KEY];
+
+    if (app_uuid == nil) {
+        CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
+        CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
+
+        app_uuid = [NSString stringWithString:(__bridge NSString*)uuidString];
+        [userDefaults setObject:app_uuid forKey:UUID_KEY];
+        [userDefaults synchronize];
+
+        CFRelease(uuidString);
+        CFRelease(uuidRef);
+    }
+
+    return app_uuid;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDV.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDV.h b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDV.h
new file mode 100644
index 0000000..9794fa2
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDV.h
@@ -0,0 +1,30 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+//  Bridge implementation file for using Cordova plugins in PhoneGap 0.9.6.
+//
+
+/*
+ Returns YES if it is at least version specified as NSString(X)
+ Usage:
+ if (IsAtLeastiOSVersion(@"5.1")) {
+ // do something for iOS 5.1 or greater
+ }
+ */
+#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.h b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.h
new file mode 100644
index 0000000..7a06e51
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.h
@@ -0,0 +1,46 @@
+/*
+ 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.
+ */
+
+//  Bridge implementation file for using Cordova plugins in PhoneGap 0.9.6.
+//
+
+#ifdef PHONEGAP_FRAMEWORK
+    #import <PhoneGap/PGPlugin.h>
+#else
+    #import "PGPlugin.h"
+#endif
+
+typedef enum {
+    CDVCommandStatus_NO_RESULT = 0,
+    CDVCommandStatus_OK,
+    CDVCommandStatus_CLASS_NOT_FOUND_EXCEPTION,
+    CDVCommandStatus_ILLEGAL_ACCESS_EXCEPTION,
+    CDVCommandStatus_INSTANTIATION_EXCEPTION,
+    CDVCommandStatus_MALFORMED_URL_EXCEPTION,
+    CDVCommandStatus_IO_EXCEPTION,
+    CDVCommandStatus_INVALID_ACTION,
+    CDVCommandStatus_JSON_EXCEPTION,
+    CDVCommandStatus_ERROR
+} CDVCommandStatus;
+
+@interface CDVPlugin : PGPlugin
+@end
+
+@interface CDVPluginResult : PluginResult
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.m b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.m
new file mode 100644
index 0000000..52ccd41
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/0.9.6/CDVPlugin.m
@@ -0,0 +1,29 @@
+/*
+ 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.
+ */
+
+//  Bridge implementation file for using Cordova plugins in PhoneGap 0.9.6.
+//
+
+#import "CDVPlugin.h"
+
+@implementation CDVPlugin
+@end
+
+@implementation CDVPluginResult
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDV.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDV.h b/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDV.h
new file mode 100644
index 0000000..1c76eaa
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDV.h
@@ -0,0 +1,32 @@
+/*
+ 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.
+ */
+
+//  Bridge implementation file for using Cordova > 1.5 plugins in 1.5.0.
+//
+
+#import <Cordova/CDV.h>
+
+/*
+ Returns YES if it is at least version specified as NSString(X)
+ Usage:
+ if (IsAtLeastiOSVersion(@"5.1")) {
+ // do something for iOS 5.1 or greater
+ }
+ */
+#define IsAtLeastiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending)

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDVPlugin.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDVPlugin.h b/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDVPlugin.h
new file mode 100644
index 0000000..6e2c4c3
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/1.5.0/CDVPlugin.h
@@ -0,0 +1,23 @@
+/*
+ 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.
+ */
+
+//  Bridge implementation file for using Cordova > 1.5 plugins in 1.5.0.
+//
+
+#import <Cordova/CDVPlugin.h>

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/compatibility/README.txt
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/compatibility/README.txt b/lib/cordova-ios/CordovaLib/Classes/compatibility/README.txt
new file mode 100644
index 0000000..8c2d644
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/compatibility/README.txt
@@ -0,0 +1,23 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+# 
+# http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+Include these headers if you are using a bleeding edge plugin in an older version of Cordova.
+
+1.5.0 -- only for 1.5.0 projects
+0.9.6 -- for projects between 0.9.6 and 1.4.1
\ No newline at end of file