added stand-alone PGViewController
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/e4e75dcb Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/e4e75dcb Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/e4e75dcb Branch: refs/heads/master Commit: e4e75dcb0399a52ff0621eddd74d2f9d7f65d08e Parents: 85f578a Author: Jesse <jesse.macfad...@nitobi.com> Authored: Tue Jan 3 12:37:14 2012 -0800 Committer: shazron <shaz...@gmail.com> Committed: Fri Jan 6 10:47:21 2012 -0800 ---------------------------------------------------------------------- PhoneGapLib/Classes/PGViewController.h | 58 +++ PhoneGapLib/Classes/PGViewController.m | 567 +++++++++++++++++++++++++++ PhoneGapLib/Classes/UIGapView.h | 18 + PhoneGapLib/Classes/UIGapView.m | 36 ++ 4 files changed, 679 insertions(+), 0 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/e4e75dcb/PhoneGapLib/Classes/PGViewController.h ---------------------------------------------------------------------- diff --git a/PhoneGapLib/Classes/PGViewController.h b/PhoneGapLib/Classes/PGViewController.h new file mode 100644 index 0000000..c7cc1e3 --- /dev/null +++ b/PhoneGapLib/Classes/PGViewController.h @@ -0,0 +1,58 @@ +// +// MainViewController.h +// Cleaver +// +// Created by Jesse MacFadyen on 11-12-08. +// Copyright 2011 Nitobi. All rights reserved. +// + +#import "UIGapView.h" + +#import "JSONKit.h" +#import "InvokedUrlCommand.h" +@class PGWhitelist; + +@interface PGViewController : UIViewController<UIWebViewDelegate> { + + IBOutlet UIGapView* webView; +} + +@property (nonatomic, retain) UIGapView *webView; + +@property (nonatomic, readonly, retain) IBOutlet UIActivityIndicatorView *activityView; +@property (nonatomic, readonly, retain) UIImageView *imageView; +@property (nonatomic, readonly, retain) NSMutableDictionary *pluginObjects; +@property (nonatomic, readonly, retain) NSDictionary *pluginsMap; +@property (nonatomic, readonly, retain) NSDictionary *settings; +@property (nonatomic, readonly, retain) PGWhitelist* whitelist; // readonly for public + ++ (NSDictionary*)getBundlePlist:(NSString *)plistName; ++ (NSString*) wwwFolderName; ++ (NSString*) pathForResource:(NSString*)resourcepath; ++ (NSString*) phoneGapVersion; ++ (NSString*) applicationDocumentsDirectory; +- (NSString*) startPage; + + +-(void)createGapView; + +- (int)executeQueuedCommands; +- (void)flushCommandQueue; + +- (id) getCommandInstance:(NSString*)pluginName; +- (void) javascriptAlert:(NSString*)text; +- (BOOL) execute:(InvokedUrlCommand*)command; +- (NSString*) appURLScheme; +- (NSDictionary*) deviceProperties; + +- (NSArray*) parseInterfaceOrientations:(NSArray*)orientations; + + + +@end + +@interface NSDictionary (LowercaseKeys) + +- (NSDictionary*) dictionaryWithLowercaseKeys; + +@end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/e4e75dcb/PhoneGapLib/Classes/PGViewController.m ---------------------------------------------------------------------- diff --git a/PhoneGapLib/Classes/PGViewController.m b/PhoneGapLib/Classes/PGViewController.m new file mode 100644 index 0000000..a76d1da --- /dev/null +++ b/PhoneGapLib/Classes/PGViewController.m @@ -0,0 +1,567 @@ +// +// MainViewController.m +// Cleaver +// +// Created by Jesse MacFadyen on 11-12-08. +// Copyright 2011 Nitobi. All rights reserved. +// + +#import "PGViewController.h" +#import "PGPlugin.h" + +#define SYMBOL_TO_NSSTRING_HELPER(x) @#x +#define SYMBOL_TO_NSSTRING(x) SYMBOL_TO_NSSTRING_HELPER(x) + +@interface PGViewController () + +@property (nonatomic, readwrite, retain) NSDictionary *settings; +@property (nonatomic, readwrite, retain) PGWhitelist* whitelist; + +@end + + +@implementation PGViewController + +@synthesize webView; +@synthesize pluginObjects, pluginsMap, whitelist; +@synthesize activityView, imageView, settings; + + +// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. +- (void)viewDidLoad +{ + [super viewDidLoad]; + + + // read from UISupportedInterfaceOrientations (or UISupportedInterfaceOrientations~iPad, if its iPad) from -Info.plist + NSArray* supportedOrientations = [self parseInterfaceOrientations: + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UISupportedInterfaceOrientations"]]; + + // read from PhoneGap.plist in the app bundle + NSString* appPlistName = @"PhoneGap"; + NSDictionary* phonegapPlist = [[self class] getBundlePlist:appPlistName]; + if (phonegapPlist == nil) { + NSLog(@"WARNING: %@.plist is missing.", appPlistName); + return; + } + self.settings = [[[NSDictionary alloc] initWithDictionary:phonegapPlist] autorelease]; + + // read from Plugins dict in PhoneGap.plist in the app bundle + NSString* pluginsKey = @"Plugins"; + NSDictionary* pluginsDict = [self.settings objectForKey:@"Plugins"]; + if (pluginsDict == nil) { + NSLog(@"WARNING: %@ key in %@.plist is missing! PhoneGap will not work, you need to have this key.", pluginsKey, appPlistName); + return; + } + + // set the whitelist + self.whitelist = [[[PGWhitelist alloc] initWithArray:[self.settings objectForKey:@"ExternalHosts"]] autorelease]; + + + + NSString* path = [PGViewController pathForResource:@"index.html"]; + NSURL *appURL = [NSURL fileURLWithPath:path];//[NSURL URLWithString:path]; + //[NSURL fileURLWithPath:path]; + + + NSURLRequest *appReq = [NSURLRequest requestWithURL:appURL cachePolicy:NSURLRequestReloadRevalidatingCacheData timeoutInterval:20.0]; + + [ self createGapView]; + [ self.webView loadRequest:appReq]; + + //[self loadingStart]; + +} + +- (NSArray*) parseInterfaceOrientations:(NSArray*)orientations +{ + NSMutableArray* result = [[[NSMutableArray alloc] init] autorelease]; + + if (orientations != nil) + { + NSEnumerator* enumerator = [orientations objectEnumerator]; + NSString* orientationString; + + while (orientationString = [enumerator nextObject]) + { + if ([orientationString isEqualToString:@"UIInterfaceOrientationPortrait"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationPortraitUpsideDown"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortraitUpsideDown]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeLeft"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft]]; + } else if ([orientationString isEqualToString:@"UIInterfaceOrientationLandscapeRight"]) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeRight]]; + } + } + } + + // default + if ([result count] == 0) { + [result addObject:[NSNumber numberWithInt:UIInterfaceOrientationPortrait]]; + } + + return result; +} + + +-(void)createGapView +{ + CGRect webViewBounds = self.view.bounds; + webViewBounds.origin = self.view.bounds.origin; + + if (!webView) + { + webView = [[ [ UIGapView alloc ] initWithFrame:webViewBounds] autorelease]; + webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight); + webView.scalesPageToFit = YES;//[enableViewportScale boolValue]; + + [ self.view addSubview:webView]; + [ self.view sendSubviewToBack:webView]; + + webView.delegate = self; + } + + +} + + +- (void)didReceiveMemoryWarning { + // Releases the view if it doesn't have a superview. + [super didReceiveMemoryWarning]; + + // Release any cached data, images, etc. that aren't in use. +} + + +- (void)viewDidUnload { + // Release any retained subviews of the main view. + // e.g. self.myOutlet = nil; +} + + + +// Override to allow orientations other than the default portrait orientation. +- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { + // Return YES for supported orientations. + return (interfaceOrientation == UIInterfaceOrientationPortrait); +} + +#pragma mark UIWebViewDelegate + +/** + When web application loads Add stuff to the DOM, mainly the user-defined settings from the Settings.plist file, and + the device's data such as device ID, platform version, etc. + */ +- (void)webViewDidStartLoad:(UIWebView *)theWebView +{ + +} + +/** + Called when the webview finishes loading. This stops the activity view and closes the imageview + */ +- (void)webViewDidFinishLoad:(UIWebView *)theWebView +{ + // Share session key with the WebView by setting PhoneGap.sessionKey + NSString *sessionKeyScript = [NSString stringWithFormat:@"PhoneGap.sessionKey = \"%@\";", @"bobbyDallas"];//]self.sessionKey]; + [theWebView stringByEvaluatingJavaScriptFromString:sessionKeyScript]; + + + NSDictionary *deviceProperties = [ self deviceProperties]; + NSMutableString *result = [[NSMutableString alloc] initWithFormat:@"DeviceInfo = %@;", [deviceProperties JSONString]]; + + /* Settings.plist + * Read the optional Settings.plist file and push these user-defined settings down into the web application. + * This can be useful for supplying build-time configuration variables down to the app to change its behaviour, + * such as specifying Full / Lite version, or localization (English vs German, for instance). + */ + + NSDictionary *temp = [[self class] getBundlePlist:@"Settings"]; + if ([temp respondsToSelector:@selector(JSONString)]) { + [result appendFormat:@"\nwindow.Settings = %@;", [temp JSONString]]; + } + + NSLog(@"Device initialization: %@", result); + [theWebView stringByEvaluatingJavaScriptFromString:result]; + [result release]; + + /* + * 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.window bringSubviewToFront:self.viewController.view]; +// } +// +// [self.viewController didRotateFromInterfaceOrientation:(UIInterfaceOrientation)[[UIDevice currentDevice] orientation]]; +} + +- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error { + NSLog(@"Failed to load webpage with error: %@", [error localizedDescription]); + /* + if ([error code] != NSURLErrorCancelled) + alert([error localizedDescription]); + */ +} + +- (BOOL)webView:(UIWebView *)_webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType +{ + NSURL *url = [request URL]; + + /* + * Execute any commands queued with PhoneGap.exec() on the JS side. + * The part of the URL after gap:// is irrelevant. + */ + if ([[url scheme] isEqualToString:@"gap"]) { + [self flushCommandQueue]; + return NO; + } + /* + * If a URL is being loaded that's a file/http/https URL, just load it internally + */ + else if ([url isFileURL]) + { + return YES; + } +// else if ([self.whitelist schemeIsAllowed:[url scheme]]) +// { +// if ([self.whitelist URLIsAllowed:url] == YES) +// { +// NSNumber *openAllInWhitelistSetting = [self.settings objectForKey:@"OpenAllWhitelistURLsInWebView"]; +// if ((nil != openAllInWhitelistSetting) && [openAllInWhitelistSetting boolValue]) { +// NSLog(@"OpenAllWhitelistURLsInWebView set: opening in webview"); +// return YES; +// } +// +// // mainDocument will be nil for an iFrame +// NSString* mainDocument = [_webView.request.mainDocumentURL absoluteString]; +// +// // anchor target="_blank" - load in Mobile Safari +// if (navigationType == UIWebViewNavigationTypeOther && mainDocument != nil) +// { +// [[UIApplication sharedApplication] openURL:url]; +// return NO; +// } +// // other anchor target - load in PhoneGap webView +// else +// { +// return YES; +// } +// } +// +// return NO; +// } + /* + * If we loaded the HTML from a string, we let the app handle it + */ +// else if (self.loadFromString == YES) +// { +// self.loadFromString = NO; +// return YES; +// } + /* + * all tel: scheme urls we let the UIWebview handle it using the default behaviour + */ + else if ([[url scheme] isEqualToString:@"tel"]) + { + return YES; + } + /* + * all about: scheme urls are not handled + */ + else if ([[url scheme] isEqualToString:@"about"]) + { + return NO; + } + /* + * We don't have a PhoneGap or web/local request, load it in the main Safari browser. + * pass this to the application to handle. Could be a mailto:d...@duderanch.com or a tel:55555555 or sms:55555555 facetime:55555555 + */ + else + { +// NSLog(@"PhoneGapDelegate::shouldStartLoadWithRequest: Received Unhandled URL %@", url); +// +// if ([[UIApplication sharedApplication] canOpenURL:url]) { +// [[UIApplication sharedApplication] openURL:url]; +// } else { // handle any custom schemes to plugins +// [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:PGPluginHandleOpenURLNotification object:url]]; +// } + + return NO; + } + + return YES; + +} + +#pragma mark GapHelpers + +- (void) javascriptAlert:(NSString*)text +{ + NSString* jsString = [NSString stringWithFormat:@"alert('%@');", text]; + [webView stringByEvaluatingJavaScriptFromString:jsString]; +} + +/*Gap stuff*/ ++ (NSString*) wwwFolderName +{ + return @"www"; +} + +- (NSString*) startPage +{ + return @"index.html"; +} + ++ (NSString*) pathForResource:(NSString*)resourcepath +{ + NSBundle * mainBundle = [NSBundle mainBundle]; + NSMutableArray *directoryParts = [NSMutableArray arrayWithArray:[resourcepath componentsSeparatedByString:@"/"]]; + NSString *filename = [directoryParts lastObject]; + [directoryParts removeLastObject]; + + NSString* directoryPartsJoined =[directoryParts componentsJoinedByString:@"/"]; + NSString* directoryStr = [self wwwFolderName]; + + if ([directoryPartsJoined length] > 0) { + directoryStr = [NSString stringWithFormat:@"%@/%@", [self wwwFolderName], [directoryParts componentsJoinedByString:@"/"]]; + } + + return [mainBundle pathForResource:filename + ofType:@"" + inDirectory:directoryStr]; +} + ++ (NSString*) applicationDocumentsDirectory { + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); + NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; + return basePath; +} + +#pragma mark PhoneGapCommands + +/** + * Fetches the command queue and executes each command. It is possible that the + * queue will not be empty after this function has completed since the executed + * commands may have run callbacks which queued more commands. + * + * Returns the number of executed commands. + */ +- (int)executeQueuedCommands +{ + // Grab all the queued commands from the JS side. + NSString* queuedCommandsJSON = [self.webView stringByEvaluatingJavaScriptFromString: + @"PhoneGap.getAndClearQueuedCommands()"]; + + + // Parse the returned JSON array. + //PG_SBJsonParser* jsonParser = [[[PG_SBJsonParser alloc] init] autorelease]; + NSArray* queuedCommands = + [queuedCommandsJSON objectFromJSONString]; + + // Iterate over and execute all of the commands. + for (NSString* commandJson in queuedCommands) { + + if(![self execute: + [InvokedUrlCommand commandFromObject: + [commandJson mutableObjectFromJSONString]]]) + { + NSLog(@"FAILED pluginJSON = %@",commandJson); + } + } + + return 0;//[queuedCommands count]; +} + +/** + * Repeatedly fetches and executes the command queue until it is empty. + */ +- (void)flushCommandQueue +{ + [self.webView stringByEvaluatingJavaScriptFromString: + @"PhoneGap.commandQueueFlushing = true"]; + + // Keep executing the command queue until no commands get executed. + // This ensures that commands that are queued while executing other + // commands are executed as well. + int numExecutedCommands = 0; + do { + numExecutedCommands = [self executeQueuedCommands]; + } while (numExecutedCommands != 0); + + [self.webView stringByEvaluatingJavaScriptFromString: + @"PhoneGap.commandQueueFlushing = false"]; +} + +- (BOOL) execute:(InvokedUrlCommand*)command +{ + if (command.className == nil || command.methodName == nil) { + return NO; + } + + // Fetch an instance of this class + PGPlugin* obj = [self getCommandInstance:command.className]; + + if (!([obj isKindOfClass:[PGPlugin class]])) { // still allow deprecated class, until 1.0 release + NSLog(@"ERROR: Plugin '%@' not found, or is not a PGPlugin. Check your plugin mapping in PhoneGap.plist.", command.className); + return NO; + } + BOOL retVal = YES; + + // construct the fill method name to ammend the second argument. + NSString* fullMethodName = [[NSString alloc] initWithFormat:@"%@:withDict:", command.methodName]; + if ([obj respondsToSelector:NSSelectorFromString(fullMethodName)]) { + [obj performSelector:NSSelectorFromString(fullMethodName) withObject:command.arguments withObject:command.options]; + } else { + // There's no method to call, so throw an error. + NSLog(@"ERROR: Method '%@' not defined in Plugin '%@'", fullMethodName, command.className); + retVal = NO; + } + [fullMethodName release]; + + return retVal; + +} + +/** + Returns an instance of a PhoneGapCommand object, based on its name. If one exists already, it is returned. + */ +-(id) getCommandInstance:(NSString*)pluginName +{ + // first, we try to find the pluginName in the pluginsMap + // (acts as a whitelist as well) if it does not exist, we return nil + // NOTE: plugin names are matched as lowercase to avoid problems - however, a + // possible issue is there can be duplicates possible if you had: + // "com.phonegap.Foo" and "com.phonegap.foo" - only the lower-cased entry will match + NSString* className = [self.pluginsMap objectForKey:[pluginName lowercaseString]]; + if (className == nil) { + return nil; + } + + id obj = [self.pluginObjects objectForKey:className]; + if (!obj) + { + // attempt to load the settings for this command class + NSDictionary* classSettings = [self.settings objectForKey:className]; + + if (classSettings) { + obj = [[NSClassFromString(className) alloc] initWithWebView:webView settings:classSettings]; + } else { + obj = [[NSClassFromString(className) alloc] initWithWebView:webView]; + } + + if (obj != nil) { + [self.pluginObjects setObject:obj forKey:className]; + [obj release]; + } else { + NSLog(@"PGPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); + } + } + return obj; +} + + +#pragma mark PhoneGapDelegate? + +- (NSDictionary*) deviceProperties +{ + UIDevice *device = [UIDevice currentDevice]; + NSMutableDictionary *devProps = [NSMutableDictionary dictionaryWithCapacity:4]; + [devProps setObject:[device model] forKey:@"platform"]; + [devProps setObject:[device systemVersion] forKey:@"version"]; + [devProps setObject:[device uniqueIdentifier] forKey:@"uuid"]; + [devProps setObject:[device name] forKey:@"name"]; +// [devProps setObject:[[self class] phoneGapVersion ] forKey:@"gap"]; +// +// id cmd = [self getCommandInstance:@"com.phonegap.connection"]; +// if (cmd && [cmd isKindOfClass:[PGConnection class]]) +// { +// NSMutableDictionary *connProps = [NSMutableDictionary dictionaryWithCapacity:3]; +// if ([cmd respondsToSelector:@selector(connectionType)]) { +// [connProps setObject:[cmd connectionType] forKey:@"type"]; +// } +// [devProps setObject:connProps forKey:@"connection"]; +// } + + NSDictionary *devReturn = [NSDictionary dictionaryWithDictionary:devProps]; + return devReturn; +} + +- (NSString*) appURLScheme +{ + NSString* URLScheme = nil; + + NSArray *URLTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleURLTypes"]; + if(URLTypes != nil ) { + NSDictionary* dict = [URLTypes objectAtIndex:0]; + if(dict != nil ) { + NSArray* URLSchemes = [dict objectForKey:@"CFBundleURLSchemes"]; + if( URLSchemes != nil ) { + URLScheme = [URLSchemes objectAtIndex:0]; + } + } + } + + return URLScheme; +} + + + +/** + Returns the contents of the named plist bundle, loaded as a dictionary object + */ ++ (NSDictionary*)getBundlePlist:(NSString *)plistName +{ + NSString *errorDesc = nil; + NSPropertyListFormat format; + NSString *plistPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"]; + NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath]; + NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization + propertyListFromData:plistXML + mutabilityOption:NSPropertyListMutableContainersAndLeaves + format:&format errorDescription:&errorDesc]; + return temp; +} + +/** + Returns the current version of phoneGap as read from the VERSION file + This only touches the filesystem once and stores the result in the class variable gapVersion + */ +static NSString *gapVersion; ++ (NSString*) phoneGapVersion +{ +#ifdef PG_VERSION + gapVersion = SYMBOL_TO_NSSTRING(PG_VERSION); +#else + + if (gapVersion == nil) { + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *filename = [mainBundle pathForResource:@"VERSION" ofType:nil]; + // read from the filesystem and save in the variable + // first, separate by new line + NSString* fileContents = [NSString stringWithContentsOfFile:filename encoding:NSUTF8StringEncoding error:NULL]; + NSArray* all_lines = [fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]]; + NSString* first_line = [all_lines objectAtIndex:0]; + + gapVersion = [first_line retain]; + } +#endif + + return gapVersion; +} + + + +- (void)dealloc { + [super dealloc]; +} + + +@end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/e4e75dcb/PhoneGapLib/Classes/UIGapView.h ---------------------------------------------------------------------- diff --git a/PhoneGapLib/Classes/UIGapView.h b/PhoneGapLib/Classes/UIGapView.h new file mode 100644 index 0000000..45277f4 --- /dev/null +++ b/PhoneGapLib/Classes/UIGapView.h @@ -0,0 +1,18 @@ +// +// UIGapView.h +// BetaGapApp +// +// Created by Jesse MacFadyen on 11-01-29. +// Copyright 2011 RisingJ.com. All rights reserved. +// + +#import <UIKit/UIKit.h> + + +@interface UIGapView : UIWebView { + + +} + + +@end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/e4e75dcb/PhoneGapLib/Classes/UIGapView.m ---------------------------------------------------------------------- diff --git a/PhoneGapLib/Classes/UIGapView.m b/PhoneGapLib/Classes/UIGapView.m new file mode 100644 index 0000000..efd939c --- /dev/null +++ b/PhoneGapLib/Classes/UIGapView.m @@ -0,0 +1,36 @@ +// +// UIGapView.m +// BetaGapApp +// +// Created by Jesse MacFadyen on 11-01-29. +// Copyright 2011 RisingJ.com. All rights reserved. +// + +#import "UIGapView.h" + + +@implementation UIGapView + + +- (void)loadRequest:(NSURLRequest *)request +{ + [super loadRequest:request]; +} + +/* +// Only override drawRect: if you perform custom drawing. +// An empty implementation adversely affects performance during animation. +- (void)drawRect:(CGRect)rect { + // Drawing code. +} +*/ + + + + +- (void)dealloc { + [super dealloc]; +} + + +@end