From c684bea3aad91d1aa8b68935fa2a02fc97647fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 11:00:01 -0300 Subject: [PATCH 01/11] Fix: after device locking socket connection might be lost. --- AUTHORS | 1 + XWebView/XWVChannel.swift | 16 ++++++++++++---- XWebView/XWVHttpServer.m | 3 ++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 31fcdde..9b0056d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,4 @@ David Kim Jonathan Dong Zhenyu Liang +Fernando Martínez diff --git a/XWebView/XWVChannel.swift b/XWebView/XWVChannel.swift index 0ae5cd9..7e4cdfe 100644 --- a/XWebView/XWVChannel.swift +++ b/XWebView/XWVChannel.swift @@ -54,7 +54,10 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { } public func bindPlugin(object: AnyObject, toNamespace namespace: String) -> XWVScriptObject? { - assert(webView != nil && typeInfo == nil) + if webView == nil || typeInfo != nil { + return nil + } + webView!.configuration.userContentController.addScriptMessageHandler(self, name: name) typeInfo = XWVMetaObject(plugin: object.dynamicType) let plugin = XWVBindingObject(namespace: namespace, channel: self, object: object) @@ -70,7 +73,11 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { } public func unbind() { - assert(typeInfo != nil) + if typeInfo == nil { + print(" Warning: can't unbind inexistent plugin.") + return + } + instances.removeAll(keepCapacity: false) webView?.configuration.userContentController.removeScriptMessageHandlerForName(name) } @@ -83,10 +90,11 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { if target == 0 { // Dispose plugin unbind() + print(" Plugin was disposed") } else { // Dispose instance let object = instances.removeValueForKey(target) - assert(object != nil) + print(" Instance \(object) was disposed") } } else if let member = typeInfo[opcode] where member.isProperty { // Update property @@ -107,7 +115,7 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { obj.userContentController(userContentController, didReceiveScriptMessage: message) } else { // discard unknown message - print("WARNING: Unknown message: \(message.body)") + print(" WARNING: Unknown message: \(message.body)") } } diff --git a/XWebView/XWVHttpServer.m b/XWebView/XWVHttpServer.m index d2b0dbf..7cf27cf 100644 --- a/XWebView/XWVHttpServer.m +++ b/XWebView/XWVHttpServer.m @@ -68,6 +68,7 @@ - (void)didCloseConnection:(NSNotification *)connection { static void ServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { XWVHttpServer *server = (__bridge XWVHttpServer *)info; CFSocketNativeHandle handle = *(CFSocketNativeHandle *)data; + assert(socket == server->_socket && type == kCFSocketAcceptCallBack); XWVHttpConnection * conn = [[XWVHttpConnection alloc] initWithNativeHandle:handle]; @@ -77,7 +78,7 @@ static void ServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, } - (BOOL)start { - assert(_socket == NULL); + if (_socket != nil) return NO; struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); From c39e229ce3f92911a0977b8cca3205c2a2b9018a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 11:03:19 -0300 Subject: [PATCH 02/11] Raise runtime exceptions for critical methods. --- XWebView/XWVException.h | 28 +++++++++++++++++++ XWebView/XWVException.m | 53 ++++++++++++++++++++++++++++++++++++ XWebView/XWVHttpConnection.m | 9 ++++-- 3 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 XWebView/XWVException.h create mode 100644 XWebView/XWVException.m diff --git a/XWebView/XWVException.h b/XWebView/XWVException.h new file mode 100644 index 0000000..cab3258 --- /dev/null +++ b/XWebView/XWVException.h @@ -0,0 +1,28 @@ +/* + Copyright 2015 XWebView + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#ifndef XWVException_h +#define XWVException_h + +#import + +@interface XWVException : NSObject + ++ (void)raiseUnlessRoot:(NSURL *)root; ++ (void)raiseUnlessBadResponse:(const NSHTTPURLResponse *)response; +@end + +#endif /* XWVException_h */ diff --git a/XWebView/XWVException.m b/XWebView/XWVException.m new file mode 100644 index 0000000..a5dd543 --- /dev/null +++ b/XWebView/XWVException.m @@ -0,0 +1,53 @@ +/* + Copyright 2015 XWebView + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +#import "XWVException.h" + +@interface XWVException () +@end + +@implementation XWVException { + +} + ++ (void)raiseUnlessRoot:(NSURL *)root { + if (root != nil) return; + NSException *exception = [NSException exceptionWithName:@"XWVRootDirectoryNotFound" + reason:@"Can't find root directory" + userInfo:nil]; + @throw exception; +} + ++ (void)raiseUnlessBadResponse:(const NSHTTPURLResponse *)response { + int class = (int)response.statusCode / 100 - 1; + if (class >= 0 && class < 5) return; + + NSDictionary *headers = response.allHeaderFields; + NSException *exception = [NSException exceptionWithName:@"XWVWrongServerStatusCode" + reason:@"Server returnered a status code >= 500" + userInfo:@{ + @"response": response, + @"headers": headers, + @"statusCode": @(response.statusCode) + }]; + @throw exception; +} + +@end diff --git a/XWebView/XWVHttpConnection.m b/XWebView/XWVHttpConnection.m index b99c6a4..ed9bc72 100644 --- a/XWebView/XWVHttpConnection.m +++ b/XWebView/XWVHttpConnection.m @@ -30,6 +30,7 @@ #endif #import "XWVHttpConnection.h" +#import "XWVException.h" static NSMutableURLRequest *parseRequest(NSMutableURLRequest *request, NSData *line); static NSHTTPURLResponse *buildResponse(NSURLRequest *request, NSURL *rootURL); @@ -62,7 +63,7 @@ - (id)initWithNativeHandle:(CFSocketNativeHandle)handle { } - (BOOL)open { - assert(_requestQueue == nil); // reopen is forbidden + if (_requestQueue != nil) return NO; CFReadStreamRef input = NULL; CFWriteStreamRef output = NULL; @@ -84,6 +85,7 @@ - (BOOL)open { if (_delegate && [_delegate respondsToSelector:@selector(didOpenConnection:)]) [_delegate didOpenConnection:self]; + return YES; } @@ -105,7 +107,7 @@ - (NSURL *)rootURL { NSURL *root; if (_delegate && [_delegate respondsToSelector:@selector(documentRoot)]) { root = [NSURL fileURLWithPath:_delegate.documentRoot isDirectory:YES]; - assert(root); + [XWVException raiseUnlessRoot:root]; } else { NSBundle *bundle = [NSBundle mainBundle]; root = bundle.resourceURL ?: bundle.bundleURL; @@ -341,7 +343,8 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { NSString *name; int class = (int)response.statusCode / 100 - 1; - assert(class >= 0 && class < 5); + [XWVException raiseUnlessBadResponse:response]; + int code = (int)response.statusCode % 100; if (code >= sizeof(HttpResponseReasonPhrase[class]) / sizeof(char *) || HttpResponseReasonPhrase[class][code] == NULL) { From 6ada562c01832fc3d30d46a0585532013e29eca8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 11:03:48 -0300 Subject: [PATCH 03/11] Improve log messages ( tag) --- XWebView/XWVUserScript.swift | 2 +- XWebView/XWebView.swift | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/XWebView/XWVUserScript.swift b/XWebView/XWVUserScript.swift index fa84af2..e89f626 100644 --- a/XWebView/XWVUserScript.swift +++ b/XWebView/XWVUserScript.swift @@ -45,7 +45,7 @@ class XWVUserScript { if webView.URL != nil { webView.evaluateJavaScript(script.source) { if let error = $1 { - print("ERROR: Inject user script in context.\n\(error)") + print(" ERROR: Inject user script in context.\n\(error)") } } } diff --git a/XWebView/XWebView.swift b/XWebView/XWebView.swift index ee52853..62549e1 100644 --- a/XWebView/XWebView.swift +++ b/XWebView/XWebView.swift @@ -86,7 +86,7 @@ extension WKWebView { condition.unlock() } if !done { - print("ERROR: Timeout to evaluate script.") + print(" ERROR: Timeout to evaluate script.") } return result } @@ -102,14 +102,13 @@ extension WKWebView { // See http://nshipster.com/swift-objc-runtime/ private static var initialized: dispatch_once_t = 0 public override class func initialize() { - //if #available(iOS 9, *) { return } guard self == WKWebView.self else { return } dispatch_once(&initialized) { let selector = Selector("loadFileURL:allowingReadAccessToURL:") let method = class_getInstanceMethod(self, Selector("_loadFileURL:allowingReadAccessToURL:")) assert(method != nil) if class_addMethod(self, selector, method_getImplementation(method), method_getTypeEncoding(method)) { - print("iOS 8.x") + print(" using backward compatibility for iOS 8.x") method_exchangeImplementations( class_getInstanceMethod(self, Selector("loadHTMLString:baseURL:")), class_getInstanceMethod(self, Selector("_loadHTMLString:baseURL:")) From 05387f95eb7ac6e5e6bd4f5386e4641ebb2b0b7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 11:03:58 -0300 Subject: [PATCH 04/11] Add small sample --- XWebView.xcodeproj/project.pbxproj | 154 +++++++++++++++++- XWebViewExample/AppDelegate.swift | 46 ++++++ .../AppIcon.appiconset/Contents.json | 38 +++++ .../Base.lproj/LaunchScreen.storyboard | 28 ++++ XWebViewExample/Base.lproj/Main.storyboard | 100 ++++++++++++ XWebViewExample/Info.plist | 40 +++++ XWebViewExample/SamplePlugin.swift | 23 +++ XWebViewExample/ViewController.swift | 48 ++++++ XWebViewExample/www/avatar_0.png | Bin 0 -> 18100 bytes XWebViewExample/www/avatar_1.png | Bin 0 -> 18259 bytes XWebViewExample/www/jquery.min.js | 16 ++ XWebViewExample/www/sample.js | 26 +++ XWebViewExample/www/sample_page.html | 34 ++++ 13 files changed, 546 insertions(+), 7 deletions(-) create mode 100644 XWebViewExample/AppDelegate.swift create mode 100644 XWebViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 XWebViewExample/Base.lproj/LaunchScreen.storyboard create mode 100644 XWebViewExample/Base.lproj/Main.storyboard create mode 100644 XWebViewExample/Info.plist create mode 100644 XWebViewExample/SamplePlugin.swift create mode 100644 XWebViewExample/ViewController.swift create mode 100644 XWebViewExample/www/avatar_0.png create mode 100644 XWebViewExample/www/avatar_1.png create mode 100644 XWebViewExample/www/jquery.min.js create mode 100644 XWebViewExample/www/sample.js create mode 100644 XWebViewExample/www/sample_page.html diff --git a/XWebView.xcodeproj/project.pbxproj b/XWebView.xcodeproj/project.pbxproj index 7cd6e87..338bea0 100644 --- a/XWebView.xcodeproj/project.pbxproj +++ b/XWebView.xcodeproj/project.pbxproj @@ -12,6 +12,15 @@ AB023EBE1A8C8BC700580A2A /* XWebView.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EE62683519FA323900EFC3F8 /* XWebView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; AB2273E51AA6FDA700F9207A /* www in Resources */ = {isa = PBXBuildFile; fileRef = AB2273E41AA6FDA700F9207A /* www */; }; ABF68ECD1A6B45FC0058267B /* XWebView.h in Headers */ = {isa = PBXBuildFile; fileRef = EE62691C19FA52FC00EFC3F8 /* XWebView.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EDAFD9ED1BB981C300DD660E /* XWVException.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAFD9EC1BB981C300DD660E /* XWVException.m */; settings = {ASSET_TAGS = (); }; }; + EDC0BA131BB9BD2800241B2D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC0BA121BB9BD2800241B2D /* AppDelegate.swift */; }; + EDC0BA151BB9BD2800241B2D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC0BA141BB9BD2800241B2D /* ViewController.swift */; }; + EDC0BA181BB9BD2800241B2D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EDC0BA161BB9BD2800241B2D /* Main.storyboard */; }; + EDC0BA1A1BB9BD2800241B2D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EDC0BA191BB9BD2800241B2D /* Assets.xcassets */; }; + EDC0BA1D1BB9BD2800241B2D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EDC0BA1B1BB9BD2800241B2D /* LaunchScreen.storyboard */; }; + EDC0BA251BB9BD7600241B2D /* SamplePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC0BA231BB9BD7600241B2D /* SamplePlugin.swift */; settings = {ASSET_TAGS = (); }; }; + EDC0BA271BB9BFBE00241B2D /* www in Resources */ = {isa = PBXBuildFile; fileRef = EDC0BA261BB9BFBE00241B2D /* www */; settings = {ASSET_TAGS = (); }; }; + EDC0BA281BB9C15100241B2D /* XWebView.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE62683519FA323900EFC3F8 /* XWebView.framework */; }; EE0A1DD31A52775400C9E6D3 /* XWVChannel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0A1DD21A52775400C9E6D3 /* XWVChannel.swift */; }; EE131CA71B5F900400A9E790 /* XWVUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE131CA61B5F900400A9E790 /* XWVUserScript.swift */; }; EE174E781A0361CB00168D96 /* xwebview.js in Resources */ = {isa = PBXBuildFile; fileRef = EE174E771A0361CB00168D96 /* xwebview.js */; }; @@ -64,6 +73,17 @@ AB023EA31A8C506600580A2A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AB023EA41A8C506600580A2A /* XWebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XWebViewTests.swift; sourceTree = ""; }; AB2273E41AA6FDA700F9207A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = ""; }; + EDAFD9EC1BB981C300DD660E /* XWVException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XWVException.m; path = XWebView/XWVException.m; sourceTree = ""; }; + EDC0BA101BB9BD2700241B2D /* XWebViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XWebViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + EDC0BA121BB9BD2800241B2D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + EDC0BA141BB9BD2800241B2D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + EDC0BA171BB9BD2800241B2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + EDC0BA191BB9BD2800241B2D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + EDC0BA1C1BB9BD2800241B2D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + EDC0BA1E1BB9BD2800241B2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EDC0BA231BB9BD7600241B2D /* SamplePlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SamplePlugin.swift; sourceTree = ""; }; + EDC0BA261BB9BFBE00241B2D /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = ""; }; + EDC0BA291BBAD11C00241B2D /* XWVException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XWVException.h; path = XWebView/XWVException.h; sourceTree = ""; }; EE0A1DD21A52775400C9E6D3 /* XWVChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XWVChannel.swift; path = XWebView/XWVChannel.swift; sourceTree = ""; }; EE131CA61B5F900400A9E790 /* XWVUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XWVUserScript.swift; path = XWebView/XWVUserScript.swift; sourceTree = ""; }; EE174E771A0361CB00168D96 /* xwebview.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = xwebview.js; path = XWebView/xwebview.js; sourceTree = ""; }; @@ -100,6 +120,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC0BA0D1BB9BD2700241B2D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC0BA281BB9C15100241B2D /* XWebView.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; EE62683119FA323900EFC3F8 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -136,12 +164,28 @@ name = "Supporting Files"; sourceTree = ""; }; + EDC0BA111BB9BD2700241B2D /* XWebViewExample */ = { + isa = PBXGroup; + children = ( + EDC0BA261BB9BFBE00241B2D /* www */, + EDC0BA231BB9BD7600241B2D /* SamplePlugin.swift */, + EDC0BA121BB9BD2800241B2D /* AppDelegate.swift */, + EDC0BA141BB9BD2800241B2D /* ViewController.swift */, + EDC0BA161BB9BD2800241B2D /* Main.storyboard */, + EDC0BA191BB9BD2800241B2D /* Assets.xcassets */, + EDC0BA1B1BB9BD2800241B2D /* LaunchScreen.storyboard */, + EDC0BA1E1BB9BD2800241B2D /* Info.plist */, + ); + path = XWebViewExample; + sourceTree = ""; + }; EE62682B19FA323900EFC3F8 = { isa = PBXGroup; children = ( EEF27EB61AFA1D89004740CF /* WebKit.framework */, EE62683719FA323900EFC3F8 /* XWebView */, AB023EA11A8C506600580A2A /* XWebViewTests */, + EDC0BA111BB9BD2700241B2D /* XWebViewExample */, EE62683619FA323900EFC3F8 /* Products */, ); sourceTree = ""; @@ -151,6 +195,7 @@ children = ( EE62683519FA323900EFC3F8 /* XWebView.framework */, AB023EA01A8C506600580A2A /* XWebViewTests.xctest */, + EDC0BA101BB9BD2700241B2D /* XWebViewExample.app */, ); name = Products; sourceTree = ""; @@ -158,6 +203,7 @@ EE62683719FA323900EFC3F8 /* XWebView */ = { isa = PBXGroup; children = ( + EDC0BA291BBAD11C00241B2D /* XWVException.h */, EEDF305F1B6555B900A21659 /* XWVInvocation.swift */, EE131CA61B5F900400A9E790 /* XWVUserScript.swift */, EE92C65E1B5ACF81000FE1DA /* XWVMetaObject.swift */, @@ -168,11 +214,12 @@ EE62691C19FA52FC00EFC3F8 /* XWebView.h */, EE0A1DD21A52775400C9E6D3 /* XWVChannel.swift */, EE62692019FA52FC00EFC3F8 /* XWebView.swift */, - EE174E771A0361CB00168D96 /* xwebview.js */, EEE6F9A31AE02CF100A2EC89 /* XWVScripting.swift */, EEE6F9A51AE02E8600A2EC89 /* XWVObject.swift */, EE33793D1AE56875009124A4 /* XWVScriptObject.swift */, EEE6F9A71AE02F5000A2EC89 /* XWVBindingObject.swift */, + EDAFD9EC1BB981C300DD660E /* XWVException.m */, + EE174E771A0361CB00168D96 /* xwebview.js */, EE62683819FA323900EFC3F8 /* Supporting Files */, ); name = XWebView; @@ -221,6 +268,23 @@ productReference = AB023EA01A8C506600580A2A /* XWebViewTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + EDC0BA0F1BB9BD2700241B2D /* XWebViewExample */ = { + isa = PBXNativeTarget; + buildConfigurationList = EDC0BA211BB9BD2800241B2D /* Build configuration list for PBXNativeTarget "XWebViewExample" */; + buildPhases = ( + EDC0BA0C1BB9BD2700241B2D /* Sources */, + EDC0BA0D1BB9BD2700241B2D /* Frameworks */, + EDC0BA0E1BB9BD2700241B2D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = XWebViewExample; + productName = XWebViewExample; + productReference = EDC0BA101BB9BD2700241B2D /* XWebViewExample.app */; + productType = "com.apple.product-type.application"; + }; EE62683419FA323900EFC3F8 /* XWebView */ = { isa = PBXNativeTarget; buildConfigurationList = EE62684B19FA323900EFC3F8 /* Build configuration list for PBXNativeTarget "XWebView" */; @@ -253,6 +317,9 @@ AB023E9F1A8C506600580A2A = { CreatedOnToolsVersion = 6.1; }; + EDC0BA0F1BB9BD2700241B2D = { + CreatedOnToolsVersion = 7.0; + }; EE62683419FA323900EFC3F8 = { CreatedOnToolsVersion = 6.1; }; @@ -273,6 +340,7 @@ targets = ( EE62683419FA323900EFC3F8 /* XWebView */, AB023E9F1A8C506600580A2A /* XWebViewTests */, + EDC0BA0F1BB9BD2700241B2D /* XWebViewExample */, ); }; /* End PBXProject section */ @@ -286,6 +354,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC0BA0E1BB9BD2700241B2D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC0BA271BB9BFBE00241B2D /* www in Resources */, + EDC0BA1D1BB9BD2800241B2D /* LaunchScreen.storyboard in Resources */, + EDC0BA1A1BB9BD2800241B2D /* Assets.xcassets in Resources */, + EDC0BA181BB9BD2800241B2D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; EE62683319FA323900EFC3F8 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -312,6 +391,16 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + EDC0BA0C1BB9BD2700241B2D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EDC0BA151BB9BD2800241B2D /* ViewController.swift in Sources */, + EDC0BA131BB9BD2800241B2D /* AppDelegate.swift in Sources */, + EDC0BA251BB9BD7600241B2D /* SamplePlugin.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; EE62683019FA323900EFC3F8 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -321,6 +410,7 @@ EEE6F9A81AE02F5000A2EC89 /* XWVBindingObject.swift in Sources */, EE131CA71B5F900400A9E790 /* XWVUserScript.swift in Sources */, EE7164921A716C9F00078FF9 /* XWVHttpServer.m in Sources */, + EDAFD9ED1BB981C300DD660E /* XWVException.m in Sources */, EE92C65F1B5ACF81000FE1DA /* XWVMetaObject.swift in Sources */, EEE6F9A61AE02E8600A2EC89 /* XWVObject.swift in Sources */, EE33793E1AE56875009124A4 /* XWVScriptObject.swift in Sources */, @@ -340,14 +430,31 @@ }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + EDC0BA161BB9BD2800241B2D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + EDC0BA171BB9BD2800241B2D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + EDC0BA1B1BB9BD2800241B2D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + EDC0BA1C1BB9BD2800241B2D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ AB023EA91A8C506600580A2A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", "$(inherited)", @@ -364,9 +471,7 @@ isa = XCBuildConfiguration; buildSettings = { CLANG_ENABLE_MODULES = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - ); + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; INFOPLIST_FILE = XWebViewTests/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "org.xwebview.$(PRODUCT_NAME:rfc1034identifier)"; @@ -374,6 +479,33 @@ }; name = Release; }; + EDC0BA1F1BB9BD2800241B2D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = XWebViewExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.xwebview.XWebViewExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + EDC0BA201BB9BD2800241B2D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = XWebViewExample/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = org.xwebview.XWebViewExample; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; EE62684919FA323900EFC3F8 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -504,6 +636,14 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + EDC0BA211BB9BD2800241B2D /* Build configuration list for PBXNativeTarget "XWebViewExample" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + EDC0BA1F1BB9BD2800241B2D /* Debug */, + EDC0BA201BB9BD2800241B2D /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; EE62682F19FA323900EFC3F8 /* Build configuration list for PBXProject "XWebView" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/XWebViewExample/AppDelegate.swift b/XWebViewExample/AppDelegate.swift new file mode 100644 index 0000000..88ed41a --- /dev/null +++ b/XWebViewExample/AppDelegate.swift @@ -0,0 +1,46 @@ +// +// AppDelegate.swift +// XWebViewExample +// +// Created by Fernando Martinez on 9/28/15. +// Copyright © 2015 XWebView. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // Override point for customization after application launch. + return true + } + + func applicationWillResignActive(application: UIApplication) { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. + } + + func applicationDidEnterBackground(application: UIApplication) { + // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. + // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. + } + + func applicationWillEnterForeground(application: UIApplication) { + // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. + } + + func applicationDidBecomeActive(application: UIApplication) { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillTerminate(application: UIApplication) { + // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. + } + + +} + diff --git a/XWebViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json b/XWebViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..118c98f --- /dev/null +++ b/XWebViewExample/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/XWebViewExample/Base.lproj/LaunchScreen.storyboard b/XWebViewExample/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..aa08113 --- /dev/null +++ b/XWebViewExample/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XWebViewExample/Base.lproj/Main.storyboard b/XWebViewExample/Base.lproj/Main.storyboard new file mode 100644 index 0000000..bcb2345 --- /dev/null +++ b/XWebViewExample/Base.lproj/Main.storyboard @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/XWebViewExample/Info.plist b/XWebViewExample/Info.plist new file mode 100644 index 0000000..6905cc6 --- /dev/null +++ b/XWebViewExample/Info.plist @@ -0,0 +1,40 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/XWebViewExample/SamplePlugin.swift b/XWebViewExample/SamplePlugin.swift new file mode 100644 index 0000000..647a6a9 --- /dev/null +++ b/XWebViewExample/SamplePlugin.swift @@ -0,0 +1,23 @@ +// +// SamplePlugin.swift +// XWebView +// +// Created by Fernando Martinez on 9/28/15. +// Copyright © 2015 XWebView. All rights reserved. +// + +import UIKit +import WebKit + +class SamplePlugin: NSObject { + func receiveMessage(message: String) { + dispatch_async(dispatch_get_main_queue(), { + UIAlertView( + title: "New Message", + message: message, + delegate: nil, + cancelButtonTitle: "OK" + ).show() + }) + } +} diff --git a/XWebViewExample/ViewController.swift b/XWebViewExample/ViewController.swift new file mode 100644 index 0000000..1f89033 --- /dev/null +++ b/XWebViewExample/ViewController.swift @@ -0,0 +1,48 @@ +// +// ViewController.swift +// XWebViewSample +// +// Created by Fernando Martinez on 9/28/15. +// Copyright © 2015 XWebView. All rights reserved. +// + +import UIKit +import WebKit +import XWebView + +class ViewController: UIViewController { + + @IBOutlet weak var messageTextField: UITextField! + @IBOutlet weak var contentView: UIView! + var webView: WKWebView! + + override func viewDidLoad() { + super.viewDidLoad() + + webView = WKWebView(frame: contentView.frame) + setupJsPlugin() + contentView.addSubview(webView!) + } + + override func viewWillAppear(animated: Bool) { + super.viewWillAppear(animated) + loadPage() + } + + func setupJsPlugin() { + let plugin = SamplePlugin() + webView.loadPlugin(plugin, namespace: "SamplePlugin") + } + + func loadPage() { + let fileUrl = NSBundle.mainBundle().URLForResource("www/sample_page", withExtension: "html")! + let baseUrl = NSBundle.mainBundle().resourceURL! + webView.loadFileURL(fileUrl, allowingReadAccessToURL: baseUrl) + } + + @IBAction func sendMessage(sender: UIButton!) { + let text = messageTextField.text! + webView.evaluateJavaScript("printMessage(\"\(text)\")", completionHandler: nil) + } +} + diff --git a/XWebViewExample/www/avatar_0.png b/XWebViewExample/www/avatar_0.png new file mode 100644 index 0000000000000000000000000000000000000000..d5d794e6ad576410a4fc1570018b85635749d481 GIT binary patch literal 18100 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 literal 0 HcmV?d00001 diff --git a/XWebViewExample/www/avatar_1.png b/XWebViewExample/www/avatar_1.png new file mode 100644 index 0000000000000000000000000000000000000000..5cc1937060e7d3aa8668856c9048f9dca78afdba GIT binary patch literal 18259 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").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bZ[a]=c}return bZ[a]}function cc(a,b){var c={};d.each(cb.concat.apply([],cb.slice(0,b)),function(){c[this]=a});return c}function bY(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bX(){try{return new a.XMLHttpRequest}catch(b){}}function bW(){d(a).unload(function(){for(var a in bU)bU[a](0,1)})}function bQ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(r,"`").replace(s,"&")}function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,q=[],r=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function C(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function w(){return!0}function v(){return!1}function g(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}d.data(a,c,f)}else f=b}return f}var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.1",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1){var f=E.call(arguments,0),g=b,h=function(a){return function(b){f[a]=arguments.length>1?E.call(arguments,0):b,--g||c.resolveWith(e,f)}};while(b--)a=f[b],a&&d.isFunction(a.promise)?a.promise().then(h(b),c.reject):--g;g||c.resolveWith(e,f)}else c!==a&&c.resolve(a);return e},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML="
a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e),b=e=f=null}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!g(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,h=b.nodeType,i=h?d.cache:b,j=h?b[d.expando]:d.expando;if(!i[j])return;if(c){var k=e?i[j][f]:i[j];if(k){delete k[c];if(!g(k))return}}if(e){delete i[j][f];if(!g(i[j]))return}var l=i[j][f];d.support.deleteExpando||i!=a?delete i[j]:i[j]=null,l?(i[j]={},h||(i[j].toJSON=d.noop),i[j][f]=l):h&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var k=i?f:0,l=i?f+1:h.length;k=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=k.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&l.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:m.test(a.nodeName)||n.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}h&&(a[c]=e);return a[c]}});var p=/\.(.*)$/,q=/^(?:textarea|input|select)$/i,r=/\./g,s=/ /g,t=/[^\w\s.|`]/g,u=function(a){return a.replace(t,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=v;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),u).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(p,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(q.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return q.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return q.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.getAttribute("type")},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}b&&(k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(d||!l.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}return k(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="
";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div
","
"]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){$(a,e),f=_(a),g=_(e);for(h=0;f[h];++h)$(f[h],g[h])}if(b){Z(a,e);if(c){f=_(a),g=_(e);for(h=0;f[h];++h)Z(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bb=/alpha\([^)]*\)/i,bc=/opacity=([^)]*)/,bd=/-([a-z])/ig,be=/([A-Z])/g,bf=/^-?\d+(?:px)?$/i,bg=/^-?\d/,bh={position:"absolute",visibility:"hidden",display:"block"},bi=["Left","Right"],bj=["Top","Bottom"],bk,bl,bm,bn=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bk(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bk)return bk(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bd,bn)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bo(a,b,e):d.swap(a,bh,function(){f=bo(a,b,e)});if(f<=0){f=bk(a,b,b),f==="0px"&&bm&&(f=bm(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bf.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bc.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bb.test(f)?f.replace(bb,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bl=function(a,c,e){var f,g,h;e=e.replace(be,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bm=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bf.test(d)&&bg.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bk=bl||bm,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bp=/%20/g,bq=/\[\]$/,br=/\r?\n/g,bs=/#.*$/,bt=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bu=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bv=/(?:^file|^widget|\-extension):$/,bw=/^(?:GET|HEAD)$/,bx=/^\/\//,by=/\?/,bz=/)<[^<]*)*<\/script>/gi,bA=/^(?:select|textarea)/i,bB=/\s+/,bC=/([?&])_=[^&]*/,bD=/(^|\-)([a-z])/g,bE=function(a,b,c){return b+c.toUpperCase()},bF=/^([\w\+\.\-]+:)\/\/([^\/?#:]*)(?::(\d+))?/,bG=d.fn.load,bH={},bI={},bJ,bK;try{bJ=c.location.href}catch(bL){bJ=c.createElement("a"),bJ.href="",bJ=bJ.href}bK=bF.exec(bJ.toLowerCase()),d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bG)return bG.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
").append(c.replace(bz,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bA.test(this.nodeName)||bu.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(br,"\r\n")}}):{name:b.name,value:c.replace(br,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bJ,isLocal:bv.test(bK[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bM(bH),ajaxTransport:bM(bI),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bP(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bQ(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bD,bE)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bt.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bs,"").replace(bx,bK[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bB),e.crossDomain||(q=bF.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bK[1]||q[2]!=bK[2]||(q[3]||(q[1]==="http:"?80:443))!=(bK[3]||(bK[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bN(bH,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!bw.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(by.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bC,"$1_="+w);e.url=x+(x===e.url?(by.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bN(bI,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bO(g,a[g],c,f);return e.join("&").replace(bp,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bR=d.now(),bS=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bR++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bS.test(b.url)||f&&bS.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bS,l),b.url===j&&(f&&(k=k.replace(bS,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bT=d.now(),bU,bV;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bX()||bY()}:bX,bV=d.ajaxSettings.xhr(),d.support.ajax=!!bV,d.support.cors=bV&&"withCredentials"in bV,bV=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),(!a.crossDomain||a.hasContent)&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bU[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bU||(bU={},bW()),h=bT++,g.onreadystatechange=bU[h]=c):c()},abort:function(){c&&c(0,1)}}}});var bZ={},b$=/^(?:toggle|show|hide)$/,b_=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,ca,cb=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(cc("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:cc("show",1),slideUp:cc("hide",1),slideToggle:cc("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!ca&&(ca=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=cf.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!cf.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=cg(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=cg(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); \ No newline at end of file diff --git a/XWebViewExample/www/sample.js b/XWebViewExample/www/sample.js new file mode 100644 index 0000000..86db654 --- /dev/null +++ b/XWebViewExample/www/sample.js @@ -0,0 +1,26 @@ +var numberOfRows = 1; + +function sendMessage(content) { + SamplePlugin.receiveMessage(content); +} + +function printMessage(message) { + numberOfRows++; + $("#list").find("tbody").append( + "" + + "" + + "" + message + "" + + "" + ); + $("#row-" + numberOfRows).click(function(e){ + var message = $($(this).children()[1]).text() + sendMessage(message); + }) +} + +$(document).ready(function(){ + $("tr").click(function(e){ + var message = $($(this).children()[1]).text() + sendMessage(message); + }); +}); \ No newline at end of file diff --git a/XWebViewExample/www/sample_page.html b/XWebViewExample/www/sample_page.html new file mode 100644 index 0000000..5b832f3 --- /dev/null +++ b/XWebViewExample/www/sample_page.html @@ -0,0 +1,34 @@ + + + + + + + + + + + +

Chat

+
Click on a row to see details
+ + + + + +
Hi! I'm XWebView
+ + \ No newline at end of file From 88f0c4c4e23f003d9e91be932567ef45c51ee5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 11:40:53 -0300 Subject: [PATCH 05/11] Bump version to 0.9.4 and improve README --- README.md | 88 ++++++++++++++++++++++++++++++++++++++++++++---- XWebView.podspec | 22 ++++++------ 2 files changed, 92 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 33b2629..f4000f1 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,91 @@ [![Build Status](https://travis-ci.org/XWebView/XWebView.svg?branch=master)](https://travis-ci.org/XWebView/XWebView) -## Introduction - XWebView is an extensible WebView which is built on top of [WKWebView](https://developer.apple.com/library/ios/documentation/WebKit/Reference/WKWebView_Ref/), the modern WebKit framework debuted in iOS 8.0. It provides fast Web runtime with carefully designed plugin API for developing sophisticated iOS native or hybrid applications. Plugins written in Objective-C or Swift programming language can be automatically exposed in JavaScript context. With capabilities offered by plugins, Web apps can look and behave exactly like native apps. They will be no longer a second-class citizen on iOS platform. +## Installing + +### Cocoapods + +Add in your `Podfile` + +``` +pod 'XWebView' '~> 0.9.4' +``` + +and install it: `pod install` + +### Carthage + +Add in yout Cartfile + +``` +github "XWebView" 0.9.4 +``` + +add checkout it: `carthage update` + +### Minimum Requirements: + +* Development: Xcode 7 +* Deployment: iOS 8.0 + +### Sample + +``` +pod try XWebView +``` + +## Usage + +With XWebView you can inject JS Plugins tha can communicate with native code of your app. There are two method for local content loading. So first of all you need to load your local html page or from an url. (You may see Example). + +### Plugins + +Plugins must be inherited from `NSObject`. + +```swift +//Sample Plugin + +class MyPlugin: NSObject { + + func onSendClicked(data: String) { + //Do something + } +} +``` + +1. Functions can receive any primitive data from JS, like strings, integers, ... +2. **Attention**! `onSendClicked` will be executed in a different thread from ui thread. So if you pretend to handle ui components you must call you method in an ui thread. + +### Injecting Plugin + +It's quite simple, just do: + +```swift +let plugin = MyPlugin() +webView.loadPlugin(plugin, namespace: "MyPlugin") +``` + +To access your plugin from js code you need to call functions from injected object named _MyPlugin_ (namespace). So: + +```javascript +MyPlugin.onSendClicked("message typed"); +``` + +### Details + +In iOS 8.x there aren't functions to load local content. So XWebView simulated a local connection for your resources folder or base folder for an HTML String template. For that you need to use: + +``` +loadFileURL:allowingReadAccessToURL: +loadHTMLString:html:baseURL: +``` + +In iOS 9.x you have `loadFileURL:allowingReadAccessToURL:` and `loadHTMLString:html:baseURL:` both of these are native and XWebView will prefer native methods. So don't worry about backward compatibility and new api methods from iOS 9.x + ## Features Basically, plugins are native classes which can export their interfaces to a JavaScript environment. Calling methods and accessing properties of a plugin object in JavaScript result in same operations to the native plugin object. If you know the [Apache Cordova](https://cordova.apache.org/), you may have the concept of plugins. Well, XWebView does more in simpler manner. @@ -24,11 +103,6 @@ XWebView is designed for embedding. It's easy to adopt since it's an extension o For more documents, please go to the project [Wiki](../../wiki). -## Minimum Requirements: - -* Development: Xcode 7 -* Deployment: iOS 8.0 - ## License XWebView is distributed under the [Apache License 2.0](LICENSE). diff --git a/XWebView.podspec b/XWebView.podspec index 220f377..5355d13 100644 --- a/XWebView.podspec +++ b/XWebView.podspec @@ -16,20 +16,20 @@ Pod::Spec.new do |s| # s.name = "XWebView" - s.version = "0.9.3" + s.version = "0.9.4" s.summary = "An extensible WebView (based on WKWebView) for iOS." s.description = <<-DESC - XWebView is an extensible WebView which is built on top of WKWebView, - the modern WebKit framework debuted in iOS 8.0. It provides fast Web - runtime with carefully designed plugin API for developing sophisticated - iOS native or hybrid applications. + XWebView is an extensible WebView which is built on top of WKWebView, + the modern WebKit framework debuted in iOS 8.0. It provides fast Web + runtime with carefully designed plugin API for developing sophisticated + iOS native or hybrid applications. - Plugins written in Objective-C or Swift programming language can be - automatically exposed in JavaScript context. With capabilities offered - by plugins, Web apps can look and behave exactly like native apps. They - will be no longer a second-class citizen on iOS platform. - DESC + Plugins written in Objective-C or Swift programming language can be + automatically exposed in JavaScript context. With capabilities offered + by plugins, Web apps can look and behave exactly like native apps. They + will be no longer a second-class citizen on iOS platform. + DESC s.homepage = "https://github.com/XWebView/XWebView" # s.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" @@ -55,7 +55,7 @@ Pod::Spec.new do |s| # profile URL. # - s.authors = { "Zhenyu Liang" => "xwebview@sofla.re", 'Jonathan Dong' => 'dongyan09@gmail.com' } + s.authors = { 'Zhenyu Liang' => 'xwebview@sofla.re', 'Jonathan Dong' => 'dongyan09@gmail.com', 'David Kim' => 'david@xwebview.org', 'Fernando Martínez' => 'contact@fernandodev.com'} # s.social_media_url = "" # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # From 048db01cfc6d68b7945c82ac1466a0184f152e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Tue, 29 Sep 2015 13:44:50 -0300 Subject: [PATCH 06/11] Enable testability --- XWebView.xcodeproj/project.pbxproj | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/XWebView.xcodeproj/project.pbxproj b/XWebView.xcodeproj/project.pbxproj index 338bea0..7da82ee 100644 --- a/XWebView.xcodeproj/project.pbxproj +++ b/XWebView.xcodeproj/project.pbxproj @@ -527,7 +527,7 @@ COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; + ENABLE_TESTABILITY = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_OPTIMIZATION_LEVEL = 0; @@ -575,6 +575,7 @@ CURRENT_PROJECT_VERSION = 1; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; @@ -599,6 +600,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_TESTABILITY = YES; INFOPLIST_FILE = XWebView/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -615,6 +617,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_TESTABILITY = YES; INFOPLIST_FILE = XWebView/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; @@ -643,6 +646,7 @@ EDC0BA201BB9BD2800241B2D /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; EE62682F19FA323900EFC3F8 /* Build configuration list for PBXProject "XWebView" */ = { isa = XCConfigurationList; From e6e2b4e2e303005ca61981f6f635b45c06d0a4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Wed, 30 Sep 2015 15:22:46 -0300 Subject: [PATCH 07/11] Add assertions and description messages for XWVChannel --- XWebView/XWVChannel.swift | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/XWebView/XWVChannel.swift b/XWebView/XWVChannel.swift index 7e4cdfe..a8b5ef2 100644 --- a/XWebView/XWVChannel.swift +++ b/XWebView/XWVChannel.swift @@ -54,11 +54,10 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { } public func bindPlugin(object: AnyObject, toNamespace namespace: String) -> XWVScriptObject? { - if webView == nil || typeInfo != nil { - return nil - } + assert(typeInfo == nil) + guard let webView = webView else { return nil } - webView!.configuration.userContentController.addScriptMessageHandler(self, name: name) + webView.configuration.userContentController.addScriptMessageHandler(self, name: name) typeInfo = XWVMetaObject(plugin: object.dynamicType) let plugin = XWVBindingObject(namespace: namespace, channel: self, object: object) @@ -66,18 +65,14 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { let script = WKUserScript(source: (object as? XWVScripting)?.javascriptStub?(stub) ?? stub, injectionTime: WKUserScriptInjectionTime.AtDocumentStart, forMainFrameOnly: true) - userScript = XWVUserScript(webView: webView!, script: script) + userScript = XWVUserScript(webView: webView, script: script) instances[0] = plugin return plugin as XWVScriptObject } public func unbind() { - if typeInfo == nil { - print(" Warning: can't unbind inexistent plugin.") - return - } - + assert(typeInfo != nil, " Error: can't unbind inexistent plugin.") instances.removeAll(keepCapacity: false) webView?.configuration.userContentController.removeScriptMessageHandlerForName(name) } @@ -94,7 +89,7 @@ public class XWVChannel : NSObject, WKScriptMessageHandler { } else { // Dispose instance let object = instances.removeValueForKey(target) - print(" Instance \(object) was disposed") + assert(object != nil, " Warning: bad instance id was received") } } else if let member = typeInfo[opcode] where member.isProperty { // Update property From 9b1d926d02ab3e2ad103c461de517cd30f826b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Wed, 30 Sep 2015 16:32:21 -0300 Subject: [PATCH 08/11] Add override for function loadHTMLString:baseURL even though in iOS 9.x --- XWebView/XWebView.swift | 42 +++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/XWebView/XWebView.swift b/XWebView/XWebView.swift index 62549e1..5dce5b7 100644 --- a/XWebView/XWebView.swift +++ b/XWebView/XWebView.swift @@ -104,16 +104,32 @@ extension WKWebView { public override class func initialize() { guard self == WKWebView.self else { return } dispatch_once(&initialized) { - let selector = Selector("loadFileURL:allowingReadAccessToURL:") - let method = class_getInstanceMethod(self, Selector("_loadFileURL:allowingReadAccessToURL:")) - assert(method != nil) - if class_addMethod(self, selector, method_getImplementation(method), method_getTypeEncoding(method)) { - print(" using backward compatibility for iOS 8.x") - method_exchangeImplementations( - class_getInstanceMethod(self, Selector("loadHTMLString:baseURL:")), - class_getInstanceMethod(self, Selector("_loadHTMLString:baseURL:")) - ) - } + let loadFileURLSelector = Selector("loadFileURL:allowingReadAccessToURL:") + let loadHTMLStringSelector = Selector("loadHTMLString:baseURL:") + let customLoadHTMLStringSelector = Selector("_loadHTMLString:baseURL:") + let customLoadFileURLSelector = Selector("_loadFileURL:allowingReadAccessToURL:") + + let loadFileURLMethod = class_getInstanceMethod( + self, + customLoadFileURLSelector + ) + let loadHTMLStringMethod = class_getInstanceMethod( + self, + customLoadHTMLStringSelector + ) + + assert(loadFileURLMethod != nil && loadHTMLStringMethod != nil) + + class_addMethod( + self, + loadFileURLSelector, + method_getImplementation(loadFileURLMethod), + method_getTypeEncoding(loadFileURLMethod) + ) + method_exchangeImplementations( + class_getInstanceMethod(self, loadHTMLStringSelector), + class_getInstanceMethod(self, customLoadHTMLStringSelector) + ) } } @@ -136,6 +152,12 @@ extension WKWebView { return nil } + // Although iOS 9.x loadHTMLString and loadData can't use baseURL with file protocol access. + // But for Simulator you can use loadHTMLString and loadData with file:/// as baseURL and access + // all of your resources. Accessing local data is restricted for devices and the unique folder + // that you can access is tmp folder. + // + // It continues to be necessary with you pretend to render pages such as dynamic templates. @objc private func _loadHTMLString(html: String, baseURL: NSURL) -> WKNavigation? { guard baseURL.fileURL else { // call original method implementation From 8792599fa781460451d9d6535765da7409cd3b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Thu, 1 Oct 2015 11:51:50 -0300 Subject: [PATCH 09/11] Fix namespace key for test expectation: 'can't define expectations with especial symbols such as ., ,, /.. --- XWebViewTests/XWVTestCase.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/XWebViewTests/XWVTestCase.swift b/XWebViewTests/XWVTestCase.swift index 383c526..d16e93e 100644 --- a/XWebViewTests/XWVTestCase.swift +++ b/XWebViewTests/XWVTestCase.swift @@ -30,13 +30,15 @@ extension XCTestExpectation : XWVScripting { class XWVTestCase : XCTestCase, WKNavigationDelegate { var webview: WKWebView! + private let nameForExpectation = "expectationWithDescription" private let namespaceForExpectation = "xwvexpectations" private var onReady: ((WKWebView)->Void)? override func setUp() { super.setUp() - let source = "function fulfill(name){\(namespaceForExpectation)[name].fulfill();}\n" + - "function expectation(name){return \(namespaceForExpectation)[name];}\n" + let source = "function fulfill(name){\(namespaceForExpectation)['\(nameForExpectation)'].fulfill();}\n" + + "function expectation(name){return \(namespaceForExpectation)[\(nameForExpectation)];}\n" + let script = WKUserScript( source: source, injectionTime: WKUserScriptInjectionTime.AtDocumentStart, @@ -49,16 +51,17 @@ class XWVTestCase : XCTestCase, WKNavigationDelegate { webview = nil super.tearDown() } - + override func expectationWithDescription(description: String) -> XCTestExpectation { let e = super.expectationWithDescription(description) - webview.loadPlugin(e, namespace: "\(namespaceForExpectation).\(description)") + webview.loadPlugin(e, namespace: "\(namespaceForExpectation).\(nameForExpectation)") return e } func loadPlugin(object: NSObject, namespace: String, script: String) { loadPlugin(object, namespace: namespace, script: script, onReady: nil) } + func loadPlugin(object: NSObject, namespace: String, script: String, onReady: ((WKWebView)->Void)?) { self.onReady = onReady webview.loadPlugin(object, namespace: namespace) @@ -74,6 +77,7 @@ class XWVTestCase : XCTestCase, WKNavigationDelegate { class XWVTestCaseTest : XWVTestCase { class Plugin : NSObject { } + func testXWVTestCase() { let desc = "selftest" _ = expectationWithDescription(desc) From ac327742fe27fd6cb9d0d0b11465d5b1d0a909e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Thu, 1 Oct 2015 11:52:09 -0300 Subject: [PATCH 10/11] Tests for loadHTMLString checking baseURL --- XWebViewTests/XWebViewTests.swift | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/XWebViewTests/XWebViewTests.swift b/XWebViewTests/XWebViewTests.swift index 4c88b71..13a1f7b 100644 --- a/XWebViewTests/XWebViewTests.swift +++ b/XWebViewTests/XWebViewTests.swift @@ -48,4 +48,38 @@ class XWebViewTests: XWVTestCase { waitForExpectationsWithTimeout(2, handler: nil) } } + + func testLoadHtmlStringWithNoBaseURL() { + expectationWithDescription("about:blank") + webview.loadHTMLString("", baseURL: nil) + waitForExpectationsWithTimeout(2, handler: nil) + } + + func testLoadHtmlStringWithFileAsBaseURL() { + let bundle = NSBundle(identifier:"org.xwebview.XWebViewTests") + let baseURL = bundle?.bundleURL.URLByAppendingPathComponent("www") + let split = "/:\\/\\/|:/" + expectationWithDescription("127.0.0.1") + webview.loadHTMLString("", baseURL: baseURL) + waitForExpectationsWithTimeout(2, handler: nil) + } + + func testLoadHtmlStringWithHostAsBaseURL() { + let bundle = NSBundle(identifier:"org.xwebview.XWebViewTests") + let baseURL = bundle?.bundleURL.URLByAppendingPathComponent("www") + expectationWithDescription("www.github.com") + webview.loadHTMLString("", baseURL: baseURL) + waitForExpectationsWithTimeout(2, handler: nil) + } + + //Test for !fileURL (default implementation) + func testLoadHTMLStringWithBaseURL2() { + _ = expectationWithDescription("loadHTMLStringWithBaseURL") + let bundle = NSBundle(identifier:"org.xwebview.XWebViewTests") + if let baseURL = bundle?.bundleURL.URLByAppendingPathComponent("www") { + XCTAssert(baseURL.checkResourceIsReachableAndReturnError(nil), "Directory not found") + webview.loadHTMLString("", baseURL: baseURL) + waitForExpectationsWithTimeout(2, handler: nil) + } + } } From 1aca559627877628e3eec59aa320b58eb63c07de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Mart=C3=ADnez?= Date: Thu, 1 Oct 2015 16:38:15 -0300 Subject: [PATCH 11/11] Replace exceptions by assertions with description --- XWebView.xcodeproj/project.pbxproj | 6 ---- XWebView/XWVException.h | 28 ---------------- XWebView/XWVException.m | 53 ------------------------------ XWebView/XWVHttpConnection.m | 6 ++-- 4 files changed, 3 insertions(+), 90 deletions(-) delete mode 100644 XWebView/XWVException.h delete mode 100644 XWebView/XWVException.m diff --git a/XWebView.xcodeproj/project.pbxproj b/XWebView.xcodeproj/project.pbxproj index 7da82ee..53283df 100644 --- a/XWebView.xcodeproj/project.pbxproj +++ b/XWebView.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ AB023EBE1A8C8BC700580A2A /* XWebView.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = EE62683519FA323900EFC3F8 /* XWebView.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; AB2273E51AA6FDA700F9207A /* www in Resources */ = {isa = PBXBuildFile; fileRef = AB2273E41AA6FDA700F9207A /* www */; }; ABF68ECD1A6B45FC0058267B /* XWebView.h in Headers */ = {isa = PBXBuildFile; fileRef = EE62691C19FA52FC00EFC3F8 /* XWebView.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EDAFD9ED1BB981C300DD660E /* XWVException.m in Sources */ = {isa = PBXBuildFile; fileRef = EDAFD9EC1BB981C300DD660E /* XWVException.m */; settings = {ASSET_TAGS = (); }; }; EDC0BA131BB9BD2800241B2D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC0BA121BB9BD2800241B2D /* AppDelegate.swift */; }; EDC0BA151BB9BD2800241B2D /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDC0BA141BB9BD2800241B2D /* ViewController.swift */; }; EDC0BA181BB9BD2800241B2D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EDC0BA161BB9BD2800241B2D /* Main.storyboard */; }; @@ -73,7 +72,6 @@ AB023EA31A8C506600580A2A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; AB023EA41A8C506600580A2A /* XWebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XWebViewTests.swift; sourceTree = ""; }; AB2273E41AA6FDA700F9207A /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = ""; }; - EDAFD9EC1BB981C300DD660E /* XWVException.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = XWVException.m; path = XWebView/XWVException.m; sourceTree = ""; }; EDC0BA101BB9BD2700241B2D /* XWebViewExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XWebViewExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; EDC0BA121BB9BD2800241B2D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; EDC0BA141BB9BD2800241B2D /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -83,7 +81,6 @@ EDC0BA1E1BB9BD2800241B2D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; EDC0BA231BB9BD7600241B2D /* SamplePlugin.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SamplePlugin.swift; sourceTree = ""; }; EDC0BA261BB9BFBE00241B2D /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; path = www; sourceTree = ""; }; - EDC0BA291BBAD11C00241B2D /* XWVException.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XWVException.h; path = XWebView/XWVException.h; sourceTree = ""; }; EE0A1DD21A52775400C9E6D3 /* XWVChannel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XWVChannel.swift; path = XWebView/XWVChannel.swift; sourceTree = ""; }; EE131CA61B5F900400A9E790 /* XWVUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = XWVUserScript.swift; path = XWebView/XWVUserScript.swift; sourceTree = ""; }; EE174E771A0361CB00168D96 /* xwebview.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = xwebview.js; path = XWebView/xwebview.js; sourceTree = ""; }; @@ -203,7 +200,6 @@ EE62683719FA323900EFC3F8 /* XWebView */ = { isa = PBXGroup; children = ( - EDC0BA291BBAD11C00241B2D /* XWVException.h */, EEDF305F1B6555B900A21659 /* XWVInvocation.swift */, EE131CA61B5F900400A9E790 /* XWVUserScript.swift */, EE92C65E1B5ACF81000FE1DA /* XWVMetaObject.swift */, @@ -218,7 +214,6 @@ EEE6F9A51AE02E8600A2EC89 /* XWVObject.swift */, EE33793D1AE56875009124A4 /* XWVScriptObject.swift */, EEE6F9A71AE02F5000A2EC89 /* XWVBindingObject.swift */, - EDAFD9EC1BB981C300DD660E /* XWVException.m */, EE174E771A0361CB00168D96 /* xwebview.js */, EE62683819FA323900EFC3F8 /* Supporting Files */, ); @@ -410,7 +405,6 @@ EEE6F9A81AE02F5000A2EC89 /* XWVBindingObject.swift in Sources */, EE131CA71B5F900400A9E790 /* XWVUserScript.swift in Sources */, EE7164921A716C9F00078FF9 /* XWVHttpServer.m in Sources */, - EDAFD9ED1BB981C300DD660E /* XWVException.m in Sources */, EE92C65F1B5ACF81000FE1DA /* XWVMetaObject.swift in Sources */, EEE6F9A61AE02E8600A2EC89 /* XWVObject.swift in Sources */, EE33793E1AE56875009124A4 /* XWVScriptObject.swift in Sources */, diff --git a/XWebView/XWVException.h b/XWebView/XWVException.h deleted file mode 100644 index cab3258..0000000 --- a/XWebView/XWVException.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - Copyright 2015 XWebView - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#ifndef XWVException_h -#define XWVException_h - -#import - -@interface XWVException : NSObject - -+ (void)raiseUnlessRoot:(NSURL *)root; -+ (void)raiseUnlessBadResponse:(const NSHTTPURLResponse *)response; -@end - -#endif /* XWVException_h */ diff --git a/XWebView/XWVException.m b/XWebView/XWVException.m deleted file mode 100644 index a5dd543..0000000 --- a/XWebView/XWVException.m +++ /dev/null @@ -1,53 +0,0 @@ -/* - Copyright 2015 XWebView - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#if !defined(__has_feature) || !__has_feature(objc_arc) -#error "This file requires ARC support." -#endif - -#import "XWVException.h" - -@interface XWVException () -@end - -@implementation XWVException { - -} - -+ (void)raiseUnlessRoot:(NSURL *)root { - if (root != nil) return; - NSException *exception = [NSException exceptionWithName:@"XWVRootDirectoryNotFound" - reason:@"Can't find root directory" - userInfo:nil]; - @throw exception; -} - -+ (void)raiseUnlessBadResponse:(const NSHTTPURLResponse *)response { - int class = (int)response.statusCode / 100 - 1; - if (class >= 0 && class < 5) return; - - NSDictionary *headers = response.allHeaderFields; - NSException *exception = [NSException exceptionWithName:@"XWVWrongServerStatusCode" - reason:@"Server returnered a status code >= 500" - userInfo:@{ - @"response": response, - @"headers": headers, - @"statusCode": @(response.statusCode) - }]; - @throw exception; -} - -@end diff --git a/XWebView/XWVHttpConnection.m b/XWebView/XWVHttpConnection.m index ed9bc72..0843d4e 100644 --- a/XWebView/XWVHttpConnection.m +++ b/XWebView/XWVHttpConnection.m @@ -30,7 +30,7 @@ #endif #import "XWVHttpConnection.h" -#import "XWVException.h" + static NSMutableURLRequest *parseRequest(NSMutableURLRequest *request, NSData *line); static NSHTTPURLResponse *buildResponse(NSURLRequest *request, NSURL *rootURL); @@ -107,7 +107,7 @@ - (NSURL *)rootURL { NSURL *root; if (_delegate && [_delegate respondsToSelector:@selector(documentRoot)]) { root = [NSURL fileURLWithPath:_delegate.documentRoot isDirectory:YES]; - [XWVException raiseUnlessRoot:root]; + NSCAssert(root != nil, @" you must set a valid documentRoot"); } else { NSBundle *bundle = [NSBundle mainBundle]; root = bundle.resourceURL ?: bundle.bundleURL; @@ -343,7 +343,7 @@ - (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode { NSString *name; int class = (int)response.statusCode / 100 - 1; - [XWVException raiseUnlessBadResponse:response]; + NSCAssert(class >= 0 && class < 5, @" status code must be in the range [0, 500)"); int code = (int)response.statusCode % 100; if (code >= sizeof(HttpResponseReasonPhrase[class]) / sizeof(char *) ||