├── .gitignore
├── .travis.yml
├── Assets
└── hero.png
├── Examples
├── README.md
├── UIWebView+WBWebViewConsole
│ ├── Podfile
│ ├── Podfile.lock
│ ├── UIWebView+WBWebViewConsole.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcuserdata
│ │ │ │ └── wutian.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ └── xcuserdata
│ │ │ └── wutian.xcuserdatad
│ │ │ └── xcschemes
│ │ │ ├── UIWebView+WBWebViewConsole.xcscheme
│ │ │ └── xcschememanagement.plist
│ ├── UIWebView+WBWebViewConsole.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── UIWebView+WBWebViewConsole
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Base.lproj
│ │ │ └── LaunchScreen.xib
│ │ ├── Images.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Info.plist
│ │ ├── WBUIWebView.h
│ │ ├── WBUIWebView.m
│ │ ├── WBWebViewController.h
│ │ ├── WBWebViewController.m
│ │ └── main.m
│ └── UIWebView+WBWebViewConsoleTests
│ │ ├── Info.plist
│ │ └── UIWebView_WBWebViewConsoleTests.m
└── WKWebView+WBWebViewConsole
│ ├── Podfile
│ ├── Podfile.lock
│ ├── WKWebView+WBWebViewConsole.xcodeproj
│ ├── project.pbxproj
│ └── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ ├── WKWebView+WBWebViewConsole.xcworkspace
│ └── contents.xcworkspacedata
│ ├── WKWebView+WBWebViewConsole
│ ├── AppDelegate.h
│ ├── AppDelegate.m
│ ├── Base.lproj
│ │ └── LaunchScreen.xib
│ ├── Images.xcassets
│ │ └── AppIcon.appiconset
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── WBWKWebView.h
│ ├── WBWKWebView.m
│ ├── WBWebViewController.h
│ ├── WBWebViewController.m
│ └── main.m
│ └── WKWebView+WBWebViewConsoleTests
│ ├── Info.plist
│ └── WKWebView_WBWebViewConsoleTests.m
├── LICENSE
├── README.md
├── WBWebViewConsole.podspec
├── WBWebViewConsole.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── wutian.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ └── WBWebViewConsole.xcscheme
└── xcuserdata
│ └── wutian.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── WBWebViewConsole
├── Controllers
│ ├── WBWebDebugConsoleViewController.h
│ └── WBWebDebugConsoleViewController.m
├── Models
│ ├── UserPrompt
│ │ ├── WBWebViewConsoleUserPromptCompletionController.h
│ │ ├── WBWebViewConsoleUserPromptCompletionController.m
│ │ ├── WBWebViewConsoleUserPromptCompletionDatasource.h
│ │ └── WBWebViewConsoleUserPromptCompletionDatasource.m
│ ├── WBJSBridgeActionPrivateConsoleLog.h
│ ├── WBJSBridgeActionPrivateConsoleLog.m
│ ├── WBWebViewConsole.h
│ ├── WBWebViewConsole.m
│ ├── WBWebViewConsoleInputHistoryEntry.h
│ ├── WBWebViewConsoleInputHistoryEntry.m
│ ├── WBWebViewConsoleMessage.h
│ └── WBWebViewConsoleMessage.m
├── Resources
│ └── WBWebBrowserConsole.bundle
│ │ ├── console_prompt_completion.js
│ │ ├── debug_icon@2x.png
│ │ ├── error_icon@2x.png
│ │ ├── info_icon@2x.png
│ │ ├── issue_icon@2x.png
│ │ ├── navigation_issue_icon@2x.png
│ │ ├── navigation_success_icon@2x.png
│ │ ├── success_icon@2x.png
│ │ ├── userinput_action_panel.png
│ │ ├── userinput_action_panel@2x.png
│ │ ├── userinput_actions.png
│ │ ├── userinput_actions@2x.png
│ │ ├── userinput_prompt@2x.png
│ │ ├── userinput_prompt_previous@2x.png
│ │ └── userinput_result@2x.png
├── Supports
│ ├── NSDictionary+WBTTypeCast.h
│ ├── NSDictionary+WBTTypeCast.m
│ ├── NSObject+WBJSONKit.h
│ ├── NSObject+WBJSONKit.m
│ ├── UIColor+WBTHelpers.h
│ ├── UIColor+WBTHelpers.m
│ ├── UIDevice+WBTHelpers.h
│ ├── UIDevice+WBTHelpers.m
│ ├── UIScrollView+WBTUtilities.h
│ ├── UIScrollView+WBTUtilities.m
│ ├── UIView+WBTSizes.h
│ ├── UIView+WBTSizes.m
│ ├── WBKeyboardObserver.h
│ ├── WBKeyboardObserver.m
│ ├── WBTTypeCastUtil.h
│ ├── WBTTypeCastUtil.m
│ ├── WBTextView.h
│ ├── WBTextView.m
│ ├── WBWebView
│ │ ├── JSBridge
│ │ │ ├── Actions
│ │ │ │ ├── WBJSBridgeAction.h
│ │ │ │ └── WBJSBridgeAction.m
│ │ │ ├── Resources
│ │ │ │ └── WBWebBrowserJSBridge.bundle
│ │ │ │ │ └── wbjs.js
│ │ │ ├── WBJSBridgeMessage.h
│ │ │ ├── WBJSBridgeMessage.m
│ │ │ ├── WBWebViewJSBridge.h
│ │ │ └── WBWebViewJSBridge.m
│ │ ├── WBWebView.h
│ │ ├── WBWebViewUserScript.h
│ │ └── WBWebViewUserScript.m
│ └── WBWebViewConsoleDefines.h
└── Views
│ ├── WBWebViewConsoleInputView.h
│ ├── WBWebViewConsoleInputView.m
│ ├── WBWebViewConsoleInputViewActionButton.h
│ ├── WBWebViewConsoleInputViewActionButton.m
│ ├── WBWebViewConsoleMessageCell.h
│ └── WBWebViewConsoleMessageCell.m
└── WBWebViewConsoleTests
├── Base
├── WBTestsUIWebView.h
└── WBTestsUIWebView.m
├── Info.plist
└── WBWebViewJSBridgeTests.m
/.gitignore:
--------------------------------------------------------------------------------
1 | # OS generated files #
2 | ######################
3 | .DS_Store*
4 | ehthumbs.db
5 | Icon?
6 | Thumbs.db
7 | # Xcode
8 | build/
9 | *.pbxuser
10 | !default.pbxuser
11 | *.mode1v3
12 | !default.mode1v3
13 | *.mode2v3
14 | !default.mode2v3
15 | *.perspectivev3
16 | !default.perspectivev3
17 | xcuserdata
18 | *.xccheckout
19 | profile
20 | *.moved-aside
21 | DerivedData
22 | *.hmap
23 | *.ipa
24 |
25 | # CocoaPods
26 | Pods
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | before_install:
3 | - brew update
4 | - brew reinstall xctool
5 | - gem update cocoapods
6 | xcode_project: WBWebViewConsole.xcodeproj
7 | xcode_scheme: WBWebViewConsole
8 | xcode_sdk:
9 | - iphonesimulator9.3
10 |
--------------------------------------------------------------------------------
/Assets/hero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/Assets/hero.png
--------------------------------------------------------------------------------
/Examples/README.md:
--------------------------------------------------------------------------------
1 | # Sample projects
2 |
3 | ## Building
4 |
5 | Run `pod install` in each sample project directory to set up their
6 | dependencies.
7 |
8 | ## License
9 |
10 | This file provided by Weibo is for non-commercial testing and evaluation
11 | purposes only. Weibo reserves all rights not expressly granted.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
16 | WEIBO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
17 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '6.0'
3 |
4 | target 'UIWebView+WBWebViewConsole' do
5 | pod 'WBWebViewConsole', :path => '../..'
6 | end
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - WBWebViewConsole (1.0.2)
3 |
4 | DEPENDENCIES:
5 | - WBWebViewConsole (from `../..`)
6 |
7 | EXTERNAL SOURCES:
8 | WBWebViewConsole:
9 | :path: "../.."
10 |
11 | SPEC CHECKSUMS:
12 | WBWebViewConsole: 1288737d136b44a281cc654828339127f959e21c
13 |
14 | PODFILE CHECKSUM: 34e4d1ba8829f9e9bd3572214cd3e5d4c12ea318
15 |
16 | COCOAPODS: 1.1.1
17 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcodeproj/xcuserdata/wutian.xcuserdatad/xcschemes/UIWebView+WBWebViewConsole.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
94 |
100 |
101 |
102 |
103 |
105 |
106 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcodeproj/xcuserdata/wutian.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | UIWebView+WBWebViewConsole.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C6B12CA21A8DCCBD004F2AD2
16 |
17 | primary
18 |
19 |
20 | C6B12CBB1A8DCCBD004F2AD2
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface AppDelegate : UIResponder
17 |
18 | @property (strong, nonatomic) UIWindow *window;
19 |
20 |
21 | @end
22 |
23 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "AppDelegate.h"
15 | #import "WBWebViewController.h"
16 |
17 | @interface AppDelegate ()
18 |
19 | @end
20 |
21 | @implementation AppDelegate
22 |
23 |
24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
25 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
26 |
27 | UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:[[WBWebViewController alloc] init]];
28 | [self.window setRootViewController:navigationController];
29 |
30 | [self.window makeKeyAndVisible];
31 | return YES;
32 | }
33 |
34 | - (void)applicationWillResignActive:(UIApplication *)application {
35 | // 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.
36 | // 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.
37 | }
38 |
39 | - (void)applicationDidEnterBackground:(UIApplication *)application {
40 | // 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.
41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
42 | }
43 |
44 | - (void)applicationWillEnterForeground:(UIApplication *)application {
45 | // 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.
46 | }
47 |
48 | - (void)applicationDidBecomeActive:(UIApplication *)application {
49 | // 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.
50 | }
51 |
52 | - (void)applicationWillTerminate:(UIApplication *)application {
53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.sina.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/WBUIWebView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBUIWebView.h
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 |
17 | #define WBUIWebViewUsesPrivateAPI 1
18 |
19 | @interface WBUIWebView : UIWebView
20 |
21 | @property (nonatomic, weak) id wb_delegate;
22 |
23 | @end
24 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/WBUIWebView.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBUIWebView.m
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBUIWebView.h"
15 | #import
16 |
17 | @interface WBUIWebView ()
18 | {
19 | struct {
20 | unsigned int shouldStartLoad:1;
21 | unsigned int didStartLoad:1;
22 | unsigned int didFinishLoad:1;
23 | unsigned int didFailLoad:1;
24 | } _delegateHas;
25 | }
26 |
27 | @property (nonatomic, strong) NSMutableArray * userScripts;
28 | @property (nonatomic, strong) WBWebViewJSBridge * JSBridge;
29 | @property (nonatomic, strong) WBWebViewConsole * console;
30 |
31 | @end
32 |
33 | @implementation WBUIWebView
34 |
35 | - (instancetype)initWithFrame:(CGRect)frame
36 | {
37 | if (self = [super initWithFrame:frame]) {
38 | self.delegate = self;
39 | self.JSBridge = [[WBWebViewJSBridge alloc] initWithWebView:self];
40 | self.console = [[WBWebViewConsole alloc] initWithWebView:self];
41 | }
42 | return self;
43 | }
44 |
45 | - (NSMutableArray *)userScripts
46 | {
47 | if (!_userScripts) {
48 | _userScripts = [NSMutableArray array];
49 | }
50 | return _userScripts;
51 | }
52 |
53 | - (void)wb_addUserScript:(WBWebViewUserScript *)userScript
54 | {
55 | if (!userScript) {
56 | return;
57 | }
58 | [self.userScripts addObject:userScript];
59 | }
60 |
61 | - (void)wb_removeAllUserScripts
62 | {
63 | [_userScripts removeAllObjects];
64 | }
65 |
66 | - (NSArray *)wb_userScripts
67 | {
68 | return [_userScripts copy];
69 | }
70 |
71 | - (void)injectUserScripts
72 | {
73 | for (WBWebViewUserScript * script in _userScripts) {
74 | [self stringByEvaluatingJavaScriptFromString:script.source];
75 | }
76 | }
77 |
78 | - (void)wb_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *, NSError *))completionHandler
79 | {
80 | NSString * result = [self stringByEvaluatingJavaScriptFromString:javaScriptString];
81 |
82 | if (completionHandler) {
83 | completionHandler(result, nil);
84 | }
85 | }
86 |
87 | - (void)setWb_delegate:(id)wb_delegate
88 | {
89 | if (_wb_delegate != wb_delegate) {
90 | _wb_delegate = wb_delegate;
91 |
92 | _delegateHas.shouldStartLoad = [wb_delegate respondsToSelector:@selector(webView:shouldStartLoadWithRequest:navigationType:)];
93 | _delegateHas.didStartLoad = [wb_delegate respondsToSelector:@selector(webViewDidStartLoad:)];
94 | _delegateHas.didFinishLoad = [wb_delegate respondsToSelector:@selector(webViewDidFinishLoad:)];
95 | _delegateHas.didFailLoad = [wb_delegate respondsToSelector:@selector(webView:didFailLoadWithError:)];
96 | }
97 | }
98 |
99 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
100 | {
101 | BOOL result = YES;
102 |
103 | if ([self.JSBridge handleWebViewRequest:request]) {
104 | result = NO;
105 | } else if (_delegateHas.shouldStartLoad) {
106 | result = [_wb_delegate webView:self shouldStartLoadWithRequest:request navigationType:navigationType];
107 | }
108 |
109 | if (result) {
110 | [self webDebugLogProvisionalNavigation:request navigationType:navigationType result:result];
111 | }
112 |
113 | return result;
114 | }
115 |
116 | - (void)webViewDidStartLoad:(UIWebView *)webView
117 | {
118 | if (_delegateHas.didStartLoad) {
119 | [_wb_delegate webViewDidStartLoad:webView];
120 | }
121 | }
122 |
123 | - (void)webViewDidFinishLoad:(UIWebView *)webView
124 | {
125 | #if !WBUIWebViewUsesPrivateAPI
126 | [self injectUserScripts];
127 | #endif
128 |
129 | if (_delegateHas.didFinishLoad) {
130 | [_wb_delegate webViewDidFinishLoad:webView];
131 | }
132 | }
133 |
134 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
135 | {
136 | if (_delegateHas.didFailLoad) {
137 | [_wb_delegate webView:webView didFailLoadWithError:error];
138 | }
139 |
140 | [self webDebugLogLoadFailedWithError:error];
141 | }
142 |
143 | #if WBUIWebViewUsesPrivateAPI
144 |
145 | #warning Private API here, DO NOT use in production code
146 |
147 | - (void)webView:(id)webView didClearWindowObject:(id)window forFrame:(id)frame
148 | {
149 | [self injectUserScripts];
150 | }
151 |
152 | #endif
153 |
154 | - (NSString *)callerWithNavigationType:(UIWebViewNavigationType)type
155 | {
156 | switch (type)
157 | {
158 | case UIWebViewNavigationTypeBackForward: return @"back/forward";
159 | case UIWebViewNavigationTypeFormResubmitted: return @"form resubmit";
160 | case UIWebViewNavigationTypeFormSubmitted: return @"form submit";
161 | case UIWebViewNavigationTypeLinkClicked: return @"link click";
162 | case UIWebViewNavigationTypeReload: return @"reload";
163 | default: return nil;
164 | }
165 | }
166 |
167 | - (void)webDebugLogInfo:(NSString *)info
168 | {
169 | [self.console addMessage:info type:WBWebViewConsoleMessageTypeLog level:WBWebViewConsoleMessageLevelInfo source:WBWebViewConsoleMessageSourceNative];
170 | }
171 |
172 | - (void)webDebugLogProvisionalNavigation:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType result:(BOOL)result
173 | {
174 | if (![[[request URL] absoluteString] isEqualToString:[[request mainDocumentURL] absoluteString]]) return;
175 |
176 | NSString * message = request.URL.absoluteString;
177 | WBWebViewConsoleMessageLevel level = result ? WBWebViewConsoleMessageLevelSuccess : WBWebViewConsoleMessageLevelWarning;
178 | NSString * caller = [self callerWithNavigationType:navigationType];
179 |
180 | if (caller)
181 | {
182 | caller = [NSString stringWithFormat:@"triggered by %@", caller];
183 | }
184 |
185 | [self.console addMessage:message level:level source:WBWebViewConsoleMessageSourceNavigation caller:caller];
186 | }
187 |
188 | - (void)webDebugLogLoadFailedWithError:(NSError *)error
189 | {
190 | if ([error.domain isEqual:NSURLErrorDomain] && error.code == NSURLErrorCancelled)
191 | {
192 | return;
193 | }
194 |
195 | NSString * url = error.userInfo[NSURLErrorFailingURLStringErrorKey];
196 | NSString * message = nil, * caller = nil;
197 |
198 | if (url)
199 | {
200 | NSMutableString * m = [NSMutableString string];
201 |
202 | [m appendFormat:@"domain: %@, ", error.domain];
203 | [m appendFormat:@"code: %ld, ", (long)error.code];
204 | [m appendFormat:@"reason: %@", error.localizedDescription];
205 |
206 | message = m;
207 | caller = url;
208 | }
209 | else
210 | {
211 | message = error.description;
212 | }
213 |
214 | [self.console addMessage:message level:WBWebViewConsoleMessageLevelError source:WBWebViewConsoleMessageSourceNative caller:caller];
215 | }
216 |
217 | @end
218 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/WBWebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewController.h
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface WBWebViewController : UIViewController
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/WBWebViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewController.m
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewController.h"
15 | #import "WBUIWebView.h"
16 | #import
17 | #import
18 |
19 | @interface WBWebViewController ()
20 |
21 | @property (nonatomic, strong) WBUIWebView * webView;
22 |
23 | @end
24 |
25 | @implementation WBWebViewController
26 |
27 | - (void)viewDidLoad
28 | {
29 | [super viewDidLoad];
30 |
31 | self.title = @"Browser";
32 |
33 | self.webView = [[WBUIWebView alloc] initWithFrame:self.view.bounds];
34 | self.webView.JSBridge.interfaceName = @"UIWebViewBridge";
35 | self.webView.JSBridge.readyEventName = @"UIWebViewBridgeReady";
36 | self.webView.JSBridge.invokeScheme = @"uiwebview-bridge://invoke";
37 | self.webView.wb_delegate = self;
38 |
39 | [self.view addSubview:self.webView];
40 |
41 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://apple.com"]]];
42 |
43 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Console" style:UIBarButtonItemStylePlain target:self action:@selector(showConsole:)];
44 | }
45 |
46 | - (void)viewDidAppear:(BOOL)animated
47 | {
48 | [super viewDidAppear:animated];
49 |
50 | [self webDebugAddContextMenuItems];
51 | }
52 |
53 | - (void)viewDidDisappear:(BOOL)animated
54 | {
55 | [super viewDidDisappear:animated];
56 |
57 | [self webDebugRemoveContextMenuItems];
58 | }
59 |
60 | - (void)showConsole:(id)sender
61 | {
62 | WBWebDebugConsoleViewController * controller = [[WBWebDebugConsoleViewController alloc] initWithConsole:_webView.console];
63 |
64 | [self.navigationController pushViewController:controller animated:YES];
65 | }
66 |
67 | - (void)webDebugAddContextMenuItems
68 | {
69 | UIMenuItem * item = [[UIMenuItem alloc] initWithTitle:@"Inspect Element" action:@selector(webDebugInspectCurrentSelectedElement:)];
70 | [[UIMenuController sharedMenuController] setMenuItems:@[item]];
71 | }
72 |
73 | - (void)webDebugRemoveContextMenuItems
74 | {
75 | [[UIMenuController sharedMenuController] setMenuItems:nil];
76 | }
77 |
78 | - (void)webDebugInspectCurrentSelectedElement:(id)sender
79 | {
80 | NSString * variable = @"WeiboConsoleLastSelection";
81 |
82 | [self.webView.console storeCurrentSelectedElementToJavaScriptVariable:variable completion:^(BOOL success) {
83 | if (success)
84 | {
85 | WBWebDebugConsoleViewController * consoleViewController = [[WBWebDebugConsoleViewController alloc] initWithConsole:self.webView.console];
86 | consoleViewController.initialCommand = variable;
87 |
88 | [self.navigationController pushViewController:consoleViewController animated:YES];
89 | }
90 | else
91 | {
92 | UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"Can not get current selected element" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
93 | [alertView show];
94 | }
95 | }];
96 | }
97 |
98 | @end
99 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsole/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // UIWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import "AppDelegate.h"
16 |
17 | int main(int argc, char * argv[]) {
18 | @autoreleasepool {
19 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsoleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.sina.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Examples/UIWebView+WBWebViewConsole/UIWebView+WBWebViewConsoleTests/UIWebView_WBWebViewConsoleTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIWebView_WBWebViewConsoleTests.m
3 | // UIWebView+WBWebViewConsoleTests
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 |
17 | @interface UIWebView_WBWebViewConsoleTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation UIWebView_WBWebViewConsoleTests
22 |
23 | - (void)setUp {
24 | [super setUp];
25 | // Put setup code here. This method is called before the invocation of each test method in the class.
26 | }
27 |
28 | - (void)tearDown {
29 | // Put teardown code here. This method is called after the invocation of each test method in the class.
30 | [super tearDown];
31 | }
32 |
33 | - (void)testExample {
34 | // This is an example of a functional test case.
35 | XCTAssert(YES, @"Pass");
36 | }
37 |
38 | - (void)testPerformanceExample {
39 | // This is an example of a performance test case.
40 | [self measureBlock:^{
41 | // Put the code you want to measure the time of here.
42 | }];
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 | platform :ios, '8.0'
3 |
4 | # also demostrates when use as a dynamic framework
5 | use_frameworks!
6 |
7 | target 'WKWebView+WBWebViewConsole' do
8 | pod 'WBWebViewConsole', :path => '../..'
9 | end
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - WBWebViewConsole (1.0.2)
3 |
4 | DEPENDENCIES:
5 | - WBWebViewConsole (from `../..`)
6 |
7 | EXTERNAL SOURCES:
8 | WBWebViewConsole:
9 | :path: "../.."
10 |
11 | SPEC CHECKSUMS:
12 | WBWebViewConsole: 1288737d136b44a281cc654828339127f959e21c
13 |
14 | PODFILE CHECKSUM: 664a31577dd723d8b22983608cc4c78e6c668e8d
15 |
16 | COCOAPODS: 1.1.1
17 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/AppDelegate.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.h
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface AppDelegate : UIResponder
17 |
18 | @property (strong, nonatomic) UIWindow *window;
19 |
20 |
21 | @end
22 |
23 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/AppDelegate.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.m
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "AppDelegate.h"
15 | #import "WBWebViewController.h"
16 |
17 | @interface AppDelegate ()
18 |
19 | @end
20 |
21 | @implementation AppDelegate
22 |
23 |
24 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
25 | self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
26 |
27 | UINavigationController * navigationController = [[UINavigationController alloc] initWithRootViewController:[[WBWebViewController alloc] init]];
28 | [self.window setRootViewController:navigationController];
29 |
30 | [self.window makeKeyAndVisible];
31 | return YES;
32 | }
33 |
34 | - (void)applicationWillResignActive:(UIApplication *)application {
35 | // 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.
36 | // 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.
37 | }
38 |
39 | - (void)applicationDidEnterBackground:(UIApplication *)application {
40 | // 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.
41 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
42 | }
43 |
44 | - (void)applicationWillEnterForeground:(UIApplication *)application {
45 | // 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.
46 | }
47 |
48 | - (void)applicationDidBecomeActive:(UIApplication *)application {
49 | // 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.
50 | }
51 |
52 | - (void)applicationWillTerminate:(UIApplication *)application {
53 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
54 | }
55 |
56 | @end
57 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.sina.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/WBWKWebView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWKWebView.h
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 |
17 | @interface WBWKWebView : WKWebView
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/WBWKWebView.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWKWebView.m
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWKWebView.h"
15 | #import
16 | #import
17 | #import
18 | #import
19 |
20 | @interface WBWKWebView ()
21 |
22 | @property (nonatomic, strong) WBWebViewJSBridge * JSBridge;
23 | @property (nonatomic, strong) WBWebViewConsole * console;
24 |
25 | @end
26 |
27 | @implementation WBWKWebView
28 |
29 | - (instancetype)initWithFrame:(CGRect)frame
30 | {
31 | if (self = [super initWithFrame:frame]) {
32 | self.navigationDelegate = self;
33 | self.JSBridge = [[WBWebViewJSBridge alloc] initWithWebView:self];
34 |
35 | self.console = [[WBWebViewConsole alloc] initWithWebView:self];
36 | }
37 | return self;
38 | }
39 |
40 | - (void)wb_addUserScript:(WBWebViewUserScript *)userScript
41 | {
42 | if (!userScript) {
43 | return;
44 | }
45 | [self.configuration.userContentController addUserScript:[[WKUserScript alloc] initWithSource:userScript.source injectionTime:(WKUserScriptInjectionTime)userScript.scriptInjectionTime forMainFrameOnly:userScript.forMainFrameOnly]];
46 | }
47 |
48 | - (void)wb_removeAllUserScripts
49 | {
50 | [self.configuration.userContentController removeAllUserScripts];
51 | }
52 |
53 | - (NSArray *)wb_userScripts
54 | {
55 | return self.configuration.userContentController.userScripts;
56 | }
57 |
58 | - (void)wb_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *, NSError *))completionHandler
59 | {
60 | [self evaluateJavaScript:javaScriptString completionHandler:^(id result, NSError * error) {
61 | if (completionHandler) {
62 | NSString * resultString = nil;
63 |
64 | if ([result isKindOfClass:[NSString class]]) {
65 | resultString = result;
66 | } else if ([result respondsToSelector:@selector(stringValue)]) {
67 | resultString = [result stringValue];
68 | } else if ([result respondsToSelector:@selector(wb_JSONString)]) {
69 | resultString = [result wb_JSONString];
70 | }
71 |
72 | completionHandler(resultString, error);
73 | }
74 | }];
75 | }
76 |
77 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
78 | {
79 | BOOL result = YES;
80 |
81 | if ([self.JSBridge handleWebViewRequest:navigationAction.request]) {
82 | result = NO;
83 | }
84 |
85 | if (result) {
86 | [self webDebugLogProvisionalNavigation:navigationAction.request navigationType:navigationAction.navigationType result:result];
87 | decisionHandler(WKNavigationActionPolicyAllow);
88 | } else {
89 | decisionHandler(WKNavigationActionPolicyCancel);
90 | }
91 | }
92 |
93 | - (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
94 | {
95 | [self webDebugLogLoadFailedWithError:error];
96 | }
97 |
98 | - (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
99 | {
100 | [self webDebugLogLoadFailedWithError:error];
101 | }
102 |
103 | - (NSString *)callerWithNavigationType:(WKNavigationType)type
104 | {
105 | switch (type)
106 | {
107 | case WKNavigationTypeBackForward: return @"back/forward";
108 | case WKNavigationTypeFormResubmitted: return @"form resubmit";
109 | case WKNavigationTypeFormSubmitted: return @"form submit";
110 | case WKNavigationTypeLinkActivated: return @"link click";
111 | case WKNavigationTypeReload: return @"reload";
112 | default: return nil;
113 | }
114 | }
115 |
116 | - (void)webDebugLogInfo:(NSString *)info
117 | {
118 | [self.console addMessage:info type:WBWebViewConsoleMessageTypeLog level:WBWebViewConsoleMessageLevelInfo source:WBWebViewConsoleMessageSourceNative];
119 | }
120 |
121 | - (void)webDebugLogProvisionalNavigation:(NSURLRequest *)request navigationType:(WKNavigationType)navigationType result:(BOOL)result
122 | {
123 | if (![[[request URL] absoluteString] isEqualToString:[[request mainDocumentURL] absoluteString]]) return;
124 |
125 | NSString * message = request.URL.absoluteString;
126 | WBWebViewConsoleMessageLevel level = result ? WBWebViewConsoleMessageLevelSuccess : WBWebViewConsoleMessageLevelWarning;
127 | NSString * caller = [self callerWithNavigationType:navigationType];
128 |
129 | if (caller)
130 | {
131 | caller = [NSString stringWithFormat:@"triggered by %@", caller];
132 | }
133 |
134 | [self.console addMessage:message level:level source:WBWebViewConsoleMessageSourceNavigation caller:caller];
135 | }
136 |
137 | - (void)webDebugLogLoadFailedWithError:(NSError *)error
138 | {
139 | if ([error.domain isEqual:NSURLErrorDomain] && error.code == NSURLErrorCancelled)
140 | {
141 | return;
142 | }
143 |
144 | NSString * url = error.userInfo[NSURLErrorFailingURLStringErrorKey];
145 | NSString * message = nil, * caller = nil;
146 |
147 | if (url)
148 | {
149 | NSMutableString * m = [NSMutableString string];
150 |
151 | [m appendFormat:@"domain: %@, ", error.domain];
152 | [m appendFormat:@"code: %ld, ", (long)error.code];
153 | [m appendFormat:@"reason: %@", error.localizedDescription];
154 |
155 | message = m;
156 | caller = url;
157 | }
158 | else
159 | {
160 | message = error.description;
161 | }
162 |
163 | [self.console addMessage:message level:WBWebViewConsoleMessageLevelError source:WBWebViewConsoleMessageSourceNative caller:caller];
164 | }
165 |
166 | @end
167 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/WBWebViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewController.h
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface WBWebViewController : UIViewController
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/WBWebViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewController.m
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewController.h"
15 | #import "WBWKWebView.h"
16 | #import
17 | #import
18 |
19 | @interface WBWebViewController ()
20 |
21 | @property (nonatomic, strong) WBWKWebView * webView;
22 |
23 | @end
24 |
25 | @implementation WBWebViewController
26 |
27 | - (void)viewDidLoad
28 | {
29 | [super viewDidLoad];
30 |
31 | self.title = @"Browser";
32 |
33 | self.webView = [[WBWKWebView alloc] initWithFrame:self.view.bounds];
34 | self.webView.JSBridge.interfaceName = @"WKWebViewBridge";
35 | self.webView.JSBridge.readyEventName = @"WKWebViewBridgeReady";
36 | self.webView.JSBridge.invokeScheme = @"wkwebview-bridge://invoke";
37 |
38 | [self.view addSubview:self.webView];
39 |
40 | [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://apple.com"]]];
41 |
42 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Console" style:UIBarButtonItemStylePlain target:self action:@selector(showConsole:)];
43 | }
44 |
45 | - (void)viewDidAppear:(BOOL)animated
46 | {
47 | [super viewDidAppear:animated];
48 |
49 | [self webDebugAddContextMenuItems];
50 | }
51 |
52 | - (void)viewDidDisappear:(BOOL)animated
53 | {
54 | [super viewDidDisappear:animated];
55 |
56 | [self webDebugRemoveContextMenuItems];
57 | }
58 |
59 | - (void)showConsole:(id)sender
60 | {
61 | WBWebDebugConsoleViewController * controller = [[WBWebDebugConsoleViewController alloc] initWithConsole:_webView.console];
62 |
63 | [self.navigationController pushViewController:controller animated:YES];
64 | }
65 |
66 | - (void)webDebugAddContextMenuItems
67 | {
68 | UIMenuItem * item = [[UIMenuItem alloc] initWithTitle:@"Inspect Element" action:@selector(webDebugInspectCurrentSelectedElement:)];
69 | [[UIMenuController sharedMenuController] setMenuItems:@[item]];
70 | }
71 |
72 | - (void)webDebugRemoveContextMenuItems
73 | {
74 | [[UIMenuController sharedMenuController] setMenuItems:nil];
75 | }
76 |
77 | - (void)webDebugInspectCurrentSelectedElement:(id)sender
78 | {
79 | NSString * variable = @"WeiboConsoleLastSelection";
80 |
81 | [self.webView.console storeCurrentSelectedElementToJavaScriptVariable:variable completion:^(BOOL success) {
82 | if (success)
83 | {
84 | WBWebDebugConsoleViewController * consoleViewController = [[WBWebDebugConsoleViewController alloc] initWithConsole:self.webView.console];
85 | consoleViewController.initialCommand = variable;
86 |
87 | [self.navigationController pushViewController:consoleViewController animated:YES];
88 | }
89 | else
90 | {
91 | UIAlertView * alertView = [[UIAlertView alloc] initWithTitle:@"Can not get current selected element" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
92 | [alertView show];
93 | }
94 | }];
95 | }
96 |
97 | @end
98 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsole/main.m:
--------------------------------------------------------------------------------
1 | //
2 | // main.m
3 | // WKWebView+WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import "AppDelegate.h"
16 |
17 | int main(int argc, char * argv[]) {
18 | @autoreleasepool {
19 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsoleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.sina.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Examples/WKWebView+WBWebViewConsole/WKWebView+WBWebViewConsoleTests/WKWebView_WBWebViewConsoleTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WKWebView_WBWebViewConsoleTests.m
3 | // WKWebView+WBWebViewConsoleTests
4 | //
5 | // Created by 吴天 on 15/2/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 |
17 | @interface WKWebView_WBWebViewConsoleTests : XCTestCase
18 |
19 | @end
20 |
21 | @implementation WKWebView_WBWebViewConsoleTests
22 |
23 | - (void)setUp {
24 | [super setUp];
25 | // Put setup code here. This method is called before the invocation of each test method in the class.
26 | }
27 |
28 | - (void)tearDown {
29 | // Put teardown code here. This method is called after the invocation of each test method in the class.
30 | [super tearDown];
31 | }
32 |
33 | - (void)testExample {
34 | // This is an example of a functional test case.
35 | XCTAssert(YES, @"Pass");
36 | }
37 |
38 | - (void)testPerformanceExample {
39 | // This is an example of a performance test case.
40 | [self measureBlock:^{
41 | // Put the code you want to measure the time of here.
42 | }];
43 | }
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD License
2 |
3 | For WBWebViewConsole software
4 |
5 | Copyright (c) 2015-present, Weibo, Corp. All rights reserved.
6 |
7 | Redistribution and use in source and binary forms, with or without modification,
8 | are permitted provided that the following conditions are met:
9 |
10 | * Redistributions of source code must retain the above copyright notice, this
11 | list of conditions and the following disclaimer.
12 |
13 | * Redistributions in binary form must reproduce the above copyright notice,
14 | this list of conditions and the following disclaimer in the documentation
15 | and/or other materials provided with the distribution.
16 |
17 | * Neither the name Weibo nor the names of its contributors may be used to
18 | endorse or promote products derived from this software without specific
19 | prior written permission.
20 |
21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
22 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
25 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
28 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WBWebViewConsole [](https://travis-ci.org/Naituw/WBWebViewConsole)
2 |
3 | WBWebViewConsole is an In-App debug console for your UIWebView && WKWebView
4 |
5 |
6 |
7 | ## Installation
8 |
9 | WBWebViewConsole is available on [CocoaPods](http://cocoapods.org). Just add the following to your project Podfile:
10 |
11 | ```
12 | pod 'WBWebViewConsole', '~> 1.0'
13 | ```
14 |
15 | Bugs are first fixed in master and then made available via a designated release. If you tend to live on the bleeding edge, you can use WBWebViewConsole from master with the following Podfile entry:
16 |
17 | ```
18 | pod 'WBWebViewConsole', :git => 'https://github.com/Naituw/WBWebViewConsole.git'
19 | ```
20 |
21 | ## Setup
22 |
23 | - Make your own `UIWebView` or `WKWebView` subclass, and implement all methods in `WBWebView` protocol
24 | - Setup `JSBridge` and `console` when WebView inits
25 | - If you are using `UIWebView`, inject userScript as early as possible after page loading. Otherwise, just use `WKUserScript` to implement.
26 | - In `UIWebView`'s `webView:shouldStartLoadWithRequest:navigationType` or `WKWebView`'s `webView:decidePolicyForNavigationAction:decisionHandler`
27 | - Pass the request to `-[JSBridge handleWebViewRequest:]` and use the return value to decide whether the navigation should start
28 |
29 | ## Usage
30 |
31 | - Use `WBWebViewConsole` to manage all messages
32 | - `addMessage:type:level:source:`
33 | - add message for specific type, level and source
34 | - `clearMessage`
35 | - empty all messages
36 | - `sendMessage`
37 | - input (eval) script
38 | - `storeCurrentSelectedElementToJavaScriptVariable:completion:`
39 | - save current selected element to a js variable
40 | - Use `WBWebDebugConsoleViewController` to display a `WBWebViewConsole`
41 | - `initWithConsole:`
42 | - designated initializer for this class
43 | - `setInitialCommand:`
44 | - set the placeholder command
45 |
46 | ## License
47 |
48 | WBWebViewConsole is BSD-licensed. see the `LICENSE` file.
49 |
50 | The files in the `/Examples` directory are licensed under a separate license as specified in `Examples/README.md`.
51 |
--------------------------------------------------------------------------------
/WBWebViewConsole.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = "WBWebViewConsole"
3 | s.platform = :ios
4 | s.ios.deployment_target = "6.0"
5 | s.version = "1.0.2"
6 | s.summary = "In-App debug console for your UIWebView && WKWebView"
7 | s.homepage = "https://github.com/Naituw/WBWebViewConsole"
8 | s.license = "BSD"
9 | s.authors = {"Naituw" => "naituw@gmail.com"}
10 | s.source = {:git => "https://github.com/Naituw/WBWebViewConsole.git", :tag => '1.0.2'}
11 | s.source_files = 'WBWebViewConsole/**/*.{h,m}'
12 | s.requires_arc = true
13 | s.resources = ["WBWebViewConsole/Resources/*", "WBWebViewConsole/Supports/WBWebView/JSBridge/Resources/*"]
14 | end
--------------------------------------------------------------------------------
/WBWebViewConsole.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WBWebViewConsole.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole.xcodeproj/project.xcworkspace/xcuserdata/wutian.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/WBWebViewConsole.xcodeproj/xcshareddata/xcschemes/WBWebViewConsole.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
94 |
100 |
101 |
102 |
103 |
105 |
106 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/WBWebViewConsole.xcodeproj/xcuserdata/wutian.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WBWebViewConsole.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | C6B12C471A8D9CAB004F2AD2
16 |
17 | primary
18 |
19 |
20 | C6B12C521A8D9CAB004F2AD2
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Controllers/WBWebDebugConsoleViewController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebDebugConsoleViewController.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @class WBWebViewConsole;
17 |
18 | @interface WBWebDebugConsoleViewController : UIViewController
19 |
20 | - (instancetype)initWithConsole:(WBWebViewConsole *)console;
21 |
22 | @property (nonatomic, strong, readonly) WBWebViewConsole * console;
23 |
24 | @property (nonatomic, strong) NSString * initialCommand;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Controllers/WBWebDebugConsoleViewController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebDebugConsoleViewController.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebDebugConsoleViewController.h"
15 | #import "WBWebViewConsole.h"
16 | #import "WBWebViewConsoleMessageCell.h"
17 | #import "WBWebViewConsoleInputView.h"
18 | #import "UIScrollView+WBTUtilities.h"
19 | #import "UIView+WBTSizes.h"
20 | #import "WBKeyboardObserver.h"
21 |
22 | @interface WBWebDebugConsoleViewController ()
23 | {
24 | struct {
25 | unsigned int viewAppeared: 1;
26 | } _flags;
27 | }
28 |
29 | @property (nonatomic, strong) WBWebViewConsole * console;
30 | @property (nonatomic, strong) UITableView * tableView;
31 | @property (nonatomic, strong) WBWebViewConsoleInputView * inputView;
32 |
33 | @end
34 |
35 | @implementation WBWebDebugConsoleViewController
36 |
37 | - (void)dealloc
38 | {
39 | [[NSNotificationCenter defaultCenter] removeObserver:self];
40 | }
41 |
42 | - (id)initWithConsole:(WBWebViewConsole *)console
43 | {
44 | if (self = [self init]) {
45 | self.console = console;
46 | }
47 | return self;
48 | }
49 |
50 | - (void)viewDidLoad
51 | {
52 | [super viewDidLoad];
53 |
54 | self.navigationItem.title = NSLocalizedString(@"Debug Console", nil);
55 |
56 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardFrameDidChangeNotification:) name:WBKeyboardObserverFrameDidUpdateNotification object:nil];
57 |
58 | self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
59 | self.tableView.delegate = self;
60 | self.tableView.dataSource = self;
61 | self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
62 |
63 | [self.view addSubview:self.tableView];
64 |
65 | self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Clear", nil) style:UIBarButtonItemStylePlain target:self action:@selector(clearMessages)];
66 |
67 | self.inputView = [[WBWebViewConsoleInputView alloc] initWithFrame:CGRectZero];
68 | [self.inputView setDelegate:self];
69 | [self.inputView setFont:[WBWebViewConsoleMessageCell messageFont]];
70 | [self.inputView setConsole:self.console];
71 | [self.view addSubview:self.inputView];
72 |
73 | if (self.initialCommand.length)
74 | {
75 | [self.inputView setText:self.initialCommand];
76 | [self.inputView.textView becomeFirstResponder];
77 | }
78 | }
79 |
80 | - (void)viewWillAppear:(BOOL)animated
81 | {
82 | [super viewWillAppear:animated];
83 |
84 | dispatch_async(dispatch_get_main_queue(), ^{
85 | [self relayoutViewsAnimated:NO];
86 |
87 | _flags.viewAppeared = YES;
88 | });
89 | }
90 |
91 | - (void)viewWillDisappear:(BOOL)animated
92 | {
93 | [super viewWillDisappear:animated];
94 |
95 | dispatch_async(dispatch_get_main_queue(), ^{
96 | _flags.viewAppeared = NO;
97 | });
98 | }
99 |
100 | - (void)dismiss:(id)sender
101 | {
102 | [self dismissViewControllerAnimated:YES completion:NULL];
103 | }
104 |
105 | - (void)setConsole:(WBWebViewConsole *)console
106 | {
107 | if (_console != console)
108 | {
109 | [self unregisterConsoleNotifications];
110 |
111 | _console = console;
112 |
113 | [self registerNotificationsForCurrentConsole];
114 | }
115 | }
116 |
117 | - (CGFloat)keyboardHeight
118 | {
119 | CGRect endFrame = [WBKeyboardObserver sharedObserver].frameEnd;
120 |
121 | if (CGRectIsNull(endFrame))
122 | {
123 | return 0;
124 | }
125 |
126 | return MAX([UIScreen mainScreen].bounds.size.height - endFrame.origin.y, 0);
127 | }
128 |
129 | - (void)relayoutViewsAnimated:(BOOL)animated
130 | {
131 | CGFloat keyboardHeight = [self keyboardHeight];
132 | CGFloat inputViewHeight = self.inputView.desiredHeight;
133 |
134 | if (animated)
135 | {
136 | WBKeyboardObserver * keyboard = [WBKeyboardObserver sharedObserver];
137 |
138 | [UIView beginAnimations:nil context:NULL];
139 | [UIView setAnimationCurve:keyboard.animationCurve];
140 | [UIView setAnimationDuration:keyboard.animationDuration ? : 0.15];
141 | }
142 |
143 | self.inputView.frame = CGRectMake(0, self.view.wbtHeight - inputViewHeight - keyboardHeight, self.view.wbtWidth, inputViewHeight);
144 | self.tableView.frame = CGRectMake(0, 0, self.view.wbtWidth, self.view.wbtHeight - inputViewHeight - keyboardHeight);
145 |
146 | if (animated)
147 | {
148 | [UIView commitAnimations];
149 | }
150 | }
151 |
152 | #pragma mark - TableView Datasource
153 |
154 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
155 | {
156 | return 2; // cleared messages + messages
157 | }
158 |
159 | - (NSArray *)messageDatasourceForSection:(NSInteger)section
160 | {
161 | if (section == 0)
162 | {
163 | return _console.clearedMessages;
164 | }
165 | else
166 | {
167 | return _console.messages;
168 | }
169 | }
170 |
171 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
172 | {
173 | return [self messageDatasourceForSection:section].count;
174 | }
175 |
176 | - (WBWebViewConsoleMessage *)messageAtIndexPath:(NSIndexPath *)indexPath
177 | {
178 | NSArray * datasource = [self messageDatasourceForSection:indexPath.section];
179 |
180 | if (indexPath.row >= datasource.count)
181 | {
182 | return nil;
183 | }
184 |
185 | return datasource[indexPath.row];
186 | }
187 |
188 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
189 | {
190 | WBWebViewConsoleMessage * message = [self messageAtIndexPath:indexPath];
191 |
192 | return [WBWebViewConsoleMessageCell rowHeightOfDataObject:message tableView:tableView];
193 | }
194 |
195 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
196 | {
197 | static NSString * const identifier = @"WBWebViewConsoleMessageCell";
198 | WBWebViewConsoleMessageCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
199 |
200 | if (!cell) {
201 | cell = [[WBWebViewConsoleMessageCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
202 | }
203 |
204 | cell.message = [self messageAtIndexPath:indexPath];
205 | cell.cleared = indexPath.section == 0;
206 | cell.delegate = self;
207 |
208 | return cell;
209 | }
210 |
211 | - (BOOL)tableView:(UITableView *)tableView shouldShowMenuForRowAtIndexPath:(NSIndexPath *)indexPath
212 | {
213 | return YES;
214 | }
215 |
216 | -(BOOL)tableView:(UITableView *)tableView canPerformAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
217 | {
218 | return (action == @selector(copy:));
219 | }
220 |
221 | - (void)tableView:(UITableView *)tableView performAction:(SEL)action forRowAtIndexPath:(NSIndexPath *)indexPath withSender:(id)sender
222 | {
223 | if (action == @selector(copy:))
224 | {
225 | WBWebViewConsoleMessage * message = [self messageAtIndexPath:indexPath];
226 |
227 | if (message)
228 | {
229 | [UIPasteboard generalPasteboard].string = message.message;
230 | }
231 | }
232 | }
233 |
234 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
235 | {
236 | [tableView deselectRowAtIndexPath:indexPath animated:YES];
237 | }
238 |
239 | - (void)consoleMessageCell:(WBWebViewConsoleMessageCell *)cell copyMessageToInputView:(WBWebViewConsoleMessage *)message
240 | {
241 | self.inputView.text = message.message;
242 | }
243 |
244 | //- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
245 | //{
246 | // if (self.inputView.bottom < self.view.height)
247 | // {
248 | // [self.view endEditing:YES];
249 | // }
250 | //}
251 |
252 | #pragma mark - Console Notifications
253 |
254 | - (void)registerNotificationsForCurrentConsole
255 | {
256 | if (!_console) return;
257 |
258 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(consoleDidAddMessage:) name:WBWebViewConsoleDidAddMessageNotification object:_console];
259 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(consoleDidClearMessages:) name:WBWebViewConsoleDidClearMessagesNotification object:_console];
260 | }
261 | - (void)unregisterConsoleNotifications
262 | {
263 | [[NSNotificationCenter defaultCenter] removeObserver:self name:WBWebViewConsoleDidAddMessageNotification object:nil];
264 | [[NSNotificationCenter defaultCenter] removeObserver:self name:WBWebViewConsoleDidClearMessagesNotification object:nil];
265 | }
266 |
267 | - (void)consoleDidAddMessage:(NSNotification *)notification
268 | {
269 | BOOL scrollToBottom = self.tableView.wbt_isScrolledToBottom;
270 |
271 | [self.tableView reloadData];
272 |
273 | if (scrollToBottom)
274 | {
275 | [self.tableView wbt_scrollToBottomAnimated:NO];
276 | }
277 | }
278 | - (void)consoleDidClearMessages:(NSNotification *)notification
279 | {
280 | [self.tableView reloadData];
281 | }
282 |
283 | #pragma mark - Actions
284 |
285 | - (void)clearMessages
286 | {
287 | [self.console clearMessages];
288 | }
289 |
290 | #pragma mark - InputView Delegate
291 |
292 | - (void)consoleInputViewHeightChanged:(WBWebViewConsoleInputView *)inputView
293 | {
294 | [self relayoutViewsAnimated:_flags.viewAppeared];
295 | }
296 |
297 | - (void)consoleInputViewDidBeginEditing:(WBWebViewConsoleInputView *)inputView
298 | {
299 | [self.tableView wbt_scrollToBottomAnimated:YES];
300 | }
301 |
302 | - (void)consoleInputView:(WBWebViewConsoleInputView *)inputView didCommitCommand:(NSString *)command
303 | {
304 | [self.tableView wbt_scrollToBottomAnimated:NO];
305 | }
306 |
307 | #pragma mark - Keyboard Notification
308 |
309 | - (void)keyboardFrameDidChangeNotification:(NSNotification *)notification
310 | {
311 | [self relayoutViewsAnimated:YES];
312 | }
313 |
314 | @end
315 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/UserPrompt/WBWebViewConsoleUserPromptCompletionController.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleUserPromptCompletionController.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @protocol WBWebView;
17 |
18 | @interface WBWebViewConsoleUserPromptCompletionController : NSObject
19 |
20 | - (instancetype)initWithWebView:(id)webView;
21 |
22 | - (void)fetchSuggestionsWithPrompt:(NSString *)prompt cursorIndex:(NSInteger)cursorIndex completion:(void (^)(NSArray * suggestions, NSRange replacementRange))completion;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/UserPrompt/WBWebViewConsoleUserPromptCompletionController.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleUserPromptCompletionController.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleUserPromptCompletionController.h"
15 | #import "WBWebView.h"
16 | #import "WBWebViewConsoleDefines.h"
17 | #import "NSObject+WBJSONKit.h"
18 | #import "NSDictionary+WBTTypeCast.h"
19 |
20 | @interface WBWebViewConsoleUserPromptCompletionController ()
21 |
22 | @property (nonatomic, weak) id webView; // weak
23 | @property (nonatomic, strong) NSString * js;
24 |
25 | @end
26 |
27 | @implementation WBWebViewConsoleUserPromptCompletionController
28 |
29 |
30 | - (instancetype)initWithWebView:(id)webView
31 | {
32 | if (self = [super init])
33 | {
34 | self.webView = webView;
35 | }
36 | return self;
37 | }
38 |
39 | - (void)fetchSuggestionsWithPrompt:(NSString *)prompt cursorIndex:(NSInteger)cursorIndex completion:(void (^)(NSArray * suggestions, NSRange replacementRange))completion
40 | {
41 | if (!completion) return;
42 |
43 | void (^failedBlock)(void) = ^{
44 | completion(nil, NSMakeRange(NSNotFound, 0));
45 | };
46 |
47 | if (!_webView)
48 | {
49 | failedBlock();
50 | return;
51 | }
52 |
53 | if (!prompt.length)
54 | {
55 | failedBlock();
56 | return;
57 | }
58 |
59 | NSString * js = self.js;
60 | js = [js stringByAppendingFormat:@"('%@', %ld)", [[prompt dataUsingEncoding:NSUTF8StringEncoding] base64Encoding], (long)cursorIndex];
61 |
62 | [_webView wb_evaluateJavaScript:js completionHandler:^(id result, NSError * error) {
63 | if (![result isKindOfClass:[NSString class]])
64 | {
65 | failedBlock();
66 | return;
67 | }
68 | NSDictionary * resultDict = [result wb_objectFromJSONString];
69 | NSArray * suggestions = [resultDict wbt_arrayForKey:@"completions"];
70 | NSInteger tokenStart = [resultDict wbt_integerForKey:@"token_start"];
71 | NSInteger tokenEnd = [resultDict wbt_integerForKey:@"token_end"];
72 |
73 | if (![suggestions isKindOfClass:[NSArray class]] || !suggestions.count)
74 | {
75 | failedBlock();
76 | return;
77 | }
78 |
79 | completion(suggestions, NSMakeRange(tokenStart, tokenEnd - tokenStart));
80 | }];
81 | }
82 |
83 | - (NSString *)js
84 | {
85 | if (!_js)
86 | {
87 | NSString * jsPath = [WBWebBrowserConsoleBundle() pathForResource:@"console_prompt_completion" ofType:@"js"];
88 | NSString * js = [NSString stringWithContentsOfFile:jsPath encoding:NSUTF8StringEncoding error:NULL];
89 | _js = js;
90 | }
91 | return _js;
92 | }
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/UserPrompt/WBWebViewConsoleUserPromptCompletionDatasource.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleUserPromptCompletionDatasource.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 |
17 | @class WBWebViewConsoleInputView;
18 |
19 | @interface WBWebViewConsoleUserPromptCompletionDatasource : NSObject
20 |
21 | @property (nonatomic, strong) NSArray * suggestions;
22 | @property (nonatomic, assign) NSRange replacementRange;
23 | @property (nonatomic, weak) WBWebViewConsoleInputView * inputView;
24 |
25 | @end
26 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/UserPrompt/WBWebViewConsoleUserPromptCompletionDatasource.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleUserPromptCompletionDatasource.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/25.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleUserPromptCompletionDatasource.h"
15 | #import "WBWebViewConsoleMessageCell.h"
16 | #import "WBWebViewConsoleInputView.h"
17 | #import "UIColor+WBTHelpers.h"
18 | #import "UIView+WBTSizes.h"
19 |
20 | @interface WBWebViewConsoleUserPromptCompletionCell : UITableViewCell
21 |
22 | @property (nonatomic, copy) NSString * suggestion;
23 |
24 | @end
25 |
26 | @implementation WBWebViewConsoleUserPromptCompletionDatasource
27 |
28 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
29 | {
30 | return 1;
31 | }
32 |
33 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
34 | {
35 | return _suggestions.count;
36 | }
37 |
38 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
39 | {
40 | static NSString * const identifier = @"WBWebViewConsoleUserPromptCompletionCell";
41 |
42 | WBWebViewConsoleUserPromptCompletionCell * cell = [tableView dequeueReusableCellWithIdentifier:identifier];
43 |
44 | if (!cell) {
45 | cell = [[WBWebViewConsoleUserPromptCompletionCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
46 | }
47 |
48 | if (indexPath.row < _suggestions.count)
49 | {
50 | cell.suggestion = _suggestions[indexPath.row];
51 | }
52 |
53 | return cell;
54 | }
55 |
56 | - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
57 | {
58 | return 36;
59 | }
60 |
61 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
62 | {
63 | [tableView deselectRowAtIndexPath:indexPath animated:NO];
64 |
65 | if (indexPath.row < _suggestions.count)
66 | {
67 | [_inputView completePromptWithSuggestion:_suggestions[indexPath.row]];
68 | }
69 | }
70 |
71 | @end
72 |
73 | @implementation WBWebViewConsoleUserPromptCompletionCell
74 |
75 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
76 | {
77 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
78 | {
79 | self.textLabel.font = [WBWebViewConsoleMessageCell messageFont];
80 | self.textLabel.textColor = [UIColor whiteColor];
81 | self.textLabel.lineBreakMode = NSLineBreakByTruncatingMiddle;
82 | }
83 | return self;
84 | }
85 |
86 | - (void)layoutSubviews
87 | {
88 | [super layoutSubviews];
89 |
90 | self.textLabel.backgroundColor = [UIColor clearColor];
91 | self.textLabel.frame = CGRectMake(30, 0, self.wbtWidth - 40, self.wbtHeight);
92 | }
93 |
94 | - (void)setSuggestion:(NSString *)suggestion
95 | {
96 | self.textLabel.text = suggestion;
97 | }
98 | - (NSString *)suggestion
99 | {
100 | return self.textLabel.text;
101 | }
102 |
103 | - (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
104 | {
105 | [super setHighlighted:highlighted animated:animated];
106 |
107 | [self.contentView setBackgroundColor:highlighted ? RGBCOLOR(129, 137, 148) : [UIColor colorWithRed:0.69 green:0.72 blue:0.76 alpha:1]];
108 | }
109 |
110 | @end
111 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBJSBridgeActionPrivateConsoleLog.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeActionPrivateConsoleLog.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/14/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBJSBridgeAction.h"
15 |
16 | @interface WBJSBridgeActionPrivateConsoleLog : WBJSBridgeAction
17 |
18 | @end
19 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBJSBridgeActionPrivateConsoleLog.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeActionPrivateConsoleLog.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/14/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBJSBridgeActionPrivateConsoleLog.h"
15 | #import "WBWebViewConsole.h"
16 | #import "WBWebViewJSBridge.h"
17 | #import "WBWebView.h"
18 | #import "NSDictionary+WBTTypeCast.h"
19 |
20 | @implementation WBJSBridgeActionPrivateConsoleLog
21 |
22 | - (void)startAction
23 | {
24 | WBWebViewConsole * debugConsole = self.bridge.webView.console;
25 |
26 | NSDictionary * message = self.message.parameters;
27 |
28 | NSString * func = [message wbt_stringForKey:@"func"];
29 |
30 | if (!func) func = @"log";
31 |
32 | NSString * url = [message wbt_stringForKey:@"file"];
33 | NSInteger line = [message wbt_integerForKey:@"lineno"];
34 | NSInteger column = [message wbt_integerForKey:@"colno"];
35 |
36 | if ([func isEqual:@"clear"])
37 | {
38 | [debugConsole addMessage:nil type:WBWebViewConsoleMessageTypeClear level:WBWebViewConsoleMessageLevelInfo source:WBWebViewConsoleMessageSourceJS];
39 | }
40 | else if ([func isEqual:@"assert"])
41 | {
42 | NSArray * args = [message wbt_arrayForKey:@"args"];
43 | id condition = [args firstObject];
44 |
45 | if ([condition isKindOfClass:[NSNumber class]])
46 | {
47 | if ([condition boolValue])
48 | {
49 | return;
50 | }
51 | }
52 |
53 | if ([condition isKindOfClass:[NSString class]])
54 | {
55 | if ([condition length] &&
56 | ![condition isEqual:@"false"] &&
57 | ![condition isEqual:@"0"] &&
58 | ![condition isEqual:@"undefined"] &&
59 | ![condition isEqual:@"null"])
60 | {
61 | return;
62 | }
63 | }
64 |
65 | NSString * message = NSLocalizedString(@"Assert Failed: ", nil);
66 |
67 | if (args.count > 1)
68 | {
69 | NSString * reason = [[args subarrayWithRange:NSMakeRange(1, args.count - 1)] componentsJoinedByString:@" "];
70 | message = [message stringByAppendingString:reason];
71 | }
72 |
73 | [debugConsole addMessage:message type:WBWebViewConsoleMessageTypeAssert level:WBWebViewConsoleMessageLevelError source:WBWebViewConsoleMessageSourceJS url:url line:line column:column];
74 | }
75 | else
76 | {
77 | NSDictionary * levelMap = @{@"warn": @(WBWebViewConsoleMessageLevelWarning),
78 | @"error": @(WBWebViewConsoleMessageLevelError),
79 | @"debug": @(WBWebViewConsoleMessageLevelDebug),
80 | @"info": @(WBWebViewConsoleMessageLevelInfo),
81 | @"log": @(WBWebViewConsoleMessageLevelLog)};
82 |
83 | WBWebViewConsoleMessageLevel level = [levelMap[func] integerValue];
84 |
85 | NSArray * args = [message wbt_arrayForKey:@"args"];
86 | NSString * string = [args componentsJoinedByString:@" "];
87 |
88 | [debugConsole addMessage:string type:WBWebViewConsoleMessageTypeLog level:level source:WBWebViewConsoleMessageSourceJS url:url line:line column:column];
89 | }
90 |
91 | [self actionSuccessedWithResult:nil];
92 | }
93 |
94 | @end
95 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBWebViewConsole.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsole.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 | #import "WBWebViewConsoleMessage.h"
15 |
16 | extern NSString * const WBWebViewConsoleDidAddMessageNotification;
17 | extern NSString * const WBWebViewConsoleDidClearMessagesNotification;
18 |
19 | extern NSString * const WBWebViewConsoleLastSelectionElementName;
20 |
21 | @protocol WBWebView;
22 |
23 | @interface WBWebViewConsole : NSObject
24 |
25 | - (instancetype)initWithWebView:(id)webView;
26 |
27 | - (void)addMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source;
28 | - (void)addMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source url:(NSString *)url line:(NSInteger)line column:(NSInteger)column;
29 | - (void)addMessage:(NSString *)message level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source caller:(NSString *)caller;
30 |
31 | - (void)clearMessages;
32 | - (void)reset;
33 |
34 | - (void)sendMessage:(NSString *)message;
35 |
36 | @property (nonatomic, strong, readonly) NSArray * messages;
37 | @property (nonatomic, strong, readonly) NSArray * clearedMessages;
38 |
39 | @property (nonatomic, weak, readonly) id webView;
40 |
41 | - (void)storeCurrentSelectedElementToJavaScriptVariable:(NSString *)variable completion:(void (^)(BOOL success))completion;
42 |
43 | - (void)fetchSuggestionsForPrompt:(NSString *)prompt cursorIndex:(NSInteger)cursorIndex completion:(void (^)(NSArray * suggestions, NSRange replacementRange))completion;
44 |
45 | @end
46 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBWebViewConsoleInputHistoryEntry.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleInputHistoryEntry.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/27.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface WBWebViewConsoleInputHistoryEntry : NSObject
17 |
18 | @property (nonatomic, copy, readonly) NSString * text;
19 | @property (nonatomic, assign, readonly) NSRange cursor;
20 |
21 | + (instancetype)emptyEntry;
22 | + (instancetype)entryWithText:(NSString *)text cursor:(NSRange)cursor;
23 |
24 | @property (nonatomic, assign, readonly) BOOL isEmpty;
25 |
26 | @end
27 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBWebViewConsoleInputHistoryEntry.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleInputHistoryEntry.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/27.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleInputHistoryEntry.h"
15 |
16 | @interface WBWebViewConsoleInputHistoryEmptyEntry : WBWebViewConsoleInputHistoryEntry
17 |
18 | @end
19 |
20 | @interface WBWebViewConsoleInputHistoryEntry ()
21 |
22 | @property (nonatomic, copy) NSString * text;
23 | @property (nonatomic, assign) NSRange cursor;
24 |
25 | @end
26 |
27 | @implementation WBWebViewConsoleInputHistoryEntry
28 |
29 |
30 | - (instancetype)init
31 | {
32 | if (self = [super init])
33 | {
34 | _cursor = NSMakeRange(NSNotFound, 0);
35 | }
36 | return self;
37 | }
38 |
39 | + (instancetype)emptyEntry
40 | {
41 | return [WBWebViewConsoleInputHistoryEmptyEntry new];
42 | }
43 | + (instancetype)entryWithText:(NSString *)text cursor:(NSRange)cursor
44 | {
45 | WBWebViewConsoleInputHistoryEntry * entry = [WBWebViewConsoleInputHistoryEntry new];
46 | entry.text = text;
47 | entry.cursor = cursor;
48 |
49 | return entry;
50 | }
51 |
52 | - (BOOL)isEmpty
53 | {
54 | return NO;
55 | }
56 |
57 | @end
58 |
59 | @implementation WBWebViewConsoleInputHistoryEmptyEntry
60 |
61 | - (BOOL)isEmpty
62 | {
63 | return YES;
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBWebViewConsoleMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleMessage.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | typedef NS_ENUM(NSInteger, WBWebViewConsoleMessageSource)
17 | {
18 | WBWebViewConsoleMessageSourceJS = 0,
19 | WBWebViewConsoleMessageSourceNavigation,
20 | WBWebViewConsoleMessageSourceUserCommand,
21 | WBWebViewConsoleMessageSourceUserCommandResult,
22 | WBWebViewConsoleMessageSourceNative,
23 | };
24 |
25 | typedef NS_ENUM(NSInteger, WBWebViewConsoleMessageType)
26 | {
27 | WBWebViewConsoleMessageTypeLog = 0,
28 | WBWebViewConsoleMessageTypeClear,
29 | WBWebViewConsoleMessageTypeAssert,
30 | };
31 |
32 | typedef NS_ENUM(NSInteger, WBWebViewConsoleMessageLevel)
33 | {
34 | WBWebViewConsoleMessageLevelNone = 0,
35 | WBWebViewConsoleMessageLevelLog = 1,
36 | WBWebViewConsoleMessageLevelWarning = 2,
37 | WBWebViewConsoleMessageLevelError = 3,
38 | WBWebViewConsoleMessageLevelDebug = 4,
39 | WBWebViewConsoleMessageLevelInfo = 5,
40 | WBWebViewConsoleMessageLevelSuccess = 6,
41 | };
42 |
43 | @interface WBWebViewConsoleMessage : NSObject
44 |
45 | @property (nonatomic, readonly) WBWebViewConsoleMessageSource source;
46 | @property (nonatomic, readonly) WBWebViewConsoleMessageType type;
47 | @property (nonatomic, readonly) WBWebViewConsoleMessageLevel level;
48 | @property (nonatomic, strong, readonly) NSString * message;
49 |
50 | @property (nonatomic, readonly) NSInteger line;
51 | @property (nonatomic, readonly) NSInteger column;
52 | @property (nonatomic, strong, readonly) NSString * url;
53 |
54 | @property (nonatomic, strong, readonly) NSString * caller;
55 |
56 | @property (nonatomic, readonly) NSInteger repeatCount;
57 |
58 | + (instancetype)messageWithMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source;
59 | + (instancetype)messageWithMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source url:(NSString *)url line:(NSInteger)line column:(NSInteger)column;
60 | + (instancetype)messageWithMessage:(NSString *)message level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source caller:(NSString *)caller;
61 |
62 | @end
63 |
64 | @interface WBWebViewConsoleMessage (Caller)
65 |
66 | @property (nonatomic, strong, readonly) NSString * defaultCaller;
67 |
68 | @end
69 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Models/WBWebViewConsoleMessage.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleMessage.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleMessage.h"
15 |
16 | @interface WBWebViewConsoleMessage ()
17 |
18 | @property (nonatomic) WBWebViewConsoleMessageSource source;
19 | @property (nonatomic) WBWebViewConsoleMessageType type;
20 | @property (nonatomic) WBWebViewConsoleMessageLevel level;
21 | @property (nonatomic, strong) NSString * message;
22 |
23 | @property (nonatomic) NSInteger line;
24 | @property (nonatomic) NSInteger column;
25 | @property (nonatomic, strong) NSString * url;
26 |
27 | @property (nonatomic, strong) NSString * caller;
28 |
29 | @end
30 |
31 | @implementation WBWebViewConsoleMessage
32 |
33 |
34 | + (instancetype)messageWithMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source
35 | {
36 | return [self messageWithMessage:message type:type level:level source:source url:nil line:0 column:0];
37 | }
38 |
39 | + (instancetype)messageWithMessage:(NSString *)message type:(WBWebViewConsoleMessageType)type level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source url:(NSString *)url line:(NSInteger)line column:(NSInteger)column
40 | {
41 | WBWebViewConsoleMessage * result = [WBWebViewConsoleMessage new];
42 |
43 | result.message = message;
44 | result.type = type;
45 | result.level = level;
46 | result.source = source;
47 | result.url = url;
48 | result.line = line;
49 | result.column = column;
50 | result.caller = result.defaultCaller;
51 |
52 | return result;
53 | }
54 |
55 | + (instancetype)messageWithMessage:(NSString *)message level:(WBWebViewConsoleMessageLevel)level source:(WBWebViewConsoleMessageSource)source caller:(NSString *)caller
56 | {
57 | WBWebViewConsoleMessage * result = [WBWebViewConsoleMessage new];
58 |
59 | result.type = WBWebViewConsoleMessageTypeLog;
60 | result.source = source;
61 | result.level = level;
62 | result.message = message;
63 | result.caller = caller;
64 |
65 | return result;
66 | }
67 |
68 | @end
69 |
70 | @implementation WBWebViewConsoleMessage (Caller)
71 |
72 | - (NSString *)defaultCaller
73 | {
74 | if (!_url.length) return nil;
75 |
76 | NSString * filename = nil;
77 | NSURL * url = [NSURL URLWithString:_url];
78 | if (url)
79 | {
80 | filename = [url.path lastPathComponent];
81 | }
82 | else
83 | {
84 | filename = [[_url componentsSeparatedByString:@"/"] lastObject];
85 | }
86 |
87 | if (!_line) return filename;
88 |
89 | return [NSString stringWithFormat:@"%@:%ld", filename, (long)_line];
90 | }
91 |
92 | @end
93 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/console_prompt_completion.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2014-present, Weibo, Corp.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree.
7 | *
8 | * This file is written based on WebKit's CodeMirrorCompletionController.js
9 | *
10 | * Copyright (C) 2013 Apple Inc. All rights reserved.
11 | *
12 | * Redistribution and use in source and binary forms, with or without
13 | * modification, are permitted provided that the following conditions
14 | * are met:
15 | * 1. Redistributions of source code must retain the above copyright
16 | * notice, this list of conditions and the following disclaimer.
17 | * 2. Redistributions in binary form must reproduce the above copyright
18 | * notice, this list of conditions and the following disclaimer in the
19 | * documentation and/or other materials provided with the distribution.
20 | *
21 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 | * THE POSSIBILITY OF SUCH DAMAGE.
32 | */
33 |
34 | (function(string, cursor) {
35 |
36 | if (typeof String.prototype.startsWith != 'function') {
37 | String.prototype.startsWith = function(str) {
38 | return this.slice(0, str.length) == str;
39 | };
40 | }
41 |
42 | if (typeof String.prototype.contains != 'function') {
43 | String.prototype.contains = function(it) {
44 | return this.indexOf(it) != -1;
45 | };
46 | }
47 |
48 | if (typeof Array.prototype.keySet != 'function') {
49 | Array.prototype.keySet = function() {
50 | var keys = {};
51 | for (var i = 0; i < this.length; ++i)
52 | keys[this[i]] = true;
53 | return keys;
54 | }
55 | }
56 |
57 | function _scanStringForExpression(string, startOffset, direction, allowMiddleAndEmpty, includeStopCharacter, ignoreInitialUnmatchedOpenBracket, stopCharactersRegex) {
58 |
59 | // console.assert(direction === -1 || direction === 1);
60 |
61 | var stopCharactersRegex = stopCharactersRegex || /[\s=:;,!+\-*/%&|^~?<>.{}()[\]]/;
62 |
63 | function isStopCharacter(character) {
64 | return stopCharactersRegex.test(character);
65 | }
66 |
67 | function isOpenBracketCharacter(character) {
68 | return /[({[]/.test(character);
69 | }
70 |
71 | function isCloseBracketCharacter(character) {
72 | return /[)}\]]/.test(character);
73 | }
74 |
75 | function matchingBracketCharacter(character) {
76 | return {
77 | "{": "}",
78 | "(": ")",
79 | "[": "]",
80 | "}": "{",
81 | ")": "(",
82 | "]": "["
83 | }[character];
84 | }
85 |
86 | var endOffset = Math.min(startOffset, string.length);
87 |
88 | var endOfLineOrWord = endOffset === string.length || isStopCharacter(string.charAt(endOffset));
89 |
90 | if (!endOfLineOrWord && !allowMiddleAndEmpty)
91 | return null;
92 |
93 | var bracketStack = [];
94 | var bracketOffsetStack = [];
95 | var lastCloseBracketOffset = NaN;
96 |
97 | var startOffset = endOffset;
98 | var firstOffset = endOffset + direction;
99 | for (var i = firstOffset; direction > 0 ? i < string.length : i >= 0; i += direction) {
100 | var character = string.charAt(i);
101 |
102 | // Ignore stop characters when we are inside brackets.
103 | if (isStopCharacter(character) && !bracketStack.length)
104 | break;
105 |
106 | if (isCloseBracketCharacter(character)) {
107 | bracketStack.push(character);
108 | bracketOffsetStack.push(i);
109 | } else if (isOpenBracketCharacter(character)) {
110 | if ((!ignoreInitialUnmatchedOpenBracket || i !== firstOffset) &&
111 | (!bracketStack.length || matchingBracketCharacter(character) !== bracketStack.lastValue))
112 | break;
113 |
114 | bracketOffsetStack.pop();
115 | bracketStack.pop();
116 | }
117 |
118 | startOffset = i + (direction > 0 ? 1 : 0);
119 | }
120 |
121 | if (bracketOffsetStack.length)
122 | startOffset = bracketOffsetStack.pop() + 1;
123 |
124 | if (includeStopCharacter && startOffset > 0 && startOffset < string.length)
125 | startOffset += direction;
126 |
127 | if (direction > 0) {
128 | var tempEndOffset = endOffset;
129 | endOffset = startOffset;
130 | startOffset = tempEndOffset;
131 | }
132 |
133 | return {
134 | string: string.substring(startOffset, endOffset),
135 | startOffset: startOffset,
136 | endOffset: endOffset
137 | };
138 | };
139 |
140 | function _generateJavaScriptCompletions(base, prefix, suffix)
141 | {
142 | // If there is a base expression then we should not attempt to match any keywords or variables.
143 | // Allow only open bracket characters at the end of the base, otherwise leave completions with
144 | // a base up to the delegate to figure out.
145 | if (base && !/[({[]$/.test(base))
146 | return [];
147 |
148 | var matchingWords = [];
149 |
150 | const allKeywords = ["break", "case", "catch", "const", "continue", "debugger", "default", "delete", "do", "else", "false", "finally", "for", "function", "if", "in",
151 | "Infinity", "instanceof", "NaN", "new", "null", "return", "switch", "this", "throw", "true", "try", "typeof", "undefined", "var", "void", "while", "with"];
152 | const valueKeywords = ["false", "Infinity", "NaN", "null", "this", "true", "undefined"];
153 |
154 | function matchKeywords(keywords)
155 | {
156 | matchingWords = matchingWords.concat(keywords.filter(function(word) {
157 | return word.startsWith(prefix);
158 | }));
159 | }
160 |
161 | switch (suffix.substring(0, 1)) {
162 | case "":
163 | case " ":
164 | matchKeywords(allKeywords);
165 | break;
166 |
167 | case ".":
168 | case "[":
169 | matchKeywords(["false", "Infinity", "NaN", "this", "true"]);
170 | break;
171 |
172 | case "(":
173 | matchKeywords(["catch", "else", "for", "function", "if", "return", "switch", "throw", "while", "with"]);
174 | break;
175 |
176 | case "{":
177 | matchKeywords(["do", "else", "finally", "return", "try"]);
178 | break;
179 |
180 | case ":":
181 | matchKeywords(["case", "default"]);
182 | break;
183 |
184 | case ";":
185 | matchKeywords(valueKeywords);
186 | matchKeywords(["break", "continue", "debugger", "return", "void"]);
187 | break;
188 | }
189 |
190 | return matchingWords;
191 | };
192 |
193 |
194 | function completionControllerCompletionsNeeded(defaultCompletions, base, prefix, suffix, forced) {
195 | // Don't allow non-forced empty prefix completions unless the base is that start of property access.
196 | if (!forced && !prefix && !/[.[]$/.test(base)) {
197 | return null;
198 | }
199 |
200 | // If the base ends with an open parentheses or open curly bracket then treat it like there is
201 | // no base so we get global object completions.
202 | if (/[({]$/.test(base))
203 | base = "";
204 |
205 | if (!base.length) {
206 | base = "window";
207 | }
208 |
209 | var lastBaseIndex = base.length - 1;
210 | var dotNotation = base[lastBaseIndex] === ".";
211 | var bracketNotation = base[lastBaseIndex] === "[";
212 |
213 | if (dotNotation || bracketNotation) {
214 | base = base.substring(0, lastBaseIndex);
215 |
216 | // Don't suggest anything for an empty base that is using dot notation.
217 | // Bracket notation with an empty base will be treated as an array.
218 | if (!base && dotNotation) {
219 | return defaultCompletions;
220 | }
221 |
222 | // Don't allow non-forced empty prefix completions if the user is entering a number, since it might be a float.
223 | // But allow number completions if the base already has a decimal, so "10.0." will suggest Number properties.
224 | if (!forced && !prefix && dotNotation && base.indexOf(".") === -1 && parseInt(base, 10) == base) {
225 | return null;
226 | }
227 |
228 | // An empty base with bracket notation is not property access, it is an array.
229 | // Clear the bracketNotation flag so completions are not quoted.
230 | if (!base && bracketNotation)
231 | bracketNotation = false;
232 | }
233 |
234 | function evaluated(result, wasThrown) {
235 | var type = typeof result;
236 | if (wasThrown || !type || type === "undefined" || (type === "object" && result == null)) {
237 | return defaultCompletions;
238 | }
239 |
240 | function getCompletions(primitiveType) {
241 | var object;
242 | if (primitiveType === "string")
243 | object = new String("");
244 | else if (primitiveType === "number")
245 | object = new Number(0);
246 | else if (primitiveType === "boolean")
247 | object = new Boolean(false);
248 | else
249 | object = this;
250 |
251 | var resultSet = {};
252 | for (var o = object; o; o = o.__proto__) {
253 | try {
254 | var names = Object.getOwnPropertyNames(o);
255 | for (var i = 0; i < names.length; ++i)
256 | resultSet[names[i]] = true;
257 | } catch (e) {
258 | // Ignore
259 | }
260 | }
261 |
262 | return resultSet;
263 | }
264 |
265 | if (type === "object" || type === "function")
266 | return getCompletions.apply(result, [type]);
267 | else if (type === "string" || type === "number" || type === "boolean")
268 | return getCompletions(type);
269 |
270 | return {};
271 | // else
272 | // console.error("Unknown result type: " + result.type);
273 | }
274 |
275 |
276 | function receivedPropertyNames(propertyNames) {
277 | propertyNames = propertyNames || {};
278 |
279 | propertyNames = Object.keys(propertyNames);
280 |
281 | var implicitSuffix = "";
282 | if (bracketNotation) {
283 | var quoteUsed = prefix[0] === "'" ? "'" : "\"";
284 | if (suffix !== "]" && suffix !== quoteUsed)
285 | implicitSuffix = "]";
286 | }
287 |
288 | var completions = defaultCompletions;
289 | var knownCompletions = completions.keySet();
290 |
291 | for (var i = 0; i < propertyNames.length; ++i) {
292 | var property = propertyNames[i];
293 |
294 | if (dotNotation && !/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(property))
295 | continue;
296 |
297 | if (bracketNotation) {
298 | if (parseInt(property) != property)
299 | property = quoteUsed + property.escapeCharacters(quoteUsed + "\\") + (suffix !== quoteUsed ? quoteUsed : "");
300 | }
301 |
302 | if (!property.startsWith(prefix) || property === prefix || property in knownCompletions)
303 | continue;
304 |
305 | completions.push(property);
306 | knownCompletions[property] = true;
307 |
308 | // if (completions.length > 100) {
309 | // break; // get first 100 properties
310 | // }
311 | }
312 |
313 | function compare(a, b) {
314 | // Try to sort in numerical order first.
315 | var numericCompareResult = a - b;
316 | if (!isNaN(numericCompareResult))
317 | return numericCompareResult;
318 |
319 | // Not numbers, sort as strings.
320 | return (a < b ? -1 : (a > b ? 1 : 0));
321 |
322 | // implementation below by webkit is extremely slow, but handles non-english strings better
323 | // return a.localeCompare(b);
324 | }
325 |
326 | completions.sort(compare);
327 |
328 | return completions;
329 | }
330 |
331 | var propertyNames = {};
332 |
333 | try {
334 | (function() {
335 | var result = eval(base);
336 | propertyNames = evaluated(result, false);
337 | })();
338 | } catch (error) {
339 | propertyNames = evaluated(null, true);
340 | }
341 |
342 | return receivedPropertyNames(propertyNames);
343 | };
344 |
345 | try {
346 | var lineString = decodeURIComponent(escape(window.atob(string)));
347 |
348 | var force = false;
349 |
350 | var backwardScanResult = _scanStringForExpression(lineString, cursor, -1, force);
351 |
352 | if (!backwardScanResult) {
353 | return;
354 | }
355 |
356 | var forwardScanResult = _scanStringForExpression(lineString, cursor, 1, true, true);
357 | var suffix = forwardScanResult.string;
358 |
359 | var _startOffset = backwardScanResult.startOffset;
360 | var _endOffset = backwardScanResult.endOffset;
361 | var _prefix = backwardScanResult.string;
362 | var _completions = [];
363 | var _implicitSuffix = "";
364 | var _forced = force;
365 |
366 | var baseExpressionStopCharactersRegex = /[\s=:;,!+\-*/%&|^~?<>]/;
367 | if (baseExpressionStopCharactersRegex)
368 | var baseScanResult = _scanStringForExpression(lineString, _startOffset, -1, true, false, true, baseExpressionStopCharactersRegex);
369 |
370 | if (!force && !backwardScanResult.string && (!baseScanResult || !baseScanResult.string)) {
371 | return;
372 | }
373 |
374 | var defaultCompletions = [];
375 |
376 | defaultCompletions = _generateJavaScriptCompletions(baseScanResult ? baseScanResult.string : null, _prefix, suffix);
377 |
378 | // TODO: add javascript default completions
379 |
380 | _completions = completionControllerCompletionsNeeded(defaultCompletions, baseScanResult ? baseScanResult.string : null, _prefix, suffix, force);
381 |
382 | var result = {
383 | completions: _completions,
384 | token_start: _startOffset,
385 | token_end: _endOffset
386 | }
387 |
388 | if (_completions.length) {
389 | return JSON.stringify(result);
390 | } else {
391 | return;
392 | }
393 | } catch (error) {
394 | return;
395 | }
396 | return;
397 | })
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/debug_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/debug_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/error_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/error_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/info_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/info_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/issue_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/issue_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/navigation_issue_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/navigation_issue_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/navigation_success_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/navigation_success_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/success_icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/success_icon@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_action_panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_action_panel.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_action_panel@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_action_panel@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_actions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_actions.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_actions@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_actions@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_prompt@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_prompt@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_prompt_previous@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_prompt_previous@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_result@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Naituw/WBWebViewConsole/2c9f5b37a7a8b875f303396addfaebef6f5b2bdf/WBWebViewConsole/Resources/WBWebBrowserConsole.bundle/userinput_result@2x.png
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/NSDictionary+WBTTypeCast.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+WBTTypeCast.h
3 | // WBTool
4 | //
5 | // Created by Wade Cheng on 8/19/13.
6 | //
7 | // Copyright (c) 2013-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | /*!
16 | * 本类主要功能是返还字典子节点的指定类型的值和遍历所有子节点让满足指定类型的子节点执行相应的block
17 | */
18 | @interface NSDictionary (WBTTypeCast)
19 | /*!
20 | * 返回当前key对应value的NSString值,没有则返回nil
21 | *
22 | * @param key 用来获取当前字典对应值的key
23 | * @return 当key对应的值为NSString类型时返回该值,没有则返回nil
24 | */
25 | - (NSString *)wbt_stringForKey:(NSString *)key;
26 |
27 | /*!
28 | * 返回当前key对应value的NSDictionary值,没有则返回nil
29 | *
30 | * @param key 用来获取当前字典对应值的key
31 | * @return 返回当前key对应value的NSDictionary值,不存在则返回Nil
32 | */
33 | - (NSDictionary *)wbt_dictForKey:(NSString *)key;
34 |
35 | /*!
36 | * 返回当前key对应value的NSArray值,没有则返回nil
37 | *
38 | * @param key 用来获取当前字典对应值的key
39 | * @return 返回当前key对应value的NSArray值,不存在则返回nil
40 | */
41 | - (NSArray *)wbt_arrayForKey:(NSString *)key;
42 |
43 | /*!
44 | * 返回当前key对应value的NSInteger值,没有则返回0
45 | *
46 | * @param key 用来获取当前字典对应值的key
47 | * @return 返回当前key对应value的NSInteger值,不存在则返回0
48 | */
49 | - (NSInteger)wbt_integerForKey:(NSString *)key;
50 |
51 | @end
52 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/NSDictionary+WBTTypeCast.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSDictionary+WBTTypeCast.m
3 | // WBTool
4 | //
5 | // Created by Wade Cheng on 8/19/13.
6 | //
7 | // Copyright (c) 2013-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import "NSDictionary+WBTTypeCast.h"
14 | #import "WBTTypeCastUtil.h"
15 |
16 | /**
17 | * 返回根据所给key值在当前字典对象上对应的值.
18 | */
19 | #define OFK [self objectForKey:key]
20 |
21 | @implementation NSDictionary (WBTTypeCast)
22 |
23 | #pragma mark - NSString
24 |
25 | - (NSString *)wbt_stringForKey:(NSString *)key defaultValue:(NSString *)defaultValue;
26 | {
27 | return wbt_stringOfValue(OFK, defaultValue);
28 | }
29 |
30 | - (NSString *)wbt_stringForKey:(NSString *)key;
31 | {
32 | return [self wbt_stringForKey:key defaultValue:nil];
33 | }
34 |
35 | #pragma mark - NSDictionary
36 |
37 | - (NSDictionary *)wbt_dictForKey:(NSString *)key defaultValue:(NSDictionary *)defaultValue
38 | {
39 | return wbt_dictOfValue(OFK, defaultValue);
40 | }
41 |
42 | - (NSDictionary *)wbt_dictForKey:(NSString *)key
43 | {
44 | return [self wbt_dictForKey:key defaultValue:nil];
45 | }
46 |
47 | - (NSDictionary *)wbt_dictionaryWithValuesForKeys:(NSArray *)keys
48 | {
49 | return [self dictionaryWithValuesForKeys:keys];
50 | }
51 |
52 | #pragma mark - NSArray
53 |
54 | - (NSArray *)wbt_arrayForKey:(NSString *)key defaultValue:(NSArray *)defaultValue
55 | {
56 | return wbt_arrayOfValue(OFK, defaultValue);
57 | }
58 |
59 | - (NSArray *)wbt_arrayForKey:(NSString *)key
60 | {
61 | return [self wbt_arrayForKey:key defaultValue:nil];
62 | }
63 |
64 | #pragma mark - NSInteger
65 |
66 | - (NSInteger)wbt_integerForKey:(NSString *)key defaultValue:(NSInteger)defaultValue;
67 | {
68 | return wbt_integerOfValue(OFK, defaultValue);
69 | }
70 |
71 | - (NSInteger)wbt_integerForKey:(NSString *)key;
72 | {
73 | return [self wbt_integerForKey:key defaultValue:0];
74 | }
75 |
76 | @end
77 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/NSObject+WBJSONKit.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+WBJSONKit.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/3/3.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface NSObject (WBJSONKit)
17 |
18 | @end
19 |
20 | @interface NSString (WBJSONKit)
21 |
22 | @property (nonatomic, copy, readonly) NSString * wb_JSONString;
23 | @property (nonatomic, copy, readonly) id wb_objectFromJSONString;
24 |
25 | @end
26 |
27 | @interface NSArray (WBJSONKit)
28 |
29 | @property (nonatomic, copy, readonly) NSString * wb_JSONString;
30 |
31 | @end
32 |
33 | @interface NSDictionary (WBJSONKit)
34 |
35 | @property (nonatomic, copy, readonly) NSString * wb_JSONString;
36 |
37 | @end
38 |
39 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/NSObject+WBJSONKit.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSObject+WBJSONKit.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/3/3.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "NSObject+WBJSONKit.h"
15 |
16 | @implementation NSObject (WBJSONKit)
17 |
18 | @end
19 |
20 | @implementation NSString (WBJSONKit)
21 |
22 | - (NSString *)wb_JSONString
23 | {
24 | return [self copy];
25 | }
26 |
27 | - (id)wb_objectFromJSONString
28 | {
29 | NSData * data = [self dataUsingEncoding:NSUTF8StringEncoding];
30 | return [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
31 | }
32 |
33 | @end
34 |
35 | @implementation NSArray (WBJSONKit)
36 |
37 | - (NSString *)wb_JSONString
38 | {
39 | NSData * data = [NSJSONSerialization dataWithJSONObject:self options:0 error:NULL];
40 |
41 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
42 | }
43 |
44 | @end
45 |
46 | @implementation NSDictionary (WBJSONKit)
47 |
48 | - (NSString *)wb_JSONString
49 | {
50 | NSData * data = [NSJSONSerialization dataWithJSONObject:self options:0 error:NULL];
51 |
52 | return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
53 | }
54 |
55 | @end
56 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIColor+WBTHelpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+WBTHelpers.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | #ifndef RGBCOLOR
16 | #define RGBCOLOR(r,g,b) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:1]
17 | #endif
18 |
19 | #ifndef RGBACOLOR
20 | #define RGBACOLOR(r,g,b,a) [UIColor colorWithRed:r/255.0 green:g/255.0 blue:b/255.0 alpha:a]
21 | #endif
22 |
23 | @interface UIColor (WBTHelpers)
24 |
25 | + (UIColor *)wbt_colorWithHexValue:(NSInteger)hex alpha:(CGFloat)alpha;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIColor+WBTHelpers.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+WBTHelpers.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "UIColor+WBTHelpers.h"
15 |
16 | @implementation UIColor (WBTHelpers)
17 |
18 | + (CGFloat)wbt_redColorFromHexRGBColor:(NSInteger)color
19 | {
20 | return (((color & 0xff0000) >> 16) / 255.0);
21 | }
22 |
23 | + (CGFloat)wbt_greenColorFromRGBColor:(NSInteger)color
24 | {
25 | return (((color & 0x00ff00) >> 8) / 255.0);
26 | }
27 |
28 | + (CGFloat)wbt_blueColorFromRGBColor:(NSInteger)color
29 | {
30 | return ((color & 0x0000ff) / 255.0);
31 | }
32 |
33 | + (UIColor *)wbt_colorWithHexValue:(NSInteger)color alpha:(CGFloat)alpha
34 | {
35 | return [UIColor colorWithRed:[UIColor wbt_redColorFromHexRGBColor:color]
36 | green:[UIColor wbt_greenColorFromRGBColor:color]
37 | blue:[UIColor wbt_blueColorFromRGBColor:color]
38 | alpha:alpha];
39 | }
40 |
41 | @end
42 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIDevice+WBTHelpers.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+WBTHelpers.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #if __IPHONE_7_0 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_7_0
14 | #define IF_IOS7_OR_GREATER(...) if ([[UIDevice currentDevice] wbt_systemMainVersion] >= 7) { __VA_ARGS__ }
15 | #else
16 | #define IF_IOS7_OR_GREATER(...)
17 | #endif
18 |
19 | #define WBAvalibleOS(os_version) ([[UIDevice currentDevice] wbt_systemMainVersion] >= os_version)
20 |
21 | #import
22 |
23 | @interface UIDevice (WBTHelpers)
24 |
25 | - (int)wbt_systemMainVersion;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIDevice+WBTHelpers.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIDevice+WBTHelpers.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "UIDevice+WBTHelpers.h"
15 |
16 | @implementation UIDevice (WBTHelpers)
17 |
18 | static int systemMainVersion = 0;
19 | - (int)wbt_systemMainVersion
20 | {
21 | if (systemMainVersion > 0) {
22 | return systemMainVersion;
23 | }
24 | systemMainVersion = [self systemVersion].intValue;
25 | return systemMainVersion;
26 | }
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIScrollView+WBTUtilities.h:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+WBTUtilities.h
3 | // WBTool
4 | //
5 | // Created by kevin on 14-7-18.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 | #import
15 |
16 | /*!
17 | * 本类主要提供了对UIScollview的相关操作
18 | */
19 | @interface UIScrollView (WBTUtilities)
20 |
21 | /*!
22 | * 判断当前contenView是否在顶部
23 | *
24 | * @return 返回YES则当前是顶部,NO则不顶部
25 | */
26 | - (BOOL)wbt_isScrolledToTop;
27 |
28 | /*!
29 | * 将contenView移动至顶部
30 | *
31 | * @param animated YES表示带动画效果,NO则不带
32 | */
33 | - (void)wbt_scrollToTopAnimated:(BOOL)animated;
34 |
35 | /*!
36 | * 判断当前contenView是否在底部
37 | *
38 | * @return 返回YES则当前是底部,NO则不在底部
39 | */
40 | - (BOOL)wbt_isScrolledToBottom;
41 |
42 | /*!
43 | * 将contenView移动至底部
44 | *
45 | * @param animated YES表示带动画效果,NO则不带
46 | */
47 | - (void)wbt_scrollToBottomAnimated:(BOOL)animated;
48 |
49 | @end
50 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIScrollView+WBTUtilities.m:
--------------------------------------------------------------------------------
1 | //
2 | // UIScrollView+WBTUtilities.m
3 | // WBTool
4 | //
5 | // Created by kevin on 14-7-18.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "UIScrollView+WBTUtilities.h"
15 |
16 | @implementation UIScrollView (WBTUtilities)
17 |
18 | - (BOOL)wbt_isScrolledToTop
19 | {
20 | return self.contentOffset.y <= self.contentInset.top;
21 | }
22 |
23 | - (void)wbt_scrollToTopAnimated:(BOOL)animated
24 | {
25 | [self setContentOffset:CGPointMake(0, -self.contentInset.top) animated:animated];
26 | }
27 |
28 | - (BOOL)wbt_isScrolledToBottom
29 | {
30 | if (self.contentOffset.y >= self.contentSize.height - self.bounds.size.height)
31 | {
32 | return YES;
33 | }
34 | return NO;
35 | }
36 |
37 | - (void)wbt_scrollToBottomAnimated:(BOOL)animated
38 | {
39 | CGRect rect = CGRectMake(self.contentOffset.x, self.contentSize.height - 1, self.contentSize.width, 1);
40 | [self scrollRectToVisible:rect animated:animated];
41 | }
42 |
43 | @end
44 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIView+WBTSizes.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014-present, Weibo, Corp.
2 | // All rights reserved.
3 | //
4 | // This source code is licensed under the BSD-style license found in the
5 | // LICENSE file in the root directory of this source tree.
6 | //
7 | // Copyright 2009-2010 Facebook
8 | //
9 | // Licensed under the Apache License, Version 2.0 (the "License");
10 | // you may not use this file except in compliance with the License.
11 | // You may obtain a copy of the License at
12 | //
13 | // http://www.apache.org/licenses/LICENSE-2.0
14 | //
15 | // Unless required by applicable law or agreed to in writing, software
16 | // distributed under the License is distributed on an "AS IS" BASIS,
17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | // See the License for the specific language governing permissions and
19 | // limitations under the License.
20 | //
21 |
22 | #import
23 |
24 | @interface UIView (WBTSizes)
25 |
26 | /*!
27 | * Shortcut for frame.origin.x.
28 | *
29 | * Sets frame.origin.x = left
30 | */
31 | @property (nonatomic) CGFloat wbtLeft;
32 |
33 | /*!
34 | * Shortcut for frame.origin.y
35 | *
36 | * Sets frame.origin.y = top
37 | */
38 | @property (nonatomic) CGFloat wbtTop;
39 |
40 | /*!
41 | * Shortcut for frame.origin.x + frame.size.width
42 | *
43 | * Sets frame.origin.x = right - frame.size.width
44 | */
45 | @property (nonatomic) CGFloat wbtRight;
46 |
47 | /*!
48 | * Shortcut for frame.origin.y + frame.size.height
49 | *
50 | * Sets frame.origin.y = bottom - frame.size.height
51 | */
52 | @property (nonatomic) CGFloat wbtBottom;
53 |
54 | /*!
55 | * Shortcut for frame.size.width
56 | *
57 | * Sets frame.size.width = width
58 | */
59 | @property (nonatomic) CGFloat wbtWidth;
60 |
61 | /*!
62 | * Shortcut for frame.size.height
63 | *
64 | * Sets frame.size.height = height
65 | */
66 | @property (nonatomic) CGFloat wbtHeight;
67 |
68 | /*!
69 | * Shortcut for center.x
70 | *
71 | * Sets center.x = centerX
72 | */
73 | @property (nonatomic) CGFloat wbtCenterX;
74 |
75 | /*!
76 | * Shortcut for center.y
77 | *
78 | * Sets center.y = centerY
79 | */
80 | @property (nonatomic) CGFloat wbtCenterY;
81 |
82 | @end
83 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/UIView+WBTSizes.m:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2014-present, Weibo, Corp.
2 | // All rights reserved.
3 | //
4 | // This source code is licensed under the BSD-style license found in the
5 | // LICENSE file in the root directory of this source tree.
6 | //
7 | // Copyright 2009-2010 Facebook
8 | //
9 | // Licensed under the Apache License, Version 2.0 (the "License");
10 | // you may not use this file except in compliance with the License.
11 | // You may obtain a copy of the License at
12 | //
13 | // http://www.apache.org/licenses/LICENSE-2.0
14 | //
15 | // Unless required by applicable law or agreed to in writing, software
16 | // distributed under the License is distributed on an "AS IS" BASIS,
17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | // See the License for the specific language governing permissions and
19 | // limitations under the License.
20 | //
21 |
22 | ///////////////////////////////////////////////////////////////////////////////////////////////////
23 | ///////////////////////////////////////////////////////////////////////////////////////////////////
24 | ///////////////////////////////////////////////////////////////////////////////////////////////////
25 |
26 | #import
27 | #import
28 | #import "UIView+WBTSizes.h"
29 |
30 | @implementation UIView (WBTSizes)
31 |
32 |
33 | ///////////////////////////////////////////////////////////////////////////////////////////////////
34 | - (CGFloat)wbtLeft {
35 | return self.frame.origin.x;
36 | }
37 |
38 |
39 | ///////////////////////////////////////////////////////////////////////////////////////////////////
40 | - (void)setWbtLeft:(CGFloat)x {
41 | CGRect frame = self.frame;
42 | frame.origin.x = x;
43 | self.frame = frame;
44 | }
45 |
46 |
47 | ///////////////////////////////////////////////////////////////////////////////////////////////////
48 | - (CGFloat)wbtTop {
49 | return self.frame.origin.y;
50 | }
51 |
52 |
53 | ///////////////////////////////////////////////////////////////////////////////////////////////////
54 | - (void)setWbtTop:(CGFloat)y {
55 | CGRect frame = self.frame;
56 | frame.origin.y = y;
57 | self.frame = frame;
58 | }
59 |
60 |
61 | ///////////////////////////////////////////////////////////////////////////////////////////////////
62 | - (CGFloat)wbtRight {
63 | return self.frame.origin.x + self.frame.size.width;
64 | }
65 |
66 |
67 | ///////////////////////////////////////////////////////////////////////////////////////////////////
68 | - (void)setWbtRight:(CGFloat)right {
69 | CGRect frame = self.frame;
70 | frame.origin.x = right - frame.size.width;
71 | self.frame = frame;
72 | }
73 |
74 |
75 | ///////////////////////////////////////////////////////////////////////////////////////////////////
76 | - (CGFloat)wbtBottom {
77 | return self.frame.origin.y + self.frame.size.height;
78 | }
79 |
80 |
81 | ///////////////////////////////////////////////////////////////////////////////////////////////////
82 | - (void)setWbtBottom:(CGFloat)bottom {
83 | CGRect frame = self.frame;
84 | frame.origin.y = bottom - frame.size.height;
85 | self.frame = frame;
86 | }
87 |
88 |
89 | ///////////////////////////////////////////////////////////////////////////////////////////////////
90 | - (CGFloat)wbtCenterX {
91 | return self.center.x;
92 | }
93 |
94 |
95 | ///////////////////////////////////////////////////////////////////////////////////////////////////
96 | - (void)setWbtCenterX:(CGFloat)centerX {
97 | self.center = CGPointMake(centerX, self.center.y);
98 | }
99 |
100 |
101 | ///////////////////////////////////////////////////////////////////////////////////////////////////
102 | - (CGFloat)wbtCenterY {
103 | return self.center.y;
104 | }
105 |
106 |
107 | ///////////////////////////////////////////////////////////////////////////////////////////////////
108 | - (void)setWbtCenterY:(CGFloat)centerY {
109 | self.center = CGPointMake(self.center.x, centerY);
110 | }
111 |
112 |
113 | ///////////////////////////////////////////////////////////////////////////////////////////////////
114 | - (CGFloat)wbtWidth {
115 | return self.frame.size.width;
116 | }
117 |
118 |
119 | ///////////////////////////////////////////////////////////////////////////////////////////////////
120 | - (void)setWbtWidth:(CGFloat)width {
121 | CGRect frame = self.frame;
122 | frame.size.width = width;
123 | self.frame = frame;
124 | }
125 |
126 |
127 | ///////////////////////////////////////////////////////////////////////////////////////////////////
128 | - (CGFloat)wbtHeight {
129 | return self.frame.size.height;
130 | }
131 |
132 |
133 | ///////////////////////////////////////////////////////////////////////////////////////////////////
134 | - (void)setWbtHeight:(CGFloat)height {
135 | CGRect frame = self.frame;
136 | frame.size.height = height;
137 | self.frame = frame;
138 | }
139 |
140 | @end
141 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBKeyboardObserver.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBKeyboardObserver.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/6/27.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 | #import
15 |
16 | extern NSString * const WBKeyboardObserverFrameDidUpdateNotification;
17 |
18 | @interface WBKeyboardObserver : NSObject
19 |
20 | + (instancetype)sharedObserver;
21 |
22 | @property (nonatomic, assign) CGRect frameBegin;
23 | @property (nonatomic, assign) CGRect frameEnd;
24 | @property (nonatomic, assign) NSTimeInterval animationDuration;
25 | @property (nonatomic, assign) UIViewAnimationCurve animationCurve;
26 |
27 | @end
28 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBKeyboardObserver.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBKeyboardObserver.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/6/27.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBKeyboardObserver.h"
15 |
16 | NSString * const WBKeyboardObserverFrameDidUpdateNotification = @"WBKeyboardObserverFrameDidUpdateNotification";
17 |
18 | @implementation WBKeyboardObserver
19 |
20 | + (void)load
21 | {
22 | [WBKeyboardObserver sharedObserver];
23 | }
24 |
25 | + (instancetype)sharedObserver
26 | {
27 | static WBKeyboardObserver * observer;
28 | static dispatch_once_t onceToken;
29 | dispatch_once(&onceToken, ^{
30 | observer = [[[self class] alloc] init];
31 | });
32 | return observer;
33 | }
34 |
35 | - (void)dealloc
36 | {
37 | [[NSNotificationCenter defaultCenter] removeObserver:self];
38 |
39 | }
40 |
41 | - (id)init
42 | {
43 | if (self = [super init])
44 | {
45 | self.frameBegin = CGRectNull;
46 | self.frameEnd = CGRectNull;
47 | self.animationDuration = 0.0;
48 | self.animationCurve = UIViewAnimationCurveEaseInOut;
49 |
50 | NSNotificationCenter * center = [NSNotificationCenter defaultCenter];
51 |
52 | [center addObserver:self selector:@selector(keyboardDidChangeNotification:) name:UIKeyboardWillChangeFrameNotification object:nil];
53 | [center addObserver:self selector:@selector(keyboardDidChangeNotification:) name:UIKeyboardWillShowNotification object:nil];
54 | [center addObserver:self selector:@selector(keyboardDidChangeNotification:) name:UIKeyboardWillHideNotification object:nil];
55 | }
56 | return self;
57 | }
58 |
59 | - (void)keyboardDidChangeNotification:(NSNotification *)notification
60 | {
61 | NSDictionary * userInfo = notification.userInfo;
62 |
63 | if (!userInfo) return;
64 |
65 | self.frameEnd = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
66 | self.frameBegin = [[userInfo objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
67 |
68 | if ([notification.name isEqual:UIKeyboardWillHideNotification]) {
69 | CGRect frameEnd = self.frameEnd;
70 | frameEnd.origin.y = MAX(self.frameEnd.origin.y, [UIScreen mainScreen].bounds.size.height);
71 | self.frameEnd = frameEnd;
72 | }
73 |
74 | self.animationDuration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
75 | self.animationCurve = [[userInfo objectForKey:UIKeyboardAnimationCurveUserInfoKey] integerValue];
76 |
77 | [[NSNotificationCenter defaultCenter] postNotificationName:WBKeyboardObserverFrameDidUpdateNotification object:self];
78 | }
79 |
80 | @end
81 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBTTypeCastUtil.h:
--------------------------------------------------------------------------------
1 | //
2 | // TypeCastUtil.h
3 | // Weibo
4 | //
5 | // Created by Wade Cheng on 8/19/13.
6 | //
7 | // Copyright (c) 2013-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 |
14 | #import
15 |
16 | /*!
17 | * 返回当前value的NSString的值,不是string则返回defaultValue
18 | *
19 | * @param value 需要判断的对象
20 | * @param defaultValue 一个默认的NSString类型值当需要判断的对象value不是NSString类型时则返回该默认值,没有则为Nil
21 | *
22 | * @return 是NSString类型返回value,不是string则返回defaultValue
23 | */
24 | NSString *wbt_stringOfValue(id value, NSString *defaultValue);
25 |
26 | /*!
27 | * 返回当前value的NSDictionary值,不是则返回defaultValue
28 | *
29 | * @param value 需要判断的对象
30 | * @param defaultValue 一个默认的NSDictionary类型值当需要判断的对象value不是NSDictionary类型时则返回该默认值,没有则为Nil
31 | *
32 | * @return 是NSDictionary类型返回value,不是则返回defaultValue
33 | */
34 | NSDictionary *wbt_dictOfValue(id value, NSDictionary *defaultValue);
35 |
36 | /*!
37 | * 返回当前value的NSArray值,不是则返回defaultValue
38 | *
39 | * @param value 需要判断的对象
40 | * @param defaultValue 一个默认的NSArray类型值当需要判断的对象value不是NSArray类型时则返回该默认值,没有则为Nil
41 | *
42 | * @return 是NSArray类型返回value,不是则返回defaultValue
43 | */
44 | NSArray *wbt_arrayOfValue(id value ,NSArray *defaultValue);
45 |
46 | /*!
47 | * 返回当前value的NSInteger值,不是则返回defaultValue
48 | *
49 | * @param value 一个默认的NSInteger类型值当需要判断的对象value不是NSInteger类型时则返回该默认值,没有则为Nil
50 | * @param defaultValue 需要返回的默认值,没有则为Nil
51 | *
52 | * @return 是NSInteger类型返回value,不是则返回defaultValue
53 | */
54 | NSInteger wbt_integerOfValue(id value, NSInteger defaultValue);
55 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBTTypeCastUtil.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBTTypeCastUtil.m
3 | // Weibo
4 | //
5 | // Created by Wade Cheng on 8/19/13.
6 | //
7 | // Copyright (c) 2013-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import "WBTTypeCastUtil.h"
14 |
15 | NSString *wbt_stringOfValue(id value, NSString *defaultValue)
16 | {
17 | if (![value isKindOfClass:[NSString class]])
18 | {
19 | if ([value isKindOfClass:[NSNumber class]])
20 | {
21 | return [value stringValue];
22 | }
23 | return defaultValue;
24 | }
25 | return value;
26 | }
27 |
28 | NSDictionary *wbt_dictOfValue(id value, NSDictionary *defaultValue)
29 | {
30 | if ([value isKindOfClass:[NSDictionary class]])
31 | return value;
32 |
33 | return defaultValue;
34 | }
35 |
36 | NSArray *wbt_arrayOfValue(id value ,NSArray *defaultValue)
37 | {
38 | if ([value isKindOfClass:[NSArray class]])
39 | return value;
40 |
41 | return defaultValue;
42 | }
43 |
44 | NSInteger wbt_integerOfValue(id value, NSInteger defaultValue)
45 | {
46 | if ([value respondsToSelector:@selector(integerValue)])
47 | return [value integerValue];
48 |
49 | return defaultValue;
50 | }
51 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBTextView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBTextView.h
3 | // Weibo
4 | //
5 | // Created by Stephen Liu on 14-3-12.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | @class WBTextView;
16 | @protocol WBTextViewDelegate
17 | @optional
18 | - (void)textViewHeightChanged:(WBTextView *)textView;
19 | - (void)textView:(WBTextView *)textView willChangeHeight:(NSInteger)height;
20 | @end
21 |
22 | @interface WBTextView : UITextView
23 | @property (nonatomic, strong)UIColor *placeHolderColor;
24 | @property (nonatomic, strong)NSString *placeHolder;
25 | @property (nonatomic, assign)BOOL wraperWithScrollView;
26 | @property (nonatomic, weak)id textDelegate;
27 | @property (nonatomic, assign)NSInteger maxNumberOfLines;
28 | @property (nonatomic, assign)NSInteger minNumberOfLines;
29 | @property (nonatomic, assign)NSInteger minHeight;
30 | @property (nonatomic, assign)NSInteger maxHeight;
31 | @property (nonatomic, assign)BOOL animateHeightChange;
32 | @end
33 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/Actions/WBJSBridgeAction.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeAction.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 | #import "WBJSBridgeMessage.h"
15 |
16 | @class WBWebViewJSBridge;
17 |
18 | @interface WBJSBridgeAction : NSObject
19 |
20 | - (instancetype)initWithBridge:(WBWebViewJSBridge *)bridge message:(WBJSBridgeMessage *)message;
21 |
22 | @property (nonatomic, weak, readonly) WBWebViewJSBridge * bridge;
23 | @property (nonatomic, strong, readonly) WBJSBridgeMessage * message;
24 |
25 | - (void)startAction;
26 |
27 | - (void)actionSuccessedWithResult:(NSDictionary *)result;
28 | - (void)actionFailed;
29 |
30 | + (Class)actionClassForActionName:(NSString *)actionName;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/Actions/WBJSBridgeAction.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeAction.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBJSBridgeAction.h"
15 | #import "WBWebViewJSBridge.h"
16 |
17 | @interface WBJSBridgeAction ()
18 |
19 | @property (nonatomic, weak) WBWebViewJSBridge * bridge;
20 | @property (nonatomic, strong) WBJSBridgeMessage * message;
21 |
22 | @end
23 |
24 | @implementation WBJSBridgeAction
25 |
26 | - (instancetype)initWithBridge:(WBWebViewJSBridge *)bridge message:(WBJSBridgeMessage *)message
27 | {
28 | if (self = [self init]) {
29 | self.bridge = bridge;
30 | self.message = message;
31 | }
32 | return self;
33 | }
34 |
35 | - (void)startAction
36 | {
37 | [self actionFailed];
38 | }
39 |
40 | - (void)actionSuccessedWithResult:(NSDictionary *)result
41 | {
42 | [self.bridge actionDidFinish:self success:YES result:result];
43 | }
44 |
45 | - (void)actionFailed
46 | {
47 | [self.bridge actionDidFinish:self success:NO result:nil];
48 | }
49 |
50 | NSString * const WBJSBridgeActionNamePrefix = @"WBJSBridgeAction";
51 |
52 | + (Class)actionClassForActionName:(NSString *)actionName
53 | {
54 | actionName = [actionName stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:[actionName substringToIndex:1].capitalizedString];
55 |
56 | NSString * actionClassName = [NSString stringWithFormat:@"%@%@", WBJSBridgeActionNamePrefix, actionName];
57 | Class klass = NSClassFromString(actionClassName);
58 |
59 | if (klass && [klass isSubclassOfClass:[WBJSBridgeAction class]]) {
60 | return klass;
61 | }
62 |
63 | return NULL;
64 | }
65 |
66 | @end
67 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/Resources/WBWebBrowserJSBridge.bundle/wbjs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2014-present, Weibo, Corp.
3 | * All rights reserved.
4 | *
5 | * This source code is licensed under the BSD-style license found in the
6 | * LICENSE file in the root directory of this source tree.
7 | */
8 |
9 | (function (config) {
10 |
11 | config = config || {};
12 |
13 | (function (root, factory) {
14 | var interface = config["interface"];
15 | if (interface && !root[interface]) {
16 | root[interface] = factory();
17 |
18 | var eventName = config["readyEvent"];
19 | var document = root['document'];
20 | if (eventName && document) {
21 | var readyEvent = document.createEvent('Events');
22 | readyEvent.initEvent(eventName);
23 | document.dispatchEvent(readyEvent);
24 | }
25 | }
26 | } (this, function () {
27 |
28 | var _callbacks = [];
29 | var _callbackIndex = 1000;
30 | var _messageQueue = [];
31 | var _invokeScheme = config['invokeScheme'];
32 |
33 | var Weibo = {
34 | invoke: function (name, params, callback) {
35 |
36 | if (typeof name !== 'string') {
37 | return;
38 | }
39 |
40 | if (!params) {
41 | params = {};
42 | }
43 |
44 | var callbackID = (_callbackIndex++).toString();
45 |
46 | if (callback) {
47 | _callbacks[callbackID] = callback;
48 | }
49 |
50 | var message = {
51 | action: name,
52 | params: params,
53 | callback_id: callbackID
54 | }
55 |
56 | _messageQueue.push(message);
57 | location.href = _invokeScheme;
58 | },
59 |
60 | _messageQueue: function () {
61 | var ret = JSON.stringify(_messageQueue);
62 | _messageQueue = [];
63 | return ret;
64 | },
65 |
66 | _handleMessage: function (message) {
67 | if (!message) {
68 | return;
69 | }
70 | var callbackID = message.callback_id;
71 | if (!callbackID) {
72 | return;
73 | }
74 |
75 | var callback = _callbacks[callbackID];
76 |
77 | if (callback) {
78 | var params = message.params;
79 | var success = !message.failed;
80 |
81 | callback(params, success);
82 | }
83 | }
84 | };
85 |
86 | return Weibo;
87 | }));
88 | })
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/WBJSBridgeMessage.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeMessage.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | @interface WBJSBridgeMessage : NSObject
16 |
17 | - (instancetype)initWithDictionary:(NSDictionary *)dict;
18 |
19 | @property (nonatomic, copy, readonly) NSString * action;
20 | @property (nonatomic, copy, readonly) NSDictionary * parameters;
21 |
22 | @property (nonatomic, copy, readonly) NSString * callbackID;
23 |
24 | @end
25 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/WBJSBridgeMessage.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBJSBridgeMessage.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBJSBridgeMessage.h"
15 | #import "NSDictionary+WBTTypeCast.h"
16 |
17 | @interface WBJSBridgeMessage ()
18 |
19 | @property (nonatomic, copy) NSString * action;
20 | @property (nonatomic, copy) NSDictionary * parameters;
21 |
22 | @property (nonatomic, copy) NSString * callbackID;
23 |
24 | @end
25 |
26 | @implementation WBJSBridgeMessage
27 |
28 | - (instancetype)initWithDictionary:(NSDictionary *)dict
29 | {
30 | if (self = [self init]) {
31 | self.action = [dict wbt_stringForKey:@"action"];
32 | self.parameters = [dict wbt_dictForKey:@"params"];
33 | self.callbackID = [dict wbt_stringForKey:@"callback_id"];
34 | }
35 | return self;
36 | }
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/WBWebViewJSBridge.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewJSBridge.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | /**
16 | * Minimal Version of Weibo's JSBridge
17 | */
18 |
19 | @protocol WBWebView;
20 | @class WBJSBridgeMessage, WBJSBridgeAction;
21 |
22 | @interface WBWebViewJSBridge : NSObject
23 |
24 | - (instancetype)initWithWebView:(id)webView;
25 |
26 | @property (nonatomic, weak, readonly) id webView;
27 |
28 | @property (nonatomic, copy) NSString * interfaceName;
29 | @property (nonatomic, copy) NSString * readyEventName;
30 | @property (nonatomic, copy) NSString * invokeScheme;
31 |
32 | - (NSString *)javascriptSource;
33 |
34 | - (BOOL)handleWebViewRequest:(NSURLRequest *)request;
35 |
36 | - (void)actionDidFinish:(WBJSBridgeAction *)action success:(BOOL)success result:(NSDictionary *)result;
37 |
38 | @end
39 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/JSBridge/WBWebViewJSBridge.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewJSBridge.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewJSBridge.h"
15 | #import "WBWebView.h"
16 | #import "WBWebViewUserScript.h"
17 | #import "WBJSBridgeMessage.h"
18 | #import "WBJSBridgeAction.h"
19 | #import "NSObject+WBJSONKit.h"
20 |
21 | @interface WBWebViewJSBridge ()
22 | {
23 | NSMutableArray *_actions;
24 |
25 | struct {
26 | unsigned int sourceNeedsUpdate: 1;
27 | } _flags;
28 | }
29 |
30 | @property (nonatomic, weak) id webView;
31 | @property (nonatomic, strong) NSString * javascriptSource;
32 |
33 | @end
34 |
35 | @implementation WBWebViewJSBridge
36 |
37 | - (instancetype)initWithWebView:(id)webView
38 | {
39 | if (self = [super init]) {
40 |
41 | _actions = [[NSMutableArray alloc] init];
42 |
43 | self.webView = webView;
44 | self.interfaceName = @"WeiboJSBridge";
45 | self.readyEventName = @"WeiboJSBridgeReady";
46 | self.invokeScheme = @"wbjs://invoke";
47 |
48 | dispatch_async(dispatch_get_main_queue(), ^{
49 | [webView wb_addUserScript:[WBWebViewUserScript scriptWithSource:self.javascriptSource injectionTime:WBUserScriptInjectionTimeAtDocumentStart mainFrameOnly:YES]];
50 | });
51 | }
52 | return self;
53 | }
54 |
55 | - (void)setInterfaceName:(NSString *)interfaceName
56 | {
57 | if (_interfaceName != interfaceName) {
58 | _interfaceName = interfaceName;
59 | _flags.sourceNeedsUpdate = YES;
60 | }
61 | }
62 |
63 | - (void)setReadyEventName:(NSString *)readyEventName
64 | {
65 | if (_readyEventName != readyEventName) {
66 | _readyEventName = readyEventName;
67 | _flags.sourceNeedsUpdate = YES;
68 | }
69 | }
70 |
71 | - (void)setInvokeScheme:(NSString *)invokeScheme
72 | {
73 | if (_invokeScheme != invokeScheme) {
74 | _invokeScheme = invokeScheme;
75 | _flags.sourceNeedsUpdate = YES;
76 | }
77 | }
78 |
79 | - (NSString *)javascriptSource
80 | {
81 | if (!_javascriptSource || _flags.sourceNeedsUpdate) {
82 |
83 | NSBundle * bundle = [NSBundle bundleWithPath:[NSString stringWithFormat:@"%@/%@", [[NSBundle bundleForClass:[self class]] bundlePath], @"WBWebBrowserJSBridge.bundle"]];
84 |
85 | _javascriptSource = [[NSString alloc] initWithContentsOfFile:[bundle pathForResource:@"wbjs" ofType:@"js"] encoding:NSUTF8StringEncoding error:NULL];
86 | NSAssert(_interfaceName, @"interfaceName must not nil");
87 | NSAssert(_readyEventName, @"readyEventName must not nil");
88 | NSAssert(_invokeScheme, @"invokeScheme must not nil");
89 |
90 | NSDictionary * config = @{@"interface": _interfaceName,
91 | @"readyEvent": _readyEventName,
92 | @"invokeScheme": _invokeScheme};
93 | NSString * json = [config wb_JSONString];
94 | _javascriptSource = [_javascriptSource stringByAppendingFormat:@"(%@)", json];
95 | }
96 | return _javascriptSource;
97 | }
98 |
99 | - (void)processMessage:(WBJSBridgeMessage *)message
100 | {
101 | WBJSBridgeAction * action = nil;
102 |
103 | Class klass = [WBJSBridgeAction actionClassForActionName:message.action];
104 |
105 | if (klass)
106 | {
107 | action = [[klass alloc] initWithBridge:self message:message];
108 | }
109 |
110 | if (action) {
111 | [_actions addObject:action];
112 | [action startAction];
113 | } else {
114 | [self sendCallbackForMessage:message success:NO result:nil];
115 | }
116 | }
117 |
118 | - (void)processMessageQueue:(NSArray *)queue
119 | {
120 | for (NSDictionary * dict in queue) {
121 | WBJSBridgeMessage * message = [[WBJSBridgeMessage alloc] initWithDictionary:dict];
122 | [self processMessage:message];
123 | }
124 | }
125 |
126 | - (void)actionDidFinish:(WBJSBridgeAction *)action success:(BOOL)success result:(NSDictionary *)result
127 | {
128 | if (![_actions containsObject:action]) return;
129 |
130 | [_actions removeObject:action];
131 |
132 | [self sendCallbackForMessage:action.message success:success result:result];
133 | }
134 |
135 | - (void)sendCallbackForMessage:(WBJSBridgeMessage *)message success:(BOOL)success result:(NSDictionary *)result
136 | {
137 | if (!message.callbackID) {
138 | return;
139 | }
140 | NSDictionary * callback = @{@"params": result ? : @{},
141 | @"failed": @(!success),
142 | @"callback_id": message.callbackID};
143 | NSString * js = [NSString stringWithFormat:@"%@._handleMessage(%@)", _interfaceName, callback.wb_JSONString];
144 | [self.webView wb_evaluateJavaScript:js completionHandler:NULL];
145 | }
146 |
147 | - (BOOL)handleWebViewRequest:(NSURLRequest *)request
148 | {
149 | NSURL * url = request.URL;
150 | if ([url.absoluteString isEqual:self.invokeScheme]) {
151 | NSString * js = [NSString stringWithFormat:@"%@._messageQueue()", _interfaceName];
152 | [_webView wb_evaluateJavaScript:js completionHandler:^(NSString * result, NSError * error) {
153 | NSArray * queue = [result wb_objectFromJSONString];
154 | if ([queue isKindOfClass:[NSArray class]]) {
155 | [self processMessageQueue:queue];
156 | }
157 | }];
158 |
159 | return YES;
160 | }
161 | return NO;
162 | }
163 |
164 | @end
165 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/WBWebView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebView.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 | #import "WBWebViewUserScript.h"
15 | #import "WBWebViewJSBridge.h"
16 |
17 | @class WBWebViewConsole;
18 |
19 | /**
20 | * Abstract version of Weibo's WBWebView
21 | * The original one is an auto-switching wrapper of UIWebView & WKWebView
22 | */
23 |
24 | @protocol WBWebView
25 |
26 | @required
27 | @property (nonatomic, strong, readonly) WBWebViewJSBridge * JSBridge;
28 | @property (nonatomic, strong, readonly) WBWebViewConsole * console;
29 |
30 | - (void)wb_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *, NSError *))completionHandler;
31 |
32 | - (void)wb_addUserScript:(WBWebViewUserScript *)userScript;
33 | - (void)wb_removeAllUserScripts;
34 |
35 | @property (nonatomic, readonly, copy) NSArray * wb_userScripts;
36 |
37 | @end
38 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/WBWebViewUserScript.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewUserScript.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/11.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #import
14 |
15 | typedef NS_ENUM(NSInteger, WBUserScriptInjectionTime) {
16 | WBUserScriptInjectionTimeAtDocumentStart,
17 | WBUserScriptInjectionTimeAtDocumentEnd
18 | };
19 |
20 | @protocol WBWebViewUserScript
21 |
22 | @property (nonatomic, copy, readonly) NSString * source;
23 | @property (nonatomic, readonly) WBUserScriptInjectionTime scriptInjectionTime;
24 | @property (nonatomic, readonly, getter=isForMainFrameOnly) BOOL forMainFrameOnly;
25 |
26 | @end
27 |
28 | @interface WBWebViewUserScript : NSObject
29 |
30 | + (id)scriptWithSource:(NSString *)source injectionTime:(WBUserScriptInjectionTime)injectionTime mainFrameOnly:(BOOL)mainFrameOnly;
31 |
32 | @end
33 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebView/WBWebViewUserScript.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewUserScript.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/11.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewUserScript.h"
15 |
16 | //#if __IPHONE_8_0 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
17 | //#if kUsesWKWebView
18 | //#import
19 | //
20 | //@interface WKUserScript (WBAdditions)
21 | //
22 | //@end
23 | //
24 | //static inline WKUserScriptInjectionTime WKInjectionTimeFromWBInjectionTime(WBUserScriptInjectionTime time)
25 | //{
26 | // switch (time)
27 | // {
28 | // case WBUserScriptInjectionTimeAtDocumentStart:
29 | // return WKUserScriptInjectionTimeAtDocumentStart;
30 | // case WBUserScriptInjectionTimeAtDocumentEnd:
31 | // default:
32 | // return WKUserScriptInjectionTimeAtDocumentEnd;
33 | // }
34 | //}
35 | //
36 | //static inline WBUserScriptInjectionTime WBInjectionTimeFromWKInjectionTime(WKUserScriptInjectionTime time)
37 | //{
38 | // switch (time)
39 | // {
40 | // case WKUserScriptInjectionTimeAtDocumentStart:
41 | // return WBUserScriptInjectionTimeAtDocumentStart;
42 | // case WKUserScriptInjectionTimeAtDocumentEnd:
43 | // default:
44 | // return WBUserScriptInjectionTimeAtDocumentEnd;
45 | // }
46 | //}
47 | //
48 | //#endif
49 | //#endif
50 |
51 | @implementation WBWebViewUserScript
52 |
53 |
54 | - (instancetype)initWithSource:(NSString *)source scriptInjectionTime:(WBUserScriptInjectionTime)injectionTime forMainFrameOnly:(BOOL)forMainFrameOnly
55 | {
56 | if (self = [self init])
57 | {
58 | _source = [source copy];
59 | _scriptInjectionTime = injectionTime;
60 | _forMainFrameOnly = forMainFrameOnly;
61 | }
62 | return self;
63 | }
64 |
65 | + (id)scriptWithSource:(NSString *)source injectionTime:(WBUserScriptInjectionTime)injectionTime mainFrameOnly:(BOOL)mainFrameOnly
66 | {
67 | //#if kUsesWKWebView
68 | // IF_IOS8_OR_GREATER({
69 | // return [[[WKUserScript alloc] initWithSource:source injectionTime:WKInjectionTimeFromWBInjectionTime(injectionTime) forMainFrameOnly:YES] autorelease];
70 | // })
71 | //#endif
72 |
73 | return [[self alloc] initWithSource:source scriptInjectionTime:injectionTime forMainFrameOnly:mainFrameOnly];
74 | }
75 |
76 | @synthesize source = _source;
77 | @synthesize scriptInjectionTime = _scriptInjectionTime;
78 | @synthesize forMainFrameOnly = _forMainFrameOnly;
79 | @end
80 |
81 | //#if __IPHONE_8_0 && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_8_0
82 | //#if kUsesWKWebView
83 | //
84 | //@implementation WKUserScript (WBAdditions)
85 | //
86 | //- (WBUserScriptInjectionTime)scriptInjectionTime
87 | //{
88 | // return WBInjectionTimeFromWKInjectionTime(self.injectionTime);
89 | //}
90 | //
91 | //@end
92 | //
93 | //#endif
94 | //#endif
95 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Supports/WBWebViewConsoleDefines.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleDefines.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 2/13/15.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 |
13 | #ifndef WBWebViewConsole_WBWebViewConsoleDefines_h
14 | #define WBWebViewConsole_WBWebViewConsoleDefines_h
15 |
16 | #import "WBWebViewConsole.h"
17 |
18 | inline static NSBundle * WBWebBrowserConsoleBundle()
19 | {
20 | return [NSBundle bundleWithPath:[NSString stringWithFormat:@"%@/%@", [NSBundle bundleForClass:[WBWebViewConsole class]].bundlePath, @"WBWebBrowserConsole.bundle"]];
21 | }
22 |
23 | #endif
24 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Views/WBWebViewConsoleInputView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleInputView.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/24.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import "WBTextView.h"
16 |
17 | @class WBWebViewConsole;
18 | @protocol WBWebViewConsoleInputViewDelegate;
19 |
20 | @interface WBWebViewConsoleInputView : UIView
21 |
22 | @property (nonatomic, weak) id delegate;
23 |
24 | @property (nonatomic, strong) NSString * text;
25 | @property (nonatomic, strong, readonly) WBTextView * textView;
26 | @property (nonatomic, strong) WBWebViewConsole * console;
27 |
28 | - (void)setFont:(UIFont *)font;
29 |
30 | @property (nonatomic, assign, readonly) CGFloat desiredHeight;
31 |
32 | - (void)completePromptWithSuggestion:(NSString *)suggestion;
33 |
34 | @end
35 |
36 | @protocol WBWebViewConsoleInputViewDelegate
37 |
38 | - (void)consoleInputViewHeightChanged:(WBWebViewConsoleInputView *)inputView;
39 | - (void)consoleInputViewDidBeginEditing:(WBWebViewConsoleInputView *)inputView;
40 | - (void)consoleInputView:(WBWebViewConsoleInputView *)inputView didCommitCommand:(NSString *)command;
41 |
42 | @end
43 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Views/WBWebViewConsoleInputViewActionButton.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleInputViewActionButton.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/28.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @interface WBWebViewConsoleInputViewActionButton : UIView
17 |
18 | @property (nonatomic, weak) UIResponder * responder;
19 |
20 | @property (nonatomic, assign) SEL previousHistorySelector;
21 | @property (nonatomic, assign) SEL nextHistorySelector;
22 | @property (nonatomic, assign) SEL dismissKeyboardSelector;
23 | @property (nonatomic, assign) SEL newlineSelector;
24 |
25 | @end
--------------------------------------------------------------------------------
/WBWebViewConsole/Views/WBWebViewConsoleInputViewActionButton.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleInputViewActionButton.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/28.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleInputViewActionButton.h"
15 | #import "UIColor+WBTHelpers.h"
16 | #import "UIView+WBTSizes.h"
17 | #import "WBWebViewConsoleDefines.h"
18 |
19 | @interface WBWebViewConsoleInputActionPanel : UIImageView
20 |
21 | @property (nonatomic, weak) WBWebViewConsoleInputViewActionButton * actionButton;
22 |
23 | - (void)reloadData;
24 |
25 | @property (nonatomic, strong) NSArray * buttons;
26 |
27 | - (id)initWithActionButton:(WBWebViewConsoleInputViewActionButton *)actionButton;
28 |
29 | - (void)highlightPoint:(CGPoint)point;
30 | - (void)selectPoint:(CGPoint)point;
31 |
32 | @end
33 |
34 | @interface WBWebViewConsoleInputViewActionButton ()
35 |
36 | @property (nonatomic, strong) UIImageView * iconImageView;
37 | @property (nonatomic, strong) WBWebViewConsoleInputActionPanel * actionPanel;
38 |
39 | @end
40 |
41 | @implementation WBWebViewConsoleInputViewActionButton
42 |
43 | - (void)dealloc
44 | {
45 |
46 | [_actionPanel setActionButton:nil];
47 | }
48 |
49 | - (id)initWithFrame:(CGRect)frame
50 | {
51 | if (self = [super initWithFrame:frame])
52 | {
53 | self.iconImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"userinput_actions.png" inBundle:WBWebBrowserConsoleBundle() compatibleWithTraitCollection:nil]];
54 | self.iconImageView.contentMode = UIViewContentModeCenter;
55 |
56 | [self addSubview:self.iconImageView];
57 |
58 | {
59 | UIPanGestureRecognizer * gesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
60 | [self addGestureRecognizer:gesture];
61 | }
62 |
63 | {
64 | UILongPressGestureRecognizer * gesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(panGesture:)];
65 | gesture.minimumPressDuration = 0.0;
66 | [self addGestureRecognizer:gesture];
67 | }
68 | }
69 | return self;
70 | }
71 |
72 | - (void)layoutSubviews
73 | {
74 | [super layoutSubviews];
75 |
76 | self.iconImageView.frame = self.bounds;
77 | }
78 |
79 | - (void)panGesture:(UIGestureRecognizer *)gestureRecognizer
80 | {
81 | UIGestureRecognizerState state = gestureRecognizer.state;
82 | CGPoint point = [gestureRecognizer locationInView:self.actionPanel];
83 |
84 | switch (state)
85 | {
86 | case UIGestureRecognizerStateBegan:
87 | {
88 | [self showActionPanel];
89 | break;
90 | }
91 | case UIGestureRecognizerStateChanged:
92 | {
93 | [self.actionPanel highlightPoint:point];
94 | break;
95 | }
96 | case UIGestureRecognizerStateEnded:
97 | {
98 | [self.actionPanel selectPoint:point];
99 | [self hideActionPanel];
100 | break;
101 | }
102 | case UIGestureRecognizerStateCancelled:
103 | case UIGestureRecognizerStateFailed:
104 | {
105 | [self hideActionPanel];
106 | break;
107 | }
108 | default:
109 | break;
110 | }
111 | }
112 |
113 | #pragma mark - Action Panel
114 |
115 | - (WBWebViewConsoleInputActionPanel *)actionPanel
116 | {
117 | if (!_actionPanel)
118 | {
119 | _actionPanel = [[WBWebViewConsoleInputActionPanel alloc] initWithActionButton:self];
120 | _actionPanel.layer.zPosition = 10;
121 | }
122 | return _actionPanel;
123 | }
124 |
125 | - (void)showActionPanel
126 | {
127 | if (!self.actionPanel.superview)
128 | {
129 | CGRect frame = CGRectMake(0, 0, 140, 198);
130 | frame.origin.x = self.wbtWidth - frame.size.width + 5;
131 | frame.origin.y = self.wbtHeight - frame.size.height + 10;
132 |
133 | frame = [self.window convertRect:frame fromView:self];
134 |
135 | _actionPanel.frame = frame;
136 |
137 | [self.window addSubview:_actionPanel];
138 | }
139 |
140 | [_actionPanel reloadData];
141 | }
142 |
143 | - (void)hideActionPanel
144 | {
145 | [_actionPanel removeFromSuperview];
146 | }
147 |
148 | @end
149 |
150 | @interface WBWebViewConsoleInputActionPanelButton : UIView
151 |
152 | @property (nonatomic, assign) SEL selector;
153 |
154 | @property (nonatomic, strong) UILabel * textLabel;
155 | @property (nonatomic, strong) UIView * seperatorView;
156 |
157 | @property (nonatomic, assign) BOOL highlighted;
158 | @property (nonatomic, assign) BOOL disabled;
159 |
160 | @end
161 |
162 | @interface WBWebViewConsoleInputActionPanel ()
163 |
164 | @property (nonatomic, strong) UIView * buttonContainerView; // for corner radius
165 |
166 | @end
167 |
168 | @implementation WBWebViewConsoleInputActionPanel
169 |
170 |
171 | - (id)initWithActionButton:(WBWebViewConsoleInputViewActionButton *)actionButton
172 | {
173 | if (self = [super initWithFrame:CGRectZero])
174 | {
175 | self.actionButton = actionButton;
176 |
177 | self.image = [[UIImage imageNamed:@"userinput_action_panel.png" inBundle:WBWebBrowserConsoleBundle() compatibleWithTraitCollection:nil] stretchableImageWithLeftCapWidth:25 topCapHeight:25];
178 |
179 | NSMutableArray * buttons = [NSMutableArray array];
180 |
181 | {
182 | WBWebViewConsoleInputActionPanelButton * button = [[WBWebViewConsoleInputActionPanelButton alloc] initWithFrame:CGRectZero];
183 |
184 | button.textLabel.text = NSLocalizedString(@"Toggle Keyboard", nil);
185 | button.selector = self.actionButton.dismissKeyboardSelector;
186 |
187 | [buttons addObject:button];
188 | }
189 |
190 | {
191 | WBWebViewConsoleInputActionPanelButton * button = [[WBWebViewConsoleInputActionPanelButton alloc] initWithFrame:CGRectZero];
192 |
193 | button.textLabel.text = NSLocalizedString(@"Next", nil);
194 | button.selector = self.actionButton.nextHistorySelector;
195 |
196 | [buttons addObject:button];
197 | }
198 |
199 | {
200 | WBWebViewConsoleInputActionPanelButton * button = [[WBWebViewConsoleInputActionPanelButton alloc] initWithFrame:CGRectZero];
201 |
202 | button.textLabel.text = NSLocalizedString(@"Previous", nil);
203 | button.selector = self.actionButton.previousHistorySelector;
204 |
205 | [buttons addObject:button];
206 | }
207 |
208 | {
209 | WBWebViewConsoleInputActionPanelButton * button = [[WBWebViewConsoleInputActionPanelButton alloc] initWithFrame:CGRectZero];
210 |
211 | button.textLabel.text = NSLocalizedString(@"New Line", nil);
212 | button.selector = self.actionButton.newlineSelector;
213 | button.seperatorView.hidden = YES;
214 |
215 | [buttons addObject:button];
216 | }
217 |
218 | self.buttons = buttons;
219 |
220 | self.buttonContainerView = [[UIView alloc] initWithFrame:self.bounds];
221 | self.buttonContainerView.layer.masksToBounds = YES;
222 | self.buttonContainerView.layer.cornerRadius = 6.0;
223 |
224 | [self addSubview:self.buttonContainerView];
225 | }
226 | return self;
227 | }
228 |
229 | - (void)reloadData
230 | {
231 | [self layoutSubviews];
232 |
233 | CGFloat width = self.buttonContainerView.wbtWidth;
234 | CGFloat height = 36;
235 | CGFloat baseY = 0;
236 |
237 | for (WBWebViewConsoleInputActionPanelButton * button in _buttons)
238 | {
239 | [_buttonContainerView addSubview:button];
240 |
241 | button.frame = CGRectMake(0, baseY, width, height);
242 | baseY += height;
243 |
244 | button.disabled = ![self.actionButton.responder canPerformAction:button.selector withSender:self.actionButton];
245 | button.highlighted = NO;
246 |
247 | [button layoutSubviews];
248 | }
249 | }
250 |
251 | - (void)layoutSubviews
252 | {
253 | [super layoutSubviews];
254 |
255 | self.buttonContainerView.frame = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(4, 10, 45, 10));
256 | }
257 |
258 | - (void)highlightPoint:(CGPoint)point
259 | {
260 | point = [_buttonContainerView convertPoint:point fromView:self];
261 |
262 | for (WBWebViewConsoleInputActionPanelButton * button in _buttons)
263 | {
264 | button.highlighted = (CGRectContainsPoint(button.frame, point));
265 | }
266 | }
267 |
268 | - (void)selectPoint:(CGPoint)point
269 | {
270 | point = [_buttonContainerView convertPoint:point fromView:self];
271 |
272 | for (WBWebViewConsoleInputActionPanelButton * button in _buttons)
273 | {
274 | button.highlighted = NO;
275 |
276 | if (CGRectContainsPoint(button.frame, point))
277 | {
278 | if (!button.disabled)
279 | {
280 | [[UIApplication sharedApplication] sendAction:button.selector to:self.actionButton.responder from:self.actionButton forEvent:nil];
281 | }
282 | return;
283 | }
284 | }
285 | }
286 |
287 | @end
288 |
289 | @implementation WBWebViewConsoleInputActionPanelButton
290 |
291 |
292 | - (id)initWithFrame:(CGRect)frame
293 | {
294 | if (self = [super initWithFrame:frame])
295 | {
296 | self.textLabel = [[UILabel alloc] initWithFrame:self.bounds];
297 | self.textLabel.backgroundColor = [UIColor clearColor];
298 | self.textLabel.font = [UIFont systemFontOfSize:14];
299 | self.textLabel.textColor = [UIColor blackColor];
300 | self.textLabel.textAlignment = NSTextAlignmentCenter;
301 | [self addSubview:self.textLabel];
302 |
303 | self.seperatorView = [[UIView alloc] initWithFrame:CGRectZero];
304 | self.seperatorView.backgroundColor = [UIColor wbt_colorWithHexValue:0xDFDFDF alpha:1.0];
305 | [self addSubview:self.seperatorView];
306 | }
307 | return self;
308 | }
309 |
310 | - (void)layoutSubviews
311 | {
312 | [super layoutSubviews];
313 |
314 | self.textLabel.frame = self.bounds;
315 |
316 | CGFloat height = 1 / [UIScreen mainScreen].scale;
317 |
318 | self.seperatorView.frame = CGRectMake(0, self.wbtHeight - height, self.wbtWidth, height);
319 | }
320 |
321 | - (void)setHighlighted:(BOOL)highlighted
322 | {
323 | if (_highlighted != highlighted)
324 | {
325 | _highlighted = highlighted;
326 |
327 | [self updateStates];
328 | }
329 | }
330 |
331 | - (void)setDisabled:(BOOL)disabled
332 | {
333 | if (_disabled != disabled)
334 | {
335 | _disabled = disabled;
336 |
337 | [self updateStates];
338 | }
339 | }
340 |
341 | - (void)updateStates
342 | {
343 | self.backgroundColor = (!_disabled && _highlighted) ? [UIColor wbt_colorWithHexValue:0x0076FF alpha:1.0] : [UIColor clearColor];
344 | self.textLabel.textColor = _disabled ? [UIColor wbt_colorWithHexValue:0xBDBDBD alpha:1.0] : (_highlighted ? [UIColor whiteColor] : [UIColor wbt_colorWithHexValue:0x565656 alpha:1.0]);
345 | }
346 |
347 | @end
348 |
--------------------------------------------------------------------------------
/WBWebViewConsole/Views/WBWebViewConsoleMessageCell.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleMessageCell.h
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 |
16 | @class WBWebViewConsoleMessage;
17 | @protocol WBWebViewConsoleMessageCellDelegate;
18 |
19 | @interface WBWebViewConsoleMessageCell : UITableViewCell
20 |
21 | @property (nonatomic, strong) WBWebViewConsoleMessage * message;
22 | @property (nonatomic, assign) BOOL cleared;
23 |
24 | @property (nonatomic, weak) id delegate;
25 |
26 | + (UIFont *)messageFont;
27 | + (CGFloat)rowHeightOfDataObject:(WBWebViewConsoleMessage *)message tableView:(UITableView *)tableView;
28 |
29 | @end
30 |
31 | @protocol WBWebViewConsoleMessageCellDelegate
32 |
33 | - (void)consoleMessageCell:(WBWebViewConsoleMessageCell *)cell copyMessageToInputView:(WBWebViewConsoleMessage *)message;
34 |
35 | @end
--------------------------------------------------------------------------------
/WBWebViewConsole/Views/WBWebViewConsoleMessageCell.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewConsoleMessageCell.m
3 | // Weibo
4 | //
5 | // Created by Wutian on 14/7/23.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBWebViewConsoleMessageCell.h"
15 | #import "WBWebViewConsoleMessage.h"
16 | #import "UIDevice+WBTHelpers.h"
17 | #import "UIColor+WBTHelpers.h"
18 | #import "UIView+WBTSizes.h"
19 | #import "WBWebViewConsoleDefines.h"
20 |
21 | @interface WBWebViewConsoleMessageCell ()
22 |
23 | @property (nonatomic, strong) UILabel * messageLabel;
24 | @property (nonatomic, strong) UILabel * callerLabel;
25 |
26 | @property (nonatomic, strong) UIImageView * iconImageView;
27 |
28 | @end
29 |
30 | @implementation WBWebViewConsoleMessageCell
31 |
32 | - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
33 | {
34 | if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])
35 | {
36 | // self.selectionStyle = UITableViewCellSelectionStyleNone;
37 | UIView * selectionView = [[UIView alloc] initWithFrame:CGRectZero];
38 | selectionView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
39 | self.selectedBackgroundView = selectionView;
40 |
41 | self.messageLabel = [[UILabel alloc] initWithFrame:CGRectZero];
42 | self.messageLabel.font = [[self class] messageFont];
43 | self.messageLabel.textColor = [UIColor blackColor];
44 | self.messageLabel.backgroundColor = [UIColor clearColor];
45 | self.messageLabel.numberOfLines = 0;
46 | self.messageLabel.wbtWidth = [UIScreen mainScreen].bounds.size.width - 40;
47 | // self.messageLabel.lineBreakMode = NSLineBreakByCharWrapping;
48 |
49 | [self.contentView addSubview:self.messageLabel];
50 |
51 | self.iconImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
52 | self.iconImageView.contentMode = UIViewContentModeCenter;
53 | self.iconImageView.backgroundColor = [UIColor clearColor];
54 |
55 | [self.contentView addSubview:self.iconImageView];
56 |
57 | self.callerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
58 | self.callerLabel.font = [[self class] messageFont];
59 | self.callerLabel.backgroundColor = [UIColor clearColor];
60 | self.callerLabel.numberOfLines = 0;
61 | self.callerLabel.wbtWidth = [UIScreen mainScreen].bounds.size.width - 40;
62 | // self.callerLabel.lineBreakMode = NSLineBreakByCharWrapping;
63 |
64 | [self.contentView addSubview:self.callerLabel];
65 | }
66 | return self;
67 | }
68 |
69 | - (BOOL)usesCustomSelectedBackgroundView
70 | {
71 | return NO;
72 | }
73 |
74 | + (UIFont *)messageFont
75 | {
76 | if (WBAvalibleOS(7))
77 | {
78 | return [UIFont fontWithName:@"Menlo" size:11];
79 | }
80 | return [UIFont fontWithName:@"Courier" size:11];
81 | }
82 |
83 | + (CGFloat)rowHeightOfDataObject:(WBWebViewConsoleMessage *)message tableView:(UITableView *)tableView
84 | {
85 | if (message.type == WBWebViewConsoleMessageTypeClear)
86 | {
87 | return 0;
88 | }
89 |
90 | CGFloat width = tableView.frame.size.width - 40;
91 |
92 | CGFloat height = 5; // top padding
93 |
94 | UIFont * font = [self messageFont];
95 |
96 | CGFloat messageHeight = ceil([message.message sizeWithFont:font constrainedToSize:CGSizeMake(width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping].height);
97 | height += messageHeight;
98 |
99 | NSString * caller = message.caller;
100 | if (caller.length)
101 | {
102 | height += 2; // padding
103 |
104 | CGFloat callerHeight = ceil([message.caller sizeWithFont:font constrainedToSize:CGSizeMake(width, CGFLOAT_MAX) lineBreakMode:NSLineBreakByWordWrapping].height);
105 | height += callerHeight;
106 | }
107 |
108 | height += 5; // bottom padding
109 |
110 | return height;
111 | }
112 |
113 | - (void)layoutSubviews
114 | {
115 | [super layoutSubviews];
116 |
117 | self.messageLabel.frame = CGRectMake(30, 5, self.frame.size.width - 40, self.messageLabel.wbtHeight);
118 | self.callerLabel.frame = CGRectMake(30, self.messageLabel.wbtBottom + 2, self.frame.size.width - 40, self.callerLabel.wbtHeight);
119 | self.iconImageView.frame = CGRectMake(0, 0, 30, 24);
120 | }
121 |
122 | //- (void)setHighlighted:(BOOL)highlighted animated:(BOOL)animated
123 | //{
124 | // [super setHighlighted:highlighted animated:animated];
125 | //
126 | // self.contentView.backgroundColor = highlighted ? [UIColor colorWithWhite:0.95 alpha:1.0] : [UIColor whiteColor];
127 | //}
128 |
129 | - (void)setMessage:(WBWebViewConsoleMessage *)message
130 | {
131 | if (_message != message)
132 | {
133 | _message = message;
134 |
135 | self.messageLabel.text = message.message;
136 | self.callerLabel.text = message.caller;
137 |
138 | [self.messageLabel sizeToFit];
139 | [self.callerLabel sizeToFit];
140 |
141 | [self updateMessageStyle];
142 | }
143 | }
144 |
145 | - (void)setCleared:(BOOL)cleared
146 | {
147 | if (_cleared != cleared)
148 | {
149 | _cleared = cleared;
150 |
151 | [self updateMessageStyle];
152 | }
153 | }
154 |
155 | - (void)updateMessageStyle
156 | {
157 | UIColor * color = nil;
158 | NSString * iconName = nil;
159 | UIColor * callerColor = [UIColor grayColor];
160 |
161 | if (_cleared)
162 | {
163 | color = [UIColor colorWithWhite:0.0 alpha:0.2];
164 | callerColor = [UIColor colorWithWhite:0.0 alpha:0.15];
165 | }
166 | else
167 | {
168 | WBWebViewConsoleMessageLevel level = _message.level;
169 | WBWebViewConsoleMessageSource source = _message.source;
170 |
171 | if (source == WBWebViewConsoleMessageSourceUserCommand)
172 | {
173 | color = RGBCOLOR(77, 153, 255);
174 | iconName = @"userinput_prompt_previous";
175 | }
176 | else if (source == WBWebViewConsoleMessageSourceUserCommandResult)
177 | {
178 | color = RGBCOLOR(0, 80, 255);
179 | iconName = @"userinput_result";
180 | }
181 | else
182 | {
183 | switch (level) {
184 | case WBWebViewConsoleMessageLevelDebug:
185 | color = [UIColor blueColor];
186 | iconName = @"debug_icon";
187 | break;
188 | case WBWebViewConsoleMessageLevelError:
189 | color = [UIColor redColor];
190 | iconName = @"error_icon";
191 | break;
192 | case WBWebViewConsoleMessageLevelWarning:
193 | color = [UIColor blackColor];
194 | iconName = @"issue_icon";
195 | if (source == WBWebViewConsoleMessageSourceNavigation)
196 | {
197 | iconName = @"navigation_issue_icon";
198 | color = RGBCOLOR(246, 187, 14);
199 | }
200 | break;
201 | case WBWebViewConsoleMessageLevelNone:
202 | color = [UIColor colorWithWhite:0.0 alpha:0.2];
203 | break;
204 | case WBWebViewConsoleMessageLevelInfo:
205 | color = [UIColor colorWithWhite:0.0 alpha:0.5];
206 | iconName = @"info_icon";
207 | break;
208 | case WBWebViewConsoleMessageLevelSuccess:
209 | color = RGBCOLOR(0, 148, 10);
210 | iconName = @"success_icon";
211 | if (source == WBWebViewConsoleMessageSourceNavigation)
212 | {
213 | iconName = @"navigation_success_icon";
214 | }
215 | break;
216 | case WBWebViewConsoleMessageLevelLog:
217 | default:
218 | color = [UIColor blackColor];
219 | break;
220 | }
221 | }
222 |
223 | if ([_message.message isEqual:@"undefined"] ||
224 | [_message.message isEqual:@"null"])
225 | {
226 | if (source != WBWebViewConsoleMessageSourceUserCommand)
227 | {
228 | color = [UIColor colorWithWhite:0.0 alpha:0.3];
229 | callerColor = [UIColor colorWithWhite:0.0 alpha:0.2];
230 | }
231 | }
232 | }
233 |
234 | self.messageLabel.textColor = color;
235 | self.callerLabel.textColor = callerColor;
236 | self.iconImageView.image = nil;
237 |
238 | if (iconName)
239 | {
240 | self.iconImageView.image = [UIImage imageNamed:iconName inBundle:WBWebBrowserConsoleBundle() compatibleWithTraitCollection:nil];
241 | }
242 | }
243 |
244 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
245 | {
246 | return (action == @selector(copy:) || action == @selector(copyToInputView:));
247 | }
248 |
249 | - (void)copyToInputView:(id)sender
250 | {
251 | if ([_delegate respondsToSelector:@selector(consoleMessageCell:copyMessageToInputView:)])
252 | {
253 | [_delegate consoleMessageCell:self copyMessageToInputView:_message];
254 | }
255 | }
256 |
257 | @end
258 |
--------------------------------------------------------------------------------
/WBWebViewConsoleTests/Base/WBTestsUIWebView.h:
--------------------------------------------------------------------------------
1 | //
2 | // WBTestsUIWebView.h
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/3/1.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import "WBWebView.h"
16 |
17 | @interface WBTestsUIWebView : UIWebView
18 |
19 | - (BOOL)syncLoadHTMLString:(NSString *)html error:(NSError **)error;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/WBWebViewConsoleTests/Base/WBTestsUIWebView.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBTestsUIWebView.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/3/1.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import "WBTestsUIWebView.h"
15 | #import "WBWebViewConsole.h"
16 |
17 | @class WBTestsUIWebView;
18 |
19 | @interface NSError (WBTestsUIWebView)
20 |
21 | - (BOOL)wb_isWebViewMainFrameError:(WBTestsUIWebView *)webView;
22 | - (BOOL)wb_shouldDisplayToWebViewUsers:(WBTestsUIWebView *)webView;
23 |
24 | @end
25 |
26 | @interface WBTestsUIWebView ()
27 |
28 | @property (nonatomic, strong) NSMutableArray * userScripts;
29 | @property (nonatomic, strong) WBWebViewJSBridge * JSBridge;
30 | @property (nonatomic, strong) WBWebViewConsole * console;
31 |
32 | @property (nonatomic, strong) NSURLRequest * loadingRequest;
33 | @property (nonatomic, strong) NSError * loadingError;
34 | @property (nonatomic, assign) NSInteger loadingCount;
35 |
36 | @property (nonatomic, assign) BOOL pendingRequest;
37 |
38 | @end
39 |
40 | @implementation WBTestsUIWebView
41 |
42 | - (instancetype)init
43 | {
44 | if (self = [self initWithFrame:CGRectMake(0, 0, 10, 10)]) {
45 |
46 | }
47 | return self;
48 | }
49 |
50 | - (instancetype)initWithFrame:(CGRect)frame
51 | {
52 | if (self = [super initWithFrame:frame]) {
53 | self.delegate = self;
54 | self.JSBridge = [[WBWebViewJSBridge alloc] initWithWebView:self];
55 | self.console = [[WBWebViewConsole alloc] initWithWebView:self];
56 | }
57 | return self;
58 | }
59 |
60 | - (NSMutableArray *)userScripts
61 | {
62 | if (!_userScripts) {
63 | _userScripts = [NSMutableArray array];
64 | }
65 | return _userScripts;
66 | }
67 |
68 | - (void)wb_addUserScript:(WBWebViewUserScript *)userScript
69 | {
70 | if (!userScript) {
71 | return;
72 | }
73 | [self.userScripts addObject:userScript];
74 | }
75 |
76 | - (void)wb_removeAllUserScripts
77 | {
78 | [_userScripts removeAllObjects];
79 | }
80 |
81 | - (NSArray *)wb_userScripts
82 | {
83 | return [_userScripts copy];
84 | }
85 |
86 | - (void)injectUserScripts
87 | {
88 | for (WBWebViewUserScript * script in _userScripts) {
89 | [self stringByEvaluatingJavaScriptFromString:script.source];
90 | }
91 | }
92 |
93 | - (void)wb_evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(NSString *, NSError *))completionHandler
94 | {
95 | NSString * result = [self stringByEvaluatingJavaScriptFromString:javaScriptString];
96 |
97 | if (completionHandler) {
98 | completionHandler(result, nil);
99 | }
100 | }
101 |
102 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
103 | {
104 | BOOL result = YES;
105 |
106 | if ([self.JSBridge handleWebViewRequest:request]) {
107 | result = NO;
108 | }
109 |
110 | if (result) {
111 | self.loadingRequest = request;
112 | }
113 |
114 | return result;
115 | }
116 |
117 | - (void)webViewDidStartLoad:(UIWebView *)webView
118 | {
119 | self.loadingCount++;
120 | }
121 |
122 | - (void)webViewDidFinishLoad:(UIWebView *)webView
123 | {
124 | [self injectUserScripts];
125 |
126 | self.loadingCount--;
127 | }
128 |
129 | - (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
130 | {
131 | if ([error wb_shouldDisplayToWebViewUsers:self]) {
132 | self.loadingError = error;
133 | }
134 | self.loadingCount--;
135 | }
136 |
137 | - (void)setLoadingCount:(NSInteger)loadingCount
138 | {
139 | if (_loadingCount != loadingCount) {
140 | _loadingCount = MAX(loadingCount, 0);
141 |
142 | if (loadingCount == 0) {
143 | _pendingRequest = NO;
144 | }
145 | }
146 | }
147 |
148 | - (BOOL)syncLoadHTMLString:(NSString *)html error:(NSError *__autoreleasing *)error
149 | {
150 | self.pendingRequest = YES;
151 | [self loadHTMLString:html baseURL:nil];
152 |
153 | while (_pendingRequest) {
154 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
155 | }
156 |
157 | if (error && _loadingError) {
158 | *error = _loadingError;
159 | }
160 |
161 | return _loadingError == nil;
162 | }
163 |
164 | @end
165 |
166 | @implementation NSError (WBTestsUIWebView)
167 |
168 | - (BOOL)wb_isWebViewMainFrameError:(WBTestsUIWebView *)webView
169 | {
170 | NSString * url = [[self userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey];
171 | NSString * host = [[NSURL URLWithString:url] host];
172 |
173 | if (![host isEqual:webView.request.URL.host] &&
174 | ![host isEqual:webView.loadingRequest.URL.host])
175 | {
176 | return NO;
177 | }
178 |
179 | return YES;
180 | }
181 |
182 | - (BOOL)wb_shouldDisplayToWebViewUsers:(WBTestsUIWebView *)webView
183 | {
184 | if (![self wb_isWebViewMainFrameError:webView]) {
185 | return NO;
186 | }
187 |
188 | if ([self.domain isEqual:NSURLErrorDomain])
189 | {
190 | if (self.code == NSURLErrorCancelled)
191 | {
192 | // interrupted by http redirect, etc.
193 | return NO;
194 | }
195 |
196 | return YES;
197 | }
198 | else if ([self.domain isEqual:@"WebKitErrorDomain"])
199 | {
200 | if (self.code == 102)
201 | {
202 | // interrupted by urlAction, JSBridge, etc.
203 | return NO;
204 | }
205 | else if (self.code == 204)
206 | {
207 | // interrupted by plugIn, such as movie(music) player
208 | return NO;
209 | }
210 |
211 | return YES;
212 | }
213 |
214 | return NO;
215 | }
216 |
217 | @end
218 |
--------------------------------------------------------------------------------
/WBWebViewConsoleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | com.sina.$(PRODUCT_NAME:rfc1034identifier)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/WBWebViewConsoleTests/WBWebViewJSBridgeTests.m:
--------------------------------------------------------------------------------
1 | //
2 | // WBWebViewJSBridgeTests.m
3 | // WBWebViewConsole
4 | //
5 | // Created by 吴天 on 15/3/1.
6 | //
7 | // Copyright (c) 2014-present, Weibo, Corp.
8 | // All rights reserved.
9 | //
10 | // This source code is licensed under the BSD-style license found in the
11 | // LICENSE file in the root directory of this source tree.
12 | //
13 |
14 | #import
15 | #import
16 | #import "WBTestsUIWebView.h"
17 | #import "WBJSBridgeAction.h"
18 | #import "NSObject+WBJSONKit.h"
19 |
20 | NSString * const WBJSBridgeFinishTestsNotification = @"WBJSBridgeFinishTestsNotification";
21 |
22 | @interface WBJSBridgeActionGetValueTests : WBJSBridgeAction
23 |
24 | @end
25 |
26 | @interface WBJSBridgeActionFinishTests : WBJSBridgeAction
27 |
28 | @end
29 |
30 | @interface WBWebViewJSBridgeTests : XCTestCase
31 |
32 | @end
33 |
34 | @implementation WBWebViewJSBridgeTests
35 |
36 | - (void)testJSBridgeInjection
37 | {
38 | WBTestsUIWebView * webView = [[WBTestsUIWebView alloc] init];
39 |
40 | webView.JSBridge.interfaceName = @"JSBridge";
41 |
42 | XCTAssert([webView syncLoadHTMLString:@"Hello" error:NULL]);
43 |
44 | XCTAssert([[webView stringByEvaluatingJavaScriptFromString:@"typeof window.JSBridge"] isEqualToString:@"object"]);
45 | }
46 |
47 | - (void)testJSBridgeInvokeAndCallback
48 | {
49 | #define QUOTE(...) #__VA_ARGS__
50 | const char * html_char = QUOTE(
51 |
52 |
53 |
54 |
55 |
88 | );
89 | #undef QUOTE
90 |
91 | NSString * html = [NSString stringWithUTF8String:html_char];
92 |
93 | WBTestsUIWebView * webView = [[WBTestsUIWebView alloc] init];
94 |
95 | webView.JSBridge.interfaceName = @"JSBridge";
96 | webView.JSBridge.readyEventName = @"JSBridgeReady";
97 |
98 | XCTAssert([webView syncLoadHTMLString:html error:NULL]);
99 |
100 | [self expectationForNotification:WBJSBridgeFinishTestsNotification object:webView.JSBridge handler:^BOOL(NSNotification *notification) {
101 |
102 | NSString * errorJSON = [webView stringByEvaluatingJavaScriptFromString:@"JSON.stringify(window.jserrors)"];
103 | NSArray * errors = [errorJSON wb_objectFromJSONString];
104 | XCTAssertFalse(errors.count, @"errors: %@", errors);
105 |
106 | XCTAssertEqualObjects(notification.userInfo[@"value"], @"weibo.cn"); // see -[WBJSBridgeActionGetValueTests startAction]
107 |
108 | return YES;
109 | }];
110 |
111 | [self waitForExpectationsWithTimeout:5 handler:NULL];
112 | }
113 |
114 | @end
115 |
116 | @implementation WBJSBridgeActionGetValueTests
117 |
118 | - (void)startAction
119 | {
120 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
121 | [self actionSuccessedWithResult:@{@"value": @"weibo.cn"}];
122 | });
123 | }
124 |
125 | @end
126 |
127 | @implementation WBJSBridgeActionFinishTests
128 |
129 | - (void)startAction
130 | {
131 | [[NSNotificationCenter defaultCenter] postNotificationName:WBJSBridgeFinishTestsNotification object:self.bridge userInfo:self.message.parameters];
132 | [self actionSuccessedWithResult:@{}];
133 | }
134 |
135 | @end
136 |
--------------------------------------------------------------------------------