Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for calling GAScriptObject as a function #16

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion Classes/GAScriptBlockObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion Classes/GAScriptEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -117,4 +117,7 @@ extern NSString* const GAJavaScriptErrorLine;
*/
- (id)evalWithFormat:(NSString *)script, ...;

- (NSString*)javascriptRunTimeFile;
- (NSString*)htmlWithEngineInjected:(NSString*)html;

@end
45 changes: 36 additions & 9 deletions Classes/GAScriptEngine.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -107,7 +110,7 @@ - (id)initWithSuperview:(UIView *)superview delegate:(id<UIWebViewDelegate>)dele
[superview addSubview:webView];
[webView release];

return [self initWithWebView:webView];
return [self initWithWebView:webView delegate:delegate];
}

- (void)dealloc
Expand All @@ -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];
}

Expand All @@ -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;
}

Expand All @@ -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, ...
Expand Down Expand Up @@ -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')
Expand All @@ -234,7 +246,7 @@ - (id)convertScriptResult:(NSString *)result
}
else if (jstype == 'u')
{
return nil;
return [NSNull null];
}
else if (jstype == 'e') // JavaScript exception
{
Expand Down Expand Up @@ -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:@"</head>" withString:[NSString stringWithFormat:@"<script src=\"%@\"></script></head>",runtimeFile]];

return injectedHtml;
}

- (void)loadScriptRuntime
{
if ([self scriptRuntimeIsLoaded]) // Don't re-evaluate the runtime javascript, because it will destroy all existing object references.
Expand Down
16 changes: 14 additions & 2 deletions Classes/GAScriptObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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;

Expand All @@ -75,6 +85,8 @@
*/
- (void)setFunctionForKey:(NSString *)key withBlock:(void(^)(NSArray* arguments))block;

- (void)detachFromEngine;

@end

#pragma mark -
Expand Down
18 changes: 17 additions & 1 deletion Classes/GAScriptObject.m
Original file line number Diff line number Diff line change
Expand Up @@ -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["])
Expand Down Expand Up @@ -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(%@.%@, %@)",
Expand Down
10 changes: 10 additions & 0 deletions README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion ga-js-runtime.js
Original file line number Diff line number Diff line change
Expand Up @@ -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:';
}
Expand Down