1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | #import "CDVInAppBrowser.h"
|
21 | #import <Cordova/CDVPluginResult.h>
|
22 | #import <Cordova/CDVUserAgentUtil.h>
|
23 |
|
24 | #define kInAppBrowserTargetSelf @"_self"
|
25 | #define kInAppBrowserTargetSystem @"_system"
|
26 | #define kInAppBrowserTargetBlank @"_blank"
|
27 |
|
28 | #define kInAppBrowserToolbarBarPositionBottom @"bottom"
|
29 | #define kInAppBrowserToolbarBarPositionTop @"top"
|
30 |
|
31 | #define TOOLBAR_HEIGHT 44.0
|
32 | #define STATUSBAR_HEIGHT 20.0
|
33 | #define LOCATIONBAR_HEIGHT 21.0
|
34 | #define FOOTER_HEIGHT ((TOOLBAR_HEIGHT) + (LOCATIONBAR_HEIGHT))
|
35 |
|
36 | #pragma mark CDVInAppBrowser
|
37 |
|
38 | @interface CDVInAppBrowser () {
|
39 | NSInteger _previousStatusBarStyle;
|
40 | }
|
41 | @end
|
42 |
|
43 | @implementation CDVInAppBrowser
|
44 |
|
45 | - (void)pluginInitialize
|
46 | {
|
47 | _previousStatusBarStyle = -1;
|
48 | _callbackIdPattern = nil;
|
49 | }
|
50 |
|
51 | - (id)settingForKey:(NSString*)key
|
52 | {
|
53 | return [self.commandDelegate.settings objectForKey:[key lowercaseString]];
|
54 | }
|
55 |
|
56 | - (void)onReset
|
57 | {
|
58 | [self close:nil];
|
59 | }
|
60 |
|
61 | - (void)close:(CDVInvokedUrlCommand*)command
|
62 | {
|
63 | if (self.inAppBrowserViewController == nil) {
|
64 | NSLog(@"IAB.close() called but it was already closed.");
|
65 | return;
|
66 | }
|
67 | // Things are cleaned up in browserExit.
|
68 | [self.inAppBrowserViewController close];
|
69 | }
|
70 |
|
71 | - (BOOL) isSystemUrl:(NSURL*)url
|
72 | {
|
73 | if ([[url host] isEqualToString:@"itunes.apple.com"]) {
|
74 | return YES;
|
75 | }
|
76 |
|
77 | return NO;
|
78 | }
|
79 |
|
80 | - (void)open:(CDVInvokedUrlCommand*)command
|
81 | {
|
82 | CDVPluginResult* pluginResult;
|
83 |
|
84 | NSString* url = [command argumentAtIndex:0];
|
85 | NSString* target = [command argumentAtIndex:1 withDefault:kInAppBrowserTargetSelf];
|
86 | NSString* options = [command argumentAtIndex:2 withDefault:@"" andClass:[NSString class]];
|
87 |
|
88 | self.callbackId = command.callbackId;
|
89 |
|
90 | if (url != nil) {
|
91 | #ifdef __CORDOVA_4_0_0
|
92 | NSURL* baseUrl = [self.webViewEngine URL];
|
93 | #else
|
94 | NSURL* baseUrl = [self.webView.request URL];
|
95 | #endif
|
96 | NSURL* absoluteUrl = [[NSURL URLWithString:url relativeToURL:baseUrl] absoluteURL];
|
97 |
|
98 | if ([self isSystemUrl:absoluteUrl]) {
|
99 | target = kInAppBrowserTargetSystem;
|
100 | }
|
101 |
|
102 | if ([target isEqualToString:kInAppBrowserTargetSelf]) {
|
103 | [self openInCordovaWebView:absoluteUrl withOptions:options];
|
104 | } else if ([target isEqualToString:kInAppBrowserTargetSystem]) {
|
105 | [self openInSystem:absoluteUrl];
|
106 | } else { // _blank or anything else
|
107 | [self openInInAppBrowser:absoluteUrl withOptions:options];
|
108 | }
|
109 |
|
110 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
111 | } else {
|
112 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"incorrect number of arguments"];
|
113 | }
|
114 |
|
115 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
|
116 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
|
117 | }
|
118 |
|
119 | - (void)openInInAppBrowser:(NSURL*)url withOptions:(NSString*)options
|
120 | {
|
121 | CDVInAppBrowserOptions* browserOptions = [CDVInAppBrowserOptions parseOptions:options];
|
122 |
|
123 | if (browserOptions.clearcache) {
|
124 | NSHTTPCookie *cookie;
|
125 | NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
126 | for (cookie in [storage cookies])
|
127 | {
|
128 | if (![cookie.domain isEqual: @".^filecookies^"]) {
|
129 | [storage deleteCookie:cookie];
|
130 | }
|
131 | }
|
132 | }
|
133 |
|
134 | if (browserOptions.clearsessioncache) {
|
135 | NSHTTPCookie *cookie;
|
136 | NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
|
137 | for (cookie in [storage cookies])
|
138 | {
|
139 | if (![cookie.domain isEqual: @".^filecookies^"] && cookie.isSessionOnly) {
|
140 | [storage deleteCookie:cookie];
|
141 | }
|
142 | }
|
143 | }
|
144 |
|
145 | if (self.inAppBrowserViewController == nil) {
|
146 | NSString* userAgent = [CDVUserAgentUtil originalUserAgent];
|
147 | NSString* overrideUserAgent = [self settingForKey:@"OverrideUserAgent"];
|
148 | NSString* appendUserAgent = [self settingForKey:@"AppendUserAgent"];
|
149 | if(overrideUserAgent){
|
150 | userAgent = overrideUserAgent;
|
151 | }
|
152 | if(appendUserAgent){
|
153 | userAgent = [userAgent stringByAppendingString: appendUserAgent];
|
154 | }
|
155 | self.inAppBrowserViewController = [[CDVInAppBrowserViewController alloc] initWithUserAgent:userAgent prevUserAgent:[self.commandDelegate userAgent] browserOptions: browserOptions];
|
156 | self.inAppBrowserViewController.navigationDelegate = self;
|
157 |
|
158 | if ([self.viewController conformsToProtocol:@protocol(CDVScreenOrientationDelegate)]) {
|
159 | self.inAppBrowserViewController.orientationDelegate = (UIViewController <CDVScreenOrientationDelegate>*)self.viewController;
|
160 | }
|
161 | }
|
162 |
|
163 | [self.inAppBrowserViewController showLocationBar:browserOptions.location];
|
164 | [self.inAppBrowserViewController showToolBar:browserOptions.toolbar :browserOptions.toolbarposition];
|
165 | if (browserOptions.closebuttoncaption != nil) {
|
166 | [self.inAppBrowserViewController setCloseButtonTitle:browserOptions.closebuttoncaption];
|
167 | }
|
168 | // Set Presentation Style
|
169 | UIModalPresentationStyle presentationStyle = UIModalPresentationFullScreen; // default
|
170 | if (browserOptions.presentationstyle != nil) {
|
171 | if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"pagesheet"]) {
|
172 | presentationStyle = UIModalPresentationPageSheet;
|
173 | } else if ([[browserOptions.presentationstyle lowercaseString] isEqualToString:@"formsheet"]) {
|
174 | presentationStyle = UIModalPresentationFormSheet;
|
175 | }
|
176 | }
|
177 | self.inAppBrowserViewController.modalPresentationStyle = presentationStyle;
|
178 |
|
179 | // Set Transition Style
|
180 | UIModalTransitionStyle transitionStyle = UIModalTransitionStyleCoverVertical; // default
|
181 | if (browserOptions.transitionstyle != nil) {
|
182 | if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"fliphorizontal"]) {
|
183 | transitionStyle = UIModalTransitionStyleFlipHorizontal;
|
184 | } else if ([[browserOptions.transitionstyle lowercaseString] isEqualToString:@"crossdissolve"]) {
|
185 | transitionStyle = UIModalTransitionStyleCrossDissolve;
|
186 | }
|
187 | }
|
188 | self.inAppBrowserViewController.modalTransitionStyle = transitionStyle;
|
189 |
|
190 | // prevent webView from bouncing
|
191 | if (browserOptions.disallowoverscroll) {
|
192 | if ([self.inAppBrowserViewController.webView respondsToSelector:@selector(scrollView)]) {
|
193 | ((UIScrollView*)[self.inAppBrowserViewController.webView scrollView]).bounces = NO;
|
194 | } else {
|
195 | for (id subview in self.inAppBrowserViewController.webView.subviews) {
|
196 | if ([[subview class] isSubclassOfClass:[UIScrollView class]]) {
|
197 | ((UIScrollView*)subview).bounces = NO;
|
198 | }
|
199 | }
|
200 | }
|
201 | }
|
202 |
|
203 | // UIWebView options
|
204 | self.inAppBrowserViewController.webView.scalesPageToFit = browserOptions.enableviewportscale;
|
205 | self.inAppBrowserViewController.webView.mediaPlaybackRequiresUserAction = browserOptions.mediaplaybackrequiresuseraction;
|
206 | self.inAppBrowserViewController.webView.allowsInlineMediaPlayback = browserOptions.allowinlinemediaplayback;
|
207 | if (IsAtLeastiOSVersion(@"6.0")) {
|
208 | self.inAppBrowserViewController.webView.keyboardDisplayRequiresUserAction = browserOptions.keyboarddisplayrequiresuseraction;
|
209 | self.inAppBrowserViewController.webView.suppressesIncrementalRendering = browserOptions.suppressesincrementalrendering;
|
210 | }
|
211 |
|
212 | [self.inAppBrowserViewController navigateTo:url];
|
213 | if (!browserOptions.hidden) {
|
214 | [self show:nil];
|
215 | }
|
216 | }
|
217 |
|
218 | - (void)show:(CDVInvokedUrlCommand*)command
|
219 | {
|
220 | if (self.inAppBrowserViewController == nil) {
|
221 | NSLog(@"Tried to show IAB after it was closed.");
|
222 | return;
|
223 | }
|
224 | if (_previousStatusBarStyle != -1) {
|
225 | NSLog(@"Tried to show IAB while already shown");
|
226 | return;
|
227 | }
|
228 |
|
229 | _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
|
230 |
|
231 | __block CDVInAppBrowserNavigationController* nav = [[CDVInAppBrowserNavigationController alloc]
|
232 | initWithRootViewController:self.inAppBrowserViewController];
|
233 | nav.orientationDelegate = self.inAppBrowserViewController;
|
234 | nav.navigationBarHidden = YES;
|
235 | nav.modalPresentationStyle = self.inAppBrowserViewController.modalPresentationStyle;
|
236 |
|
237 | __weak CDVInAppBrowser* weakSelf = self;
|
238 |
|
239 | // Run later to avoid the "took a long time" log message.
|
240 | dispatch_async(dispatch_get_main_queue(), ^{
|
241 | if (weakSelf.inAppBrowserViewController != nil) {
|
242 | CGRect frame = [[UIScreen mainScreen] bounds];
|
243 | UIWindow *tmpWindow = [[UIWindow alloc] initWithFrame:frame];
|
244 | UIViewController *tmpController = [[UIViewController alloc] init];
|
245 | [tmpWindow setRootViewController:tmpController];
|
246 | [tmpWindow setWindowLevel:UIWindowLevelNormal];
|
247 |
|
248 | [tmpWindow makeKeyAndVisible];
|
249 | [tmpController presentViewController:nav animated:YES completion:nil];
|
250 | }
|
251 | });
|
252 | }
|
253 |
|
254 | - (void)hide:(CDVInvokedUrlCommand*)command
|
255 | {
|
256 | if (self.inAppBrowserViewController == nil) {
|
257 | NSLog(@"Tried to hide IAB after it was closed.");
|
258 | return;
|
259 |
|
260 |
|
261 | }
|
262 | if (_previousStatusBarStyle == -1) {
|
263 | NSLog(@"Tried to hide IAB while already hidden");
|
264 | return;
|
265 | }
|
266 |
|
267 | _previousStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
|
268 |
|
269 | // Run later to avoid the "took a long time" log message.
|
270 | dispatch_async(dispatch_get_main_queue(), ^{
|
271 | if (self.inAppBrowserViewController != nil) {
|
272 | _previousStatusBarStyle = -1;
|
273 | [self.viewController dismissViewControllerAnimated:YES completion:nil];
|
274 | }
|
275 | });
|
276 | }
|
277 |
|
278 | - (void)openInCordovaWebView:(NSURL*)url withOptions:(NSString*)options
|
279 | {
|
280 | NSURLRequest* request = [NSURLRequest requestWithURL:url];
|
281 |
|
282 | #ifdef __CORDOVA_4_0_0
|
283 | // the webview engine itself will filter for this according to <allow-navigation> policy
|
284 | // in config.xml for cordova-ios-4.0
|
285 | [self.webViewEngine loadRequest:request];
|
286 | #else
|
287 | if ([self.commandDelegate URLIsWhitelisted:url]) {
|
288 | [self.webView loadRequest:request];
|
289 | } else { // this assumes the InAppBrowser can be excepted from the white-list
|
290 | [self openInInAppBrowser:url withOptions:options];
|
291 | }
|
292 | #endif
|
293 | }
|
294 |
|
295 | - (void)openInSystem:(NSURL*)url
|
296 | {
|
297 | [[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
|
298 | [[UIApplication sharedApplication] openURL:url];
|
299 | }
|
300 |
|
301 | // This is a helper method for the inject{Script|Style}{Code|File} API calls, which
|
302 | // provides a consistent method for injecting JavaScript code into the document.
|
303 | //
|
304 | // If a wrapper string is supplied, then the source string will be JSON-encoded (adding
|
305 | // quotes) and wrapped using string formatting. (The wrapper string should have a single
|
306 | // '%@' marker).
|
307 | //
|
308 | // If no wrapper is supplied, then the source string is executed directly.
|
309 |
|
310 | - (void)injectDeferredObject:(NSString*)source withWrapper:(NSString*)jsWrapper
|
311 | {
|
312 | // Ensure an iframe bridge is created to communicate with the CDVInAppBrowserViewController
|
313 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:@"(function(d){_cdvIframeBridge=d.getElementById('_cdvIframeBridge');if(!_cdvIframeBridge) {var e = _cdvIframeBridge = d.createElement('iframe');e.id='_cdvIframeBridge'; e.style.display='none';d.body.appendChild(e);}})(document)"];
|
314 |
|
315 | if (jsWrapper != nil) {
|
316 | NSData* jsonData = [NSJSONSerialization dataWithJSONObject:@[source] options:0 error:nil];
|
317 | NSString* sourceArrayString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
|
318 | if (sourceArrayString) {
|
319 | NSString* sourceString = [sourceArrayString substringWithRange:NSMakeRange(1, [sourceArrayString length] - 2)];
|
320 | NSString* jsToInject = [NSString stringWithFormat:jsWrapper, sourceString];
|
321 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:jsToInject];
|
322 | }
|
323 | } else {
|
324 | [self.inAppBrowserViewController.webView stringByEvaluatingJavaScriptFromString:source];
|
325 | }
|
326 | }
|
327 |
|
328 | - (void)injectScriptCode:(CDVInvokedUrlCommand*)command
|
329 | {
|
330 | NSString* jsWrapper = nil;
|
331 |
|
332 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
|
333 | jsWrapper = [NSString stringWithFormat:@"_cdvIframeBridge.src='gap-iab://%@/'+encodeURIComponent(JSON.stringify([eval(%%@)]));", command.callbackId];
|
334 | }
|
335 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
|
336 | }
|
337 |
|
338 | - (void)injectScriptFile:(CDVInvokedUrlCommand*)command
|
339 | {
|
340 | NSString* jsWrapper;
|
341 |
|
342 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
|
343 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('script'); c.src = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
|
344 | } else {
|
345 | jsWrapper = @"(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document)";
|
346 | }
|
347 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
|
348 | }
|
349 |
|
350 | - (void)injectStyleCode:(CDVInvokedUrlCommand*)command
|
351 | {
|
352 | NSString* jsWrapper;
|
353 |
|
354 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
|
355 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('style'); c.innerHTML = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
|
356 | } else {
|
357 | jsWrapper = @"(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document)";
|
358 | }
|
359 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
|
360 | }
|
361 |
|
362 | - (void)injectStyleFile:(CDVInvokedUrlCommand*)command
|
363 | {
|
364 | NSString* jsWrapper;
|
365 |
|
366 | if ((command.callbackId != nil) && ![command.callbackId isEqualToString:@"INVALID"]) {
|
367 | jsWrapper = [NSString stringWithFormat:@"(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%@; c.onload = function() { _cdvIframeBridge.src='gap-iab://%@'; }; d.body.appendChild(c); })(document)", command.callbackId];
|
368 | } else {
|
369 | jsWrapper = @"(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document)";
|
370 | }
|
371 | [self injectDeferredObject:[command argumentAtIndex:0] withWrapper:jsWrapper];
|
372 | }
|
373 |
|
374 | - (BOOL)isValidCallbackId:(NSString *)callbackId
|
375 | {
|
376 | NSError *err = nil;
|
377 | // Initialize on first use
|
378 | if (self.callbackIdPattern == nil) {
|
379 | self.callbackIdPattern = [NSRegularExpression regularExpressionWithPattern:@"^InAppBrowser[0-9]{1,10}$" options:0 error:&err];
|
380 | if (err != nil) {
|
381 | // Couldn't initialize Regex; No is safer than Yes.
|
382 | return NO;
|
383 | }
|
384 | }
|
385 | if ([self.callbackIdPattern firstMatchInString:callbackId options:0 range:NSMakeRange(0, [callbackId length])]) {
|
386 | return YES;
|
387 | }
|
388 | return NO;
|
389 | }
|
390 |
|
391 |
|
392 |
|
393 |
|
394 |
|
395 |
|
396 |
|
397 |
|
398 |
|
399 |
|
400 |
|
401 |
|
402 |
|
403 |
|
404 |
|
405 |
|
406 | - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
|
407 | {
|
408 | NSURL* url = request.URL;
|
409 | BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
|
410 |
|
411 | // See if the url uses the 'gap-iab' protocol. If so, the host should be the id of a callback to execute,
|
412 | // and the path, if present, should be a JSON-encoded value to pass to the callback.
|
413 | if ([[url scheme] isEqualToString:@"gap-iab"]) {
|
414 | NSString* scriptCallbackId = [url host];
|
415 | CDVPluginResult* pluginResult = nil;
|
416 |
|
417 | if ([self isValidCallbackId:scriptCallbackId]) {
|
418 | NSString* scriptResult = [url path];
|
419 | NSError* __autoreleasing error = nil;
|
420 |
|
421 | // The message should be a JSON-encoded array of the result of the script which executed.
|
422 | if ((scriptResult != nil) && ([scriptResult length] > 1)) {
|
423 | scriptResult = [scriptResult substringFromIndex:1];
|
424 | NSData* decodedResult = [NSJSONSerialization JSONObjectWithData:[scriptResult dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
|
425 | if ((error == nil) && [decodedResult isKindOfClass:[NSArray class]]) {
|
426 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:(NSArray*)decodedResult];
|
427 | } else {
|
428 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION];
|
429 | }
|
430 | } else {
|
431 | pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:@[]];
|
432 | }
|
433 | [self.commandDelegate sendPluginResult:pluginResult callbackId:scriptCallbackId];
|
434 | return NO;
|
435 | }
|
436 | }
|
437 | //if is an app store link, let the system handle it, otherwise it fails to load it
|
438 | else if ([[ url scheme] isEqualToString:@"itms-appss"] || [[ url scheme] isEqualToString:@"itms-apps"]) {
|
439 | [theWebView stopLoading];
|
440 | [self openInSystem:url];
|
441 | return NO;
|
442 | }
|
443 | else if ((self.callbackId != nil) && isTopLevelNavigation) {
|
444 | // Send a loadstart event for each top-level navigation (includes redirects).
|
445 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
|
446 | messageAsDictionary:@{@"type":@"loadstart", @"url":[url absoluteString]}];
|
447 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
|
448 |
|
449 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
|
450 | }
|
451 |
|
452 | return YES;
|
453 | }
|
454 |
|
455 | - (void)webViewDidStartLoad:(UIWebView*)theWebView
|
456 | {
|
457 | }
|
458 |
|
459 | - (void)webViewDidFinishLoad:(UIWebView*)theWebView
|
460 | {
|
461 | if (self.callbackId != nil) {
|
462 | // TODO: It would be more useful to return the URL the page is actually on (e.g. if it's been redirected).
|
463 | NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
|
464 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
|
465 | messageAsDictionary:@{@"type":@"loadstop", @"url":url}];
|
466 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
|
467 |
|
468 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
|
469 | }
|
470 | }
|
471 |
|
472 | - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
|
473 | {
|
474 | if (self.callbackId != nil) {
|
475 | NSString* url = [self.inAppBrowserViewController.currentURL absoluteString];
|
476 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR
|
477 | messageAsDictionary:@{@"type":@"loaderror", @"url":url, @"code": [NSNumber numberWithInteger:error.code], @"message": error.localizedDescription}];
|
478 | [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
|
479 |
|
480 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
|
481 | }
|
482 | }
|
483 |
|
484 | - (void)browserExit
|
485 | {
|
486 | if (self.callbackId != nil) {
|
487 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
|
488 | messageAsDictionary:@{@"type":@"exit"}];
|
489 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
|
490 | self.callbackId = nil;
|
491 | }
|
492 | // Set navigationDelegate to nil to ensure no callbacks are received from it.
|
493 | self.inAppBrowserViewController.navigationDelegate = nil;
|
494 | // Don't recycle the ViewController since it may be consuming a lot of memory.
|
495 | // Also - this is required for the PDF/User-Agent bug work-around.
|
496 | self.inAppBrowserViewController = nil;
|
497 |
|
498 | if (IsAtLeastiOSVersion(@"7.0")) {
|
499 | if (_previousStatusBarStyle != -1) {
|
500 | [[UIApplication sharedApplication] setStatusBarStyle:_previousStatusBarStyle];
|
501 | }
|
502 | }
|
503 |
|
504 | _previousStatusBarStyle = -1; // this value was reset before reapplying it. caused statusbar to stay black on ios7
|
505 | }
|
506 |
|
507 | @end
|
508 |
|
509 | #pragma mark CDVInAppBrowserViewController
|
510 |
|
511 | @implementation CDVInAppBrowserViewController
|
512 |
|
513 | @synthesize currentURL;
|
514 |
|
515 | - (id)initWithUserAgent:(NSString*)userAgent prevUserAgent:(NSString*)prevUserAgent browserOptions: (CDVInAppBrowserOptions*) browserOptions
|
516 | {
|
517 | self = [super init];
|
518 | if (self != nil) {
|
519 | _userAgent = userAgent;
|
520 | _prevUserAgent = prevUserAgent;
|
521 | _browserOptions = browserOptions;
|
522 | #ifdef __CORDOVA_4_0_0
|
523 | _webViewDelegate = [[CDVUIWebViewDelegate alloc] initWithDelegate:self];
|
524 | #else
|
525 | _webViewDelegate = [[CDVWebViewDelegate alloc] initWithDelegate:self];
|
526 | #endif
|
527 |
|
528 | [self createViews];
|
529 | }
|
530 |
|
531 | return self;
|
532 | }
|
533 |
|
534 | // Prevent crashes on closing windows
|
535 | -(void)dealloc {
|
536 | self.webView.delegate = nil;
|
537 | }
|
538 |
|
539 | - (void)createViews
|
540 | {
|
541 | // We create the views in code for primarily for ease of upgrades and not requiring an external .xib to be included
|
542 |
|
543 | CGRect webViewBounds = self.view.bounds;
|
544 | BOOL toolbarIsAtBottom = ![_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop];
|
545 | webViewBounds.size.height -= _browserOptions.location ? FOOTER_HEIGHT : TOOLBAR_HEIGHT;
|
546 | self.webView = [[UIWebView alloc] initWithFrame:webViewBounds];
|
547 |
|
548 | self.webView.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);
|
549 |
|
550 | [self.view addSubview:self.webView];
|
551 | [self.view sendSubviewToBack:self.webView];
|
552 |
|
553 | self.webView.delegate = _webViewDelegate;
|
554 | self.webView.backgroundColor = [UIColor whiteColor];
|
555 |
|
556 | self.webView.clearsContextBeforeDrawing = YES;
|
557 | self.webView.clipsToBounds = YES;
|
558 | self.webView.contentMode = UIViewContentModeScaleToFill;
|
559 | self.webView.multipleTouchEnabled = YES;
|
560 | self.webView.opaque = YES;
|
561 | self.webView.scalesPageToFit = NO;
|
562 | self.webView.userInteractionEnabled = YES;
|
563 |
|
564 | self.spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
|
565 | self.spinner.alpha = 1.000;
|
566 | self.spinner.autoresizesSubviews = YES;
|
567 | self.spinner.autoresizingMask = (UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin);
|
568 | self.spinner.clearsContextBeforeDrawing = NO;
|
569 | self.spinner.clipsToBounds = NO;
|
570 | self.spinner.contentMode = UIViewContentModeScaleToFill;
|
571 | self.spinner.frame = CGRectMake(CGRectGetMidX(self.webView.frame), CGRectGetMidY(self.webView.frame), 20.0, 20.0);
|
572 | self.spinner.hidden = NO;
|
573 | self.spinner.hidesWhenStopped = YES;
|
574 | self.spinner.multipleTouchEnabled = NO;
|
575 | self.spinner.opaque = NO;
|
576 | self.spinner.userInteractionEnabled = NO;
|
577 | [self.spinner stopAnimating];
|
578 |
|
579 | self.closeButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(close)];
|
580 | self.closeButton.enabled = YES;
|
581 |
|
582 | UIBarButtonItem* flexibleSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
|
583 |
|
584 | UIBarButtonItem* fixedSpaceButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
|
585 | fixedSpaceButton.width = 20;
|
586 |
|
587 | float toolbarY = toolbarIsAtBottom ? self.view.bounds.size.height - TOOLBAR_HEIGHT : 0.0;
|
588 | CGRect toolbarFrame = CGRectMake(0.0, toolbarY, self.view.bounds.size.width, TOOLBAR_HEIGHT);
|
589 |
|
590 | self.toolbar = [[UIToolbar alloc] initWithFrame:toolbarFrame];
|
591 | self.toolbar.alpha = 1.000;
|
592 | self.toolbar.autoresizesSubviews = YES;
|
593 | self.toolbar.autoresizingMask = toolbarIsAtBottom ? (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin) : UIViewAutoresizingFlexibleWidth;
|
594 | self.toolbar.barStyle = UIBarStyleBlackOpaque;
|
595 | self.toolbar.clearsContextBeforeDrawing = NO;
|
596 | self.toolbar.clipsToBounds = NO;
|
597 | self.toolbar.contentMode = UIViewContentModeScaleToFill;
|
598 | self.toolbar.hidden = NO;
|
599 | self.toolbar.multipleTouchEnabled = NO;
|
600 | self.toolbar.opaque = NO;
|
601 | self.toolbar.userInteractionEnabled = YES;
|
602 |
|
603 | CGFloat labelInset = 5.0;
|
604 | float locationBarY = toolbarIsAtBottom ? self.view.bounds.size.height - FOOTER_HEIGHT : self.view.bounds.size.height - LOCATIONBAR_HEIGHT;
|
605 |
|
606 | self.addressLabel = [[UILabel alloc] initWithFrame:CGRectMake(labelInset, locationBarY, self.view.bounds.size.width - labelInset, LOCATIONBAR_HEIGHT)];
|
607 | self.addressLabel.adjustsFontSizeToFitWidth = NO;
|
608 | self.addressLabel.alpha = 1.000;
|
609 | self.addressLabel.autoresizesSubviews = YES;
|
610 | self.addressLabel.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
|
611 | self.addressLabel.backgroundColor = [UIColor clearColor];
|
612 | self.addressLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
|
613 | self.addressLabel.clearsContextBeforeDrawing = YES;
|
614 | self.addressLabel.clipsToBounds = YES;
|
615 | self.addressLabel.contentMode = UIViewContentModeScaleToFill;
|
616 | self.addressLabel.enabled = YES;
|
617 | self.addressLabel.hidden = NO;
|
618 | self.addressLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
619 |
|
620 | if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumScaleFactor:")]) {
|
621 | [self.addressLabel setValue:@(10.0/[UIFont labelFontSize]) forKey:@"minimumScaleFactor"];
|
622 | } else if ([self.addressLabel respondsToSelector:NSSelectorFromString(@"setMinimumFontSize:")]) {
|
623 | [self.addressLabel setValue:@(10.0) forKey:@"minimumFontSize"];
|
624 | }
|
625 |
|
626 | self.addressLabel.multipleTouchEnabled = NO;
|
627 | self.addressLabel.numberOfLines = 1;
|
628 | self.addressLabel.opaque = NO;
|
629 | self.addressLabel.shadowOffset = CGSizeMake(0.0, -1.0);
|
630 | self.addressLabel.text = NSLocalizedString(@"Loading...", nil);
|
631 | self.addressLabel.textAlignment = NSTextAlignmentLeft;
|
632 | self.addressLabel.textColor = [UIColor colorWithWhite:1.000 alpha:1.000];
|
633 | self.addressLabel.userInteractionEnabled = NO;
|
634 |
|
635 | NSString* frontArrowString = NSLocalizedString(@"►", nil); // create arrow from Unicode char
|
636 | self.forwardButton = [[UIBarButtonItem alloc] initWithTitle:frontArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goForward:)];
|
637 | self.forwardButton.enabled = YES;
|
638 | self.forwardButton.imageInsets = UIEdgeInsetsZero;
|
639 |
|
640 | NSString* backArrowString = NSLocalizedString(@"◄", nil); // create arrow from Unicode char
|
641 | self.backButton = [[UIBarButtonItem alloc] initWithTitle:backArrowString style:UIBarButtonItemStylePlain target:self action:@selector(goBack:)];
|
642 | self.backButton.enabled = YES;
|
643 | self.backButton.imageInsets = UIEdgeInsetsZero;
|
644 |
|
645 | [self.toolbar setItems:@[self.closeButton, flexibleSpaceButton, self.backButton, fixedSpaceButton, self.forwardButton]];
|
646 |
|
647 | self.view.backgroundColor = [UIColor grayColor];
|
648 | [self.view addSubview:self.toolbar];
|
649 | [self.view addSubview:self.addressLabel];
|
650 | [self.view addSubview:self.spinner];
|
651 | }
|
652 |
|
653 | - (void) setWebViewFrame : (CGRect) frame {
|
654 | NSLog(@"Setting the WebView's frame to %@", NSStringFromCGRect(frame));
|
655 | [self.webView setFrame:frame];
|
656 | }
|
657 |
|
658 | - (void)setCloseButtonTitle:(NSString*)title
|
659 | {
|
660 | // the advantage of using UIBarButtonSystemItemDone is the system will localize it for you automatically
|
661 | // but, if you want to set this yourself, knock yourself out (we can't set the title for a system Done button, so we have to create a new one)
|
662 | self.closeButton = nil;
|
663 | self.closeButton = [[UIBarButtonItem alloc] initWithTitle:title style:UIBarButtonItemStyleBordered target:self action:@selector(close)];
|
664 | self.closeButton.enabled = YES;
|
665 | self.closeButton.tintColor = [UIColor colorWithRed:60.0 / 255.0 green:136.0 / 255.0 blue:230.0 / 255.0 alpha:1];
|
666 |
|
667 | NSMutableArray* items = [self.toolbar.items mutableCopy];
|
668 | [items replaceObjectAtIndex:0 withObject:self.closeButton];
|
669 | [self.toolbar setItems:items];
|
670 | }
|
671 |
|
672 | - (void)showLocationBar:(BOOL)show
|
673 | {
|
674 | CGRect locationbarFrame = self.addressLabel.frame;
|
675 |
|
676 | BOOL toolbarVisible = !self.toolbar.hidden;
|
677 |
|
678 | // prevent double show/hide
|
679 | if (show == !(self.addressLabel.hidden)) {
|
680 | return;
|
681 | }
|
682 |
|
683 | if (show) {
|
684 | self.addressLabel.hidden = NO;
|
685 |
|
686 | if (toolbarVisible) {
|
687 | // toolBar at the bottom, leave as is
|
688 | // put locationBar on top of the toolBar
|
689 |
|
690 | CGRect webViewBounds = self.view.bounds;
|
691 | webViewBounds.size.height -= FOOTER_HEIGHT;
|
692 | [self setWebViewFrame:webViewBounds];
|
693 |
|
694 | locationbarFrame.origin.y = webViewBounds.size.height;
|
695 | self.addressLabel.frame = locationbarFrame;
|
696 | } else {
|
697 | // no toolBar, so put locationBar at the bottom
|
698 |
|
699 | CGRect webViewBounds = self.view.bounds;
|
700 | webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
|
701 | [self setWebViewFrame:webViewBounds];
|
702 |
|
703 | locationbarFrame.origin.y = webViewBounds.size.height;
|
704 | self.addressLabel.frame = locationbarFrame;
|
705 | }
|
706 | } else {
|
707 | self.addressLabel.hidden = YES;
|
708 |
|
709 | if (toolbarVisible) {
|
710 | // locationBar is on top of toolBar, hide locationBar
|
711 |
|
712 | // webView take up whole height less toolBar height
|
713 | CGRect webViewBounds = self.view.bounds;
|
714 | webViewBounds.size.height -= TOOLBAR_HEIGHT;
|
715 | [self setWebViewFrame:webViewBounds];
|
716 | } else {
|
717 | // no toolBar, expand webView to screen dimensions
|
718 | [self setWebViewFrame:self.view.bounds];
|
719 | }
|
720 | }
|
721 | }
|
722 |
|
723 | - (void)showToolBar:(BOOL)show : (NSString *) toolbarPosition
|
724 | {
|
725 | CGRect toolbarFrame = self.toolbar.frame;
|
726 | CGRect locationbarFrame = self.addressLabel.frame;
|
727 |
|
728 | BOOL locationbarVisible = !self.addressLabel.hidden;
|
729 |
|
730 | // prevent double show/hide
|
731 | if (show == !(self.toolbar.hidden)) {
|
732 | return;
|
733 | }
|
734 |
|
735 | if (show) {
|
736 | self.toolbar.hidden = NO;
|
737 | CGRect webViewBounds = self.view.bounds;
|
738 |
|
739 | if (locationbarVisible) {
|
740 | // locationBar at the bottom, move locationBar up
|
741 | // put toolBar at the bottom
|
742 | webViewBounds.size.height -= FOOTER_HEIGHT;
|
743 | locationbarFrame.origin.y = webViewBounds.size.height;
|
744 | self.addressLabel.frame = locationbarFrame;
|
745 | self.toolbar.frame = toolbarFrame;
|
746 | } else {
|
747 | // no locationBar, so put toolBar at the bottom
|
748 | CGRect webViewBounds = self.view.bounds;
|
749 | webViewBounds.size.height -= TOOLBAR_HEIGHT;
|
750 | self.toolbar.frame = toolbarFrame;
|
751 | }
|
752 |
|
753 | if ([toolbarPosition isEqualToString:kInAppBrowserToolbarBarPositionTop]) {
|
754 | toolbarFrame.origin.y = 0;
|
755 | webViewBounds.origin.y += toolbarFrame.size.height;
|
756 | [self setWebViewFrame:webViewBounds];
|
757 | } else {
|
758 | toolbarFrame.origin.y = (webViewBounds.size.height + LOCATIONBAR_HEIGHT);
|
759 | }
|
760 | [self setWebViewFrame:webViewBounds];
|
761 |
|
762 | } else {
|
763 | self.toolbar.hidden = YES;
|
764 |
|
765 | if (locationbarVisible) {
|
766 | // locationBar is on top of toolBar, hide toolBar
|
767 | // put locationBar at the bottom
|
768 |
|
769 | // webView take up whole height less locationBar height
|
770 | CGRect webViewBounds = self.view.bounds;
|
771 | webViewBounds.size.height -= LOCATIONBAR_HEIGHT;
|
772 | [self setWebViewFrame:webViewBounds];
|
773 |
|
774 | // move locationBar down
|
775 | locationbarFrame.origin.y = webViewBounds.size.height;
|
776 | self.addressLabel.frame = locationbarFrame;
|
777 | } else {
|
778 | // no locationBar, expand webView to screen dimensions
|
779 | [self setWebViewFrame:self.view.bounds];
|
780 | }
|
781 | }
|
782 | }
|
783 |
|
784 | - (void)viewDidLoad
|
785 | {
|
786 | [super viewDidLoad];
|
787 | }
|
788 |
|
789 | - (void)viewDidUnload
|
790 | {
|
791 | [self.webView loadHTMLString:nil baseURL:nil];
|
792 | [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
|
793 | [super viewDidUnload];
|
794 | }
|
795 |
|
796 | - (UIStatusBarStyle)preferredStatusBarStyle
|
797 | {
|
798 | return UIStatusBarStyleDefault;
|
799 | }
|
800 |
|
801 | - (BOOL)prefersStatusBarHidden {
|
802 | return NO;
|
803 | }
|
804 |
|
805 | - (void)close
|
806 | {
|
807 | [CDVUserAgentUtil releaseLock:&_userAgentLockToken];
|
808 | self.currentURL = nil;
|
809 |
|
810 | if ((self.navigationDelegate != nil) && [self.navigationDelegate respondsToSelector:@selector(browserExit)]) {
|
811 | [self.navigationDelegate browserExit];
|
812 | }
|
813 |
|
814 | __weak UIViewController* weakSelf = self;
|
815 |
|
816 | // Run later to avoid the "took a long time" log message.
|
817 | dispatch_async(dispatch_get_main_queue(), ^{
|
818 | if ([weakSelf respondsToSelector:@selector(presentingViewController)]) {
|
819 | [[weakSelf presentingViewController] dismissViewControllerAnimated:YES completion:nil];
|
820 | } else {
|
821 | [[weakSelf parentViewController] dismissViewControllerAnimated:YES completion:nil];
|
822 | }
|
823 | });
|
824 | }
|
825 |
|
826 | - (void)navigateTo:(NSURL*)url
|
827 | {
|
828 | NSURLRequest* request = [NSURLRequest requestWithURL:url];
|
829 |
|
830 | if (_userAgentLockToken != 0) {
|
831 | [self.webView loadRequest:request];
|
832 | } else {
|
833 | __weak CDVInAppBrowserViewController* weakSelf = self;
|
834 | [CDVUserAgentUtil acquireLock:^(NSInteger lockToken) {
|
835 | _userAgentLockToken = lockToken;
|
836 | [CDVUserAgentUtil setUserAgent:_userAgent lockToken:lockToken];
|
837 | [weakSelf.webView loadRequest:request];
|
838 | }];
|
839 | }
|
840 | }
|
841 |
|
842 | - (void)goBack:(id)sender
|
843 | {
|
844 | [self.webView goBack];
|
845 | }
|
846 |
|
847 | - (void)goForward:(id)sender
|
848 | {
|
849 | [self.webView goForward];
|
850 | }
|
851 |
|
852 | - (void)viewWillAppear:(BOOL)animated
|
853 | {
|
854 | if (IsAtLeastiOSVersion(@"7.0")) {
|
855 | [[UIApplication sharedApplication] setStatusBarStyle:[self preferredStatusBarStyle]];
|
856 | }
|
857 | [self rePositionViews];
|
858 |
|
859 | [super viewWillAppear:animated];
|
860 | }
|
861 |
|
862 | //
|
863 | // On iOS 7 the status bar is part of the view's dimensions, therefore it's height has to be taken into account.
|
864 | // The height of it could be hardcoded as 20 pixels, but that would assume that the upcoming releases of iOS won't
|
865 | // change that value.
|
866 | //
|
867 | - (float) getStatusBarOffset {
|
868 | CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
|
869 | float statusBarOffset = IsAtLeastiOSVersion(@"7.0") ? MIN(statusBarFrame.size.width, statusBarFrame.size.height) : 0.0;
|
870 | return statusBarOffset;
|
871 | }
|
872 |
|
873 | - (void) rePositionViews {
|
874 | if ([_browserOptions.toolbarposition isEqualToString:kInAppBrowserToolbarBarPositionTop]) {
|
875 | [self.webView setFrame:CGRectMake(self.webView.frame.origin.x, TOOLBAR_HEIGHT, self.webView.frame.size.width, self.webView.frame.size.height)];
|
876 | [self.toolbar setFrame:CGRectMake(self.toolbar.frame.origin.x, [self getStatusBarOffset], self.toolbar.frame.size.width, self.toolbar.frame.size.height)];
|
877 | }
|
878 | }
|
879 |
|
880 | #pragma mark UIWebViewDelegate
|
881 |
|
882 | - (void)webViewDidStartLoad:(UIWebView*)theWebView
|
883 | {
|
884 | // loading url, start spinner, update back/forward
|
885 |
|
886 | self.addressLabel.text = NSLocalizedString(@"Loading...", nil);
|
887 | self.backButton.enabled = theWebView.canGoBack;
|
888 | self.forwardButton.enabled = theWebView.canGoForward;
|
889 |
|
890 | [self.spinner startAnimating];
|
891 |
|
892 | return [self.navigationDelegate webViewDidStartLoad:theWebView];
|
893 | }
|
894 |
|
895 | - (BOOL)webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType
|
896 | {
|
897 | BOOL isTopLevelNavigation = [request.URL isEqual:[request mainDocumentURL]];
|
898 |
|
899 | if (isTopLevelNavigation) {
|
900 | self.currentURL = request.URL;
|
901 | }
|
902 | return [self.navigationDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType];
|
903 | }
|
904 |
|
905 | - (void)webViewDidFinishLoad:(UIWebView*)theWebView
|
906 | {
|
907 | // update url, stop spinner, update back/forward
|
908 |
|
909 | self.addressLabel.text = [self.currentURL absoluteString];
|
910 | self.backButton.enabled = theWebView.canGoBack;
|
911 | self.forwardButton.enabled = theWebView.canGoForward;
|
912 |
|
913 | [self.spinner stopAnimating];
|
914 |
|
915 | // Work around a bug where the first time a PDF is opened, all UIWebViews
|
916 | // reload their User-Agent from NSUserDefaults.
|
917 | // This work-around makes the following assumptions:
|
918 | // 1. The app has only a single Cordova Webview. If not, then the app should
|
919 | // take it upon themselves to load a PDF in the background as a part of
|
920 | // their start-up flow.
|
921 | // 2. That the PDF does not require any additional network requests. We change
|
922 | // the user-agent here back to that of the CDVViewController, so requests
|
923 | // from it must pass through its white-list. This *does* break PDFs that
|
924 | // contain links to other remote PDF/websites.
|
925 | // More info at https://issues.apache.org/jira/browse/CB-2225
|
926 | BOOL isPDF = [@"true" isEqualToString :[theWebView stringByEvaluatingJavaScriptFromString:@"document.body==null"]];
|
927 | if (isPDF) {
|
928 | [CDVUserAgentUtil setUserAgent:_prevUserAgent lockToken:_userAgentLockToken];
|
929 | }
|
930 |
|
931 | [self.navigationDelegate webViewDidFinishLoad:theWebView];
|
932 | }
|
933 |
|
934 | - (void)webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error
|
935 | {
|
936 | // log fail message, stop spinner, update back/forward
|
937 | NSLog(@"webView:didFailLoadWithError - %ld: %@", (long)error.code, [error localizedDescription]);
|
938 |
|
939 | self.backButton.enabled = theWebView.canGoBack;
|
940 | self.forwardButton.enabled = theWebView.canGoForward;
|
941 | [self.spinner stopAnimating];
|
942 |
|
943 | self.addressLabel.text = NSLocalizedString(@"Load Error", nil);
|
944 |
|
945 | [self.navigationDelegate webView:theWebView didFailLoadWithError:error];
|
946 | }
|
947 |
|
948 | #pragma mark CDVScreenOrientationDelegate
|
949 |
|
950 | - (BOOL)shouldAutorotate
|
951 | {
|
952 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
|
953 | return [self.orientationDelegate shouldAutorotate];
|
954 | }
|
955 | return YES;
|
956 | }
|
957 |
|
958 | - (NSUInteger)supportedInterfaceOrientations
|
959 | {
|
960 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
|
961 | return [self.orientationDelegate supportedInterfaceOrientations];
|
962 | }
|
963 |
|
964 | return 1 << UIInterfaceOrientationPortrait;
|
965 | }
|
966 |
|
967 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
968 | {
|
969 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
|
970 | return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
|
971 | }
|
972 |
|
973 | return YES;
|
974 | }
|
975 |
|
976 | @end
|
977 |
|
978 | @implementation CDVInAppBrowserOptions
|
979 |
|
980 | - (id)init
|
981 | {
|
982 | if (self = [super init]) {
|
983 | // default values
|
984 | self.location = YES;
|
985 | self.toolbar = YES;
|
986 | self.closebuttoncaption = nil;
|
987 | self.toolbarposition = kInAppBrowserToolbarBarPositionBottom;
|
988 | self.clearcache = NO;
|
989 | self.clearsessioncache = NO;
|
990 |
|
991 | self.enableviewportscale = NO;
|
992 | self.mediaplaybackrequiresuseraction = NO;
|
993 | self.allowinlinemediaplayback = NO;
|
994 | self.keyboarddisplayrequiresuseraction = YES;
|
995 | self.suppressesincrementalrendering = NO;
|
996 | self.hidden = NO;
|
997 | self.disallowoverscroll = NO;
|
998 | }
|
999 |
|
1000 | return self;
|
1001 | }
|
1002 |
|
1003 | + (CDVInAppBrowserOptions*)parseOptions:(NSString*)options
|
1004 | {
|
1005 | CDVInAppBrowserOptions* obj = [[CDVInAppBrowserOptions alloc] init];
|
1006 |
|
1007 | // NOTE: this parsing does not handle quotes within values
|
1008 | NSArray* pairs = [options componentsSeparatedByString:@","];
|
1009 |
|
1010 | // parse keys and values, set the properties
|
1011 | for (NSString* pair in pairs) {
|
1012 | NSArray* keyvalue = [pair componentsSeparatedByString:@"="];
|
1013 |
|
1014 | if ([keyvalue count] == 2) {
|
1015 | NSString* key = [[keyvalue objectAtIndex:0] lowercaseString];
|
1016 | NSString* value = [keyvalue objectAtIndex:1];
|
1017 | NSString* value_lc = [value lowercaseString];
|
1018 |
|
1019 | BOOL isBoolean = [value_lc isEqualToString:@"yes"] || [value_lc isEqualToString:@"no"];
|
1020 | NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
|
1021 | [numberFormatter setAllowsFloats:YES];
|
1022 | BOOL isNumber = [numberFormatter numberFromString:value_lc] != nil;
|
1023 |
|
1024 | // set the property according to the key name
|
1025 | if ([obj respondsToSelector:NSSelectorFromString(key)]) {
|
1026 | if (isNumber) {
|
1027 | [obj setValue:[numberFormatter numberFromString:value_lc] forKey:key];
|
1028 | } else if (isBoolean) {
|
1029 | [obj setValue:[NSNumber numberWithBool:[value_lc isEqualToString:@"yes"]] forKey:key];
|
1030 | } else {
|
1031 | [obj setValue:value forKey:key];
|
1032 | }
|
1033 | }
|
1034 | }
|
1035 | }
|
1036 |
|
1037 | return obj;
|
1038 | }
|
1039 |
|
1040 | @end
|
1041 |
|
1042 | @implementation CDVInAppBrowserNavigationController : UINavigationController
|
1043 |
|
1044 | - (void) dismissViewControllerAnimated:(BOOL)flag completion:(void (^)(void))completion {
|
1045 | if ( self.presentedViewController) {
|
1046 | [super dismissViewControllerAnimated:flag completion:completion];
|
1047 | }
|
1048 | }
|
1049 |
|
1050 | - (void) viewDidLoad {
|
1051 |
|
1052 | CGRect statusBarFrame = [self invertFrameIfNeeded:[UIApplication sharedApplication].statusBarFrame];
|
1053 | statusBarFrame.size.height = STATUSBAR_HEIGHT;
|
1054 | // simplified from: http://stackoverflow.com/a/25669695/219684
|
1055 |
|
1056 | UIToolbar* bgToolbar = [[UIToolbar alloc] initWithFrame:statusBarFrame];
|
1057 | bgToolbar.barStyle = UIBarStyleDefault;
|
1058 | [bgToolbar setAutoresizingMask:UIViewAutoresizingFlexibleWidth];
|
1059 | [self.view addSubview:bgToolbar];
|
1060 |
|
1061 | [super viewDidLoad];
|
1062 | }
|
1063 |
|
1064 | - (CGRect) invertFrameIfNeeded:(CGRect)rect {
|
1065 | // We need to invert since on iOS 7 frames are always in Portrait context
|
1066 | if (!IsAtLeastiOSVersion(@"8.0")) {
|
1067 | if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
|
1068 | CGFloat temp = rect.size.width;
|
1069 | rect.size.width = rect.size.height;
|
1070 | rect.size.height = temp;
|
1071 | }
|
1072 | rect.origin = CGPointZero;
|
1073 | }
|
1074 | return rect;
|
1075 | }
|
1076 |
|
1077 | #pragma mark CDVScreenOrientationDelegate
|
1078 |
|
1079 | - (BOOL)shouldAutorotate
|
1080 | {
|
1081 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotate)]) {
|
1082 | return [self.orientationDelegate shouldAutorotate];
|
1083 | }
|
1084 | return YES;
|
1085 | }
|
1086 |
|
1087 | - (NSUInteger)supportedInterfaceOrientations
|
1088 | {
|
1089 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(supportedInterfaceOrientations)]) {
|
1090 | return [self.orientationDelegate supportedInterfaceOrientations];
|
1091 | }
|
1092 |
|
1093 | return 1 << UIInterfaceOrientationPortrait;
|
1094 | }
|
1095 |
|
1096 | - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
|
1097 | {
|
1098 | if ((self.orientationDelegate != nil) && [self.orientationDelegate respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
|
1099 | return [self.orientationDelegate shouldAutorotateToInterfaceOrientation:interfaceOrientation];
|
1100 | }
|
1101 |
|
1102 | return YES;
|
1103 | }
|
1104 |
|
1105 |
|
1106 | @end
|
1107 |
|