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

[11/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/CDVGlobalization.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.h b/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.h
new file mode 100644
index 0000000..0384656
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.h
@@ -0,0 +1,150 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import <Foundation/Foundation.h>
+#import "CDVPlugin.h"
+
+#define CDV_FORMAT_SHORT 0
+#define CDV_FORMAT_MEDIUM 1
+#define CDV_FORMAT_LONG 2
+#define CDV_FORMAT_FULL 3
+#define CDV_SELECTOR_MONTHS 0
+#define CDV_SELECTOR_DAYS 1
+
+enum CDVGlobalizationError {
+    CDV_UNKNOWN_ERROR = 0,
+    CDV_FORMATTING_ERROR = 1,
+    CDV_PARSING_ERROR = 2,
+    CDV_PATTERN_ERROR = 3,
+};
+typedef NSUInteger CDVGlobalizationError;
+
+@interface CDVGlobalization : CDVPlugin {
+    CFLocaleRef currentLocale;
+}
+
+- (void)getPreferredLanguage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns the string identifier for the clients current locale setting.
+ * It returns the locale identifier string to the successCB callback with a
+ * properties object as a parameter. If there is an error getting the locale,
+ * then the errorCB callback is invoked.
+ */
+- (void)getLocaleName:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns a date formatted as a string according to the clients user preferences and
+ * calendar using the time zone of the client. It returns the formatted date string to the
+ * successCB callback with a properties object as a parameter. If there is an error
+ * formatting the date, then the errorCB callback is invoked.
+ *
+ * options: "date" contains the number of milliseconds that represents the JavaScript date
+ */
+- (void)dateToString:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Parses a date formatted as a string according to the clients user
+ * preferences and calendar using the time zone of the client and returns
+ * the corresponding date object. It returns the date to the successCB
+ * callback with a properties object as a parameter. If there is an error
+ * parsing the date string, then the errorCB callback is invoked.
+ *
+ * options: "dateString" contains the JavaScript string to parse for a date
+ */
+- (void)stringToDate:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns a pattern string for formatting and parsing dates according to the clients
+ * user preferences. It returns the pattern to the successCB callback with a
+ * properties object as a parameter. If there is an error obtaining the pattern,
+ * then the errorCB callback is invoked.
+ *
+ */
+- (void)getDatePattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns an array of either the names of the months or days of the week
+ * according to the clients user preferences and calendar. It returns the array of names to the
+ * successCB callback with a properties object as a parameter. If there is an error obtaining the
+ * names, then the errorCB callback is invoked.
+ *
+ */
+- (void)getDateNames:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns whether daylight savings time is in effect for a given date using the clients
+ * time zone and calendar. It returns whether or not daylight savings time is in effect
+ * to the successCB callback with a properties object as a parameter. If there is an error
+ * reading the date, then the errorCB callback is invoked.
+ *
+ * options: "date" contains the number of milliseconds that represents the JavaScript date
+ *
+ */
+- (void)isDayLightSavingsTime:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns the first day of the week according to the clients user preferences and calendar.
+ * The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+ * It returns the day to the successCB callback with a properties object as a parameter.
+ * If there is an error obtaining the pattern, then the errorCB callback is invoked.
+ *
+ */
+- (void)getFirstDayOfWeek:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns a number formatted as a string according to the clients user preferences.
+ * It returns the formatted number string to the successCB callback with a properties object as a
+ * parameter. If there is an error formatting the number, then the errorCB callback is invoked.
+ *
+ * options: "number" contains the JavaScript number to format
+ *
+ */
+- (void)numberToString:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Parses a number formatted as a string according to the clients user preferences and
+ * returns the corresponding number. It returns the number to the successCB callback with a
+ * properties object as a parameter. If there is an error parsing the number string, then
+ * the errorCB callback is invoked.
+ *
+ * options: "numberString" contains the JavaScript string to parse for a number
+ *
+ */
+- (void)stringToNumber:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns a pattern string for formatting and parsing numbers according to the clients user
+ * preferences. It returns the pattern to the successCB callback with a properties object as a
+ * parameter. If there is an error obtaining the pattern, then the errorCB callback is invoked.
+ *
+ */
+- (void)getNumberPattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+/**
+ * Returns a pattern string for formatting and parsing currency values according to the clients
+ * user preferences and ISO 4217 currency code. It returns the pattern to the successCB callback with a
+ * properties object as a parameter. If there is an error obtaining the pattern, then the errorCB
+ * callback is invoked.
+ *
+ * options: "currencyCode" contains the ISO currency code from JavaScript
+ */
+- (void)getCurrencyPattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.m b/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.m
new file mode 100644
index 0000000..4d960cd
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVGlobalization.m
@@ -0,0 +1,790 @@
+/*
+ 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 "CDVGlobalization.h"
+
+@implementation CDVGlobalization
+
+- (id)initWithWebView:(UIWebView*)theWebView
+{
+    self = (CDVGlobalization*)[super initWithWebView:theWebView];
+    if (self) {
+        currentLocale = CFLocaleCopyCurrent();
+    }
+    return self;
+}
+
+- (void)getPreferredLanguage:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    NSString* callbackId = [arguments objectAtIndex:0];
+    CDVPluginResult* result = nil;
+
+    NSLog(@"log1");
+    // Source: http://stackoverflow.com/questions/3910244/getting-current-device-language-in-ios
+    // (should be OK)
+    NSString* language = [[NSLocale preferredLanguages] objectAtIndex:0];
+
+    if (language) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:language forKey:@"value"];
+
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+                               messageAsDictionary:dictionary];
+    } else {
+        // TBD is this ever expected to happen?
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_UNKNOWN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unknown error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+}
+
+- (void)getLocaleName:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callbackId = [arguments objectAtIndex:0];
+    NSDictionary* dictionary = nil;
+
+    NSLocale* locale = [NSLocale currentLocale];
+
+    if (locale) {
+        dictionary = [NSDictionary dictionaryWithObject:[locale localeIdentifier] forKey:@"value"];
+
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    } else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_UNKNOWN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unknown error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+}
+
+- (void)dateToString:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CFDateFormatterStyle style = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle dateStyle = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle timeStyle = kCFDateFormatterShortStyle;
+    NSDate* date = nil;
+    NSString* dateString = nil;
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+
+    id milliseconds = [options valueForKey:@"date"];
+
+    if (milliseconds && [milliseconds isKindOfClass:[NSNumber class]]) {
+        // get the number of seconds since 1970 and create the date object
+        date = [NSDate dateWithTimeIntervalSince1970:[milliseconds doubleValue] / 1000];
+    }
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired format length
+                if ([key isEqualToString:@"formatLength"]) {
+                    if ([item isEqualToString:@"short"]) {
+                        style = kCFDateFormatterShortStyle;
+                    } else if ([item isEqualToString:@"medium"]) {
+                        style = kCFDateFormatterMediumStyle;
+                    } else if ([item isEqualToString:@"long"]) {
+                        style = kCFDateFormatterLongStyle;
+                    } else if ([item isEqualToString:@"full"]) {
+                        style = kCFDateFormatterFullStyle;
+                    }
+                }
+                // get the type of date and time to generate
+                else if ([key isEqualToString:@"selector"]) {
+                    if ([item isEqualToString:@"date"]) {
+                        dateStyle = style;
+                        timeStyle = kCFDateFormatterNoStyle;
+                    } else if ([item isEqualToString:@"time"]) {
+                        dateStyle = kCFDateFormatterNoStyle;
+                        timeStyle = style;
+                    } else if ([item isEqualToString:@"date and time"]) {
+                        dateStyle = style;
+                        timeStyle = style;
+                    }
+                }
+            }
+        }
+    }
+
+    // create the formatter using the user's current default locale and formats for dates and times
+    CFDateFormatterRef formatter = CFDateFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        dateStyle,
+        timeStyle);
+    // if we have a valid date object then call the formatter
+    if (date) {
+        dateString = (__bridge_transfer NSString*)CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
+            formatter,
+            (__bridge CFDateRef)date);
+    }
+
+    // if the date was converted to a string successfully then return the result
+    if (dateString) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:dateString forKey:@"value"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        // DLog(@"GlobalizationCommand dateToString unable to format %@", [date description]);
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_FORMATTING_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Formatting error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)stringToDate:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CFDateFormatterStyle style = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle dateStyle = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle timeStyle = kCFDateFormatterShortStyle;
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+    NSString* dateString = nil;
+    NSDateComponents* comps = nil;
+
+    // get the string that is to be parsed for a date
+    id ms = [options valueForKey:@"dateString"];
+
+    if (ms && [ms isKindOfClass:[NSString class]]) {
+        dateString = ms;
+    }
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired format length
+                if ([key isEqualToString:@"formatLength"]) {
+                    if ([item isEqualToString:@"short"]) {
+                        style = kCFDateFormatterShortStyle;
+                    } else if ([item isEqualToString:@"medium"]) {
+                        style = kCFDateFormatterMediumStyle;
+                    } else if ([item isEqualToString:@"long"]) {
+                        style = kCFDateFormatterLongStyle;
+                    } else if ([item isEqualToString:@"full"]) {
+                        style = kCFDateFormatterFullStyle;
+                    }
+                }
+                // get the type of date and time to generate
+                else if ([key isEqualToString:@"selector"]) {
+                    if ([item isEqualToString:@"date"]) {
+                        dateStyle = style;
+                        timeStyle = kCFDateFormatterNoStyle;
+                    } else if ([item isEqualToString:@"time"]) {
+                        dateStyle = kCFDateFormatterNoStyle;
+                        timeStyle = style;
+                    } else if ([item isEqualToString:@"date and time"]) {
+                        dateStyle = style;
+                        timeStyle = style;
+                    }
+                }
+            }
+        }
+    }
+
+    // get the user's default settings for date and time formats
+    CFDateFormatterRef formatter = CFDateFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        dateStyle,
+        timeStyle);
+
+    // set the parsing to be more lenient
+    CFDateFormatterSetProperty(formatter, kCFDateFormatterIsLenient, kCFBooleanTrue);
+
+    // parse tha date and time string
+    CFDateRef date = CFDateFormatterCreateDateFromString(kCFAllocatorDefault,
+        formatter,
+        (__bridge CFStringRef)dateString,
+        NULL);
+
+    // if we were able to parse the date then get the date and time components
+    if (date != NULL) {
+        NSCalendar* calendar = [NSCalendar currentCalendar];
+
+        unsigned unitFlags = NSYearCalendarUnit |
+            NSMonthCalendarUnit |
+            NSDayCalendarUnit |
+            NSHourCalendarUnit |
+            NSMinuteCalendarUnit |
+            NSSecondCalendarUnit;
+
+        comps = [calendar components:unitFlags fromDate:(__bridge NSDate*)date];
+        CFRelease(date);
+    }
+
+    // put the various elements of the date and time into a dictionary
+    if (comps != nil) {
+        NSArray* keys = [NSArray arrayWithObjects:@"year", @"month", @"day", @"hour", @"minute", @"second", @"millisecond", nil];
+        NSArray* values = [NSArray arrayWithObjects:[NSNumber numberWithInt:[comps year]],
+            [NSNumber numberWithInt:[comps month] - 1],
+            [NSNumber numberWithInt:[comps day]],
+            [NSNumber numberWithInt:[comps hour]],
+            [NSNumber numberWithInt:[comps minute]],
+            [NSNumber numberWithInt:[comps second]],
+            [NSNumber numberWithInt:0],                /* iOS does not provide milliseconds */
+            nil];
+
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        // Dlog(@"GlobalizationCommand stringToDate unable to parse %@", dateString);
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_PARSING_ERROR] forKey:@"code"];
+        [dictionary setValue:@"unable to parse" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)getDatePattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CFDateFormatterStyle style = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle dateStyle = kCFDateFormatterShortStyle;
+    CFDateFormatterStyle timeStyle = kCFDateFormatterShortStyle;
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired format length
+                if ([key isEqualToString:@"formatLength"]) {
+                    if ([item isEqualToString:@"short"]) {
+                        style = kCFDateFormatterShortStyle;
+                    } else if ([item isEqualToString:@"medium"]) {
+                        style = kCFDateFormatterMediumStyle;
+                    } else if ([item isEqualToString:@"long"]) {
+                        style = kCFDateFormatterLongStyle;
+                    } else if ([item isEqualToString:@"full"]) {
+                        style = kCFDateFormatterFullStyle;
+                    }
+                }
+                // get the type of date and time to generate
+                else if ([key isEqualToString:@"selector"]) {
+                    if ([item isEqualToString:@"date"]) {
+                        dateStyle = style;
+                        timeStyle = kCFDateFormatterNoStyle;
+                    } else if ([item isEqualToString:@"time"]) {
+                        dateStyle = kCFDateFormatterNoStyle;
+                        timeStyle = style;
+                    } else if ([item isEqualToString:@"date and time"]) {
+                        dateStyle = style;
+                        timeStyle = style;
+                    }
+                }
+            }
+        }
+    }
+
+    // get the user's default settings for date and time formats
+    CFDateFormatterRef formatter = CFDateFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        dateStyle,
+        timeStyle);
+
+    // get the date pattern to apply when formatting and parsing
+    CFStringRef datePattern = CFDateFormatterGetFormat(formatter);
+    // get the user's current time zone information
+    CFTimeZoneRef timezone = (CFTimeZoneRef)CFDateFormatterCopyProperty(formatter, kCFDateFormatterTimeZone);
+
+    // put the pattern and time zone information into the dictionary
+    if ((datePattern != nil) && (timezone != nil)) {
+        NSArray* keys = [NSArray arrayWithObjects:@"pattern", @"timezone", @"utc_offset", @"dst_offset", nil];
+        NSArray* values = [NSArray arrayWithObjects:((__bridge NSString*)datePattern),
+            [((__bridge NSTimeZone*)timezone)abbreviation],
+            [NSNumber numberWithLong:[((__bridge NSTimeZone*)timezone)secondsFromGMT]],
+            [NSNumber numberWithDouble:[((__bridge NSTimeZone*)timezone)daylightSavingTimeOffset]],
+            nil];
+
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_PATTERN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Pattern error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    if (timezone) {
+        CFRelease(timezone);
+    }
+    CFRelease(formatter);
+}
+
+- (void)getDateNames:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    int style = CDV_FORMAT_LONG;
+    int selector = CDV_SELECTOR_MONTHS;
+    CFStringRef dataStyle = kCFDateFormatterMonthSymbols;
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired type of name
+                if ([key isEqualToString:@"type"]) {
+                    if ([item isEqualToString:@"narrow"]) {
+                        style = CDV_FORMAT_SHORT;
+                    } else if ([item isEqualToString:@"wide"]) {
+                        style = CDV_FORMAT_LONG;
+                    }
+                }
+                // determine if months or days are needed
+                else if ([key isEqualToString:@"item"]) {
+                    if ([item isEqualToString:@"months"]) {
+                        selector = CDV_SELECTOR_MONTHS;
+                    } else if ([item isEqualToString:@"days"]) {
+                        selector = CDV_SELECTOR_DAYS;
+                    }
+                }
+            }
+        }
+    }
+
+    CFDateFormatterRef formatter = CFDateFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        kCFDateFormatterFullStyle,
+        kCFDateFormatterFullStyle);
+
+    if ((selector == CDV_SELECTOR_MONTHS) && (style == CDV_FORMAT_LONG)) {
+        dataStyle = kCFDateFormatterMonthSymbols;
+    } else if ((selector == CDV_SELECTOR_MONTHS) && (style == CDV_FORMAT_SHORT)) {
+        dataStyle = kCFDateFormatterShortMonthSymbols;
+    } else if ((selector == CDV_SELECTOR_DAYS) && (style == CDV_FORMAT_LONG)) {
+        dataStyle = kCFDateFormatterWeekdaySymbols;
+    } else if ((selector == CDV_SELECTOR_DAYS) && (style == CDV_FORMAT_SHORT)) {
+        dataStyle = kCFDateFormatterShortWeekdaySymbols;
+    }
+
+    CFArrayRef names = (CFArrayRef)CFDateFormatterCopyProperty(formatter, dataStyle);
+
+    if (names) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:((__bridge NSArray*)names) forKey:@"value"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+        CFRelease(names);
+    }
+    // error
+    else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_UNKNOWN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unknown error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)isDayLightSavingsTime:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    NSDate* date = nil;
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+
+    id milliseconds = [options valueForKey:@"date"];
+
+    if (milliseconds && [milliseconds isKindOfClass:[NSNumber class]]) {
+        // get the number of seconds since 1970 and create the date object
+        date = [NSDate dateWithTimeIntervalSince1970:[milliseconds doubleValue] / 1000];
+    }
+
+    if (date) {
+        // get the current calendar for the user and check if the date is using DST
+        NSCalendar* calendar = [NSCalendar currentCalendar];
+        NSTimeZone* timezone = [calendar timeZone];
+        NSNumber* dst = [NSNumber numberWithBool:[timezone isDaylightSavingTimeForDate:date]];
+
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:dst forKey:@"dst"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_UNKNOWN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unknown error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+}
+
+- (void)getFirstDayOfWeek:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+
+    NSCalendar* calendar = [NSCalendar autoupdatingCurrentCalendar];
+
+    NSNumber* day = [NSNumber numberWithInt:[calendar firstWeekday]];
+
+    if (day) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:day forKey:@"value"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_UNKNOWN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unknown error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+}
+
+- (void)numberToString:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+    CFNumberFormatterStyle style = kCFNumberFormatterDecimalStyle;
+    NSNumber* number = nil;
+
+    id value = [options valueForKey:@"number"];
+
+    if (value && [value isKindOfClass:[NSNumber class]]) {
+        number = (NSNumber*)value;
+    }
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired style of formatting
+                if ([key isEqualToString:@"type"]) {
+                    if ([item isEqualToString:@"percent"]) {
+                        style = kCFNumberFormatterPercentStyle;
+                    } else if ([item isEqualToString:@"currency"]) {
+                        style = kCFNumberFormatterCurrencyStyle;
+                    } else if ([item isEqualToString:@"decimal"]) {
+                        style = kCFNumberFormatterDecimalStyle;
+                    }
+                }
+            }
+        }
+    }
+
+    CFNumberFormatterRef formatter = CFNumberFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        style);
+
+    // get the localized string based upon the locale and user preferences
+    NSString* numberString = (__bridge_transfer NSString*)CFNumberFormatterCreateStringWithNumber(kCFAllocatorDefault,
+        formatter,
+        (__bridge CFNumberRef)number);
+
+    if (numberString) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:numberString forKey:@"value"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        // DLog(@"GlobalizationCommand numberToString unable to format %@", [number stringValue]);
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_FORMATTING_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unable to format" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)stringToNumber:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+    CFNumberFormatterStyle style = kCFNumberFormatterDecimalStyle;
+    NSString* numberString = nil;
+    double doubleValue;
+
+    id value = [options valueForKey:@"numberString"];
+
+    if (value && [value isKindOfClass:[NSString class]]) {
+        numberString = (NSString*)value;
+    }
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired style of formatting
+                if ([key isEqualToString:@"type"]) {
+                    if ([item isEqualToString:@"percent"]) {
+                        style = kCFNumberFormatterPercentStyle;
+                    } else if ([item isEqualToString:@"currency"]) {
+                        style = kCFNumberFormatterCurrencyStyle;
+                    } else if ([item isEqualToString:@"decimal"]) {
+                        style = kCFNumberFormatterDecimalStyle;
+                    }
+                }
+            }
+        }
+    }
+
+    CFNumberFormatterRef formatter = CFNumberFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        style);
+
+    // we need to make this lenient so as to avoid problems with parsing currencies that have non-breaking space characters
+    if (style == kCFNumberFormatterCurrencyStyle) {
+        CFNumberFormatterSetProperty(formatter, kCFNumberFormatterIsLenient, kCFBooleanTrue);
+    }
+
+    // parse againist the largest type to avoid data loss
+    Boolean rc = CFNumberFormatterGetValueFromString(formatter,
+        (__bridge CFStringRef)numberString,
+        NULL,
+        kCFNumberDoubleType,
+        &doubleValue);
+
+    if (rc) {
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObject:[NSNumber numberWithDouble:doubleValue] forKey:@"value"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        // DLog(@"GlobalizationCommand stringToNumber unable to parse %@", numberString);
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_PARSING_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unable to parse" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)getNumberPattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+    CFNumberFormatterStyle style = kCFNumberFormatterDecimalStyle;
+    CFStringRef symbolType = NULL;
+    NSString* symbol = @"";
+
+    // see if any options have been specified
+    id items = [options valueForKey:@"options"];
+
+    if (items && [items isKindOfClass:[NSMutableDictionary class]]) {
+        NSEnumerator* enumerator = [items keyEnumerator];
+        id key;
+
+        // iterate through all the options
+        while ((key = [enumerator nextObject])) {
+            id item = [items valueForKey:key];
+
+            // make sure that only string values are present
+            if ([item isKindOfClass:[NSString class]]) {
+                // get the desired style of formatting
+                if ([key isEqualToString:@"type"]) {
+                    if ([item isEqualToString:@"percent"]) {
+                        style = kCFNumberFormatterPercentStyle;
+                    } else if ([item isEqualToString:@"currency"]) {
+                        style = kCFNumberFormatterCurrencyStyle;
+                    } else if ([item isEqualToString:@"decimal"]) {
+                        style = kCFNumberFormatterDecimalStyle;
+                    }
+                }
+            }
+        }
+    }
+
+    CFNumberFormatterRef formatter = CFNumberFormatterCreate(kCFAllocatorDefault,
+        currentLocale,
+        style);
+
+    NSString* numberPattern = (__bridge NSString*)CFNumberFormatterGetFormat(formatter);
+
+    if (style == kCFNumberFormatterCurrencyStyle) {
+        symbolType = kCFNumberFormatterCurrencySymbol;
+    } else if (style == kCFNumberFormatterPercentStyle) {
+        symbolType = kCFNumberFormatterPercentSymbol;
+    }
+
+    if (symbolType) {
+        symbol = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, symbolType);
+    }
+
+    NSString* decimal = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterDecimalSeparator);
+    NSString* grouping = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterGroupingSeparator);
+    NSString* posSign = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterPlusSign);
+    NSString* negSign = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterMinusSign);
+    NSNumber* fracDigits = (__bridge_transfer NSNumber*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterMinFractionDigits);
+    NSNumber* roundingDigits = (__bridge_transfer NSNumber*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterRoundingIncrement);
+
+    // put the pattern information into the dictionary
+    if (numberPattern != nil) {
+        NSArray* keys = [NSArray arrayWithObjects:@"pattern", @"symbol", @"fraction", @"rounding",
+            @"positive", @"negative", @"decimal", @"grouping", nil];
+        NSArray* values = [NSArray arrayWithObjects:numberPattern, symbol, fracDigits, roundingDigits,
+            posSign, negSign, decimal, grouping, nil];
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+    }
+    // error
+    else {
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_PATTERN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Pattern error" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+
+    CFRelease(formatter);
+}
+
+- (void)getCurrencyPattern:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options
+{
+    CDVPluginResult* result = nil;
+    NSString* callBackId = [arguments objectAtIndex:0];
+    NSString* currencyCode = nil;
+    NSString* numberPattern = nil;
+    NSString* decimal = nil;
+    NSString* grouping = nil;
+    int32_t defaultFractionDigits;
+    double roundingIncrement;
+    Boolean rc;
+
+    id value = [options valueForKey:@"currencyCode"];
+
+    if (value && [value isKindOfClass:[NSString class]]) {
+        currencyCode = (NSString*)value;
+    }
+
+    // first see if there is base currency info available and fill in the currency_info structure
+    rc = CFNumberFormatterGetDecimalInfoForCurrencyCode((__bridge CFStringRef)currencyCode, &defaultFractionDigits, &roundingIncrement);
+
+    // now set the currency code in the formatter
+    if (rc) {
+        CFNumberFormatterRef formatter = CFNumberFormatterCreate(kCFAllocatorDefault,
+            currentLocale,
+            kCFNumberFormatterCurrencyStyle);
+
+        CFNumberFormatterSetProperty(formatter, kCFNumberFormatterCurrencyCode, (__bridge CFStringRef)currencyCode);
+        CFNumberFormatterSetProperty(formatter, kCFNumberFormatterInternationalCurrencySymbol, (__bridge CFStringRef)currencyCode);
+
+        numberPattern = (__bridge NSString*)CFNumberFormatterGetFormat(formatter);
+        decimal = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterCurrencyDecimalSeparator);
+        grouping = (__bridge_transfer NSString*)CFNumberFormatterCopyProperty(formatter, kCFNumberFormatterCurrencyGroupingSeparator);
+
+        NSArray* keys = [NSArray arrayWithObjects:@"pattern", @"code", @"fraction", @"rounding",
+            @"decimal", @"grouping", nil];
+        NSArray* values = [NSArray arrayWithObjects:numberPattern, currencyCode, [NSNumber numberWithInt:defaultFractionDigits],
+            [NSNumber numberWithDouble:roundingIncrement], decimal, grouping, nil];
+        NSDictionary* dictionary = [NSDictionary dictionaryWithObjects:values forKeys:keys];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:dictionary];
+        CFRelease(formatter);
+    }
+    // error
+    else {
+        // DLog(@"GlobalizationCommand getCurrencyPattern unable to get pattern for %@", currencyCode);
+        NSMutableDictionary* dictionary = [NSMutableDictionary dictionaryWithCapacity:2];
+        [dictionary setValue:[NSNumber numberWithInt:CDV_PATTERN_ERROR] forKey:@"code"];
+        [dictionary setValue:@"Unable to get pattern" forKey:@"message"];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:dictionary];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:callBackId];
+}
+
+- (void)dealloc
+{
+    if (currentLocale) {
+        CFRelease(currentLocale);
+        currentLocale = nil;
+    }
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
new file mode 100644
index 0000000..0a3ef70
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.h
@@ -0,0 +1,85 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVPlugin.h"
+#import "CDVInvokedUrlCommand.h"
+#import "CDVScreenOrientationDelegate.h"
+
+@class CDVInAppBrowserViewController;
+
+@protocol CDVInAppBrowserNavigationDelegate <NSObject>
+
+- (void)browserLoadStart:(NSURL*)url;
+- (void)browserLoadStop:(NSURL*)url;
+- (void)browserExit;
+
+@end
+
+@interface CDVInAppBrowser : CDVPlugin <CDVInAppBrowserNavigationDelegate>{}
+
+@property (nonatomic, retain) CDVInAppBrowserViewController* inAppBrowserViewController;
+@property (nonatomic, copy) NSString* callbackId;
+
+- (void)open:(CDVInvokedUrlCommand*)command;
+- (void)close:(CDVInvokedUrlCommand*)command;
+
+@end
+
+@interface CDVInAppBrowserViewController : UIViewController <UIWebViewDelegate>{
+    @private
+    NSURL* _requestedURL;
+    NSString* _userAgent;
+    NSString* _prevUserAgent;
+    BOOL _isPDF;
+}
+
+@property (nonatomic, strong) IBOutlet UIWebView* webView;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* closeButton;
+@property (nonatomic, strong) IBOutlet UILabel* addressLabel;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* backButton;
+@property (nonatomic, strong) IBOutlet UIBarButtonItem* forwardButton;
+@property (nonatomic, strong) IBOutlet UIActivityIndicatorView* spinner;
+@property (nonatomic, strong) IBOutlet UIToolbar* toolbar;
+
+@property (nonatomic, weak) id <CDVScreenOrientationDelegate> orientationDelegate;
+@property (nonatomic, weak) id <CDVInAppBrowserNavigationDelegate> navigationDelegate;
+
+- (void)close;
+- (void)navigateTo:(NSURL*)url;
+- (void)showLocationBar:(BOOL)show;
+
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent;
+
+@end
+
+@interface CDVInAppBrowserOptions : NSObject {}
+
+@property (nonatomic, assign) BOOL location;
+@property (nonatomic, copy) NSString* presentationstyle;
+@property (nonatomic, copy) NSString* transitionstyle;
+
+@property (nonatomic, assign) BOOL enableviewportscale;
+@property (nonatomic, assign) BOOL mediaplaybackrequiresuseraction;
+@property (nonatomic, assign) BOOL allowinlinemediaplayback;
+@property (nonatomic, assign) BOOL keyboarddisplayrequiresuseraction;
+@property (nonatomic, assign) BOOL suppressesincrementalrendering;
+
++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
new file mode 100644
index 0000000..6d7b770
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInAppBrowser.m
@@ -0,0 +1,564 @@
+/*
+ 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 "CDVInAppBrowser.h"
+#import "CDVPluginResult.h"
+#import "CDVViewController.h"
+#import "CDVUserAgentUtil.h"
+
+#define    kInAppBrowserTargetSelf @"_self"
+#define    kInAppBrowserTargetSystem @"_system"
+#define    kInAppBrowserTargetBlank @"_blank"
+
+#define    TOOLBAR_HEIGHT 44.0
+#define    LOCATIONBAR_HEIGHT 21.0
+#define    FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT))
+
+#pragma mark CDVInAppBrowser
+
+@implementation CDVInAppBrowser
+
+- (CDVInAppBrowser*)initWithWebView:(UIWebView*)theWebView
+{
+    self = [super initWithWebView:theWebView];
+    if (self != nil) {
+        // your initialization here
+    }
+
+    return self;
+}
+
+- (void)onReset
+{
+    [self close:nil];
+}
+
+- (void)close:(CDVInvokedUrlCommand*)command
+{
+    if (self.inAppBrowserViewController != nil) {
+        [self.inAppBrowserViewController close];
+        self.inAppBrowserViewController = nil;
+    }
+
+    self.callbackId = nil;
+}
+
+- (void)open:(CDVInvokedUrlCommand*)command
+{
+    CDVPluginResult* pluginResult;
+
+    NSString* url = [command argumentAtIndex:0];
+    NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
+    NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
+
+    self.callbackId = command.callbackId;
+
+    if (url != nil) {
+        NSURL* baseUrl = [self.webView.request URL];
+        NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
+        if ([target isEqualToString:kInAppBrowserTargetSelf]) {
+            [self openInCordovaWebView:absoluteUrl withOptions:options];
+        } else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
+            [self openInSystem:absoluteUrl];
+        } else { // _blank or anything else
+            [self openInInAppBrowser:absoluteUrl withOptions:options];
+        }
+
+        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+    } else {
+        pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"];
+    }
+
+    [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
+{
+    if (self.inAppBrowserViewController == nil) {
+        NSString* originalUA = [CDVUserAgentUtil originalUserAgent];
+        self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:originalUA prevUserAgent:[self.commandDelegate userAgent]];
+        self.inAppBrowserViewController.navigationDelegate = self;
+
+        if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
+            self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController;
+        }
+    }
+
+    CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
+    [self.inAppBrowserViewController showLocationBar:browserOptions.location];
+
+    // Set Presentation Style
+    UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
+    if (browserOptions.presentationstyle != nil) {
+        if ([browserOptions.presentationstyle isEqualToString:@"pagesheet"]) {
+            presentationStyle = UIModalPresentationPageSheet;
+        } else if ([browserOptions.presentationstyle isEqualToString:@"formsheet"]) {
+            presentationStyle = UIModalPresentationFormSheet;
+        }
+    }
+    self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
+
+    // Set Transition Style
+    UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
+    if (browserOptions.transitionstyle != nil) {
+        if ([browserOptions.transitionstyle isEqualToString:@"fliphorizontal"]) {
+            transitionStyle = UIModalTransitionStyleFlipHorizontal;
+        } else if ([browserOptions.transitionstyle isEqualToString:@"crossdissolve"]) {
+            transitionStyle = UIModalTransitionStyleCrossDissolve;
+        }
+    }
+    self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
+
+    // UIWebView options
+    self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
+    self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
+    self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
+    self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
+    self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
+
+    if (self.viewController.modalViewController != self.inAppBrowserViewController) {
+        [self.viewController presentModalViewController:self.inAppBrowserViewController animated:YES];
+    }
+    [self.inAppBrowserViewController navigateTo:url];
+}
+
+- (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
+{
+    BOOL passesWhitelist = YES;
+
+    if ([self.viewController isKindOfClass:[CDVViewController class]]) {
+        CDVViewController* vc = (CDVViewController*)self.viewController;
+        if ([vc.whitelist schemeIsAllowed:[url scheme]]) {
+            passesWhitelist = [vc.whitelist URLIsAllowed:url];
+        }
+    } else { // something went wrong, we can't get the whitelist
+        passesWhitelist = NO;
+    }
+
+    if (passesWhitelist) {
+        NSURLRequest* request = [NSURLRequest requestWithURL:url];
+        [self.webView loadRequest:request];
+    } else { // this assumes the InAppBrowser can be excepted from the white-list
+        [self openInInAppBrowser:url withOptions:options];
+    }
+}
+
+- (void)openInSystem:(NSURL*)url
+{
+    if ([[UIApplication sharedApplication] canOpenURL:url]) {
+        [[UIApplication sharedApplication] openURL:url];
+    } else { // handle any custom schemes to plugins
+        [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
+    }
+}
+
+#pragma mark CDVInAppBrowserNavigationDelegate
+
+- (void)browserLoadStart:(NSURL*)url
+{
+    if (self.callbackId != nil) {
+        CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+                                                      messageAsDictionary:@ {@"type":@"loadstart", @"url":[url absoluteString]}];
+        [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+        [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+    }
+}
+
+- (void)browserLoadStop:(NSURL*)url
+{
+    if (self.callbackId != nil) {
+        CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+                                                      messageAsDictionary:@ {@"type":@"loadstop", @"url":[url absoluteString]}];
+        [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+        [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+    }
+}
+
+- (void)browserExit
+{
+    if (self.callbackId != nil) {
+        CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
+                                                      messageAsDictionary:@ {@"type":@"exit"}];
+        [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+
+        [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
+    }
+    // Don't recycle the ViewController since it may be consuming a lot of memory.
+    // Also - this is required for the PDF/User-Agent bug work-around.
+    self.inAppBrowserViewController = nil;
+}
+
+@end
+
+#pragma mark CDVInAppBrowserViewController
+
+@implementation CDVInAppBrowserViewController
+
+- (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent
+{
+    self = [super init];
+    if (self != nil) {
+        _userAgent = userAgent;
+        _prevUserAgent = prevUserAgent;
+        [self createViews];
+    }
+
+    return self;
+}
+
+- (void)createViews
+{
+    // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
+
+    CGRect webViewBounds = self.view.bounds;
+
+    webViewBounds.size.height -= FOOTER_HEIGHT;
+
+    if (!self.webView) {
+        [CDVUserAgentUtil setUserAgent:_userAgent];
+
+        self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
+        self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
+
+        [self.view addSubview:self.webView];
+        [self.view sendSubviewToBack:self.webView];
+
+        self.webView.delegate = self;
+        self.webView.backgroundColor = [UIColor whiteColor];
+
+        self.webView.clearsContextBeforeDrawing = YES;
+        self.webView.clipsToBounds = YES;
+        self.webView.contentMode = UIViewContentModeScaleToFill;
+        self.webView.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
+        self.webView.multipleTouchEnabled = YES;
+        self.webView.opaque = YES;
+        self.webView.scalesPageToFit = NO;
+        self.webView.userInteractionEnabled = YES;
+    }
+
+    self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];
+    self.spinner.alpha = 1.000;
+    self.spinner.autoresizesSubviews = YES;
+    self.spinner.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
+    self.spinner.clearsContextBeforeDrawing = NO;
+    self.spinner.clipsToBounds = NO;
+    self.spinner.contentMode = UIViewContentModeScaleToFill;
+    self.spinner.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
+    self.spinner.frame = CGRectMake(454.0, 231.0, 20.0, 20.0);
+    self.spinner.hidden = YES;
+    self.spinner.hidesWhenStopped = YES;
+    self.spinner.multipleTouchEnabled = NO;
+    self.spinner.opaque = NO;
+    self.spinner.userInteractionEnabled = NO;
+    [self.spinner stopAnimating];
+
+    self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)];
+    self.closeButton.enabled = YES;
+    self.closeButton.imageInsets = UIEdgeInsetsZero;
+    self.closeButton.style = UIBarButtonItemStylePlain;
+    self.closeButton.width = 32.000;
+
+    UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
+
+    UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
+    fixedSpaceButton.width = 20;
+
+    self.toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0, (self.view.bounds.size.height - TOOLBAR_HEIGHT), self.view.bounds.size.width, TOOLBAR_HEIGHT)];
+    self.toolbar.alpha = 1.000;
+    self.toolbar.autoresizesSubviews = YES;
+    self.toolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
+    self.toolbar.barStyle = UIBarStyleBlackOpaque;
+    self.toolbar.clearsContextBeforeDrawing = NO;
+    self.toolbar.clipsToBounds = NO;
+    self.toolbar.contentMode = UIViewContentModeScaleToFill;
+    self.toolbar.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
+    self.toolbar.hidden = NO;
+    self.toolbar.multipleTouchEnabled = NO;
+    self.toolbar.opaque = NO;
+    self.toolbar.userInteractionEnabled = YES;
+
+    CGFloat labelInset = 5.0;
+    self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, (self.view.bounds.size.height - FOOTER_HEIGHT), self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)];
+    self.addressLabel.adjustsFontSizeToFitWidth = NO;
+    self.addressLabel.alpha = 1.000;
+    self.addressLabel.autoresizesSubviews = YES;
+    self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
+    self.addressLabel.backgroundColor = [UIColor clearColor];
+    self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
+    self.addressLabel.clearsContextBeforeDrawing = YES;
+    self.addressLabel.clipsToBounds = YES;
+    self.addressLabel.contentMode = UIViewContentModeScaleToFill;
+    self.addressLabel.contentStretch = CGRectFromString(@"{{0, 0}, {1, 1}}");
+    self.addressLabel.enabled = YES;
+    self.addressLabel.hidden = NO;
+    self.addressLabel.lineBreakMode = UILineBreakModeTailTruncation;
+    self.addressLabel.minimumFontSize = 10.000;
+    self.addressLabel.multipleTouchEnabled = NO;
+    self.addressLabel.numberOfLines = 1;
+    self.addressLabel.opaque = NO;
+    self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0);
+    self.addressLabel.text = @"Loading...";
+    self.addressLabel.textAlignment = UITextAlignmentLeft;
+    self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000];
+    self.addressLabel.userInteractionEnabled = NO;
+
+    NSString* frontArrowString = @"►"; // create arrow from Unicode char
+    self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)];
+    self.forwardButton.enabled = YES;
+    self.forwardButton.imageInsets = UIEdgeInsetsZero;
+
+    NSString* backArrowString = @"◄"; // create arrow from Unicode char
+    self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)];
+    self.backButton.enabled = YES;
+    self.backButton.imageInsets = UIEdgeInsetsZero;
+
+    [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]];
+
+    self.view.backgroundColor = [UIColor grayColor];
+    [self.view addSubview:self.toolbar];
+    [self.view addSubview:self.addressLabel];
+    [self.view addSubview:self.spinner];
+}
+
+- (void)showLocationBar:(BOOL)show
+{
+    CGRect addressLabelFrame = self.addressLabel.frame;
+    BOOL locationBarVisible = (addressLabelFrame.size.height > 0);
+
+    // prevent double show/hide
+    if (locationBarVisible == show) {
+        return;
+    }
+
+    if (show) {
+        CGRect webViewBounds = self.view.bounds;
+        webViewBounds.size.height -= FOOTER_HEIGHT;
+        self.webView.frame = webViewBounds;
+
+        CGRect addressLabelFrame = self.addressLabel.frame;
+        addressLabelFrame.size.height = LOCATIONBAR_HEIGHT;
+        self.addressLabel.frame = addressLabelFrame;
+    } else {
+        CGRect webViewBounds = self.view.bounds;
+        webViewBounds.size.height -= TOOLBAR_HEIGHT;
+        self.webView.frame = webViewBounds;
+
+        CGRect addressLabelFrame = self.addressLabel.frame;
+        addressLabelFrame.size.height = 0;
+        self.addressLabel.frame = addressLabelFrame;
+    }
+}
+
+- (void)viewDidLoad
+{
+    [super viewDidLoad];
+}
+
+- (void)viewDidUnload
+{
+    [self.webView loadHTMLString:nil baseURL:nil];
+    [super viewDidUnload];
+}
+
+- (void)close
+{
+    if ([self respondsToSelector:@selector(presentingViewController)]) {
+        [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil];
+    } else {
+        [[self parentViewController] dismissModalViewControllerAnimated:YES];
+    }
+
+    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) {
+        [self.navigationDelegate browserExit];
+    }
+}
+
+- (void)navigateTo:(NSURL*)url
+{
+    NSURLRequest* request = [NSURLRequest requestWithURL:url];
+
+    [self.webView loadRequest:request];
+}
+
+- (void)goBack:(id)sender
+{
+    [self.webView goBack];
+}
+
+- (void)goForward:(id)sender
+{
+    [self.webView goForward];
+}
+
+#pragma mark UIWebViewDelegate
+
+- (void)webViewDidStartLoad:(UIWebView*)theWebView
+{
+    // loading url, start spinner, update back/forward
+
+    self.addressLabel.text = @"Loading...";
+    self.backButton.enabled = theWebView.canGoBack;
+    self.forwardButton.enabled = theWebView.canGoForward;
+
+    [self.spinner startAnimating];
+
+    NSURL* url = theWebView.request.URL;
+    // This is probably a bug, but it works on iOS 5 and 6 to know when a PDF
+    // is being loaded.
+    _isPDF = [[url absoluteString] length] == 0;
+
+    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStart:)]) {
+        if (url == nil) {
+            url = _requestedURL;
+        }
+        [self.navigationDelegate browserLoadStart:url];
+    }
+}
+
+- (void)webViewDidFinishLoad:(UIWebView*)theWebView
+{
+    // update url, stop spinner, update back/forward
+
+    self.addressLabel.text = theWebView.request.URL.absoluteString;
+    self.backButton.enabled = theWebView.canGoBack;
+    self.forwardButton.enabled = theWebView.canGoForward;
+
+    [self.spinner stopAnimating];
+
+    // Work around a bug where the first time a PDF is opened, all UIWebViews
+    // reload their User-Agent from NSUserDefaults.
+    // This work-around makes the following assumptions:
+    // 1. The app has only a single Cordova Webview. If not, then the app should
+    //    take it upon themselves to load a PDF in the background as a part of
+    //    their start-up flow.
+    // 2. That the PDF does not require any additional network requests. We change
+    //    the user-agent here back to that of the CDVViewController, so requests
+    //    from it must pass through its white-list. This *does* break PDFs that
+    //    contain links to other remote PDF/websites.
+    // More info at https://issues.apache.org/jira/browse/CB-2225
+    if (_isPDF) {
+        [CDVUserAgentUtil setUserAgent:_prevUserAgent];
+    }
+
+    if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserLoadStop:)]) {
+        NSURL* url = theWebView.request.URL;
+        [self.navigationDelegate browserLoadStop:url];
+    }
+}
+
+- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
+{
+    // log fail message, stop spinner, update back/forward
+    NSLog(@"webView:didFailLoadWithError - %@", [error localizedDescription]);
+
+    self.backButton.enabled = theWebView.canGoBack;
+    self.forwardButton.enabled = theWebView.canGoForward;
+    [self.spinner stopAnimating];
+
+    self.addressLabel.text = @"Load Error";
+}
+
+#pragma mark CDVScreenOrientationDelegate
+
+- (BOOL)shouldAutorotate
+{
+    if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
+        return [self.orientationDelegate shouldAutorotate];
+    }
+    return YES;
+}
+
+- (NSUInteger)supportedInterfaceOrientations
+{
+    if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
+        return [self.orientationDelegate supportedInterfaceOrientations];
+    }
+
+    return 1 << UIInterfaceOrientationPortrait;
+}
+
+- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
+{
+    if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
+        return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
+    }
+
+    return YES;
+}
+
+@end
+
+@implementation CDVInAppBrowserOptions
+
+- (id)init
+{
+    if (self = [super init]) {
+        // default values
+        self.location = YES;
+
+        self.enableviewportscale = NO;
+        self.mediaplaybackrequiresuseraction = NO;
+        self.allowinlinemediaplayback = NO;
+        self.keyboarddisplayrequiresuseraction = YES;
+        self.suppressesincrementalrendering = NO;
+    }
+
+    return self;
+}
+
++ (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
+{
+    CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init];
+
+    // NOTE: this parsing does not handle quotes within values
+    NSArray* pairs = [options componentsSeparatedByString:@","];
+
+    // parse keys and values, set the properties
+    for (NSString* pair in pairs) {
+        NSArray* keyvalue = [pair componentsSeparatedByString:@"="];
+
+        if ([keyvalue count] == 2) {
+            NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
+            NSString* value = [[keyvalue objectAtIndex:1] lowercaseString];
+
+            BOOL isBoolean = [value isEqualToString:@"yes"] || [value isEqualToString:@"no"];
+            NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
+            [numberFormatter setAllowsFloats:YES];
+            BOOL isNumber = [numberFormatter numberFromString:value] != nil;
+
+            // set the property according to the key name
+            if ([obj respondsToSelector:NSSelectorFromString(key)]) {
+                if (isNumber) {
+                    [obj setValue:[numberFormatter numberFromString:value] forKey:key];
+                } else if (isBoolean) {
+                    [obj setValue:[NSNumber numberWithBool:[value isEqualToString:@"yes"]] forKey:key];
+                } else {
+                    [obj setValue:value forKey:key];
+                }
+            }
+        }
+    }
+
+    return obj;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.h b/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.h
new file mode 100644
index 0000000..6eb0099
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.h
@@ -0,0 +1,57 @@
+/*
+ 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 CDVInvokedUrlCommand : NSObject {
+    NSString* _callbackId;
+    NSString* _className;
+    NSString* _methodName;
+    NSArray* _arguments;
+}
+
+@property (nonatomic, readonly) NSArray* arguments;
+@property (nonatomic, readonly) NSString* callbackId;
+@property (nonatomic, readonly) NSString* className;
+@property (nonatomic, readonly) NSString* methodName;
+
++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry;
+
+- (id)initWithArguments:(NSArray*)arguments
+   callbackId          :(NSString*)callbackId
+    className           :(NSString*)className
+   methodName          :(NSString*)methodName;
+
+- (id)initFromJson:(NSArray*)jsonEntry;
+
+// The first NSDictionary found in the arguments will be returned in legacyDict.
+// The arguments array with be prepended with the callbackId and have the first
+// dict removed from it.
+- (void)legacyArguments:(NSMutableArray**)legacyArguments andDict:(NSMutableDictionary**)legacyDict;
+
+// Returns the argument at the given index.
+// If index >= the number of arguments, returns nil.
+// If the argument at the given index is NSNull, returns nil.
+- (id)argumentAtIndex:(NSUInteger)index;
+// Same as above, but returns defaultValue instead of nil.
+- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue;
+// Same as above, but returns defaultValue instead of nil, and if the argument is not of the expected class, returns defaultValue
+- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.m b/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.m
new file mode 100644
index 0000000..6c7a856
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVInvokedUrlCommand.m
@@ -0,0 +1,140 @@
+/*
+ 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 "CDVInvokedUrlCommand.h"
+#import "CDVJSON.h"
+#import "NSData+Base64.h"
+
+@implementation CDVInvokedUrlCommand
+
+@synthesize arguments = _arguments;
+@synthesize callbackId = _callbackId;
+@synthesize className = _className;
+@synthesize methodName = _methodName;
+
++ (CDVInvokedUrlCommand*)commandFromJson:(NSArray*)jsonEntry
+{
+    return [[CDVInvokedUrlCommand alloc] initFromJson:jsonEntry];
+}
+
+- (id)initFromJson:(NSArray*)jsonEntry
+{
+    id tmp = [jsonEntry objectAtIndex:0];
+    NSString* callbackId = tmp == [NSNull null] ? nil : tmp;
+    NSString* className = [jsonEntry objectAtIndex:1];
+    NSString* methodName = [jsonEntry objectAtIndex:2];
+    NSMutableArray* arguments = [jsonEntry objectAtIndex:3];
+
+    return [self initWithArguments:arguments
+                        callbackId:callbackId
+                         className:className
+                        methodName:methodName];
+}
+
+- (id)initWithArguments:(NSArray*)arguments
+             callbackId:(NSString*)callbackId
+              className:(NSString*)className
+             methodName:(NSString*)methodName
+{
+    self = [super init];
+    if (self != nil) {
+        _arguments = arguments;
+        _callbackId = callbackId;
+        _className = className;
+        _methodName = methodName;
+    }
+    [self massageArguments];
+    return self;
+}
+
+- (void)massageArguments
+{
+    NSMutableArray* newArgs = nil;
+
+    for (NSUInteger i = 0, count = [_arguments count]; i < count; ++i) {
+        id arg = [_arguments objectAtIndex:i];
+        if (![arg isKindOfClass:[NSDictionary class]]) {
+            continue;
+        }
+        NSDictionary* dict = arg;
+        NSString* type = [dict objectForKey:@"CDVType"];
+        if (!type || ![type isEqualToString:@"ArrayBuffer"]) {
+            continue;
+        }
+        NSString* data = [dict objectForKey:@"data"];
+        if (!data) {
+            continue;
+        }
+        if (newArgs == nil) {
+            newArgs = [NSMutableArray arrayWithArray:_arguments];
+            _arguments = newArgs;
+        }
+        [newArgs replaceObjectAtIndex:i withObject:[NSData dataFromBase64String:data]];
+    }
+}
+
+- (void)legacyArguments:(NSMutableArray**)legacyArguments andDict:(NSMutableDictionary**)legacyDict
+{
+    NSMutableArray* newArguments = [NSMutableArray arrayWithArray:_arguments];
+
+    for (NSUInteger i = 0; i < [newArguments count]; ++i) {
+        if ([[newArguments objectAtIndex:i] isKindOfClass:[NSDictionary class]]) {
+            if (legacyDict != NULL) {
+                *legacyDict = [newArguments objectAtIndex:i];
+            }
+            [newArguments removeObjectAtIndex:i];
+            break;
+        }
+    }
+
+    // Legacy (two versions back) has no callbackId.
+    if (_callbackId != nil) {
+        [newArguments insertObject:_callbackId atIndex:0];
+    }
+    if (legacyArguments != NULL) {
+        *legacyArguments = newArguments;
+    }
+}
+
+- (id)argumentAtIndex:(NSUInteger)index
+{
+    return [self argumentAtIndex:index withDefault:nil];
+}
+
+- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue
+{
+    return [self argumentAtIndex:index withDefault:defaultValue andClass:nil];
+}
+
+- (id)argumentAtIndex:(NSUInteger)index withDefault:(id)defaultValue andClass:(Class)aClass
+{
+    if (index >= [_arguments count]) {
+        return defaultValue;
+    }
+    id ret = [_arguments objectAtIndex:index];
+    if (ret == [NSNull null]) {
+        ret = defaultValue;
+    }
+    if ((aClass != nil) && ![ret isKindOfClass:aClass]) {
+        ret = defaultValue;
+    }
+    return ret;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVJSON.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVJSON.h b/lib/cordova-ios/CordovaLib/Classes/CDVJSON.h
new file mode 100644
index 0000000..eaa895e
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVJSON.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.
+ */
+
+@interface NSArray (CDVJSONSerializing)
+- (NSString*)JSONString;
+@end
+
+@interface NSDictionary (CDVJSONSerializing)
+- (NSString*)JSONString;
+@end
+
+@interface NSString (CDVJSONSerializing)
+- (id)JSONObject;
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVJSON.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVJSON.m b/lib/cordova-ios/CordovaLib/Classes/CDVJSON.m
new file mode 100644
index 0000000..78267e5
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVJSON.m
@@ -0,0 +1,77 @@
+/*
+ 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 "CDVJSON.h"
+#import <Foundation/NSJSONSerialization.h>
+
+@implementation NSArray (CDVJSONSerializing)
+
+- (NSString*)JSONString
+{
+    NSError* error = nil;
+    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
+                                                       options:NSJSONWritingPrettyPrinted
+                                                         error:&error];
+
+    if (error != nil) {
+        NSLog(@"NSArray JSONString error: %@", [error localizedDescription]);
+        return nil;
+    } else {
+        return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+    }
+}
+
+@end
+
+@implementation NSDictionary (CDVJSONSerializing)
+
+- (NSString*)JSONString
+{
+    NSError* error = nil;
+    NSData* jsonData = [NSJSONSerialization dataWithJSONObject:self
+                                                       options:NSJSONWritingPrettyPrinted
+                                                         error:&error];
+
+    if (error != nil) {
+        NSLog(@"NSDictionary JSONString error: %@", [error localizedDescription]);
+        return nil;
+    } else {
+        return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+    }
+}
+
+@end
+
+@implementation NSString (CDVJSONSerializing)
+
+- (id)JSONObject
+{
+    NSError* error = nil;
+    id object = [NSJSONSerialization JSONObjectWithData:[self dataUsingEncoding:NSUTF8StringEncoding]
+                                                options:kNilOptions
+                                                  error:&error];
+
+    if (error != nil) {
+        NSLog(@"NSString JSONObject error: %@", [error localizedDescription]);
+    }
+
+    return object;
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.h b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.h
new file mode 100644
index 0000000..e5e3112
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVLocalStorage.h
@@ -0,0 +1,50 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License.  You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied.  See the License for the
+ specific language governing permissions and limitations
+ under the License.
+ */
+
+#import "CDVPlugin.h"
+
+#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain"
+#define kCDVLocalStorageFileOperationError 1
+
+@interface CDVLocalStorage : CDVPlugin <UIWebViewDelegate>
+
+@property (nonatomic, readonly, strong) NSMutableArray* backupInfo;
+
+- (BOOL)shouldBackup;
+- (BOOL)shouldRestore;
+- (void)backup:(CDVInvokedUrlCommand*)command;
+- (void)restore:(CDVInvokedUrlCommand*)command;
+
++ (void)__fixupDatabaseLocationsWithBackupType:(NSString*)backupType;
+// Visible for testing.
++ (BOOL)__verifyAndFixDatabaseLocationsWithAppPlistDict:(NSMutableDictionary*)appPlistDict
+    bundlePath                                          :(NSString*)bundlePath
+   fileManager                                         :(NSFileManager*)fileManager;
+@end
+
+@interface CDVBackupInfo : NSObject
+
+@property (nonatomic, copy) NSString* original;
+@property (nonatomic, copy) NSString* backup;
+@property (nonatomic, copy) NSString* label;
+
+- (BOOL)shouldBackup;
+- (BOOL)shouldRestore;
+
+@end