Created
January 11, 2009 02:04
-
-
Save jon/45607 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UIWebView+SmartJS.h | |
// | |
// Created by Jon Olson on 12/27/08. | |
// Copyright 2008-2009 Ballistic Pigeon, LLC. All rights reserved. | |
// | |
#import <Foundation/Foundation.h> | |
extern NSString * const SmartJSErrorDomain; | |
enum { | |
SmartJSSyntaxError = -1, | |
SmartJSTypeError = -2, | |
SmartJSRangeError = -3, | |
SmartJSURIError = -4, | |
SmartJSReferenceError = -5, | |
SmartJSEvalError = -100000L | |
}; | |
@interface UIWebView (SmartJS) | |
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string error:(NSError **)error; | |
@end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// UIWebView+SmartJS.m | |
// | |
// Created by Jon Olson on 12/27/08. | |
// Copyright 2008-2009 Ballistic Pigeon, LLC. All rights reserved. | |
// | |
#import "UIWebView+SmartJS.h" | |
NSString * const SmartJSErrorDomain = @"com.ballisticpigeon.smartjavascript.SmartJSErrorDomain"; | |
// Naïve but correct implementation. | |
// It would be substantially better to do all the replacements in one pass | |
// This requires dealing natively with Unicode (or at least UTF8) | |
// There's also the questions of how many times we end up copying the string (4 right now, maybe?) | |
// We are still linear time, though, inasmuch as we do our replacment exactly 4 times | |
// On the whole, this is a mess best solved by a proper regular expression processor | |
static NSString *EscapeStringAsJavascriptCode(NSString *string) { | |
NSMutableString *mutable = [[string mutableCopyWithZone:nil] autorelease]; | |
[mutable replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:0 range:NSMakeRange(0, [string length])]; | |
[mutable replaceOccurrencesOfString:@"\\" withString:@"\\\\" options:0 range:NSMakeRange(0, [string length])]; | |
[mutable replaceOccurrencesOfString:@"\r" withString:@"\\r" options:0 range:NSMakeRange(0, [string length])]; | |
[mutable replaceOccurrencesOfString:@"\n" withString:@"\\n" options:0 range:NSMakeRange(0, [string length])]; | |
return [NSString stringWithFormat:@"\"%@\"", mutable]; | |
} | |
static NSString const *safeJSFormat = @"(function() { try { return 'success:' + eval(%@); } catch (e) { return 'error:' + e.name + ':' + e.message})()"; | |
@implementation UIWebView (SmartJS) | |
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)string error:(NSError **)error { | |
NSString *code = [NSString stringWithFormat:(NSString *)safeJSFormat, string]; | |
NSString *result = [self stringByEvaluatingJavaScriptFromString:code]; // This should ALWAYS work | |
NSRange faultStatusRange = [result rangeOfString:@":"]; | |
if (faultStatusRange.location == NSNotFound) | |
[NSException raise:@"SmartJSResultParseException" format:@"Unable to parse the result from SmartJavaScript evaluator. This indicates a bug in the SmartJavaScript extension. The resulting text was: %@", result]; | |
NSString *faultStatus = [result substringToIndex:faultStatusRange.location-1]; | |
NSString *body = [result substringFromIndex:faultStatusRange.location+1]; | |
if ([faultStatus isEqualToString:@"success"]) { | |
*error = nil; // No error | |
return body; // Return their requested result | |
} | |
NSRange errorNameRange = [body rangeOfString:@":"]; | |
if (errorNameRange.location == NSNotFound) | |
[NSException raise:@"SmartJSResultParseException" format:@"Unable to parse the result from SmartJavaScript evaluator. This indicates a bug in the SmartJavaScript extension. The resulting text was: %@", result]; // Return the full result as the : detected earlier could have been part of whatever unparsable string was returned | |
NSString *errorName = [result substringToIndex:errorNameRange.location-1]; | |
NSString *errorDescription = [result substringFromIndex:errorNameRange.location+1]; | |
NSDictionary *errorUserInfo = [NSDictionary dictionaryWithObjectsAndKeys:errorName, NSLocalizedDescriptionKey, errorDescription, NSLocalizedFailureReasonErrorKey, nil]; | |
if ([errorName isEqualToString:@"SyntaxError"]) | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSSyntaxError userInfo:errorUserInfo]; | |
else if ([errorName isEqualToString:@"TypeError"]) | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSTypeError userInfo:errorUserInfo]; | |
else if ([errorName isEqualToString:@"RangeError"]) | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSRangeError userInfo:errorUserInfo]; | |
else if ([errorName isEqualToString:@"URIError"]) | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSURIError userInfo:errorUserInfo]; | |
else if ([errorName isEqualToString:@"ReferenceError"]) | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSReferenceError userInfo:errorUserInfo]; | |
else | |
*error = [NSError errorWithDomain:SmartJSErrorDomain code:SmartJSEvalError userInfo:errorUserInfo]; | |
return nil; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment