diff --git a/Classes/GAScriptBlockObject.m b/Classes/GAScriptBlockObject.m index ebe9ac8..ec6e8fa 100644 --- a/Classes/GAScriptBlockObject.m +++ b/Classes/GAScriptBlockObject.m @@ -44,7 +44,7 @@ - (id)initWithBlock:(GAScriptBlock)block if ((self = [super init])) { m_block = [block copy]; - m_blockId = [[NSString alloc] initWithFormat:@"block-%u", [self hash]]; + m_blockId = [[NSString alloc] initWithFormat:@"block-%lu", (unsigned long)[self hash]]; } return self; diff --git a/Classes/GAScriptEngine.h b/Classes/GAScriptEngine.h index 85d7ce8..29ec88d 100644 --- a/Classes/GAScriptEngine.h +++ b/Classes/GAScriptEngine.h @@ -72,7 +72,7 @@ extern NSString* const GAJavaScriptErrorLine; /** * The designated initializer. */ -- (id)initWithWebView:(UIWebView *)webView; +- (id)initWithWebView:(UIWebView *)webView delegate:(id)_delegate; /** * Initializer that creates a hidden UIWebView inside the provided view. Use this initializer @@ -117,4 +117,7 @@ extern NSString* const GAJavaScriptErrorLine; */ - (id)evalWithFormat:(NSString *)script, ...; +- (NSString*)javascriptRunTimeFile; +- (NSString*)htmlWithEngineInjected:(NSString*)html; + @end diff --git a/Classes/GAScriptEngine.m b/Classes/GAScriptEngine.m index 9a85831..02a26fa 100644 --- a/Classes/GAScriptEngine.m +++ b/Classes/GAScriptEngine.m @@ -41,6 +41,8 @@ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE @interface GAScriptEngine () +@property (nonatomic, retain) NSMutableSet *jsObjects; + - (id)convertScriptResult:(NSString *)result; - (NSArray *)arrayFromJavaScript:(NSString *)result; @@ -73,15 +75,16 @@ + (GAScriptEngine *)scriptEngineForView:(UIWebView *)webView return (GAScriptEngine *)webView.delegate; } -- (id)initWithWebView:(UIWebView *)webView +- (id)initWithWebView:(UIWebView *)webView delegate:(id)_delegate { if ((self = [super init])) { m_webView = [webView retain]; - m_delegate = [webView delegate]; + m_delegate = _delegate; m_webView.delegate = self; - m_receivers = [[NSMutableArray alloc] initWithCapacity:4]; + m_receivers = [[NSMutableArray alloc] initWithCapacity:4]; + self.jsObjects = [NSMutableSet set]; } static dispatch_once_t onceToken; @@ -107,7 +110,7 @@ - (id)initWithSuperview:(UIView *)superview delegate:(id)dele [superview addSubview:webView]; [webView release]; - return [self initWithWebView:webView]; + return [self initWithWebView:webView delegate:delegate]; } - (void)dealloc @@ -119,7 +122,12 @@ - (void)dealloc [m_webView release]; [m_receivers release]; [m_blocks release]; - + + for (GAScriptObject *jsObject in self.jsObjects) { + [jsObject detachFromEngine]; + } + self.jsObjects = nil; + [super dealloc]; } @@ -128,6 +136,7 @@ - (GAScriptObject *)newScriptObject NSString* objRef = [m_webView stringByEvaluatingJavaScriptFromString:@"GAJavaScript.makeReference(new Object())"]; GAScriptObject* jsObject = [[GAScriptObject alloc] initForReference:objRef withEngine:self]; + [self.jsObjects addObject:jsObject]; return jsObject; } @@ -137,13 +146,15 @@ - (GAScriptObject *)newScriptObject:(NSString *)constructorName NSString* objRef = [m_webView stringByEvaluatingJavaScriptFromString:js]; GAScriptObject* jsObject = [[GAScriptObject alloc] initForReference:objRef withEngine:self]; - return jsObject; + [self.jsObjects addObject:jsObject]; + return jsObject; } - (GAScriptObject *)scriptObjectWithReference:(NSString *)reference { GAScriptObject* jsObject = [[GAScriptObject alloc] initForReference:reference withEngine:self]; - return [jsObject autorelease]; + [self.jsObjects addObject:jsObject]; + return [jsObject autorelease]; } - (id)evalWithFormat:(NSString *)format, ... @@ -206,9 +217,10 @@ - (id)convertScriptResult:(NSString *)result result = [result substringFromIndex:2]; // Objects don't serialize to a string above. - if (jstype == 'o') + if (jstype == 'o' || jstype == 'f') { GAScriptObject* subObj = [[GAScriptObject alloc] initForReference:result withEngine:self]; + [self.jsObjects addObject:subObj]; return [subObj autorelease]; } else if (jstype == 'd') @@ -234,7 +246,7 @@ - (id)convertScriptResult:(NSString *)result } else if (jstype == 'u') { - return nil; + return [NSNull null]; } else if (jstype == 'e') // JavaScript exception { @@ -282,6 +294,21 @@ - (void)addBlockCallback:(GAScriptBlockObject *)blockObject #pragma mark Private +- (NSString*)javascriptRunTimeFile{ + return [[NSBundle mainBundle] pathForResource:@"ga-js-runtime" ofType:@"js"]; +} + +- (NSString*)htmlWithEngineInjected:(NSString*)html{ + if ([self scriptRuntimeIsLoaded]) // Don't re-evaluate the runtime javascript, because it will destroy all existing object references. + return html; + + NSString *runtimeFile = [self javascriptRunTimeFile]; + + NSString *injectedHtml = [html stringByReplacingOccurrencesOfString:@"" withString:[NSString stringWithFormat:@"",runtimeFile]]; + + return injectedHtml; +} + - (void)loadScriptRuntime { if ([self scriptRuntimeIsLoaded]) // Don't re-evaluate the runtime javascript, because it will destroy all existing object references. diff --git a/Classes/GAScriptObject.h b/Classes/GAScriptObject.h index 72cd18f..32a0274 100755 --- a/Classes/GAScriptObject.h +++ b/Classes/GAScriptObject.h @@ -47,12 +47,22 @@ * Designated initializer. */ - (id)initForReference:(NSString *)reference withEngine:(GAScriptEngine *)engine; - + /** * Array containing the names of all the JS properties. */ - (NSArray *)allKeys; +/** + * Call the object as function with no arguments on this object. + */ +-(id)callAsFunction; + +/** + * Call a function on this object, with arguments. + */ +-(id)callAsFunctionWithArguments:(NSArray*)arguments; + /** * Call a function with no arguments on this object. */ @@ -64,7 +74,7 @@ - (id)callFunction:(NSString *)functionName withObject:(id)argument; /** - * Call a function on this object, with a single argument. + * Call a function on this object, with arguments. */ - (id)callFunction:(NSString *)functionName withArguments:(NSArray *)arguments; @@ -75,6 +85,8 @@ */ - (void)setFunctionForKey:(NSString *)key withBlock:(void(^)(NSArray* arguments))block; +- (void)detachFromEngine; + @end #pragma mark - diff --git a/Classes/GAScriptObject.m b/Classes/GAScriptObject.m index 15aa686..1a4a250 100755 --- a/Classes/GAScriptObject.m +++ b/Classes/GAScriptObject.m @@ -71,12 +71,16 @@ - (id)initForReference:(NSString *)reference withEngine:(GAScriptEngine *)engine - (void)dealloc { - [self releaseReference]; + [self performSelectorOnMainThread:@selector(releaseReference) withObject:nil waitUntilDone:YES]; [m_objReference release]; [super dealloc]; } +- (void)detachFromEngine { + m_engine = nil; +} + - (void)releaseReference { if ([m_objReference hasPrefix:@"GAJavaScript.ref["]) @@ -132,6 +136,18 @@ - (NSArray *)allKeys return [m_engine evalWithFormat:@"GAJavaScript.propsOf(%@)", m_objReference]; } +-(id)callAsFunction +{ + return [m_engine evalWithFormat:@"GAJavaScript.callFunction(%@,window)", + m_objReference]; +} + +-(id)callAsFunctionWithArguments:(NSArray*)arguments +{ + return [m_engine evalWithFormat:@"GAJavaScript.callFunction(%@,window,%@)", + m_objReference,[arguments stringForJavaScript]]; +} + - (id)callFunction:(NSString *)functionName { return [m_engine evalWithFormat:@"GAJavaScript.callFunction(%@.%@, %@)", diff --git a/README.markdown b/README.markdown index 3c6b7f8..8b7391e 100644 --- a/README.markdown +++ b/README.markdown @@ -121,6 +121,16 @@ Like other UIWebView wrappers, GAJavaScript supports callbacks from JavaScript t Note that the call is asynchronous, due to the limitations of UIWebView. So there's no return value to performSelector(). However, you can make multiple invocations, and they will be executed in order. +A solution to the no-return-value problem is to use callback: +#### Objective-C + -(void)getValueWithCallback:(GAScriptObject*)callback{ + [callback callAsFunctionWithArguments:@[@"Hello"]]; + } +#### JavaScript + GAJavaScript.performSelector('getValueWithCallback:', function(value){ + // value == "Hello" + }); + And now with blocks: #### Objective-C diff --git a/ga-js-runtime.js b/ga-js-runtime.js index caab4b4..977fc72 100644 --- a/ga-js-runtime.js +++ b/ga-js-runtime.js @@ -141,7 +141,9 @@ return { } else if (type === 'object') { return 'o:' + this.makeReference(value); - } + }else if (type === 'function') { + return 'f:' + this.makeReference(value); + } else if (type === 'null') { return 'x:'; }