From 6b6860dea529e6cb980296193f65fbaf15e5d13b Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:25:15 +0100 Subject: [PATCH 1/6] Update CordovaLib --- CordovaLib/Classes/CDVAvailability.h | 3 +- CordovaLib/Classes/CDVCamera.h | 1 + CordovaLib/Classes/CDVCamera.m | 162 ++++++---- CordovaLib/Classes/CDVCapture.m | 1 - CordovaLib/Classes/CDVCommandDelegate.h | 5 + CordovaLib/Classes/CDVCommandDelegateImpl.m | 11 + CordovaLib/Classes/CDVConfigParser.h | 3 +- CordovaLib/Classes/CDVConfigParser.m | 31 +- CordovaLib/Classes/CDVDevice.m | 1 + CordovaLib/Classes/CDVFile.m | 301 ++++++++++++------ CordovaLib/Classes/CDVFileTransfer.h | 4 +- CordovaLib/Classes/CDVFileTransfer.m | 99 ++++-- CordovaLib/Classes/CDVInAppBrowser.m | 21 +- CordovaLib/Classes/CDVLocalStorage.h | 2 +- CordovaLib/Classes/CDVLocalStorage.m | 48 +-- CordovaLib/Classes/CDVLocation.m | 14 +- CordovaLib/Classes/CDVPlugin.h | 4 +- CordovaLib/Classes/CDVPlugin.m | 51 ++- CordovaLib/Classes/CDVSound.h | 8 +- CordovaLib/Classes/CDVSound.m | 153 +++++++-- CordovaLib/Classes/CDVSplashScreen.h | 6 +- CordovaLib/Classes/CDVSplashScreen.m | 151 ++++++++- CordovaLib/Classes/CDVURLProtocol.m | 83 +++-- CordovaLib/Classes/CDVUserAgentUtil.h | 2 +- CordovaLib/Classes/CDVUserAgentUtil.m | 10 +- CordovaLib/Classes/CDVViewController.h | 7 +- CordovaLib/Classes/CDVViewController.m | 230 +++---------- CordovaLib/Classes/CDVWebViewDelegate.h | 37 +++ CordovaLib/Classes/CDVWebViewDelegate.m | 157 +++++++++ .../CordovaLib.xcodeproj/project.pbxproj | 20 +- CordovaLib/VERSION | 2 +- 31 files changed, 1042 insertions(+), 586 deletions(-) create mode 100755 CordovaLib/Classes/CDVWebViewDelegate.h create mode 100755 CordovaLib/Classes/CDVWebViewDelegate.m diff --git a/CordovaLib/Classes/CDVAvailability.h b/CordovaLib/Classes/CDVAvailability.h index 67583be..33c6799 100755 --- a/CordovaLib/Classes/CDVAvailability.h +++ b/CordovaLib/Classes/CDVAvailability.h @@ -36,6 +36,7 @@ #define __CORDOVA_2_2_0 20200 #define __CORDOVA_2_3_0 20300 #define __CORDOVA_2_4_0 20400 +#define __CORDOVA_2_5_0 20500 #define __CORDOVA_NA 99999 /* not available */ /* @@ -46,7 +47,7 @@ #endif */ #ifndef CORDOVA_VERSION_MIN_REQUIRED - #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_4_0 + #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_2_5_0 #endif /* diff --git a/CordovaLib/Classes/CDVCamera.h b/CordovaLib/Classes/CDVCamera.h index e32da7d..204d25f 100755 --- a/CordovaLib/Classes/CDVCamera.h +++ b/CordovaLib/Classes/CDVCamera.h @@ -80,6 +80,7 @@ typedef NSUInteger CDVMediaType; - (void)takePicture:(CDVInvokedUrlCommand*)command; - (void)postImage:(UIImage*)anImage withFilename:(NSString*)filename toUrl:(NSURL*)url; - (void)cleanup:(CDVInvokedUrlCommand*)command; +- (void)repositionPopover:(CDVInvokedUrlCommand*)command; - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info; - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo; diff --git a/CordovaLib/Classes/CDVCamera.m b/CordovaLib/Classes/CDVCamera.m index 4cf5c82..aabe844 100755 --- a/CordovaLib/Classes/CDVCamera.m +++ b/CordovaLib/Classes/CDVCamera.m @@ -93,6 +93,13 @@ - (void)takePicture:(CDVInvokedUrlCommand*)command targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]); } + // If a popover is already open, close it; we only want one at a time. + if (([[self pickerController] popoverController] != nil) && [[[self pickerController] popoverController] isPopoverVisible]) { + [[[self pickerController] popoverController] dismissPopoverAnimated:YES]; + [[[self pickerController] popoverController] setDelegate:nil]; + [[self pickerController] setPopoverController:nil]; + } + CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init]; self.pickerController = cameraPicker; @@ -128,28 +135,8 @@ - (void)takePicture:(CDVInvokedUrlCommand*)command if (cameraPicker.popoverController == nil) { cameraPicker.popoverController = [[NSClassFromString (@"UIPopoverController")alloc] initWithContentViewController:cameraPicker]; } - int x = 0; - int y = 32; - int width = 320; - int height = 480; - UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny; NSDictionary* options = [command.arguments objectAtIndex:10 withDefault:nil]; - if (options) { - x = [options integerValueForKey:@"x" defaultValue:0]; - y = [options integerValueForKey:@"y" defaultValue:32]; - width = [options integerValueForKey:@"width" defaultValue:320]; - height = [options integerValueForKey:@"height" defaultValue:480]; - arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny]; - if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) { - arrowDirection = UIPopoverArrowDirectionAny; - } - } - - cameraPicker.popoverController.delegate = self; - [cameraPicker.popoverController presentPopoverFromRect:CGRectMake(x, y, width, height) - inView:[self.webView superview] - permittedArrowDirections:arrowDirection - animated:YES]; + [self displayPopover:options]; } else { if ([self.viewController respondsToSelector:@selector(presentViewController:::)]) { [self.viewController presentViewController:cameraPicker animated:YES completion:nil]; @@ -160,6 +147,39 @@ - (void)takePicture:(CDVInvokedUrlCommand*)command self.hasPendingOperation = YES; } +- (void)repositionPopover:(CDVInvokedUrlCommand*)command +{ + NSDictionary* options = [command.arguments objectAtIndex:0 withDefault:nil]; + + [self displayPopover:options]; +} + +- (void)displayPopover:(NSDictionary*)options +{ + int x = 0; + int y = 32; + int width = 320; + int height = 480; + UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny; + + if (options) { + x = [options integerValueForKey:@"x" defaultValue:0]; + y = [options integerValueForKey:@"y" defaultValue:32]; + width = [options integerValueForKey:@"width" defaultValue:320]; + height = [options integerValueForKey:@"height" defaultValue:480]; + arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny]; + if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) { + arrowDirection = UIPopoverArrowDirectionAny; + } + } + + [[[self pickerController] popoverController] setDelegate:self]; + [[[self pickerController] popoverController] presentPopoverFromRect:CGRectMake(x, y, width, height) + inView:[self.webView superview] + permittedArrowDirections:arrowDirection + animated:YES]; +} + - (void)cleanup:(CDVInvokedUrlCommand*)command { // empty the tmp directory @@ -232,66 +252,68 @@ - (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingM NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType]; // IMAGE TYPE if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) { - // get the image - UIImage* image = nil; - if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) { - image = [info objectForKey:UIImagePickerControllerEditedImage]; + if (cameraPicker.returnType == DestinationTypeNativeUri) { + NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri]; } else { - image = [info objectForKey:UIImagePickerControllerOriginalImage]; - } + // get the image + UIImage* image = nil; + if (cameraPicker.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) { + image = [info objectForKey:UIImagePickerControllerEditedImage]; + } else { + image = [info objectForKey:UIImagePickerControllerOriginalImage]; + } - if (cameraPicker.saveToPhotoAlbum) { - UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); - } + if (cameraPicker.saveToPhotoAlbum) { + UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); + } - if (cameraPicker.correctOrientation) { - image = [self imageCorrectedForCaptureOrientation:image]; - } + if (cameraPicker.correctOrientation) { + image = [self imageCorrectedForCaptureOrientation:image]; + } - UIImage* scaledImage = nil; + UIImage* scaledImage = nil; - if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) { - // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping - if (cameraPicker.cropToSize) { - scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize]; - } else { - scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize]; + if ((cameraPicker.targetSize.width > 0) && (cameraPicker.targetSize.height > 0)) { + // if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping + if (cameraPicker.cropToSize) { + scaledImage = [self imageByScalingAndCroppingForSize:image toSize:cameraPicker.targetSize]; + } else { + scaledImage = [self imageByScalingNotCroppingForSize:image toSize:cameraPicker.targetSize]; + } } - } - NSData* data = nil; + NSData* data = nil; - if (cameraPicker.encodingType == EncodingTypePNG) { - data = UIImagePNGRepresentation(scaledImage == nil ? image : scaledImage); - } else { - data = UIImageJPEGRepresentation(scaledImage == nil ? image : scaledImage, cameraPicker.quality / 100.0f); - } + if (cameraPicker.encodingType == EncodingTypePNG) { + data = UIImagePNGRepresentation(scaledImage == nil ? image : scaledImage); + } else { + data = UIImageJPEGRepresentation(scaledImage == nil ? image : scaledImage, cameraPicker.quality / 100.0f); + } - if (cameraPicker.returnType == DestinationTypeFileUri) { - // write to temp directory and return URI - // get the temp directory path - NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath]; - NSError* err = nil; - NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe - // generate unique file name - NSString* filePath; - - int i = 1; - do { - filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"]; - } while ([fileMgr fileExistsAtPath:filePath]); - - // save file - if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + if (cameraPicker.returnType == DestinationTypeFileUri) { + // write to temp directory and return URI + // get the temp directory path + NSString* docsPath = [NSTemporaryDirectory ()stringByStandardizingPath]; + NSError* err = nil; + NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by apple (vs [NSFileManager defaultManager]) to be threadsafe + // generate unique file name + NSString* filePath; + + int i = 1; + do { + filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, cameraPicker.encodingType == EncodingTypePNG ? @"png":@"jpg"]; + } while ([fileMgr fileExistsAtPath:filePath]); + + // save file + if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]]; + } else { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]]; + } } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[NSURL fileURLWithPath:filePath] absoluteString]]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]]; } - } else if (cameraPicker.returnType == DestinationTypeNativeUri) { - NSString* nativeUri = [(NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL] absoluteString]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri]; - } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[data base64EncodedString]]; } } // NOT IMAGE TYPE (MOVIE) diff --git a/CordovaLib/Classes/CDVCapture.m b/CordovaLib/Classes/CDVCapture.m index 95c3f17..ed9f664 100755 --- a/CordovaLib/Classes/CDVCapture.m +++ b/CordovaLib/Classes/CDVCapture.m @@ -20,7 +20,6 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDVCapture.h" #import "CDVJSON.h" #import "CDVAvailability.h" -#import "CDVViewController.h" #define kW3CMediaFormatHeight @"height" #define kW3CMediaFormatWidth @"width" diff --git a/CordovaLib/Classes/CDVCommandDelegate.h b/CordovaLib/Classes/CDVCommandDelegate.h index 6b1dedd..e177c63 100755 --- a/CordovaLib/Classes/CDVCommandDelegate.h +++ b/CordovaLib/Classes/CDVCommandDelegate.h @@ -22,9 +22,12 @@ @class CDVPlugin; @class CDVPluginResult; +@class CDVWhitelist; @protocol CDVCommandDelegate +@property (nonatomic, readonly) NSDictionary* settings; + - (NSString*)pathForResource:(NSString*)resourcepath; - (id)getCommandInstance:(NSString*)pluginName; - (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className CDV_DEPRECATED(2.2, "Use CDVViewController to register plugins, or use config.xml."); @@ -46,5 +49,7 @@ - (void)runInBackground:(void (^)())block; // Returns the User-Agent of the associated UIWebView. - (NSString*)userAgent; +// Returns whether the given URL passes the white-list. +- (BOOL)URLIsWhitelisted:(NSURL*)url; @end diff --git a/CordovaLib/Classes/CDVCommandDelegateImpl.m b/CordovaLib/Classes/CDVCommandDelegateImpl.m index 8845e40..e399289 100755 --- a/CordovaLib/Classes/CDVCommandDelegateImpl.m +++ b/CordovaLib/Classes/CDVCommandDelegateImpl.m @@ -137,4 +137,15 @@ - (NSString*)userAgent return [_viewController userAgent]; } +- (BOOL)URLIsWhitelisted:(NSURL*)url +{ + return ![_viewController.whitelist schemeIsAllowed:[url scheme]] || + [_viewController.whitelist URLIsAllowed:url]; +} + +- (NSDictionary*)settings +{ + return _viewController.settings; +} + @end diff --git a/CordovaLib/Classes/CDVConfigParser.h b/CordovaLib/Classes/CDVConfigParser.h index 0f4fa4c..7392580 100755 --- a/CordovaLib/Classes/CDVConfigParser.h +++ b/CordovaLib/Classes/CDVConfigParser.h @@ -22,8 +22,7 @@ @property (nonatomic, readonly, strong) NSMutableDictionary* pluginsDict; @property (nonatomic, readonly, strong) NSMutableDictionary* settings; @property (nonatomic, readonly, strong) NSMutableArray* whitelistHosts; +@property (nonatomic, readonly, strong) NSMutableArray* startupPluginNames; @property (nonatomic, readonly, strong) NSString* startPage; -- (NSString*)getStartPage; - @end diff --git a/CordovaLib/Classes/CDVConfigParser.m b/CordovaLib/Classes/CDVConfigParser.m index 3938b3e..6fd5913 100755 --- a/CordovaLib/Classes/CDVConfigParser.m +++ b/CordovaLib/Classes/CDVConfigParser.m @@ -24,21 +24,23 @@ @interface CDVConfigParser () @property (nonatomic, readwrite, strong) NSMutableDictionary* pluginsDict; @property (nonatomic, readwrite, strong) NSMutableDictionary* settings; @property (nonatomic, readwrite, strong) NSMutableArray* whitelistHosts; +@property (nonatomic, readwrite, strong) NSMutableArray* startupPluginNames; @property (nonatomic, readwrite, strong) NSString* startPage; @end @implementation CDVConfigParser -@synthesize pluginsDict, settings, whitelistHosts, startPage; +@synthesize pluginsDict, settings, whitelistHosts, startPage, startupPluginNames; - (id)init { self = [super init]; if (self != nil) { - self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:4]; - self.settings = [[NSMutableDictionary alloc] initWithCapacity:4]; - self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:1]; + self.pluginsDict = [[NSMutableDictionary alloc] initWithCapacity:30]; + self.settings = [[NSMutableDictionary alloc] initWithCapacity:30]; + self.whitelistHosts = [[NSMutableArray alloc] initWithCapacity:30]; + self.startupPluginNames = [[NSMutableArray alloc] initWithCapacity:8]; } return self; } @@ -46,13 +48,17 @@ - (id)init - (void)parser:(NSXMLParser*)parser didStartElement:(NSString*)elementName namespaceURI:(NSString*)namespaceURI qualifiedName:(NSString*)qualifiedName attributes:(NSDictionary*)attributeDict { if ([elementName isEqualToString:@"preference"]) { - [settings setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]]; + settings[attributeDict[@"name"]] = attributeDict[@"value"]; } else if ([elementName isEqualToString:@"plugin"]) { - [pluginsDict setObject:[attributeDict objectForKey:@"value"] forKey:[attributeDict objectForKey:@"name"]]; + NSString* name = [attributeDict[@"name"] lowercaseString]; + pluginsDict[name] = attributeDict[@"value"]; + if ([@"true" isEqualToString:attributeDict[@"onload"]]) { + [self.startupPluginNames addObject:name]; + } } else if ([elementName isEqualToString:@"access"]) { - [whitelistHosts addObject:[attributeDict objectForKey:@"origin"]]; + [whitelistHosts addObject:attributeDict[@"origin"]]; } else if ([elementName isEqualToString:@"content"]) { - self.startPage = [attributeDict objectForKey:@"src"]; + self.startPage = attributeDict[@"src"]; } } @@ -61,13 +67,4 @@ - (void)parser:(NSXMLParser*)parser parseErrorOccurred:(NSError*)parseError NSAssert(NO, @"config.xml parse error line %d col %d", [parser lineNumber], [parser columnNumber]); } -- (NSString*)getStartPage -{ - if (self.startPage != nil) { - return self.startPage; - } else { - return @"index.html"; - } -} - @end diff --git a/CordovaLib/Classes/CDVDevice.m b/CordovaLib/Classes/CDVDevice.m index 46195e8..cc7ad89 100755 --- a/CordovaLib/Classes/CDVDevice.m +++ b/CordovaLib/Classes/CDVDevice.m @@ -58,6 +58,7 @@ - (void)getDeviceInfo:(CDVInvokedUrlCommand*)command NSDictionary* temp = [CDVViewController getBundlePlist:@"Settings"]; if ([temp respondsToSelector:@selector(JSONString)]) { + NSLog(@"Deprecation warning: window.Setting will be removed Aug 2013. Refer to https://issues.apache.org/jira/browse/CB-2433"); NSString* js = [NSString stringWithFormat:@"window.Settings = %@;", [temp JSONString]]; [self.commandDelegate evalJs:js]; } diff --git a/CordovaLib/Classes/CDVFile.m b/CordovaLib/Classes/CDVFile.m index 11b2b77..d52405d 100755 --- a/CordovaLib/Classes/CDVFile.m +++ b/CordovaLib/Classes/CDVFile.m @@ -22,6 +22,9 @@ Licensed to the Apache Software Foundation (ASF) under one #import "NSDictionary+Extensions.h" #import "CDVJSON.h" #import "NSData+Base64.h" +#import +#import +#import #import #import "CDVAvailability.h" #import "sys/xattr.h" @@ -434,9 +437,9 @@ - (void)getParent:(CDVInvokedUrlCommand*)command // arguments are URL encoded NSString* fullPath = [command.arguments objectAtIndex:0]; - // return unsupported result for assets-library URLs + // we don't (yet?) support getting the parent of an asset if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"remove not supported for assets-library URLs."]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } @@ -477,11 +480,33 @@ - (void)getMetadata:(CDVInvokedUrlCommand*)command { // arguments NSString* argPath = [command.arguments objectAtIndex:0]; + __block CDVPluginResult* result = nil; - // return unsupported result for assets-library URLs if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getMetadata not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + // In this case, we need to use an asynchronous method to retrieve the file. + // Because of this, we can't just assign to `result` and send it at the end of the method. + // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Retrieve the metadata and send it off. + NSDate* date = [asset valueForProperty:ALAssetPropertyDate]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + // We couldn't find the asset. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + }; + // TODO(maxw): Consider making this a class variable since it's the same every time. + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock]; return; } @@ -489,7 +514,6 @@ - (void)getMetadata:(CDVInvokedUrlCommand*)command NSFileManager* fileMgr = [[NSFileManager alloc] init]; NSError* __autoreleasing error = nil; - CDVPluginResult* result = nil; NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:testPath error:&error]; @@ -501,7 +525,7 @@ - (void)getMetadata:(CDVInvokedUrlCommand*)command } else { // didn't get fileAttribs CDVFileError errorCode = ABORT_ERR; - NSLog(@"error getting metadata: %@", [error localizedDescription]); + NSLog (@"error getting metadata: %@", [error localizedDescription]); if ([error code] == NSFileNoSuchFileError) { errorCode = NOT_FOUND_ERR; } @@ -524,34 +548,29 @@ - (void)setMetadata:(CDVInvokedUrlCommand*)command // arguments NSString* filePath = [command.arguments objectAtIndex:0]; NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:nil]; - - // return unsupported result for assets-library URLs - if ([filePath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"setMetadata not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - CDVPluginResult* result = nil; BOOL ok = NO; - // we only care about this iCloud key for now. - // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute) - NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup"; - id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey]; - - if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) { - if (IsAtLeastiOSVersion(@"5.1")) { - NSURL* url = [NSURL fileURLWithPath:filePath]; - NSError* __autoreleasing error = nil; - - ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error]; - } else { // below 5.1 (deprecated - only really supported in 5.01) - u_int8_t value = [iCloudBackupExtendedAttributeValue intValue]; - if (value == 0) { // remove the attribute (allow backup, the default) - ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0); - } else { // set the attribute (skip backup) - ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0); + // setMetadata doesn't make sense for asset library files + if (![filePath hasPrefix:kCDVAssetsLibraryPrefix]) { + // we only care about this iCloud key for now. + // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute) + NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup"; + id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey]; + + if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) { + if (IsAtLeastiOSVersion(@"5.1")) { + NSURL* url = [NSURL fileURLWithPath:filePath]; + NSError* __autoreleasing error = nil; + + ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error]; + } else { // below 5.1 (deprecated - only really supported in 5.01) + u_int8_t value = [iCloudBackupExtendedAttributeValue intValue]; + if (value == 0) { // remove the attribute (allow backup, the default) + ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0); + } else { // set the attribute (skip backup) + ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0); + } } } } @@ -570,26 +589,21 @@ - (void)setMetadata:(CDVInvokedUrlCommand*)command * 0 - NSString* fullPath * * returns NO_MODIFICATION_ALLOWED_ERR if is top level directory or no permission to delete dir - * returns INVALID_MODIFICATION_ERR if is dir and is not empty + * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file * returns NOT_FOUND_ERR if file or dir is not found */ - (void)remove:(CDVInvokedUrlCommand*)command { // arguments NSString* fullPath = [command.arguments objectAtIndex:0]; - - // return unsupported result for assets-library URLs - if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"remove not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - CDVPluginResult* result = nil; CDVFileError errorCode = 0; // !! 0 not currently defined - // error if try to remove top level (documents or tmp) dir - if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) { + // return error for assets-library URLs + if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) { + errorCode = INVALID_MODIFICATION_ERR; + } else if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) { + // error if try to remove top level (documents or tmp) dir errorCode = NO_MODIFICATION_ALLOWED_ERR; } else { NSFileManager* fileMgr = [[NSFileManager alloc] init]; @@ -738,14 +752,7 @@ - (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy // optional argument NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent]; // use last component from appPath if new name not provided - // return unsupported result for assets-library URLs - if ([srcFullPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"moveTo/copyTo not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - CDVPluginResult* result = nil; + __block CDVPluginResult* result = nil; CDVFileError errCode = 0; // !! Currently 0 is not defined, use this to signal error !! /*NSString* destRootPath = nil; @@ -762,12 +769,59 @@ - (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy errCode = ENCODING_ERR; } else { NSString* newFullPath = [destRootPath stringByAppendingPathComponent:newName]; + NSFileManager* fileMgr = [[NSFileManager alloc] init]; if ([newFullPath isEqualToString:srcFullPath]) { // source and destination can not be the same errCode = INVALID_MODIFICATION_ERR; - } else { - NSFileManager* fileMgr = [[NSFileManager alloc] init]; + } else if ([srcFullPath hasPrefix:kCDVAssetsLibraryPrefix]) { + if (bCopy) { + // Copying (as opposed to moving) an assets library file is okay. + // In this case, we need to use an asynchronous method to retrieve the file. + // Because of this, we can't just assign to `result` and send it at the end of the method. + // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Get the data and try to copy it over. + if (![fileMgr fileExistsAtPath:destRootPath]) { + // The destination directory doesn't exist. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return; + } else if ([fileMgr fileExistsAtPath:newFullPath]) { + // A file already exists at the destination path. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:PATH_EXISTS_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + return; + } + // We're good to go! Write the file to the new destination. + ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; + Byte* buffer = (Byte*)malloc ([assetRepresentation size]); + NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil]; + NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; + [data writeToFile:newFullPath atomically:YES]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newFullPath isDirectory:NO]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + // We couldn't find the asset. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + }; + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:[NSURL URLWithString:srcFullPath] resultBlock:resultBlock failureBlock:failureBlock]; + return; + } else { + // Moving an assets library file is not doable, since we can't remove it. + errCode = INVALID_MODIFICATION_ERR; + } + } else { BOOL bSrcIsDir = NO; BOOL bDestIsDir = NO; BOOL bNewIsDir = NO; @@ -890,37 +944,67 @@ - (void)getFileMetadata:(CDVInvokedUrlCommand*)command // arguments NSString* argPath = [command.arguments objectAtIndex:0]; - // return unsupported result for assets-library URLs - if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFileMetadata not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - - CDVPluginResult* result = nil; + __block CDVPluginResult* result = nil; NSString* fullPath = argPath; // [self getFullPath: argPath]; if (fullPath) { - NSFileManager* fileMgr = [[NSFileManager alloc] init]; - BOOL bIsDir = NO; - // make sure it exists and is not a directory - BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir]; - if (!bExists || bIsDir) { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) { + // In this case, we need to use an asynchronous method to retrieve the file. + // Because of this, we can't just assign to `result` and send it at the end of the method. + // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Populate the dictionary and send it off. + NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5]; + ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; + [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"]; + [fileInfo setObject:argPath forKey:@"fullPath"]; + NSString* filename = [assetRepresentation filename]; + [fileInfo setObject:filename forKey:@"name"]; + [fileInfo setObject:[self getMimeTypeFromPath:filename] forKey:@"type"]; + NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate]; + NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000]; + [fileInfo setObject:msDate forKey:@"lastModifiedDate"]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + // We couldn't find the asset. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + }; + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock]; + return; } else { - // create dictionary of file info - NSError* __autoreleasing error = nil; - NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error]; - NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5]; - [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"]; - [fileInfo setObject:argPath forKey:@"fullPath"]; - [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping - [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"]; - NSDate* modDate = [fileAttrs fileModificationDate]; - NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000]; - [fileInfo setObject:msDate forKey:@"lastModifiedDate"]; - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]; + NSFileManager* fileMgr = [[NSFileManager alloc] init]; + BOOL bIsDir = NO; + // make sure it exists and is not a directory + BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir]; + if (!bExists || bIsDir) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + } else { + // create dictionary of file info + NSError* __autoreleasing error = nil; + NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error]; + NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5]; + [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"]; + [fileInfo setObject:argPath forKey:@"fullPath"]; + [fileInfo setObject:@"" forKey:@"type"]; // can't easily get the mimetype unless create URL, send request and read response so skipping + [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"]; + NSDate* modDate = [fileAttrs fileModificationDate]; + NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000]; + [fileInfo setObject:msDate forKey:@"lastModifiedDate"]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]; + } } } if (!result) { @@ -991,19 +1075,15 @@ - (void)readAsText:(CDVInvokedUrlCommand*)command end = [[command.arguments objectAtIndex:3] integerValue]; } - // return unsupported result for assets-library URLs - if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readAsText not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - // NSString* encoding = [command.arguments objectAtIndex:2]; // not currently used CDVPluginResult* result = nil; NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:argPath]; - if (!file) { + if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { + // can't read assets-library URLs as text + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR]; + } else if (!file) { // invalid path entry result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; } else { @@ -1054,18 +1134,41 @@ - (void)readAsDataURL:(CDVInvokedUrlCommand*)command end = [[command.arguments objectAtIndex:2] integerValue]; } - // return unsupported result for assets-library URLs - if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readAsDataURL not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - CDVFileError errCode = ABORT_ERR; - CDVPluginResult* result = nil; + __block CDVPluginResult* result = nil; if (!argPath) { errCode = SYNTAX_ERR; + } else if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { + // In this case, we need to use an asynchronous method to retrieve the file. + // Because of this, we can't just assign to `result` and send it at the end of the method. + // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Get the data and send it off. + ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; + Byte* buffer = (Byte*)malloc ([assetRepresentation size]); + NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil]; + NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; + NSString* mimeType = [self getMimeTypeFromPath:[assetRepresentation filename]]; + NSString* dataString = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, [data base64EncodedString]]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:dataString]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } else { + // We couldn't find the asset. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + }; + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send the appropriate error. + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock]; + return; } else { NSString* mimeType = [self getMimeTypeFromPath:argPath]; if (!mimeType) { @@ -1134,9 +1237,9 @@ - (void)truncate:(CDVInvokedUrlCommand*)command NSString* argPath = [command.arguments objectAtIndex:0]; unsigned long long pos = (unsigned long long)[[command.arguments objectAtIndex:1] longLongValue]; - // return unsupported result for assets-library URLs + // assets-library files can't be truncated if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"truncate not supported for assets-library URLs."]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } @@ -1181,9 +1284,9 @@ - (void)write:(CDVInvokedUrlCommand*)command NSString* argData = [arguments objectAtIndex:1]; unsigned long long pos = (unsigned long long)[[arguments objectAtIndex:2] longLongValue]; - // return unsupported result for assets-library URLs + // text can't be written into assets-library files if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"write not supported for assets-library URLs."]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR]; [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; return; } diff --git a/CordovaLib/Classes/CDVFileTransfer.h b/CordovaLib/Classes/CDVFileTransfer.h index 5392236..f96bb7d 100755 --- a/CordovaLib/Classes/CDVFileTransfer.h +++ b/CordovaLib/Classes/CDVFileTransfer.h @@ -50,7 +50,8 @@ extern NSString* const kOptionsKeyCookie; - (NSMutableDictionary*)createFileTransferError:(int)code AndSource :(NSString*)source AndTarget :(NSString*)target - AndHttpStatus :(int)httpStatus; + AndHttpStatus :(int)httpStatus + AndBody :(NSString*)body; @property (readonly) NSMutableDictionary* activeTransfers; @end @@ -64,6 +65,7 @@ extern NSString* const kOptionsKeyCookie; @property (nonatomic, copy) NSString* objectId; @property (nonatomic, copy) NSString* source; @property (nonatomic, copy) NSString* target; +@property (nonatomic, copy) NSString* mimeType; @property (assign) int responseCode; // atomic @property (nonatomic, assign) NSInteger bytesTransfered; @property (nonatomic, assign) NSInteger bytesExpected; diff --git a/CordovaLib/Classes/CDVFileTransfer.m b/CordovaLib/Classes/CDVFileTransfer.m index 8e05658..4ccdce6 100755 --- a/CordovaLib/Classes/CDVFileTransfer.m +++ b/CordovaLib/Classes/CDVFileTransfer.m @@ -19,7 +19,10 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDV.h" -#include +#import +#import +#import +#import @interface CDVFileTransfer () // Sets the requests headers for the request. @@ -27,7 +30,7 @@ - (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLReques // Creates a delegate to handle an upload. - (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command; // Creates an NSData* for the file for the given upload arguments. -- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command; +- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command; @end // Buffer size to use for streaming uploads. @@ -254,35 +257,60 @@ - (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)comm return delegate; } -- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command +- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command { NSString* target = (NSString*)[command.arguments objectAtIndex:0]; NSError* __autoreleasing err = nil; - // Extract the path part out of a file: URL. - NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path]; - // Memory map the file so that it can be read efficiently even if it is large. - NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err]; + // return unsupported result for assets-library URLs + if ([target hasPrefix:kCDVAssetsLibraryPrefix]) { + // Instead, we return after calling the asynchronous method and send `result` in each of the blocks. + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Get the data and send it off. + ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; + Byte* buffer = (Byte*)malloc ([assetRepresentation size]); + NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil]; + NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; + [self uploadData:fileData command:command]; + } else { + // We couldn't find the asset. Send the appropriate error. + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + } + }; + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send the appropriate error. + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:[NSURL URLWithString:target] resultBlock:resultBlock failureBlock:failureBlock]; + return; + } else { + // Extract the path part out of a file: URL. + NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path]; + + // Memory map the file so that it can be read efficiently even if it is large. + NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err]; - if (err != nil) { - NSLog(@"Error opening file %@: %@", target, err); + if (err != nil) { + NSLog (@"Error opening file %@: %@", target, err); + } + [self uploadData:fileData command:command]; } - return fileData; } - (void)upload:(CDVInvokedUrlCommand*)command { - NSString* argPath = [command.arguments objectAtIndex:0]; - - // return unsupported result for assets-library URLs - if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"upload not supported for assets-library URLs."]; - [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; - return; - } - // fileData and req are split into helper functions to ease the unit testing of delegateForUpload. - NSData* fileData = [self fileDataForUploadCommand:command]; + // First, get the file data. This method will call `uploadData:command`. + [self fileDataForUploadCommand:command]; +} + +- (void)uploadData:(NSData*)fileData command:(CDVInvokedUrlCommand*)command +{ NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData]; if (req == nil) { @@ -396,13 +424,15 @@ - (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target AndHttpStatus:(int)httpStatus + AndBody:(NSString*)body { - NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:4]; + NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:5]; [result setObject:[NSNumber numberWithInt:code] forKey:@"code"]; [result setObject:source forKey:@"source"]; [result setObject:target forKey:@"target"]; [result setObject:[NSNumber numberWithInt:httpStatus] forKey:@"http_status"]; + [result setObject:body forKey:@"body"]; NSLog(@"FileTransferError %@", result); return result; @@ -426,7 +456,8 @@ @implementation CDVFileTransferDelegate - (void)connectionDidFinishLoading:(NSURLConnection*)connection { NSString* uploadResponse = nil; - BOOL downloadResponse; + NSString* downloadResponse = nil; + BOOL downloadWriteOK = NO; NSMutableDictionary* uploadResult; CDVPluginResult* result = nil; NSError* __autoreleasing error = nil; @@ -437,9 +468,10 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection NSLog(@"File Transfer Finished with response code %d", self.responseCode); if (self.direction == CDV_TRANSFER_UPLOAD) { + uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + if ((self.responseCode >= 200) && (self.responseCode < 300)) { // create dictionary to return FileUploadResult object - uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; uploadResult = [NSMutableDictionary dictionaryWithCapacity:3]; if (uploadResponse != nil) { [uploadResult setObject:uploadResponse forKey:@"response"]; @@ -448,7 +480,7 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection [uploadResult setObject:[NSNumber numberWithInt:self.responseCode] forKey:@"responseCode"]; result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadResult]; } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:uploadResponse]]; } } if (self.direction == CDV_TRANSFER_DOWNLOAD) { @@ -464,11 +496,12 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection [[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:nil]; } - downloadResponse = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error]; + downloadWriteOK = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error]; - if (downloadResponse == NO) { + if (downloadWriteOK == NO) { // send our results back - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]]; + downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]]; } else { DLog(@"File Transfer Download success"); @@ -479,10 +512,13 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection } @catch(id exception) { // jump back to main thread - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]]; + downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]]; } } else { - result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]]; + downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]]; } } @@ -494,6 +530,8 @@ - (void)connectionDidFinishLoading:(NSURLConnection*)connection - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response { + self.mimeType = [response MIMEType]; + // required for iOS 4.3, for some reason; response is // a plain NSURLResponse, not the HTTP subclass if ([response isKindOfClass:[NSHTTPURLResponse class]]) { @@ -513,7 +551,8 @@ - (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLRespons - (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error { - CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]]; + NSString* body = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding]; + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:body]]; NSLog(@"File Transfer Error: %@", [error localizedDescription]); diff --git a/CordovaLib/Classes/CDVInAppBrowser.m b/CordovaLib/Classes/CDVInAppBrowser.m index 45bf705..14671a5 100755 --- a/CordovaLib/Classes/CDVInAppBrowser.m +++ b/CordovaLib/Classes/CDVInAppBrowser.m @@ -19,7 +19,6 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDVInAppBrowser.h" #import "CDVPluginResult.h" -#import "CDVViewController.h" #import "CDVUserAgentUtil.h" #define kInAppBrowserTargetSelf @"_self" @@ -143,18 +142,7 @@ - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options - (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) { + if ([self.commandDelegate URLIsWhitelisted:url]) { NSURLRequest* request = [NSURLRequest requestWithURL:url]; [self.webView loadRequest:request]; } else { // this assumes the InAppBrowser can be excepted from the white-list @@ -374,15 +362,13 @@ - (void)viewDidLoad - (void)viewDidUnload { [self.webView loadHTMLString:nil baseURL:nil]; + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; [super viewDidUnload]; } - (void)close { - if (_userAgentLockToken != 0) { - [CDVUserAgentUtil releaseLock:_userAgentLockToken]; - _userAgentLockToken = 0; - } + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; if ([self respondsToSelector:@selector(presentingViewController)]) { [[self presentingViewController] dismissViewControllerAnimated:YES completion:nil]; @@ -398,6 +384,7 @@ - (void)close - (void)navigateTo:(NSURL*)url { NSURLRequest* request = [NSURLRequest requestWithURL:url]; + _requestedURL = url; if (_userAgentLockToken != 0) { diff --git a/CordovaLib/Classes/CDVLocalStorage.h b/CordovaLib/Classes/CDVLocalStorage.h index e5e3112..cc6613f 100755 --- a/CordovaLib/Classes/CDVLocalStorage.h +++ b/CordovaLib/Classes/CDVLocalStorage.h @@ -22,7 +22,7 @@ #define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain" #define kCDVLocalStorageFileOperationError 1 -@interface CDVLocalStorage : CDVPlugin +@interface CDVLocalStorage : CDVPlugin @property (nonatomic, readonly, strong) NSMutableArray* backupInfo; diff --git a/CordovaLib/Classes/CDVLocalStorage.m b/CordovaLib/Classes/CDVLocalStorage.m index 217f611..68175f1 100755 --- a/CordovaLib/Classes/CDVLocalStorage.m +++ b/CordovaLib/Classes/CDVLocalStorage.m @@ -31,21 +31,13 @@ @implementation CDVLocalStorage @synthesize backupInfo, webviewDelegate; -- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings +- (void)pluginInitialize { - self = (CDVLocalStorage*)[super initWithWebView:theWebView settings:classSettings]; - if (self) { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive) - name:UIApplicationWillResignActiveNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive) + name:UIApplicationWillResignActiveNotification object:nil]; + BOOL cloudBackup = [@"cloud" isEqualToString:self.commandDelegate.settings[@"BackupWebStorage"]]; - self.backupInfo = [[self class] createBackupInfoWithCloudBackup:[(NSString*)[classSettings objectForKey:@"backupType"] isEqualToString:@"cloud"]]; - - // over-ride current webview delegate (for restore reasons) - self.webviewDelegate = theWebView.delegate; - theWebView.delegate = self; - } - - return self; + self.backupInfo = [[self class] createBackupInfoWithCloudBackup:cloudBackup]; } #pragma mark - @@ -411,37 +403,9 @@ - (void)onAppTerminate [self onResignActive]; } -#pragma mark - -#pragma mark UIWebviewDelegate implementation and forwarding - -- (void)webViewDidStartLoad:(UIWebView*)theWebView +- (void)onReset { [self restore:nil]; - - return [self.webviewDelegate webViewDidStartLoad:theWebView]; -} - -- (void)webViewDidFinishLoad:(UIWebView*)theWebView -{ - return [self.webviewDelegate webViewDidFinishLoad:theWebView]; -} - -- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error -{ - return [self.webviewDelegate webView:theWebView didFailLoadWithError:error]; -} - -- (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType -{ - return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; -} - -#pragma mark - -#pragma mark Over-rides - -- (void)dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver:self]; // this will remove all notification unless added using addObserverForName:object:queue:usingBlock: } @end diff --git a/CordovaLib/Classes/CDVLocation.m b/CordovaLib/Classes/CDVLocation.m index 9814f35..07af30e 100755 --- a/CordovaLib/Classes/CDVLocation.m +++ b/CordovaLib/Classes/CDVLocation.m @@ -18,7 +18,6 @@ Licensed to the Apache Software Foundation (ASF) under one */ #import "CDVLocation.h" -#import "CDVViewController.h" #import "NSArray+Comparisons.h" #pragma mark Constants @@ -477,17 +476,8 @@ - (void)stopHeading:(CDVInvokedUrlCommand*)command // helper method to check the orientation and start updating headings - (void)startHeadingWithFilter:(CLLocationDegrees)filter { - if ([self.locationManager respondsToSelector:@selector(headingOrientation)]) { - UIDeviceOrientation currentOrientation = [[UIDevice currentDevice] orientation]; - if (currentOrientation != UIDeviceOrientationUnknown) { - CDVViewController* cdvViewController = (CDVViewController*)self.viewController; - - if ([cdvViewController supportsOrientation:currentOrientation]) { - self.locationManager.headingOrientation = (CLDeviceOrientation)currentOrientation; - // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same - } - } - } + // FYI UIDeviceOrientation and CLDeviceOrientation enums are currently the same + self.locationManager.headingOrientation = (CLDeviceOrientation)self.viewController.interfaceOrientation; self.locationManager.headingFilter = filter; [self.locationManager startUpdatingHeading]; self.headingData.headingStatus = HEADINGSTARTING; diff --git a/CordovaLib/Classes/CDVPlugin.h b/CordovaLib/Classes/CDVPlugin.h index 8c59a2b..f5b50eb 100755 --- a/CordovaLib/Classes/CDVPlugin.h +++ b/CordovaLib/Classes/CDVPlugin.h @@ -23,6 +23,7 @@ #import "NSMutableArray+QueueAdditions.h" #import "CDVCommandDelegate.h" +#define CDVPageDidLoadNotification @"CDVPageDidLoadNotification" #define CDVPluginHandleOpenURLNotification @"CDVPluginHandleOpenURLNotification" #define CDVPluginResetNotification @"CDVPluginResetNotification" #define CDVLocalNotification @"CDVLocalNotification" @@ -30,14 +31,13 @@ @interface CDVPlugin : NSObject {} @property (nonatomic, weak) UIWebView* webView; -@property (nonatomic, strong) NSDictionary* settings; @property (nonatomic, weak) UIViewController* viewController; @property (nonatomic, weak) id commandDelegate; @property (readonly, assign) BOOL hasPendingOperation; -- (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings; - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView; +- (void)pluginInitialize; - (void)handleOpenURL:(NSNotification*)notification; - (void)onAppTerminate; diff --git a/CordovaLib/Classes/CDVPlugin.m b/CordovaLib/Classes/CDVPlugin.m index 53023c7..a42d241 100755 --- a/CordovaLib/Classes/CDVPlugin.m +++ b/CordovaLib/Classes/CDVPlugin.m @@ -26,16 +26,12 @@ @interface CDVPlugin () @end @implementation CDVPlugin -@synthesize webView, settings, viewController, commandDelegate, hasPendingOperation; +@synthesize webView, viewController, commandDelegate, hasPendingOperation; +// Do not override these methods. Use pluginInitialize instead. - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView settings:(NSDictionary*)classSettings { - self = [self initWithWebView:theWebView]; - if (self) { - self.settings = classSettings; - self.hasPendingOperation = NO; - } - return self; + return [self initWithWebView:theWebView]; } - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView @@ -48,26 +44,29 @@ - (CDVPlugin*)initWithWebView:(UIWebView*)theWebView [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onReset) name:CDVPluginResetNotification object:nil]; self.webView = theWebView; - - // You can listen to more app notifications, see: - // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 - - /* - // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; - - // Added in 2.3.0+ - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil]; - - */ } return self; } +- (void)pluginInitialize +{ + // You can listen to more app notifications, see: + // http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIApplication_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40006728-CH3-DontLinkElementID_4 + + // NOTE: if you want to use these, make sure you uncomment the corresponding notification handler + + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onPause) name:UIApplicationDidEnterBackgroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResume) name:UIApplicationWillEnterForegroundNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationDidChange) name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; + + // Added in 2.3.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didReceiveLocalNotification:) name:CDVLocalNotification object:nil]; + + // Added in 2.5.0 + // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad:) name:CDVPageDidLoadNotification object:nil]; +} + - (void)dispose { viewController = nil; @@ -140,9 +139,9 @@ - (NSString*)error:(CDVPluginResult*)pluginResult callbackId:(NSString*)callback } // default implementation does nothing, ideally, we are not registered for notification if we aren't going to do anything. -//- (void)didReceiveLocalNotification:(NSNotification *)notification -//{ +// - (void)didReceiveLocalNotification:(NSNotification *)notification +// { // // UILocalNotification* localNotification = [notification object]; // get the payload as a LocalNotification -//} +// } @end diff --git a/CordovaLib/Classes/CDVSound.h b/CordovaLib/Classes/CDVSound.h index 6551621..8dcf98e 100755 --- a/CordovaLib/Classes/CDVSound.h +++ b/CordovaLib/Classes/CDVSound.h @@ -98,7 +98,13 @@ typedef NSUInteger CDVMediaMsg; - (BOOL)hasAudioSession; // helper methods -- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId; +- (NSURL*)urlForRecording:(NSString*)resourcePath; +- (NSURL*)urlForPlaying:(NSString*)resourcePath; +- (NSURL*)urlForResource:(NSString*)resourcePath CDV_DEPRECATED(2.5, "Use specific api for playing or recording"); + +- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId CDV_DEPRECATED(2.5, "Use updated audioFileForResource api"); + +- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord; - (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId; - (NSString*)createMediaErrorWithCode:(CDVMediaError)code message:(NSString*)message; diff --git a/CordovaLib/Classes/CDVSound.m b/CordovaLib/Classes/CDVSound.m index e6f729a..99515d7 100755 --- a/CordovaLib/Classes/CDVSound.m +++ b/CordovaLib/Classes/CDVSound.m @@ -18,20 +18,18 @@ Licensed to the Apache Software Foundation (ASF) under one */ #import "CDVSound.h" -#import "CDVViewController.h" #import "NSArray+Comparisons.h" #import "CDVJSON.h" #define DOCUMENTS_SCHEME_PREFIX @"documents://" #define HTTP_SCHEME_PREFIX @"http://" #define HTTPS_SCHEME_PREFIX @"https://" +#define RECORDING_WAV @"wav" @implementation CDVSound @synthesize soundCache, avSession; -// Maps a url for a resource path -// "Naked" resource paths are assumed to be from the www folder as its base - (NSURL*)urlForResource:(NSString*)resourcePath { NSURL* resourceURL = nil; @@ -44,7 +42,8 @@ - (NSURL*)urlForResource:(NSString*)resourcePath NSLog(@"Will use resource '%@' from the Internet.", resourcePath); resourceURL = [NSURL URLWithString:resourcePath]; } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) { - filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", [CDVViewController applicationDocumentsDirectory]]]; + NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]]; NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath); } else { // attempt to find file path in www directory @@ -71,8 +70,100 @@ - (NSURL*)urlForResource:(NSString*)resourcePath return resourceURL; } -// Creates or gets the cached audio file resource object +// Maps a url for a resource path for recording +- (NSURL*)urlForRecording:(NSString*)resourcePath +{ + NSURL* resourceURL = nil; + NSString* filePath = nil; + NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + + // first check for correct extension + if ([[resourcePath pathExtension] caseInsensitiveCompare:RECORDING_WAV] != NSOrderedSame) { + resourceURL = nil; + NSLog(@"Resource for recording must have %@ extension", RECORDING_WAV); + } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) { + // try to find Documents:// resources + filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]]; + NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath); + } else { + // if resourcePath is not from FileSystem put in tmp dir, else attempt to use provided resource path + NSString* tmpPath = [NSTemporaryDirectory ()stringByStandardizingPath]; + BOOL isTmp = [resourcePath rangeOfString:tmpPath].location != NSNotFound; + BOOL isDoc = [resourcePath rangeOfString:docsPath].location != NSNotFound; + if (!isTmp && !isDoc) { + // put in temp dir + filePath = [NSString stringWithFormat:@"%@/%@", tmpPath, resourcePath]; + } else { + filePath = resourcePath; + } + } + + if (filePath != nil) { + // create resourceURL + resourceURL = [NSURL fileURLWithPath:filePath]; + } + return resourceURL; +} + +// Maps a url for a resource path for playing +// "Naked" resource paths are assumed to be from the www folder as its base +- (NSURL*)urlForPlaying:(NSString*)resourcePath +{ + NSURL* resourceURL = nil; + NSString* filePath = nil; + + // first try to find HTTP:// or Documents:// resources + + if ([resourcePath hasPrefix:HTTP_SCHEME_PREFIX] || [resourcePath hasPrefix:HTTPS_SCHEME_PREFIX]) { + // if it is a http url, use it + NSLog(@"Will use resource '%@' from the Internet.", resourcePath); + resourceURL = [NSURL URLWithString:resourcePath]; + } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) { + NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0]; + filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]]; + NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath); + } else { + // attempt to find file path in www directory or LocalFileSystem.TEMPORARY directory + filePath = [self.commandDelegate pathForResource:resourcePath]; + if (filePath == nil) { + // see if this exists in the documents/temp directory from a previous recording + NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory ()stringByStandardizingPath], resourcePath]; + if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) { + // inefficient as existence will be checked again below but only way to determine if file exists from previous recording + filePath = testPath; + NSLog(@"Will attempt to use file resource from LocalFileSystem.TEMPORARY directory"); + } else { + // attempt to use path provided + filePath = resourcePath; + NSLog(@"Will attempt to use file resource '%@'", filePath); + } + } else { + NSLog(@"Found resource '%@' in the web folder.", filePath); + } + } + // check that file exists for all but HTTP_SHEME_PREFIX + if (filePath != nil) { + // create resourceURL + resourceURL = [NSURL fileURLWithPath:filePath]; + // try to access file + NSFileManager* fMgr = [NSFileManager defaultManager]; + if (![fMgr fileExistsAtPath:filePath]) { + resourceURL = nil; + NSLog(@"Unknown resource '%@'", resourcePath); + } + } + + return resourceURL; +} + - (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId +{ + // will maintain backwards compatibility with original implementation + return [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO]; +} + +// Creates or gets the cached audio file resource object +- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord { BOOL bError = NO; CDVMediaError errcode = MEDIA_ERR_NONE_SUPPORTED; @@ -88,31 +179,37 @@ - (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*) } if (audioFile == nil) { // validate resourcePath and create - if ((resourcePath == nil) || ![resourcePath isKindOfClass:[NSString class]] || [resourcePath isEqualToString:@""]) { bError = YES; errcode = MEDIA_ERR_ABORTED; errMsg = @"invalid media src argument"; } else { - resourceURL = [self urlForResource:resourcePath]; + audioFile = [[CDVAudioFile alloc] init]; + audioFile.resourcePath = resourcePath; + audioFile.resourceURL = nil; // validate resourceURL when actually play or record + [[self soundCache] setObject:audioFile forKey:mediaId]; + } + } + if (bValidate && (audioFile.resourceURL == nil)) { + if (bRecord) { + resourceURL = [self urlForRecording:resourcePath]; + } else { + resourceURL = [self urlForPlaying:resourcePath]; } - if (resourceURL == nil) { bError = YES; errcode = MEDIA_ERR_ABORTED; errMsg = [NSString stringWithFormat:@"Cannot use audio file from resource '%@'", resourcePath]; - } - if (bError) { - // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, errcode]; - jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:errcode message:errMsg]]; - [self.commandDelegate evalJs:jsString]; } else { - audioFile = [[CDVAudioFile alloc] init]; - audioFile.resourcePath = resourcePath; audioFile.resourceURL = resourceURL; - [[self soundCache] setObject:audioFile forKey:mediaId]; } } + + if (bError) { + jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:errcode message:errMsg]]; + [self.commandDelegate evalJs:jsString]; + } + return audioFile; } @@ -150,7 +247,7 @@ - (void)create:(CDVInvokedUrlCommand*)command NSString* mediaId = [command.arguments objectAtIndex:0]; NSString* resourcePath = [command.arguments objectAtIndex:1]; - CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId]; + CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:NO forRecording:NO]; if (audioFile == nil) { NSString* errorMessage = [NSString stringWithFormat:@"Failed to initialize Media file with path %@", resourcePath]; @@ -194,9 +291,8 @@ - (void)startPlayingAudio:(CDVInvokedUrlCommand*)command BOOL bError = NO; NSString* jsString = nil; - CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId]; - - if (audioFile != nil) { + CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO]; + if ((audioFile != nil) && (audioFile.resourceURL != nil)) { if (audioFile.player == nil) { bError = [self prepareToPlay:audioFile withId:mediaId]; } @@ -275,7 +371,12 @@ - (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId if ([resourceURL isFileURL]) { audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:resourceURL error:&playerError]; } else { - NSURLRequest* request = [NSURLRequest requestWithURL:resourceURL]; + NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:resourceURL]; + NSString* userAgent = [self.commandDelegate userAgent]; + if (userAgent) { + [request setValue:userAgent forHTTPHeaderField:@"User-Agent"]; + } + NSURLResponse* __autoreleasing response = nil; NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&playerError]; if (playerError) { @@ -412,11 +513,11 @@ - (void)startRecordingAudio:(CDVInvokedUrlCommand*)command #pragma unused(callbackId) NSString* mediaId = [command.arguments objectAtIndex:0]; - CDVAudioFile* audioFile = [self audioFileForResource:[command.arguments objectAtIndex:1] withId:mediaId]; + CDVAudioFile* audioFile = [self audioFileForResource:[command.arguments objectAtIndex:1] withId:mediaId doValidation:YES forRecording:YES]; NSString* jsString = nil; NSString* errorMsg = @""; - if (audioFile != nil) { + if ((audioFile != nil) && (audioFile.resourceURL != nil)) { NSError* __autoreleasing error = nil; if (audioFile.recorder != nil) { @@ -463,9 +564,9 @@ - (void)startRecordingAudio:(CDVInvokedUrlCommand*)command jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]]; } } else { - // file does not exist - NSLog(@"Could not start recording audio, file '%@' does not exist.", audioFile.resourcePath); - jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:@"File to record to does not exist"]]; + // file did not validate + NSString* errorMsg = [NSString stringWithFormat:@"Could not record audio at '%@'", audioFile.resourcePath]; + jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]]; } if (jsString) { [self.commandDelegate evalJs:jsString]; diff --git a/CordovaLib/Classes/CDVSplashScreen.h b/CordovaLib/Classes/CDVSplashScreen.h index b0d8615..a0868a0 100755 --- a/CordovaLib/Classes/CDVSplashScreen.h +++ b/CordovaLib/Classes/CDVSplashScreen.h @@ -20,7 +20,11 @@ #import #import "CDVPlugin.h" -@interface CDVSplashScreen : CDVPlugin {} +@interface CDVSplashScreen : CDVPlugin { + UIActivityIndicatorView* _activityView; + UIImageView* _imageView; + UIView* _parentView; +} - (void)show:(CDVInvokedUrlCommand*)command; - (void)hide:(CDVInvokedUrlCommand*)command; diff --git a/CordovaLib/Classes/CDVSplashScreen.m b/CordovaLib/Classes/CDVSplashScreen.m index 2512328..cba1b53 100755 --- a/CordovaLib/Classes/CDVSplashScreen.m +++ b/CordovaLib/Classes/CDVSplashScreen.m @@ -18,32 +18,157 @@ Licensed to the Apache Software Foundation (ASF) under one */ #import "CDVSplashScreen.h" -#import "CDVViewController.h" + +#define kSplashScreenStateShow 0 +#define kSplashScreenStateHide 1 + +#define kSplashScreenDurationDefault 0.25f @implementation CDVSplashScreen -- (void)__show:(BOOL)show +- (void)pluginInitialize { - // Legacy support - once deprecated classes removed, clean this up - id delegate = [[UIApplication sharedApplication] delegate]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pageDidLoad) name:CDVPageDidLoadNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onOrientationWillChange:) name:UIApplicationWillChangeStatusBarOrientationNotification object:nil]; - if ([delegate respondsToSelector:@selector(viewController)]) { - id vc = [delegate performSelector:@selector(viewController)]; - if ([vc isKindOfClass:[CDVViewController class]]) { - ((CDVViewController*)vc).imageView.hidden = !show; - ((CDVViewController*)vc).activityView.hidden = !show; - } - } + [self show:nil]; } - (void)show:(CDVInvokedUrlCommand*)command { - [self __show:YES]; + [self updateSplashScreenWithState:kSplashScreenStateShow]; } - (void)hide:(CDVInvokedUrlCommand*)command { - [self __show:NO]; + [self updateSplashScreenWithState:kSplashScreenStateHide]; +} + +- (void)pageDidLoad +{ + id autoHideSplashScreenValue = [self.commandDelegate.settings objectForKey:@"AutoHideSplashScreen"]; + + // if value is missing, default to yes + if ((autoHideSplashScreenValue == nil) || [autoHideSplashScreenValue boolValue]) { + [self hide:nil]; + } +} + +- (void)onOrientationWillChange:(NSNotification*)notification +{ + if (_imageView != nil) { + UIInterfaceOrientation orientation = [notification.userInfo[UIApplicationStatusBarOrientationUserInfoKey] intValue]; + [self updateSplashImageForOrientation:orientation]; + } +} + +- (void)createViews +{ + /* + * 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.commandDelegate.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; + } + + _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:topActivityIndicatorStyle]; + _activityView.tag = 2; + _activityView.center = self.viewController.view.center; + [_activityView startAnimating]; + + _imageView = [[UIImageView alloc] init]; + [self.viewController.view addSubview:_imageView]; + [self.viewController.view.superview addSubview:_activityView]; + [self.viewController.view.superview layoutSubviews]; +} + +- (void)updateSplashImageForOrientation:(UIInterfaceOrientation)orientation +{ + // IPHONE (default) + NSString* imageName = @"Default"; + + if (CDV_IsIPhone5()) { + imageName = [imageName stringByAppendingString:@"-568h"]; + } else if (CDV_IsIPad()) { + // set default to portrait upside down + imageName = @"Default-Portrait"; // @"Default-PortraitUpsideDown.png"; + + if (orientation == UIInterfaceOrientationLandscapeLeft) { + imageName = @"Default-Landscape.png"; // @"Default-LandscapeLeft.png"; + } else if (orientation == UIInterfaceOrientationLandscapeRight) { + imageName = @"Default-Landscape.png"; // @"Default-LandscapeRight.png"; + } + } + + _imageView.image = [UIImage imageNamed:imageName]; + _imageView.frame = CGRectMake(0, 0, _imageView.image.size.width, _imageView.image.size.height); +} + +- (void)updateSplashScreenWithState:(int)state +{ + float toAlpha = state == kSplashScreenStateShow ? 1.0f : 0.0f; + BOOL hidden = state == kSplashScreenStateShow ? NO : YES; + + id fadeSplashScreenValue = [self.commandDelegate.settings objectForKey:@"FadeSplashScreen"]; + id fadeSplashScreenDuration = [self.commandDelegate.settings objectForKey:@"FadeSplashScreenDuration"]; + + float fadeDuration = fadeSplashScreenDuration == nil ? kSplashScreenDurationDefault : [fadeSplashScreenDuration floatValue]; + + if ((fadeSplashScreenValue == nil) || ![fadeSplashScreenValue boolValue]) { + fadeDuration = 0; + } + if (hidden && (_imageView == nil)) { + return; + } else if (_imageView == nil) { + [self createViews]; + fadeDuration = 0; + } + + if (!hidden) { + [self updateSplashImageForOrientation:self.viewController.interfaceOrientation]; + } + + if (fadeDuration == 0) { + [_imageView setHidden:hidden]; + [_activityView setHidden:hidden]; + } else { + if (state == kSplashScreenStateShow) { + // reset states + [_imageView setHidden:NO]; + [_activityView setHidden:NO]; + [_imageView setAlpha:0.0f]; + [_activityView setAlpha:0.0f]; + } + + [UIView transitionWithView:self.viewController.view + duration:fadeDuration + options:UIViewAnimationOptionTransitionNone + animations:^(void) { + [_imageView setAlpha:toAlpha]; + [_activityView setAlpha:toAlpha]; + } + completion:^(BOOL finished) { + if (state == kSplashScreenStateHide) { + // Clean-up resources. + [_imageView removeFromSuperview]; + [_activityView removeFromSuperview]; + _imageView = nil; + _activityView = nil; + } + }]; + } } @end diff --git a/CordovaLib/Classes/CDVURLProtocol.m b/CordovaLib/Classes/CDVURLProtocol.m index a645288..1959c77 100755 --- a/CordovaLib/Classes/CDVURLProtocol.m +++ b/CordovaLib/Classes/CDVURLProtocol.m @@ -17,14 +17,17 @@ Licensed to the Apache Software Foundation (ASF) under one under the License. */ +#import +#import +#import +#import #import "CDVURLProtocol.h" #import "CDVCommandQueue.h" #import "CDVWhitelist.h" #import "CDVViewController.h" +#import "CDVFile.h" @interface CDVHTTPURLResponse : NSHTTPURLResponse -- (id)initWithUnauthorizedURL:(NSURL*)url; -- (id)initWithBlankResponse:(NSURL*)url; @property (nonatomic) NSInteger statusCode; @end @@ -106,7 +109,9 @@ + (BOOL)canInitWithRequest:(NSURLRequest*)theRequest NSURL* theUrl = [theRequest URL]; CDVViewController* viewController = viewControllerForRequest(theRequest); - if (viewController != nil) { + if ([[theUrl absoluteString] hasPrefix:kCDVAssetsLibraryPrefix]) { + return YES; + } else if (viewController != nil) { if ([[theUrl path] isEqualToString:@"/!gap_exec"]) { NSString* queuedCommandsJSON = [theRequest valueForHTTPHeaderField:@"cmds"]; NSString* requestId = [theRequest valueForHTTPHeaderField:@"rc"]; @@ -151,21 +156,35 @@ - (void)startLoading NSURL* url = [[self request] URL]; if ([[url path] isEqualToString:@"/!gap_exec"]) { - CDVHTTPURLResponse* response = [[CDVHTTPURLResponse alloc] initWithBlankResponse:url]; - [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - [[self client] URLProtocolDidFinishLoading:self]; + [self sendResponseWithResponseCode:200 data:nil mimeType:nil]; + return; + } else if ([[url absoluteString] hasPrefix:kCDVAssetsLibraryPrefix]) { + ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) { + if (asset) { + // We have the asset! Get the data and send it along. + ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation]; + NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass ((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType); + Byte* buffer = (Byte*)malloc ([assetRepresentation size]); + NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil]; + NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES]; + [self sendResponseWithResponseCode:200 data:data mimeType:MIMEType]; + } else { + // Retrieving the asset failed for some reason. Send an error. + [self sendResponseWithResponseCode:404 data:nil mimeType:nil]; + } + }; + ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) { + // Retrieving the asset failed for some reason. Send an error. + [self sendResponseWithResponseCode:401 data:nil mimeType:nil]; + }; + + ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init]; + [assetsLibrary assetForURL:url resultBlock:resultBlock failureBlock:failureBlock]; return; } NSString* body = [gWhitelist errorStringForURL:url]; - - CDVHTTPURLResponse* response = [[CDVHTTPURLResponse alloc] initWithUnauthorizedURL:url]; - - [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; - - [[self client] URLProtocol:self didLoadData:[body dataUsingEncoding:NSASCIIStringEncoding]]; - - [[self client] URLProtocolDidFinishLoading:self]; + [self sendResponseWithResponseCode:401 data:[body dataUsingEncoding:NSASCIIStringEncoding] mimeType:nil]; } - (void)stopLoading @@ -178,29 +197,31 @@ + (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest return NO; } -@end - -@implementation CDVHTTPURLResponse -@synthesize statusCode; - -- (id)initWithUnauthorizedURL:(NSURL*)url +- (void)sendResponseWithResponseCode:(NSInteger)statusCode data:(NSData*)data mimeType:(NSString*)mimeType { - self = [super initWithURL:url MIMEType:@"text/plain" expectedContentLength:-1 textEncodingName:@"UTF-8"]; - if (self) { - self.statusCode = 401; + if (mimeType == nil) { + mimeType = @"text/plain"; } - return self; -} + NSString* encodingName = [@"text/plain" isEqualToString:mimeType] ? @"UTF-8" : nil; + CDVHTTPURLResponse* response = + [[CDVHTTPURLResponse alloc] initWithURL:[[self request] URL] + MIMEType:mimeType + expectedContentLength:[data length] + textEncodingName:encodingName]; + response.statusCode = statusCode; -- (id)initWithBlankResponse:(NSURL*)url -{ - self = [super initWithURL:url MIMEType:@"text/plain" expectedContentLength:-1 textEncodingName:@"UTF-8"]; - if (self) { - self.statusCode = 200; + [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed]; + if (data != nil) { + [[self client] URLProtocol:self didLoadData:data]; } - return self; + [[self client] URLProtocolDidFinishLoading:self]; } +@end + +@implementation CDVHTTPURLResponse +@synthesize statusCode; + - (NSDictionary*)allHeaderFields { return nil; diff --git a/CordovaLib/Classes/CDVUserAgentUtil.h b/CordovaLib/Classes/CDVUserAgentUtil.h index 662b674..4de382f 100755 --- a/CordovaLib/Classes/CDVUserAgentUtil.h +++ b/CordovaLib/Classes/CDVUserAgentUtil.h @@ -22,6 +22,6 @@ @interface CDVUserAgentUtil : NSObject + (NSString*)originalUserAgent; + (void)acquireLock:(void (^)(NSInteger lockToken))block; -+ (void)releaseLock:(NSInteger)lockToken; ++ (void)releaseLock:(NSInteger*)lockToken; + (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken; @end diff --git a/CordovaLib/Classes/CDVUserAgentUtil.m b/CordovaLib/Classes/CDVUserAgentUtil.m index e2baef0..5c43c51 100755 --- a/CordovaLib/Classes/CDVUserAgentUtil.m +++ b/CordovaLib/Classes/CDVUserAgentUtil.m @@ -84,11 +84,14 @@ + (void)acquireLock:(void (^)(NSInteger lockToken))block } } -+ (void)releaseLock:(NSInteger)lockToken ++ (void)releaseLock:(NSInteger*)lockToken { - NSAssert(gCurrentLockToken == lockToken, @"Got token %d, expected %d", lockToken, gCurrentLockToken); + if (*lockToken == 0) { + return; + } + NSAssert(gCurrentLockToken == *lockToken, @"Got token %d, expected %d", *lockToken, gCurrentLockToken); - VerboseLog(@"Released lock %d", lockToken); + VerboseLog(@"Released lock %d", *lockToken); if ([gPendingSetUserAgentBlocks count] > 0) { void (^block)() = [gPendingSetUserAgentBlocks objectAtIndex:0]; [gPendingSetUserAgentBlocks removeObjectAtIndex:0]; @@ -98,6 +101,7 @@ + (void)releaseLock:(NSInteger)lockToken } else { gCurrentLockToken = 0; } + *lockToken = 0; } + (void)setUserAgent:(NSString*)value lockToken:(NSInteger)lockToken diff --git a/CordovaLib/Classes/CDVViewController.h b/CordovaLib/Classes/CDVViewController.h index ed6f7fc..82e22f6 100755 --- a/CordovaLib/Classes/CDVViewController.h +++ b/CordovaLib/Classes/CDVViewController.h @@ -40,14 +40,11 @@ @property (nonatomic, readonly, strong) NSMutableDictionary* pluginObjects; @property (nonatomic, readonly, strong) NSDictionary* pluginsMap; -@property (nonatomic, readonly, strong) NSDictionary* settings; +@property (nonatomic, readonly, strong) NSMutableDictionary* settings; @property (nonatomic, readonly, strong) NSXMLParser* configParser; @property (nonatomic, readonly, strong) CDVWhitelist* whitelist; // readonly for public @property (nonatomic, readonly, assign) BOOL loadFromString; - -@property (nonatomic, readwrite, assign) BOOL useSplashScreen; -@property (nonatomic, readonly, strong) IBOutlet UIActivityIndicatorView* activityView; -@property (nonatomic, readonly, strong) UIImageView* imageView; +@property (nonatomic, readwrite, assign) BOOL useSplashScreen CDV_DEPRECATED(2.5, "Add/Remove the SplashScreen plugin instead of setting this property."); @property (nonatomic, readwrite, copy) NSString* wwwFolderName; @property (nonatomic, readwrite, copy) NSString* startPage; diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m index 36ae785..bec716d 100755 --- a/CordovaLib/Classes/CDVViewController.m +++ b/CordovaLib/Classes/CDVViewController.m @@ -23,23 +23,24 @@ Licensed to the Apache Software Foundation (ASF) under one #import "CDVCommandDelegateImpl.h" #import "CDVConfigParser.h" #import "CDVUserAgentUtil.h" +#import "CDVWebViewDelegate.h" #define degreesToRadian(x) (M_PI * (x) / 180.0) @interface CDVViewController () { NSInteger _userAgentLockToken; + CDVWebViewDelegate* _webViewDelegate; } @property (nonatomic, readwrite, strong) NSXMLParser* configParser; -@property (nonatomic, readwrite, strong) NSDictionary* settings; +@property (nonatomic, readwrite, strong) NSMutableDictionary* settings; @property (nonatomic, readwrite, strong) CDVWhitelist* whitelist; @property (nonatomic, readwrite, strong) NSMutableDictionary* pluginObjects; +@property (nonatomic, readwrite, strong) NSArray* startupPluginNames; @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; @@ -49,9 +50,9 @@ @interface CDVViewController () { @implementation CDVViewController @synthesize webView, supportedOrientations; -@synthesize pluginObjects, pluginsMap, whitelist; +@synthesize pluginObjects, pluginsMap, whitelist, startupPluginNames; @synthesize configParser, settings, loadFromString; -@synthesize imageView, activityView, useSplashScreen; +@synthesize useSplashScreen; @synthesize wwwFolderName, startPage, initialized, openURL; @synthesize commandDelegate = _commandDelegate; @synthesize commandQueue = _commandQueue; @@ -61,9 +62,6 @@ - (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:) @@ -87,6 +85,7 @@ - (void)__init // load config.xml settings [self loadSettings]; + useSplashScreen = YES; } } @@ -162,16 +161,20 @@ - (void)loadSettings [configParser parse]; // Get the plugin dictionary, whitelist and settings from the delegate. - self.pluginsMap = [delegate.pluginsDict dictionaryWithLowercaseKeys]; + self.pluginsMap = delegate.pluginsDict; + self.startupPluginNames = delegate.startupPluginNames; self.whitelist = [[CDVWhitelist alloc] initWithArray:delegate.whitelistHosts]; self.settings = delegate.settings; // And the start folder/page. self.wwwFolderName = @"www"; - self.startPage = [delegate getStartPage]; + self.startPage = delegate.startPage; + if (self.startPage == nil) { + self.startPage = @"index.html"; + } // Initialize the plugin objects dict. - self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:4]; + self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20]; } // Implement viewDidLoad to do additional setup after loading the view, typically from a nib. @@ -202,13 +205,14 @@ - (void)viewDidLoad NSString* backupWebStorageType = @"cloud"; // default value - id backupWebStorage = [self.settings objectForKey:@"BackupWebStorage"]; + id backupWebStorage = self.settings[@"BackupWebStorage"]; if ([backupWebStorage isKindOfClass:[NSString class]]) { backupWebStorageType = backupWebStorage; } else if ([backupWebStorage isKindOfClass:[NSNumber class]]) { NSLog(@"Deprecated: BackupWebStorage boolean property is a string property now (none, local, cloud). A boolean value of 'true' will be mapped to 'cloud'. Consult the docs: http://docs.cordova.io/en/edge/guide_project-settings_ios_index.md.html#Project%%20Settings%%20for%%20iOS"); backupWebStorageType = [(NSNumber*) backupWebStorage boolValue] ? @"cloud" : @"none"; } + self.settings[@"BackupWebStorage"] = backupWebStorageType; if (IsAtLeastiOSVersion(@"5.1")) { [CDVLocalStorage __fixupDatabaseLocationsWithBackupType:backupWebStorageType]; @@ -243,8 +247,7 @@ - (void)viewDidLoad */ 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])]; + [self registerPlugin:[[CDVLocalStorage alloc] initWithWebView:self.webView] withClassName:NSStringFromClass([CDVLocalStorage class])]; } /* @@ -304,6 +307,15 @@ - (void)viewDidLoad } } + for (NSString* pluginName in self.startupPluginNames) { + [self getCommandInstance:pluginName]; + } + + // TODO: Remove this explicit instantiation once we move to cordova-CLI. + if (useSplashScreen) { + [self getCommandInstance:@"splashscreen"]; + } + // ///////////////// [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) { _userAgentLockToken = lockToken; @@ -441,7 +453,8 @@ - (void)createGapView [self.view addSubview:self.webView]; [self.view sendSubviewToBack:self.webView]; - self.webView.delegate = self; + _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self]; + self.webView.delegate = _webViewDelegate; // register this viewcontroller with the NSURLProtocol, only after the User-Agent is set [CDVURLProtocol registerViewController:self]; @@ -480,6 +493,7 @@ - (void)viewDidUnload self.webView.delegate = nil; self.webView = nil; + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; } #pragma mark UIWebViewDelegate @@ -490,33 +504,19 @@ - (void)viewDidUnload */ - (void)webViewDidStartLoad:(UIWebView*)theWebView { + NSLog(@"Resetting plugins due to page load."); [_commandQueue resetRequestId]; - [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:nil]]; + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginResetNotification object:self.webView]]; } /** - Called when the webview finishes loading. This stops the activity view and closes the imageview + Called when the webview finishes loading. This stops the activity view. */ - (void)webViewDidFinishLoad:(UIWebView*)theWebView { - if (_userAgentLockToken != 0) { - [CDVUserAgentUtil releaseLock:_userAgentLockToken]; - _userAgentLockToken = 0; - } - - /* - * 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]]; + NSLog(@"Finished load of: %@", theWebView.request.URL); + // It's safe to release the lock even if this is just a sub-frame that's finished loading. + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; // The .onNativeReady().fire() will work when cordova.js is already loaded. // The _nativeReady = true; is used when this is run before cordova.js is loaded. @@ -524,22 +524,21 @@ - (void)webViewDidFinishLoad:(UIWebView*)theWebView // Don't use [commandDelegate evalJs] here since it relies on cordova.js being loaded already. [self.webView stringByEvaluatingJavaScriptFromString:nativeReady]; + /* + * Hide the Top Activity THROBBER in the Battery Bar + */ + [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; + [self processOpenUrl]; + + [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPageDidLoadNotification object:nil]]; } -- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error +- (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error { - if (_userAgentLockToken != 0) { - [CDVUserAgentUtil releaseLock:_userAgentLockToken]; - _userAgentLockToken = 0; - } + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; 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 @@ -595,8 +594,6 @@ - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest* * 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 { @@ -647,131 +644,6 @@ + (NSString*)applicationDocumentsDirectory 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 @@ -785,6 +657,7 @@ - (void)registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className } [self.pluginObjects setObject:plugin forKey:className]; + [plugin pluginInitialize]; } /** @@ -805,16 +678,9 @@ - (id)getCommandInstance:(NSString*)pluginName id obj = [self.pluginObjects objectForKey:className]; if (!obj) { - // attempt to load the settings for this command class - NSDictionary* classSettings = [self.settings objectForKey:className]; + obj = [[NSClassFromString (className)alloc] initWithWebView:webView]; - if (classSettings) { - obj = [[NSClassFromString (className)alloc] initWithWebView:webView settings:classSettings]; - } else { - obj = [[NSClassFromString (className)alloc] initWithWebView:webView]; - } - - if ((obj != nil) && [obj isKindOfClass:[CDVPlugin class]]) { + if (obj != nil) { [self registerPlugin:obj withClassName:className]; } else { NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); @@ -955,9 +821,9 @@ - (void)dealloc [[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; + [CDVUserAgentUtil releaseLock:&_userAgentLockToken]; [_commandQueue dispose]; [[self.pluginObjects allValues] makeObjectsPerformSelector:@selector(dispose)]; } diff --git a/CordovaLib/Classes/CDVWebViewDelegate.h b/CordovaLib/Classes/CDVWebViewDelegate.h new file mode 100755 index 0000000..8a89a22 --- /dev/null +++ b/CordovaLib/Classes/CDVWebViewDelegate.h @@ -0,0 +1,37 @@ +/* + 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 + +/** + * Distinguishes top-level navigations from sub-frame navigations. + * shouldStartLoadWithRequest is called for every request, but didStartLoad + * and didFinishLoad is called only for top-level navigations. + * Relevant bug: CB-2389 + */ +@interface CDVWebViewDelegate : NSObject { + __weak NSObject * _delegate; + NSInteger _loadCount; + NSInteger _state; + NSInteger _curLoadToken; +} + +- (id)initWithDelegate:(NSObject *)delegate; + +@end diff --git a/CordovaLib/Classes/CDVWebViewDelegate.m b/CordovaLib/Classes/CDVWebViewDelegate.m new file mode 100755 index 0000000..9ee8186 --- /dev/null +++ b/CordovaLib/Classes/CDVWebViewDelegate.m @@ -0,0 +1,157 @@ +/* + 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 "CDVWebViewDelegate.h" +#import "CDVAvailability.h" + +typedef enum { + STATE_NORMAL, + STATE_SHOULD_LOAD_MISSING, + STATE_WAITING_FOR_START, + STATE_WAITING_FOR_FINISH +} State; + +@implementation CDVWebViewDelegate + +- (id)initWithDelegate:(NSObject *)delegate +{ + self = [super init]; + if (self != nil) { + _delegate = delegate; + _loadCount = -1; + _state = STATE_NORMAL; + } + return self; +} + +- (BOOL)isPageLoaded:(UIWebView*)webView +{ + NSString* readyState = [webView stringByEvaluatingJavaScriptFromString:@"document.readyState"]; + + return [readyState isEqualToString:@"loaded"] || [readyState isEqualToString:@"complete"]; +} + +- (BOOL)isJsLoadTokenSet:(UIWebView*)webView +{ + NSString* loadToken = [webView stringByEvaluatingJavaScriptFromString:@"window.__cordovaLoadToken"]; + + return [[NSString stringWithFormat:@"%d", _curLoadToken] isEqualToString:loadToken]; +} + +- (void)setLoadToken:(UIWebView*)webView +{ + _curLoadToken += 1; + [webView stringByEvaluatingJavaScriptFromString:[NSString stringWithFormat:@"window.__cordovaLoadToken=%d", _curLoadToken]]; +} + +- (void)pollForPageLoadStart:(UIWebView*)webView +{ + if ((_state != STATE_WAITING_FOR_START) && (_state != STATE_SHOULD_LOAD_MISSING)) { + return; + } + if (![self isJsLoadTokenSet:webView]) { + _state = STATE_WAITING_FOR_FINISH; + [self setLoadToken:webView]; + [_delegate webViewDidStartLoad:webView]; + [self pollForPageLoadFinish:webView]; + } +} + +- (void)pollForPageLoadFinish:(UIWebView*)webView +{ + if (_state != STATE_WAITING_FOR_FINISH) { + return; + } + if ([self isPageLoaded:webView]) { + _state = STATE_SHOULD_LOAD_MISSING; + [_delegate webViewDidFinishLoad:webView]; + } else { + [self performSelector:@selector(pollForPageLoaded) withObject:webView afterDelay:50]; + } +} + +- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + BOOL shouldLoad = [_delegate webView:webView shouldStartLoadWithRequest:request navigationType:navigationType]; + + if (shouldLoad) { + BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]]; + if (isTopLevelNavigation) { + _loadCount = 0; + _state = STATE_NORMAL; + } + } + return shouldLoad; +} + +- (void)webViewDidStartLoad:(UIWebView*)webView +{ + if (_state == STATE_NORMAL) { + if (_loadCount == 0) { + [_delegate webViewDidStartLoad:webView]; + _loadCount += 1; + } else if (_loadCount > 0) { + _loadCount += 1; + } else if (!IsAtLeastiOSVersion(@"6.0")) { + // If history.go(-1) is used pre-iOS6, the shouldStartLoadWithRequest function is not called. + // Without shouldLoad, we can't distinguish an iframe from a top-level navigation. + // We could try to distinguish using [UIWebView canGoForward], but that's too much complexity, + // and would work only on the first time it was used. + + // Our work-around is to set a JS variable and poll until it disappears (from a naviagtion). + _state = STATE_WAITING_FOR_START; + [self setLoadToken:webView]; + } + } else { + [self pollForPageLoadStart:webView]; + [self pollForPageLoadFinish:webView]; + } +} + +- (void)webViewDidFinishLoad:(UIWebView*)webView +{ + if (_state == STATE_NORMAL) { + if (_loadCount == 1) { + [_delegate webViewDidFinishLoad:webView]; + _loadCount -= 1; + } else if (_loadCount > 1) { + _loadCount -= 1; + } + } else { + [self pollForPageLoadStart:webView]; + [self pollForPageLoadFinish:webView]; + } +} + +- (void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)error +{ + if (_state == STATE_NORMAL) { + if (_loadCount == 1) { + [_delegate webView:webView didFailLoadWithError:error]; + _loadCount -= 1; + } else if (_loadCount > 1) { + _loadCount -= 1; + } + } else { + [self pollForPageLoadStart:webView]; + [self pollForPageLoadFinish:webView]; + } +} + +@end diff --git a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj index 53d2d23..4868020 100755 --- a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj +++ b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj @@ -84,6 +84,8 @@ EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */ = {isa = PBXBuildFile; fileRef = EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */; }; EBA3557315ABD38C00F4DE24 /* NSArray+Comparisons.h in Headers */ = {isa = PBXBuildFile; fileRef = EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */; settings = {ATTRIBUTES = (Public, ); }; }; EBA3557515ABD38C00F4DE24 /* NSArray+Comparisons.m in Sources */ = {isa = PBXBuildFile; fileRef = EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */; }; + EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */; }; + EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */ = {isa = PBXBuildFile; fileRef = F858FBC4166009A8007DA594 /* CDVConfigParser.h */; settings = {ATTRIBUTES = (Public, ); }; }; F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */ = {isa = PBXBuildFile; fileRef = F858FBC5166009A8007DA594 /* CDVConfigParser.m */; }; /* End PBXBuildFile section */ @@ -180,6 +182,8 @@ EB96673A16A8970900D86CDF /* CDVUserAgentUtil.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVUserAgentUtil.m; path = Classes/CDVUserAgentUtil.m; sourceTree = ""; }; EBA3557115ABD38C00F4DE24 /* NSArray+Comparisons.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSArray+Comparisons.h"; path = "Classes/NSArray+Comparisons.h"; sourceTree = ""; }; EBA3557215ABD38C00F4DE24 /* NSArray+Comparisons.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSArray+Comparisons.m"; path = "Classes/NSArray+Comparisons.m"; sourceTree = ""; }; + EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVWebViewDelegate.m; path = Classes/CDVWebViewDelegate.m; sourceTree = ""; }; + EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVWebViewDelegate.h; path = Classes/CDVWebViewDelegate.h; sourceTree = ""; }; F858FBC4166009A8007DA594 /* CDVConfigParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVConfigParser.h; path = Classes/CDVConfigParser.h; sourceTree = ""; }; F858FBC5166009A8007DA594 /* CDVConfigParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVConfigParser.m; path = Classes/CDVConfigParser.m; sourceTree = ""; }; /* End PBXFileReference section */ @@ -257,6 +261,8 @@ 888700D710922F56009987E8 /* Commands */ = { isa = PBXGroup; children = ( + EBFF4DBA16D3FE2E008F452B /* CDVWebViewDelegate.m */, + EBFF4DBB16D3FE2E008F452B /* CDVWebViewDelegate.h */, 30C5F1DD15AF9E950052A00D /* CDVDevice.h */, 30C5F1DE15AF9E950052A00D /* CDVDevice.m */, 301F2F2914F3C9CA003FE9FC /* CDV.h */, @@ -399,6 +405,7 @@ 3073E9ED1656D51200957977 /* CDVScreenOrientationDelegate.h in Headers */, F858FBC6166009A8007DA594 /* CDVConfigParser.h in Headers */, 30F3930B169F839700B22307 /* CDVJSON.h in Headers */, + EBFF4DBD16D3FE2E008F452B /* CDVWebViewDelegate.h in Headers */, EB96673B16A8970A00D86CDF /* CDVUserAgentUtil.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -429,7 +436,7 @@ 0867D690FE84028FC02AAC07 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0430; + LastUpgradeCheck = 0460; }; buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "CordovaLib" */; compatibilityVersion = "Xcode 3.2"; @@ -494,6 +501,7 @@ F858FBC7166009A8007DA594 /* CDVConfigParser.m in Sources */, 30F3930C169F839700B22307 /* CDVJSON.m in Sources */, EB96673C16A8970A00D86CDF /* CDVUserAgentUtil.m in Sources */, + EBFF4DBC16D3FE2E008F452B /* CDVWebViewDelegate.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -564,12 +572,17 @@ armv7s, ); "ARCHS[sdk=iphonesimulator*]" = i386; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_OPTIMIZATION_LEVEL = 0; GCC_PREPROCESSOR_DEFINITIONS = ""; GCC_THUMB_SUPPORT = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.3; ONLY_ACTIVE_ARCH = NO; @@ -592,11 +605,16 @@ armv7s, ); "ARCHS[sdk=iphonesimulator*]" = i386; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; GCC_C_LANGUAGE_STANDARD = c99; GCC_PREPROCESSOR_DEFINITIONS = ""; GCC_THUMB_SUPPORT = NO; GCC_VERSION = com.apple.compilers.llvm.clang.1_0; GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 4.3; ONLY_ACTIVE_ARCH = NO; diff --git a/CordovaLib/VERSION b/CordovaLib/VERSION index 197c4d5..437459c 100755 --- a/CordovaLib/VERSION +++ b/CordovaLib/VERSION @@ -1 +1 @@ -2.4.0 +2.5.0 From dfc0c579aee8cabf27872d50ce25bb2c53f909e1 Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:26:25 +0100 Subject: [PATCH 2/6] Update Cordova config --- TDA-Enrichment/config.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/TDA-Enrichment/config.xml b/TDA-Enrichment/config.xml index c1b47af..b7c24ae 100644 --- a/TDA-Enrichment/config.xml +++ b/TDA-Enrichment/config.xml @@ -1,5 +1,5 @@ - + @@ -7,11 +7,13 @@ - + + + @@ -43,4 +45,4 @@ - + From a4a0485cac58d59c70b49600c99491c44160c98a Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:27:29 +0100 Subject: [PATCH 3/6] Update Cordova JavaScript --- www/{cordova-2.4.0.js => cordova-2.5.0.js} | 523 ++++++++++++--------- 1 file changed, 289 insertions(+), 234 deletions(-) rename www/{cordova-2.4.0.js => cordova-2.5.0.js} (95%) diff --git a/www/cordova-2.4.0.js b/www/cordova-2.5.0.js similarity index 95% rename from www/cordova-2.4.0.js rename to www/cordova-2.5.0.js index bc15819..3d83df3 100755 --- a/www/cordova-2.4.0.js +++ b/www/cordova-2.5.0.js @@ -1,8 +1,8 @@ // Platform: ios -// commit ac725f6ae0bd655789771e2a40b8d60cb4c8c221 +// commit f50d20a87431c79a54572263729461883f611a53 -// File generated at :: Tue Feb 05 2013 05:30:42 GMT+0100 (CET) +// File generated at :: Tue Feb 26 2013 14:26:19 GMT-0800 (PST) /* Licensed to the Apache Software Foundation (ASF) under one @@ -399,6 +399,7 @@ function each(objects, func, context) { } function clobber(obj, key, value) { + exports.replaceHookForTesting(obj, key); obj[key] = value; // Getters can only be overridden by getters. if (obj[key] !== value) { @@ -482,19 +483,18 @@ function recursiveMerge(target, src) { } } -module.exports = { - buildIntoButDoNotClobber: function(objects, target) { - include(target, objects, false, false); - }, - buildIntoAndClobber: function(objects, target) { - include(target, objects, true, false); - }, - buildIntoAndMerge: function(objects, target) { - include(target, objects, true, true); - }, - recursiveMerge: recursiveMerge, - assignOrWrapInDeprecateGetter: assignOrWrapInDeprecateGetter +exports.buildIntoButDoNotClobber = function(objects, target) { + include(target, objects, false, false); }; +exports.buildIntoAndClobber = function(objects, target) { + include(target, objects, true, false); +}; +exports.buildIntoAndMerge = function(objects, target) { + include(target, objects, true, true); +}; +exports.recursiveMerge = recursiveMerge; +exports.assignOrWrapInDeprecateGetter = assignOrWrapInDeprecateGetter; +exports.replaceHookForTesting = function() {}; }); @@ -774,176 +774,6 @@ module.exports = { }; }); -// file: lib/common/common.js -define("cordova/common", function(require, exports, module) { - -module.exports = { - defaults: { - cordova: { - path: 'cordova', - children: { - exec: { - path: 'cordova/exec' - }, - logger: { - path: 'cordova/plugin/logger' - } - } - }, - Cordova: { - children: { - exec: { - path: 'cordova/exec' - } - } - }, - open : { - path: 'cordova/plugin/InAppBrowser' - }, - navigator: { - children: { - notification: { - path: 'cordova/plugin/notification' - }, - accelerometer: { - path: 'cordova/plugin/accelerometer' - }, - battery: { - path: 'cordova/plugin/battery' - }, - camera:{ - path: 'cordova/plugin/Camera' - }, - compass:{ - path: 'cordova/plugin/compass' - }, - contacts: { - path: 'cordova/plugin/contacts' - }, - device:{ - children:{ - capture: { - path: 'cordova/plugin/capture' - } - } - }, - geolocation: { - path: 'cordova/plugin/geolocation' - }, - globalization: { - path: 'cordova/plugin/globalization' - }, - network: { - children: { - connection: { - path: 'cordova/plugin/network', - deprecated: 'navigator.network.connection is deprecated. Use navigator.connection instead.' - } - } - }, - splashscreen: { - path: 'cordova/plugin/splashscreen' - } - } - }, - Acceleration: { - path: 'cordova/plugin/Acceleration' - }, - Camera:{ - path: 'cordova/plugin/CameraConstants' - }, - CameraPopoverOptions: { - path: 'cordova/plugin/CameraPopoverOptions' - }, - CaptureError: { - path: 'cordova/plugin/CaptureError' - }, - CaptureAudioOptions:{ - path: 'cordova/plugin/CaptureAudioOptions' - }, - CaptureImageOptions: { - path: 'cordova/plugin/CaptureImageOptions' - }, - CaptureVideoOptions: { - path: 'cordova/plugin/CaptureVideoOptions' - }, - CompassHeading:{ - path: 'cordova/plugin/CompassHeading' - }, - CompassError:{ - path: 'cordova/plugin/CompassError' - }, - ConfigurationData: { - path: 'cordova/plugin/ConfigurationData' - }, - Connection: { - path: 'cordova/plugin/Connection' - }, - Contact: { - path: 'cordova/plugin/Contact' - }, - ContactAddress: { - path: 'cordova/plugin/ContactAddress' - }, - ContactError: { - path: 'cordova/plugin/ContactError' - }, - ContactField: { - path: 'cordova/plugin/ContactField' - }, - ContactFindOptions: { - path: 'cordova/plugin/ContactFindOptions' - }, - ContactName: { - path: 'cordova/plugin/ContactName' - }, - ContactOrganization: { - path: 'cordova/plugin/ContactOrganization' - }, - Coordinates: { - path: 'cordova/plugin/Coordinates' - }, - device: { - path: 'cordova/plugin/device' - }, - GlobalizationError: { - path: 'cordova/plugin/GlobalizationError' - }, - Media: { - path: 'cordova/plugin/Media' - }, - MediaError: { - path: 'cordova/plugin/MediaError' - }, - MediaFile: { - path: 'cordova/plugin/MediaFile' - }, - MediaFileData:{ - path: 'cordova/plugin/MediaFileData' - }, - Position: { - path: 'cordova/plugin/Position' - }, - PositionError: { - path: 'cordova/plugin/PositionError' - }, - ProgressEvent: { - path: 'cordova/plugin/ProgressEvent' - } - }, - clobbers: { - navigator: { - children: { - connection: { - path: 'cordova/plugin/network' - } - } - } - } -}; - -}); - // file: lib/ios/exec.js define("cordova/exec", function(require, exports, module) { @@ -993,6 +823,9 @@ function shouldBundleCommandJson() { } function massageArgsJsToNative(args) { + if (!args || utils.typeName(args) != 'Array') { + return args; + } var encodeArrayBufferAs8bitString = function(ab) { return String.fromCharCode.apply(null, new Uint8Array(ab)); }; @@ -1197,9 +1030,9 @@ function prepareNamespace(symbolPath, context) { var parts = symbolPath.split('.'); var cur = context; for (var i = 0, part; part = parts[i]; ++i) { - cur[part] = cur[part] || {}; + cur = cur[part] = cur[part] || {}; } - return cur[parts[i-1]]; + return cur; } exports.mapModules = function(context) { @@ -1221,7 +1054,7 @@ exports.mapModules = function(context) { if (strategy == 'm' && target) { builder.recursiveMerge(target, module); } else if ((strategy == 'd' && !target) || (strategy != 'd')) { - if (target) { + if (!(symbolPath in origSymbols)) { origSymbols[symbolPath] = target; } builder.assignOrWrapInDeprecateGetter(parentObj, lastName, module, deprecationMsg); @@ -1263,43 +1096,13 @@ module.exports = { initialize:function() { var modulemapper = require('cordova/modulemapper'); + modulemapper.loadMatchingModules(/cordova.*\/plugininit$/); + modulemapper.loadMatchingModules(/cordova.*\/symbols$/); modulemapper.mapModules(window); - }, - clobbers: { - MediaError: { // exists natively, override - path: "cordova/plugin/MediaError" - }, - console: { - path: 'cordova/plugin/ios/console' - }, - open : { - path: 'cordova/plugin/InAppBrowser' - } - }, - merges:{ - Contact:{ - path: "cordova/plugin/ios/Contact" - }, - navigator:{ - children:{ - notification:{ - path:"cordova/plugin/ios/notification" - }, - contacts:{ - path:"cordova/plugin/ios/contacts" - }, - geolocation: { - path: 'cordova/plugin/geolocation' - } - } - } } }; -// use the native logger -var logger = require("cordova/plugin/logger"); -logger.useConsole(false); }); @@ -1322,7 +1125,8 @@ define("cordova/plugin/Camera", function(require, exports, module) { var argscheck = require('cordova/argscheck'), exec = require('cordova/exec'), - Camera = require('cordova/plugin/CameraConstants'); + Camera = require('cordova/plugin/CameraConstants'), + CameraPopoverHandle = require('cordova/plugin/CameraPopoverHandle'); var cameraExport = {}; @@ -1362,6 +1166,7 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) { mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]; exec(successCallback, errorCallback, "Camera", "takePicture", args); + return new CameraPopoverHandle(); }; cameraExport.cleanup = function(successCallback, errorCallback) { @@ -1406,6 +1211,25 @@ module.exports = { }); +// file: lib/ios/plugin/CameraPopoverHandle.js +define("cordova/plugin/CameraPopoverHandle", function(require, exports, module) { + +var exec = require('cordova/exec'); + +/** + * A handle to an image picker popover. + */ +var CameraPopoverHandle = function() { + this.setPosition = function(popoverOptions) { + var args = [ popoverOptions ]; + exec(null, null, "Camera", "repositionPopover", args); + }; +}; + +module.exports = CameraPopoverHandle; + +}); + // file: lib/common/plugin/CameraPopoverOptions.js define("cordova/plugin/CameraPopoverOptions", function(require, exports, module) { @@ -1531,9 +1355,9 @@ module.exports = CompassError; define("cordova/plugin/CompassHeading", function(require, exports, module) { var CompassHeading = function(magneticHeading, trueHeading, headingAccuracy, timestamp) { - this.magneticHeading = magneticHeading || null; - this.trueHeading = trueHeading || null; - this.headingAccuracy = headingAccuracy || null; + this.magneticHeading = magneticHeading; + this.trueHeading = trueHeading; + this.headingAccuracy = headingAccuracy; this.timestamp = timestamp || new Date().getTime(); }; @@ -2906,11 +2730,12 @@ define("cordova/plugin/FileTransferError", function(require, exports, module) { * FileTransferError * @constructor */ -var FileTransferError = function(code, source, target, status) { +var FileTransferError = function(code, source, target, status, body) { this.code = code || null; this.source = source || null; this.target = target || null; this.http_status = status || null; + this.body = body || null; }; FileTransferError.FILE_NOT_FOUND_ERR = 1; @@ -3314,8 +3139,6 @@ module.exports = function(strUrl, strWindowName, strWindowFeatures) { return iab; }; -//Export the original open so it can be used if needed -module.exports._orig = window.open; }); @@ -3886,6 +3709,17 @@ module.exports = accelerometer; }); +// file: lib/common/plugin/accelerometer/symbols.js +define("cordova/plugin/accelerometer/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.defaults('cordova/plugin/Acceleration', 'Acceleration'); +modulemapper.defaults('cordova/plugin/accelerometer', 'navigator.accelerometer'); + +}); + // file: lib/common/plugin/battery.js define("cordova/plugin/battery", function(require, exports, module) { @@ -3970,6 +3804,28 @@ module.exports = battery; }); +// file: lib/common/plugin/battery/symbols.js +define("cordova/plugin/battery/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.defaults('cordova/plugin/battery', 'navigator.battery'); + +}); + +// file: lib/common/plugin/camera/symbols.js +define("cordova/plugin/camera/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.defaults('cordova/plugin/Camera', 'navigator.camera'); +modulemapper.defaults('cordova/plugin/CameraConstants', 'Camera'); +modulemapper.defaults('cordova/plugin/CameraPopoverOptions', 'CameraPopoverOptions'); + +}); + // file: lib/common/plugin/capture.js define("cordova/plugin/capture", function(require, exports, module) { @@ -4048,6 +3904,22 @@ module.exports = new Capture(); }); +// file: lib/common/plugin/capture/symbols.js +define("cordova/plugin/capture/symbols", function(require, exports, module) { + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/CaptureError', 'CaptureError'); +modulemapper.clobbers('cordova/plugin/CaptureAudioOptions', 'CaptureAudioOptions'); +modulemapper.clobbers('cordova/plugin/CaptureImageOptions', 'CaptureImageOptions'); +modulemapper.clobbers('cordova/plugin/CaptureVideoOptions', 'CaptureVideoOptions'); +modulemapper.clobbers('cordova/plugin/ConfigurationData', 'ConfigurationData'); +modulemapper.clobbers('cordova/plugin/MediaFile', 'MediaFile'); +modulemapper.clobbers('cordova/plugin/MediaFileData', 'MediaFileData'); +modulemapper.clobbers('cordova/plugin/capture', 'navigator.device.capture'); + +}); + // file: lib/common/plugin/compass.js define("cordova/plugin/compass", function(require, exports, module) { @@ -4135,6 +4007,18 @@ module.exports = compass; }); +// file: lib/common/plugin/compass/symbols.js +define("cordova/plugin/compass/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/CompassHeading', 'CompassHeading'); +modulemapper.clobbers('cordova/plugin/CompassError', 'CompassError'); +modulemapper.clobbers('cordova/plugin/compass', 'navigator.compass'); + +}); + // file: lib/common/plugin/console-via-logger.js define("cordova/plugin/console-via-logger", function(require, exports, module) { @@ -4368,6 +4252,23 @@ module.exports = contacts; }); +// file: lib/common/plugin/contacts/symbols.js +define("cordova/plugin/contacts/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/contacts', 'navigator.contacts'); +modulemapper.clobbers('cordova/plugin/Contact', 'Contact'); +modulemapper.clobbers('cordova/plugin/ContactAddress', 'ContactAddress'); +modulemapper.clobbers('cordova/plugin/ContactError', 'ContactError'); +modulemapper.clobbers('cordova/plugin/ContactField', 'ContactField'); +modulemapper.clobbers('cordova/plugin/ContactFindOptions', 'ContactFindOptions'); +modulemapper.clobbers('cordova/plugin/ContactName', 'ContactName'); +modulemapper.clobbers('cordova/plugin/ContactOrganization', 'ContactOrganization'); + +}); + // file: lib/common/plugin/device.js define("cordova/plugin/device", function(require, exports, module) { @@ -4427,6 +4328,16 @@ module.exports = new Device(); }); +// file: lib/common/plugin/device/symbols.js +define("cordova/plugin/device/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/device', 'device'); + +}); + // file: lib/common/plugin/echo.js define("cordova/plugin/echo", function(require, exports, module) { @@ -4474,20 +4385,30 @@ module.exports = function(exportFunc) { exportFunc('cordova/plugin/FileError', 'FileError'); exportFunc('cordova/plugin/FileReader', 'FileReader'); exportFunc('cordova/plugin/FileSystem', 'FileSystem'); - exportFunc('cordova/plugin/FileTransfer', 'FileTransfer'); - exportFunc('cordova/plugin/FileTransferError', 'FileTransferError'); exportFunc('cordova/plugin/FileUploadOptions', 'FileUploadOptions'); exportFunc('cordova/plugin/FileUploadResult', 'FileUploadResult'); exportFunc('cordova/plugin/FileWriter', 'FileWriter'); exportFunc('cordova/plugin/Flags', 'Flags'); exportFunc('cordova/plugin/LocalFileSystem', 'LocalFileSystem'); exportFunc('cordova/plugin/Metadata', 'Metadata'); + exportFunc('cordova/plugin/ProgressEvent', 'ProgressEvent'); exportFunc('cordova/plugin/requestFileSystem', 'requestFileSystem'); exportFunc('cordova/plugin/resolveLocalFileSystemURI', 'resolveLocalFileSystemURI'); }; }); +// file: lib/common/plugin/filetransfer/symbols.js +define("cordova/plugin/filetransfer/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/FileTransfer', 'FileTransfer'); +modulemapper.clobbers('cordova/plugin/FileTransferError', 'FileTransferError'); + +}); + // file: lib/common/plugin/geolocation.js define("cordova/plugin/geolocation", function(require, exports, module) { @@ -4684,6 +4605,19 @@ module.exports = geolocation; }); +// file: lib/common/plugin/geolocation/symbols.js +define("cordova/plugin/geolocation/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.defaults('cordova/plugin/geolocation', 'navigator.geolocation'); +modulemapper.clobbers('cordova/plugin/PositionError', 'PositionError'); +modulemapper.clobbers('cordova/plugin/Position', 'Position'); +modulemapper.clobbers('cordova/plugin/Coordinates', 'Coordinates'); + +}); + // file: lib/common/plugin/globalization.js define("cordova/plugin/globalization", function(require, exports, module) { @@ -5060,6 +4994,27 @@ module.exports = globalization; }); +// file: lib/common/plugin/globalization/symbols.js +define("cordova/plugin/globalization/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/globalization', 'navigator.globalization'); +modulemapper.clobbers('cordova/plugin/GlobalizationError', 'GlobalizationError'); + +}); + +// file: lib/ios/plugin/inappbrowser/symbols.js +define("cordova/plugin/inappbrowser/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/InAppBrowser', 'open'); + +}); + // file: lib/ios/plugin/ios/Contact.js define("cordova/plugin/ios/Contact", function(require, exports, module) { @@ -5181,6 +5136,15 @@ module.exports = console; }); +// file: lib/ios/plugin/ios/console/symbols.js +define("cordova/plugin/ios/console/symbols", function(require, exports, module) { + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/ios/console', 'console'); + +}); + // file: lib/ios/plugin/ios/contacts.js define("cordova/plugin/ios/contacts", function(require, exports, module) { @@ -5228,6 +5192,36 @@ module.exports = { }); +// file: lib/ios/plugin/ios/contacts/symbols.js +define("cordova/plugin/ios/contacts/symbols", function(require, exports, module) { + +require('cordova/plugin/contacts/symbols'); + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.merges('cordova/plugin/ios/contacts', 'navigator.contacts'); +modulemapper.merges('cordova/plugin/ios/Contact', 'Contact'); + +}); + +// file: lib/ios/plugin/ios/geolocation/symbols.js +define("cordova/plugin/ios/geolocation/symbols", function(require, exports, module) { + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.merges('cordova/plugin/geolocation', 'navigator.geolocation'); + +}); + +// file: lib/ios/plugin/ios/logger/plugininit.js +define("cordova/plugin/ios/logger/plugininit", function(require, exports, module) { + +// use the native logger +var logger = require("cordova/plugin/logger"); +logger.useConsole(false); + +}); + // file: lib/ios/plugin/ios/notification.js define("cordova/plugin/ios/notification", function(require, exports, module) { @@ -5469,6 +5463,27 @@ document.addEventListener("deviceready", logger.__onDeviceReady, false); }); +// file: lib/common/plugin/logger/symbols.js +define("cordova/plugin/logger/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/logger', 'cordova.logger'); + +}); + +// file: lib/ios/plugin/media/symbols.js +define("cordova/plugin/media/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.defaults('cordova/plugin/Media', 'Media'); +modulemapper.clobbers('cordova/plugin/MediaError', 'MediaError'); + +}); + // file: lib/common/plugin/network.js define("cordova/plugin/network", function(require, exports, module) { @@ -5541,6 +5556,18 @@ module.exports = me; }); +// file: lib/common/plugin/networkstatus/symbols.js +define("cordova/plugin/networkstatus/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/network', 'navigator.network.connection', 'navigator.network.connection is deprecated. Use navigator.connection instead.'); +modulemapper.clobbers('cordova/plugin/network', 'navigator.connection'); +modulemapper.defaults('cordova/plugin/Connection', 'Connection'); + +}); + // file: lib/common/plugin/notification.js define("cordova/plugin/notification", function(require, exports, module) { @@ -5603,6 +5630,17 @@ module.exports = { }); +// file: lib/ios/plugin/notification/symbols.js +define("cordova/plugin/notification/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/notification', 'navigator.notification'); +modulemapper.merges('cordova/plugin/ios/notification', 'navigator.notification'); + +}); + // file: lib/common/plugin/requestFileSystem.js define("cordova/plugin/requestFileSystem", function(require, exports, module) { @@ -5716,6 +5754,29 @@ module.exports = splashscreen; }); +// file: lib/common/plugin/splashscreen/symbols.js +define("cordova/plugin/splashscreen/symbols", function(require, exports, module) { + + +var modulemapper = require('cordova/modulemapper'); + +modulemapper.clobbers('cordova/plugin/splashscreen', 'navigator.splashscreen'); + +}); + +// file: lib/common/symbols.js +define("cordova/symbols", function(require, exports, module) { + +var modulemapper = require('cordova/modulemapper'); + +// Use merges here in case others symbols files depend on this running first, +// but fail to declare the dependency with a require(). +modulemapper.merges('cordova', 'cordova'); +modulemapper.clobbers('cordova/exec', 'cordova.exec'); +modulemapper.clobbers('cordova/exec', 'Cordova.exec'); + +}); + // file: lib/common/utils.js define("cordova/utils", function(require, exports, module) { @@ -5984,14 +6045,8 @@ window.cordova = require('cordova'); */ channel.join(function() { var builder = require('cordova/builder'), - base = require('cordova/common'), platform = require('cordova/platform'); - // Drop the common globals into the window object, but be nice and don't overwrite anything. - builder.buildIntoButDoNotClobber(base.defaults, context); - builder.buildIntoAndClobber(base.clobbers, context); - builder.buildIntoAndMerge(base.merges, context); - builder.buildIntoButDoNotClobber(platform.defaults, context); builder.buildIntoAndClobber(platform.clobbers, context); builder.buildIntoAndMerge(platform.merges, context); From 3effa9be2597debc92ee159e532fc935366ecf50 Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:27:52 +0100 Subject: [PATCH 4/6] Update Cordova script --- cordova/emulate | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/cordova/emulate b/cordova/emulate index ef7198e..f26cb3a 100755 --- a/cordova/emulate +++ b/cordova/emulate @@ -1,4 +1,4 @@ -#! /bin/sh +#! /bin/bash # # Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file @@ -7,9 +7,9 @@ # 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 @@ -33,11 +33,8 @@ PROJECT_PATH="$(dirname "$CORDOVA_PATH")" XCODEPROJ=$( ls "$PROJECT_PATH" | grep .xcodeproj ) PROJECT_NAME=$(basename "$XCODEPROJ" .xcodeproj) -APP_PATH=$1 - -if [ $# -lt 1 ]; then - APP_PATH="$PROJECT_PATH/build/$PROJECT_NAME.app" -fi +APP_PATH=${1:-$PROJECT_PATH/build/$PROJECT_NAME.app} +DEVICE_FAMILY=${2:-${DEVICE_FAMILY:-iphone}} if [ ! -d "$APP_PATH" ]; then echo "Project '$APP_PATH' is not built. Building." @@ -51,7 +48,7 @@ fi # launch using ios-sim if which ios-sim >/dev/null; then - ios-sim launch "$APP_PATH" --stderr "$CORDOVA_PATH/console.log" --stdout "$CORDOVA_PATH/console.log" & + ios-sim launch "$APP_PATH" --family "$DEVICE_FAMILY" --stderr "$CORDOVA_PATH/console.log" --stdout "$CORDOVA_PATH/console.log" & else echo -e '\033[31mError: ios-sim was not found. Please download, build and install version 1.4 or greater from https://github.com/phonegap/ios-sim into your path. Or "brew install ios-sim" using homebrew: http://mxcl.github.com/homebrew/\033[m'; exit 1; fi From e7a8658f233162eaa9f020e6584e37fc53549059 Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:28:19 +0100 Subject: [PATCH 5/6] Update AppDelegate --- TDA-Enrichment/Classes/AppDelegate.m | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/TDA-Enrichment/Classes/AppDelegate.m b/TDA-Enrichment/Classes/AppDelegate.m index 6799172..8e1da99 100644 --- a/TDA-Enrichment/Classes/AppDelegate.m +++ b/TDA-Enrichment/Classes/AppDelegate.m @@ -19,7 +19,7 @@ Licensed to the Apache Software Foundation (ASF) under one // // AppDelegate.m -// cordova-2.4.0 +// cordova-2.5.0 // // Created by George Garside on 11/01/2013. // Copyright George Garside 2012-2013. All rights reserved. @@ -45,6 +45,11 @@ - (id)init [cookieStorage setCookieAcceptPolicy:NSHTTPCookieAcceptPolicyAlways]; + int cacheSizeMemory = 8 * 1024 * 1024; // 8MB + int cacheSizeDisk = 32 * 1024 * 1024; // 32MB + NSURLCache* sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:cacheSizeMemory diskCapacity:cacheSizeDisk diskPath:@"nsurlcache"]; + [NSURLCache setSharedURLCache:sharedCache]; + self = [super init]; return self; } @@ -82,7 +87,7 @@ - (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(N } // this happens while we are running ( in the background, or from within our own app ) -// only valid if cordova-2.4.0-Info.plist specifies a protocol to handle +// only valid if cordova-2.5.0-Info.plist specifies a protocol to handle - (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url { if (!url) { @@ -115,4 +120,9 @@ - (NSUInteger)application:(UIApplication*)application supportedInterfaceOrientat return supportedInterfaceOrientations; } +- (void)applicationDidReceiveMemoryWarning:(UIApplication*)application +{ + [[NSURLCache sharedURLCache] removeAllCachedResponses]; +} + @end From 9de46b5577243bc7ad9706ccc9c14f7b44559e94 Mon Sep 17 00:00:00 2001 From: George Garside Date: Fri, 19 Apr 2013 17:28:37 +0100 Subject: [PATCH 6/6] Reference Cordova JavaScript --- www/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/index.html b/www/index.html index deaf528..6c54b6a 100644 --- a/www/index.html +++ b/www/index.html @@ -9,7 +9,7 @@ - +