├── docs
├── public
│ ├── fonts
│ │ ├── aller-bold.eot
│ │ ├── aller-bold.ttf
│ │ ├── aller-bold.woff
│ │ ├── aller-light.eot
│ │ ├── aller-light.ttf
│ │ ├── aller-light.woff
│ │ ├── novecento-bold.eot
│ │ ├── novecento-bold.ttf
│ │ └── novecento-bold.woff
│ └── stylesheets
│ │ └── normalize.css
├── docco.css
└── index.html
├── WebView
├── WebView
│ ├── Images.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── WebView_58x58.png
│ │ │ ├── WebView_80x80.png
│ │ │ ├── WebView_120x120.png
│ │ │ └── Contents.json
│ │ └── LaunchImage.launchimage
│ │ │ └── Contents.json
│ ├── Info.plist
│ ├── AppDelegate.swift
│ ├── Base.lproj
│ │ └── Main.storyboard
│ └── ViewController.swift
├── WebView.xcodeproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata
│ │ │ └── paulguo.xcuserdatad
│ │ │ ├── UserInterfaceState.xcuserstate
│ │ │ └── WorkspaceSettings.xcsettings
│ ├── xcuserdata
│ │ └── paulguo.xcuserdatad
│ │ │ └── xcschemes
│ │ │ ├── xcschememanagement.plist
│ │ │ └── WebView.xcscheme
│ └── project.pbxproj
└── WebViewTests
│ ├── Info.plist
│ └── WebViewTests.swift
├── .gitignore
├── guide
└── index.md
├── abc.json
├── package.json
├── demo
├── index.html
└── bridge.html
├── README.md
├── Gruntfile.js
└── index.js
/docs/public/fonts/aller-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-bold.eot
--------------------------------------------------------------------------------
/docs/public/fonts/aller-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-bold.ttf
--------------------------------------------------------------------------------
/docs/public/fonts/aller-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-bold.woff
--------------------------------------------------------------------------------
/docs/public/fonts/aller-light.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-light.eot
--------------------------------------------------------------------------------
/docs/public/fonts/aller-light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-light.ttf
--------------------------------------------------------------------------------
/docs/public/fonts/aller-light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/aller-light.woff
--------------------------------------------------------------------------------
/docs/public/fonts/novecento-bold.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/novecento-bold.eot
--------------------------------------------------------------------------------
/docs/public/fonts/novecento-bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/novecento-bold.ttf
--------------------------------------------------------------------------------
/docs/public/fonts/novecento-bold.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/docs/public/fonts/novecento-bold.woff
--------------------------------------------------------------------------------
/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_58x58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_58x58.png
--------------------------------------------------------------------------------
/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_80x80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_80x80.png
--------------------------------------------------------------------------------
/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/WebView/WebView/Images.xcassets/AppIcon.appiconset/WebView_120x120.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | */node_modules
4 | npm-debug.log
5 | *.swp
6 | .sw*
7 | .idea/*
8 | .editorconfig
9 | .jshintrc
10 | .travis.yml
11 | .sass-cache/
12 | .project
13 | mods/.sass-cache/
14 |
--------------------------------------------------------------------------------
/guide/index.md:
--------------------------------------------------------------------------------
1 | ## 综述
2 |
3 | bridge是。
4 |
5 | ## 快速使用
6 |
7 | ### 初始化组件
8 |
9 | S.use('gallery/bridge/index', function (S, Bridge) {
10 | var bridge = new Bridge();
11 | })
12 |
13 | ## API说明
14 |
15 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/project.xcworkspace/xcuserdata/paulguo.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PaulGuo/bridge/HEAD/WebView/WebView.xcodeproj/project.xcworkspace/xcuserdata/paulguo.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/abc.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bridge",
3 | "version": "0.1.1",
4 | "componentName": "Bridge",
5 | "desc": "",
6 | "cover": "",
7 | "tag": "",
8 | "author": {
9 | "name": "",
10 | "email": "",
11 | "page": ""
12 | },
13 | "type": "kissy-gallery",
14 | "type-url": "http://gallery.kissyui.com/guide"
15 | }
--------------------------------------------------------------------------------
/WebView/WebView/Images.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "orientation" : "portrait",
5 | "idiom" : "iphone",
6 | "extent" : "full-screen",
7 | "minimum-system-version" : "7.0",
8 | "scale" : "2x"
9 | },
10 | {
11 | "orientation" : "portrait",
12 | "idiom" : "iphone",
13 | "subtype" : "retina4",
14 | "extent" : "full-screen",
15 | "minimum-system-version" : "7.0",
16 | "scale" : "2x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "bridge",
3 | "version": "0.0.1",
4 | "componentName":"Bridge",
5 | "devDependencies": {
6 | "grunt": "~0.4.1",
7 | "grunt-contrib-copy": "",
8 | "grunt-contrib-uglify": "~0.2.0",
9 | "grunt-contrib-clean": "~0.4.0",
10 | "grunt-contrib-less": "~0.5.0",
11 | "grunt-css-combo": "~0.2.1",
12 | "grunt-contrib-cssmin": "~0.5.0",
13 | "grunt-contrib-watch": "~0.3.1",
14 | "grunt-kmc": "",
15 | "grunt-exec": "~0.4.1",
16 | "fs-extra":"",
17 | "grunt-replace":""
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/WebView/WebView/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "WebView_58x58.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "40x40",
11 | "idiom" : "iphone",
12 | "filename" : "WebView_80x80.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "60x60",
17 | "idiom" : "iphone",
18 | "filename" : "WebView_120x120.png",
19 | "scale" : "2x"
20 | }
21 | ],
22 | "info" : {
23 | "version" : 1,
24 | "author" : "xcode"
25 | }
26 | }
--------------------------------------------------------------------------------
/demo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | bridge的demo
6 |
7 |
8 |
9 |
10 | bridge
11 |
12 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/xcuserdata/paulguo.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | WebView.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 11DF46DC194CE33F0072FDE3
16 |
17 | primary
18 |
19 |
20 | 11DF46EE194CE3400072FDE3
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/project.xcworkspace/xcuserdata/paulguo.xcuserdatad/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | BuildLocationStyle
6 | UseAppPreferences
7 | CustomBuildLocationType
8 | RelativeToDerivedData
9 | DerivedDataLocationStyle
10 | Default
11 | IssueFilterStyle
12 | ShowActiveSchemeOnly
13 | LiveSourceIssuesEnabled
14 |
15 | SnapshotAutomaticallyBeforeSignificantChanges
16 |
17 | SnapshotLocationStyle
18 | Default
19 |
20 |
21 |
--------------------------------------------------------------------------------
/WebView/WebViewTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | PaulGuo.${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 |
--------------------------------------------------------------------------------
/WebView/WebViewTests/WebViewTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebViewTests.swift
3 | // WebViewTests
4 | //
5 | // Created by 破锣锅 on 14-6-15.
6 | // Copyright (c) 2014年 破锣锅. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class WebViewTests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | XCTAssert(true, "Pass")
26 | }
27 |
28 | func testPerformanceExample() {
29 | // This is an example of a performance test case.
30 | self.measureBlock() {
31 | // Put the code you want to measure the time of here.
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/WebView/WebView/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | PaulGuo.${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 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/WebView/WebView/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WebView
4 | //
5 | // Created by 破锣锅 on 14-6-15.
6 | // Copyright (c) 2014年 破锣锅. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(application: UIApplication) {
23 | // 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.
24 | // 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.
25 | }
26 |
27 | func applicationDidEnterBackground(application: UIApplication) {
28 | // 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.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(application: UIApplication) {
33 | // 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.
34 | }
35 |
36 | func applicationDidBecomeActive(application: UIApplication) {
37 | // 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.
38 | }
39 |
40 | func applicationWillTerminate(application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/WebView/WebView/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/WebView/WebView/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // WebView
4 | //
5 | // Created by 破锣锅 on 14-6-15.
6 | // Copyright (c) 2014年 破锣锅. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ViewController: UIViewController {
12 |
13 | var webview: UIWebView!
14 |
15 | override func viewDidLoad() {
16 | super.viewDidLoad()
17 | // Do any additional setup after loading the view, typically from a nib.
18 |
19 | self.modifyUserAgent()
20 | self.webviewInit()
21 | self.webviewLoad()
22 | self.messageTimerInit()
23 | }
24 |
25 | override func didReceiveMemoryWarning() {
26 | super.didReceiveMemoryWarning()
27 | // Dispose of any resources that can be recreated.
28 | }
29 |
30 | func webviewInit() {
31 | self.webview = UIWebView(frame: UIScreen.mainScreen().applicationFrame)
32 | self.view.addSubview(self.webview)
33 | }
34 |
35 | func webviewLoad() {
36 | //var url = NSURL(string: "http://h5.m.taobao.com/trip/hotel/search/index.html")
37 | var url = NSURL(string: "http://b.ued.taobao.net/liuhuo.gk/bridge/demo/example.html")
38 | var request = NSURLRequest(URL: url)
39 | self.webview.reload()
40 | self.webview.loadRequest(request)
41 | }
42 |
43 | func messageTimerInit() {
44 | var messageTimer = NSTimer.scheduledTimerWithTimeInterval(0.02, target: self, selector: "messageLoop", userInfo: nil, repeats: true)
45 | }
46 |
47 | func messageLoop() {
48 | let FECTCHMESSAGE = "if(this.messageQueueFetch) messageQueueFetch();"
49 | var messages: NSString = webview.stringByEvaluatingJavaScriptFromString(FECTCHMESSAGE)
50 |
51 | if(messages.length > 0) {
52 | println("BRIDGE PROTOCOL DETECTED:")
53 | println(messages)
54 | }
55 | }
56 |
57 | func modifyUserAgent() {
58 | var clientVersion: NSString = "AliApp(LX/4.0.0) AliTrip/4.0.0"
59 | // for "avoid show blank when webview first time lanch after app lanch
60 | var origin: NSString = UIWebView().stringByEvaluatingJavaScriptFromString("navigator.userAgent")
61 | var userAgent: NSString = "\(origin) \(clientVersion)"
62 | NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent": userAgent])
63 | NSUserDefaults.standardUserDefaults().synchronize()
64 | }
65 |
66 |
67 | }
68 |
69 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/xcuserdata/paulguo.xcuserdatad/xcschemes/WebView.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
61 |
62 |
68 |
69 |
70 |
71 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
91 |
92 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Native2H5
2 | `mClient协议约定及优先级梳理 v0.1`
3 |
4 | ---
5 |
6 | ### Overview
7 |
8 | - H5 WebView特性方法
9 | - 客户端协议接口约定(握手通讯)
10 | - 客户端页面跳转及跳转拦截
11 | - 客户端唤醒(调研总结)
12 | - 降级方案(页面生存环境自适应)
13 |
14 | ##### 面向Android和iOS的JS调用原理
15 |
16 | 1. `Android` 通过桥(客户端暴露在WebView全局对象下的一个对象,内挂各种API方法)的方式进行调用,如`window['Android_Bridge']['method'](JSON)`。
17 |
18 | 2. `iOS`通过自定义`Scheme`(如`native://method?data=JSON`)方式调用。
19 |
20 | 3. 需要回调的接口需要将函数名称在调用时一并传给客户端,同时将回调函数通过唯一名称挂在全局,待客户端执行回调后移除该全局函数。
21 |
22 | 4. 握手通讯,`mclient`初始化时发送`ready`消息,当客户端拦截到`ready`消息后返回`Ok`状态码,`mclient`拿到成功状态码后则认为初始化完成,否则将认为页面自身处在非客户端或者第三方WebView环境中进行执行降级逻辑处理。
23 |
24 | `mClient`是为H5和客户端交互通讯而产生的一个中间件,即一个`JavaScript`的SDK。负责处理H5和客户端的方法调用、通信及H5页面自身的降级处理(非内嵌在客户端的情况)。
25 |
26 | H5内嵌客户端需要借助WebView完成自身的展现和“存活”,H5自身由于权限有限,有些功能和特性需要借助客户端辅助提供一些属性和方法来实现。如设备蜂鸣、震动,网络类型的识别(2G/3G/Wifi等)相关特性,除此之外还有一些特有的事件(如点击`back`键,网络变化,电量报警等)。倘若WebView的功能能够足够完善,H5就能够更大的发挥其优势,就会有更大的用武之地。下面是参考借鉴`PhoneGap`所罗列的一些常用的属性、方法抑或事件(`排序按照优先级由高到低,非必要的特性已经暂时从下表中去除`)。
27 |
28 | ### Storage
29 |
30 | ##### Objects:
31 |
32 | - localStorage
33 |
34 | ##### localStorage
35 |
36 | 开启本地存储特性
37 |
38 | ---
39 |
40 | ### UserAgent
41 |
42 | 请参照规范格式设置`WebView`的`UserAgent`,避免页面进行设备适配时取不到该字段的值或者取到的信息不正确。
43 |
44 | ---
45 |
46 | ### Connection
47 |
48 | ##### Properties
49 |
50 | - connection.type
51 |
52 | ##### Constants
53 |
54 | - Connection.UNKNOWN
55 | - Connection.ETHERNET
56 | - Connection.WIFI
57 | - Connection.CELL_2G
58 | - Connection.CELL_3G
59 | - Connection.CELL_4G
60 | - Connection.CELL
61 | - Connection.NONE
62 |
63 | ##### connection.type
64 |
65 | 获取当前用户网络环境类型,wifi/2G/3G, etc.
66 |
67 | 备注:`mclient`提供JavaScript钩子,通过钩子获取设备当前所处的网络环境及网络类型。iOS若在非Wifi网络环境下无法区分2G/3G状态,则默认认为是3G即可。
68 |
69 | ---
70 |
71 | ### Notification
72 |
73 | ##### Methods:
74 |
75 | - alert
76 | - confirm
77 | - prompt
78 | - beep
79 | - vibrate
80 |
81 | ##### notification.alert
82 |
83 | 警告提示框
84 |
85 | ##### notification.confirm
86 |
87 | 确认提示框
88 |
89 | ##### notification.prompt
90 |
91 | 提示对话框
92 |
93 | ##### notification.beep
94 |
95 | 蜂鸣声
96 |
97 | ##### notification.vibrate
98 |
99 | 设备震动
100 |
101 | ---
102 |
103 | ### Device
104 |
105 | ##### Properties:
106 |
107 | - name
108 | - mclient
109 | - platform
110 | - uuid
111 | - model
112 | - version
113 |
114 | ##### device.name
115 |
116 | 设备名称
117 |
118 | ##### device.mclient
119 |
120 | 当前所用`mclient`的版本号
121 |
122 | ##### device.platform
123 |
124 | 平台类型
125 |
126 | ##### device.uuid
127 |
128 | 设备全球唯一标识
129 |
130 | ##### device.model
131 |
132 | 设备信息
133 |
134 | ##### device.version
135 |
136 | 操作系统版本号
137 |
138 | ##### Events
139 |
140 | `按照优先级排序`
141 |
142 | - backbutton
143 | - deviceready
144 | - online
145 | - offline
146 | - pause
147 | - resume
148 | - batterycritical
149 | - batterylow
150 | - batterystatus
151 | - menubutton
152 | - searchbutton
153 | - startcallbutton
154 | - endcallbutton
155 | - volumedownbutton
156 | - volumeupbutton
157 | - shake
158 |
159 | ---
160 |
161 | ### Client
162 |
163 | ##### Properties:
164 |
165 | - version
166 |
167 | ##### client.version
168 |
169 | 客户端版本号
170 |
171 | ---
172 |
173 | ### InAppBrowser
174 |
175 | ##### Methods:
176 |
177 | - window.open
178 |
179 | ##### window.open
180 |
181 | Events:
182 |
183 | - loadstart
184 | - loadstop
185 | - loaderror
186 | - exit
187 |
188 | ---
189 |
190 | ### 页面跳转/拦截
191 |
192 | ##### 跳转的页面类型:
193 |
194 | - 商品详情页
195 | - 系统登录框
196 | - 跳转到AppStore
197 | - 任意页面(如客户端首页)
198 |
199 | ##### 跳转方式:
200 |
201 | - 协议约定方式
202 | - 客户端拦截(借鉴主站客户端方式,规则是否能做成可配置的更加灵活)
203 |
204 | ### 协议约定
205 |
206 | 下述为原有的协议约定(是否需要调整或者变更还待确定),如有改动或者新增请及时同步变更信息。
207 |
208 | ###### a. 单VIEW操作
209 |
210 | | 方法名 |说明 | 需要的参数名 |
211 | | -----------------------|:------------------|:-------------------|
212 | | open_system_browser |用浏览器打开url |url |
213 | | client_appstore_call |跳转到appstore |url |
214 | | set_browser_title |设置顶部title |title |
215 | | client_page_back |客户端页面回退(back到webview上一页) |- |
216 | | client_alert |警告提示框,要求客户端自定义风格 |title, msg, ok_wording, callback |
217 | | client_confirm |确认提示框,俩按钮的提示框 |title, msg, ok_wording, cancle_wording, ok_callback, cancel_callback |
218 | | get_client_info |获取客户端类型/版本信息 |callback |
219 | | get_client_location |获取客户端定位信息 |callback, failback |
220 | | show_loading |显示菊花 | |
221 | | close_loading |关闭菊花 | |
222 |
223 | ###### b. 多VIEW操作
224 |
225 | | 方法名 |说明 | 需要的参数名 |
226 | | -----------------------|:------------------|:-------------------|
227 | | open |跳转(进入)到下一个view |url, param, callback |
228 | | back |回退到上一个view |args, callback |
229 |
230 |
231 | ###### c. 通用操作
232 |
233 | | 方法名 | 说明 | 需要的参数名 |
234 | | -----------------------|:------------------|:-------------------|
235 | | start_data_statistics | 客户端埋点 | control_type, control_name, args |
236 |
237 | 注释:
238 |
239 | - control_type: 埋点类型,0为`page`, 1为`button`, etc.
240 | - control_name: 埋点名称,如`1ActGoodsList`.
241 | - args: 为了方便扩展灵活的附加参数对象。
242 |
243 | ### 降级方案
244 |
245 | 当“握手”信号检测不到响应时,即认为非客户端,按照H5页面逻辑处理。
246 |
247 | ### 统一封装
248 |
249 | 完善`mClient`形成统一的`API`接口供页面调用,针对客户端和H5自适应的逻辑处理应由`mClient`内部完成,业务方应当不需要关注。
--------------------------------------------------------------------------------
/docs/public/stylesheets/normalize.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */
2 |
3 | /* ==========================================================================
4 | HTML5 display definitions
5 | ========================================================================== */
6 |
7 | /*
8 | * Corrects `block` display not defined in IE 8/9.
9 | */
10 |
11 | article,
12 | aside,
13 | details,
14 | figcaption,
15 | figure,
16 | footer,
17 | header,
18 | hgroup,
19 | nav,
20 | section,
21 | summary {
22 | display: block;
23 | }
24 |
25 | /*
26 | * Corrects `inline-block` display not defined in IE 8/9.
27 | */
28 |
29 | audio,
30 | canvas,
31 | video {
32 | display: inline-block;
33 | }
34 |
35 | /*
36 | * Prevents modern browsers from displaying `audio` without controls.
37 | * Remove excess height in iOS 5 devices.
38 | */
39 |
40 | audio:not([controls]) {
41 | display: none;
42 | height: 0;
43 | }
44 |
45 | /*
46 | * Addresses styling for `hidden` attribute not present in IE 8/9.
47 | */
48 |
49 | [hidden] {
50 | display: none;
51 | }
52 |
53 | /* ==========================================================================
54 | Base
55 | ========================================================================== */
56 |
57 | /*
58 | * 1. Sets default font family to sans-serif.
59 | * 2. Prevents iOS text size adjust after orientation change, without disabling
60 | * user zoom.
61 | */
62 |
63 | html {
64 | font-family: sans-serif; /* 1 */
65 | -webkit-text-size-adjust: 100%; /* 2 */
66 | -ms-text-size-adjust: 100%; /* 2 */
67 | }
68 |
69 | /*
70 | * Removes default margin.
71 | */
72 |
73 | body {
74 | margin: 0;
75 | }
76 |
77 | /* ==========================================================================
78 | Links
79 | ========================================================================== */
80 |
81 | /*
82 | * Addresses `outline` inconsistency between Chrome and other browsers.
83 | */
84 |
85 | a:focus {
86 | outline: thin dotted;
87 | }
88 |
89 | /*
90 | * Improves readability when focused and also mouse hovered in all browsers.
91 | */
92 |
93 | a:active,
94 | a:hover {
95 | outline: 0;
96 | }
97 |
98 | /* ==========================================================================
99 | Typography
100 | ========================================================================== */
101 |
102 | /*
103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
104 | * Safari 5, and Chrome.
105 | */
106 |
107 | h1 {
108 | font-size: 2em;
109 | }
110 |
111 | /*
112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
113 | */
114 |
115 | abbr[title] {
116 | border-bottom: 1px dotted;
117 | }
118 |
119 | /*
120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
121 | */
122 |
123 | b,
124 | strong {
125 | font-weight: bold;
126 | }
127 |
128 | /*
129 | * Addresses styling not present in Safari 5 and Chrome.
130 | */
131 |
132 | dfn {
133 | font-style: italic;
134 | }
135 |
136 | /*
137 | * Addresses styling not present in IE 8/9.
138 | */
139 |
140 | mark {
141 | background: #ff0;
142 | color: #000;
143 | }
144 |
145 |
146 | /*
147 | * Corrects font family set oddly in Safari 5 and Chrome.
148 | */
149 |
150 | code,
151 | kbd,
152 | pre,
153 | samp {
154 | font-family: monospace, serif;
155 | font-size: 1em;
156 | }
157 |
158 | /*
159 | * Improves readability of pre-formatted text in all browsers.
160 | */
161 |
162 | pre {
163 | white-space: pre;
164 | white-space: pre-wrap;
165 | word-wrap: break-word;
166 | }
167 |
168 | /*
169 | * Sets consistent quote types.
170 | */
171 |
172 | q {
173 | quotes: "\201C" "\201D" "\2018" "\2019";
174 | }
175 |
176 | /*
177 | * Addresses inconsistent and variable font size in all browsers.
178 | */
179 |
180 | small {
181 | font-size: 80%;
182 | }
183 |
184 | /*
185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers.
186 | */
187 |
188 | sub,
189 | sup {
190 | font-size: 75%;
191 | line-height: 0;
192 | position: relative;
193 | vertical-align: baseline;
194 | }
195 |
196 | sup {
197 | top: -0.5em;
198 | }
199 |
200 | sub {
201 | bottom: -0.25em;
202 | }
203 |
204 | /* ==========================================================================
205 | Embedded content
206 | ========================================================================== */
207 |
208 | /*
209 | * Removes border when inside `a` element in IE 8/9.
210 | */
211 |
212 | img {
213 | border: 0;
214 | }
215 |
216 | /*
217 | * Corrects overflow displayed oddly in IE 9.
218 | */
219 |
220 | svg:not(:root) {
221 | overflow: hidden;
222 | }
223 |
224 | /* ==========================================================================
225 | Figures
226 | ========================================================================== */
227 |
228 | /*
229 | * Addresses margin not present in IE 8/9 and Safari 5.
230 | */
231 |
232 | figure {
233 | margin: 0;
234 | }
235 |
236 | /* ==========================================================================
237 | Forms
238 | ========================================================================== */
239 |
240 | /*
241 | * Define consistent border, margin, and padding.
242 | */
243 |
244 | fieldset {
245 | border: 1px solid #c0c0c0;
246 | margin: 0 2px;
247 | padding: 0.35em 0.625em 0.75em;
248 | }
249 |
250 | /*
251 | * 1. Corrects color not being inherited in IE 8/9.
252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets.
253 | */
254 |
255 | legend {
256 | border: 0; /* 1 */
257 | padding: 0; /* 2 */
258 | }
259 |
260 | /*
261 | * 1. Corrects font family not being inherited in all browsers.
262 | * 2. Corrects font size not being inherited in all browsers.
263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
264 | */
265 |
266 | button,
267 | input,
268 | select,
269 | textarea {
270 | font-family: inherit; /* 1 */
271 | font-size: 100%; /* 2 */
272 | margin: 0; /* 3 */
273 | }
274 |
275 | /*
276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
277 | * the UA stylesheet.
278 | */
279 |
280 | button,
281 | input {
282 | line-height: normal;
283 | }
284 |
285 | /*
286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
287 | * and `video` controls.
288 | * 2. Corrects inability to style clickable `input` types in iOS.
289 | * 3. Improves usability and consistency of cursor style between image-type
290 | * `input` and others.
291 | */
292 |
293 | button,
294 | html input[type="button"], /* 1 */
295 | input[type="reset"],
296 | input[type="submit"] {
297 | -webkit-appearance: button; /* 2 */
298 | cursor: pointer; /* 3 */
299 | }
300 |
301 | /*
302 | * Re-set default cursor for disabled elements.
303 | */
304 |
305 | button[disabled],
306 | input[disabled] {
307 | cursor: default;
308 | }
309 |
310 | /*
311 | * 1. Addresses box sizing set to `content-box` in IE 8/9.
312 | * 2. Removes excess padding in IE 8/9.
313 | */
314 |
315 | input[type="checkbox"],
316 | input[type="radio"] {
317 | box-sizing: border-box; /* 1 */
318 | padding: 0; /* 2 */
319 | }
320 |
321 | /*
322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
324 | * (include `-moz` to future-proof).
325 | */
326 |
327 | input[type="search"] {
328 | -webkit-appearance: textfield; /* 1 */
329 | -moz-box-sizing: content-box;
330 | -webkit-box-sizing: content-box; /* 2 */
331 | box-sizing: content-box;
332 | }
333 |
334 | /*
335 | * Removes inner padding and search cancel button in Safari 5 and Chrome
336 | * on OS X.
337 | */
338 |
339 | input[type="search"]::-webkit-search-cancel-button,
340 | input[type="search"]::-webkit-search-decoration {
341 | -webkit-appearance: none;
342 | }
343 |
344 | /*
345 | * Removes inner padding and border in Firefox 4+.
346 | */
347 |
348 | button::-moz-focus-inner,
349 | input::-moz-focus-inner {
350 | border: 0;
351 | padding: 0;
352 | }
353 |
354 | /*
355 | * 1. Removes default vertical scrollbar in IE 8/9.
356 | * 2. Improves readability and alignment in all browsers.
357 | */
358 |
359 | textarea {
360 | overflow: auto; /* 1 */
361 | vertical-align: top; /* 2 */
362 | }
363 |
364 | /* ==========================================================================
365 | Tables
366 | ========================================================================== */
367 |
368 | /*
369 | * Remove most spacing between table cells.
370 | */
371 |
372 | table {
373 | border-collapse: collapse;
374 | border-spacing: 0;
375 | }
--------------------------------------------------------------------------------
/docs/docco.css:
--------------------------------------------------------------------------------
1 | /*--------------------- Typography ----------------------------*/
2 |
3 | @font-face {
4 | font-family: 'aller-light';
5 | src: url('public/fonts/aller-light.eot');
6 | src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
7 | url('public/fonts/aller-light.woff') format('woff'),
8 | url('public/fonts/aller-light.ttf') format('truetype');
9 | font-weight: normal;
10 | font-style: normal;
11 | }
12 |
13 | @font-face {
14 | font-family: 'aller-bold';
15 | src: url('public/fonts/aller-bold.eot');
16 | src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
17 | url('public/fonts/aller-bold.woff') format('woff'),
18 | url('public/fonts/aller-bold.ttf') format('truetype');
19 | font-weight: normal;
20 | font-style: normal;
21 | }
22 |
23 | @font-face {
24 | font-family: 'novecento-bold';
25 | src: url('public/fonts/novecento-bold.eot');
26 | src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'),
27 | url('public/fonts/novecento-bold.woff') format('woff'),
28 | url('public/fonts/novecento-bold.ttf') format('truetype');
29 | font-weight: normal;
30 | font-style: normal;
31 | }
32 |
33 | /*--------------------- Layout ----------------------------*/
34 | html { height: 100%; }
35 | body {
36 | font-family: "aller-light";
37 | font-size: 14px;
38 | line-height: 18px;
39 | color: #30404f;
40 | margin: 0; padding: 0;
41 | height:100%;
42 | }
43 | #container { min-height: 100%; }
44 |
45 | a {
46 | color: #000;
47 | }
48 |
49 | b, strong {
50 | font-weight: normal;
51 | font-family: "aller-bold";
52 | }
53 |
54 | p {
55 | margin: 15px 0 0px;
56 | }
57 | .annotation ul, .annotation ol {
58 | margin: 25px 0;
59 | }
60 | .annotation ul li, .annotation ol li {
61 | font-size: 14px;
62 | line-height: 18px;
63 | margin: 10px 0;
64 | }
65 |
66 | h1, h2, h3, h4, h5, h6 {
67 | color: #112233;
68 | line-height: 1em;
69 | font-weight: normal;
70 | font-family: "novecento-bold";
71 | text-transform: uppercase;
72 | margin: 30px 0 15px 0;
73 | }
74 |
75 | h1 {
76 | margin-top: 40px;
77 | }
78 |
79 | hr {
80 | border: 0;
81 | background: 1px #ddd;
82 | height: 1px;
83 | margin: 20px 0;
84 | }
85 |
86 | pre, tt, code {
87 | font-size: 12px; line-height: 16px;
88 | font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
89 | margin: 0; padding: 0;
90 | }
91 | .annotation pre {
92 | display: block;
93 | margin: 0;
94 | padding: 7px 10px;
95 | background: #fcfcfc;
96 | -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
97 | -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
98 | box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
99 | overflow-x: auto;
100 | }
101 | .annotation pre code {
102 | border: 0;
103 | padding: 0;
104 | background: transparent;
105 | }
106 |
107 |
108 | blockquote {
109 | border-left: 5px solid #ccc;
110 | margin: 0;
111 | padding: 1px 0 1px 1em;
112 | }
113 | .sections blockquote p {
114 | font-family: Menlo, Consolas, Monaco, monospace;
115 | font-size: 12px; line-height: 16px;
116 | color: #999;
117 | margin: 10px 0 0;
118 | white-space: pre-wrap;
119 | }
120 |
121 | ul.sections {
122 | list-style: none;
123 | padding:0 0 5px 0;;
124 | margin:0;
125 | }
126 |
127 | /*
128 | Force border-box so that % widths fit the parent
129 | container without overlap because of margin/padding.
130 |
131 | More Info : http://www.quirksmode.org/css/box.html
132 | */
133 | ul.sections > li > div {
134 | -moz-box-sizing: border-box; /* firefox */
135 | -ms-box-sizing: border-box; /* ie */
136 | -webkit-box-sizing: border-box; /* webkit */
137 | -khtml-box-sizing: border-box; /* konqueror */
138 | box-sizing: border-box; /* css3 */
139 | }
140 |
141 |
142 | /*---------------------- Jump Page -----------------------------*/
143 | #jump_to, #jump_page {
144 | margin: 0;
145 | background: white;
146 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
147 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
148 | font: 16px Arial;
149 | cursor: pointer;
150 | text-align: right;
151 | list-style: none;
152 | }
153 |
154 | #jump_to a {
155 | text-decoration: none;
156 | }
157 |
158 | #jump_to a.large {
159 | display: none;
160 | }
161 | #jump_to a.small {
162 | font-size: 22px;
163 | font-weight: bold;
164 | color: #676767;
165 | }
166 |
167 | #jump_to, #jump_wrapper {
168 | position: fixed;
169 | right: 0; top: 0;
170 | padding: 10px 15px;
171 | margin:0;
172 | }
173 |
174 | #jump_wrapper {
175 | display: none;
176 | padding:0;
177 | }
178 |
179 | #jump_to:hover #jump_wrapper {
180 | display: block;
181 | }
182 |
183 | #jump_page {
184 | padding: 5px 0 3px;
185 | margin: 0 0 25px 25px;
186 | }
187 |
188 | #jump_page .source {
189 | display: block;
190 | padding: 15px;
191 | text-decoration: none;
192 | border-top: 1px solid #eee;
193 | }
194 |
195 | #jump_page .source:hover {
196 | background: #f5f5ff;
197 | }
198 |
199 | #jump_page .source:first-child {
200 | }
201 |
202 | /*---------------------- Low resolutions (> 320px) ---------------------*/
203 | @media only screen and (min-width: 320px) {
204 | .pilwrap { display: none; }
205 |
206 | ul.sections > li > div {
207 | display: block;
208 | padding:5px 10px 0 10px;
209 | }
210 |
211 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
212 | padding-left: 30px;
213 | }
214 |
215 | ul.sections > li > div.content {
216 | overflow-x:auto;
217 | -webkit-box-shadow: inset 0 0 5px #e5e5ee;
218 | box-shadow: inset 0 0 5px #e5e5ee;
219 | border: 1px solid #dedede;
220 | margin:5px 10px 5px 10px;
221 | padding-bottom: 5px;
222 | }
223 |
224 | ul.sections > li > div.annotation pre {
225 | margin: 7px 0 7px;
226 | padding-left: 15px;
227 | }
228 |
229 | ul.sections > li > div.annotation p tt, .annotation code {
230 | background: #f8f8ff;
231 | border: 1px solid #dedede;
232 | font-size: 12px;
233 | padding: 0 0.2em;
234 | }
235 | }
236 |
237 | /*---------------------- (> 481px) ---------------------*/
238 | @media only screen and (min-width: 481px) {
239 | #container {
240 | position: relative;
241 | }
242 | body {
243 | background-color: #F5F5FF;
244 | font-size: 15px;
245 | line-height: 21px;
246 | }
247 | pre, tt, code {
248 | line-height: 18px;
249 | }
250 | p, ul, ol {
251 | margin: 0 0 15px;
252 | }
253 |
254 |
255 | #jump_to {
256 | padding: 5px 10px;
257 | }
258 | #jump_wrapper {
259 | padding: 0;
260 | }
261 | #jump_to, #jump_page {
262 | font: 10px Arial;
263 | text-transform: uppercase;
264 | }
265 | #jump_page .source {
266 | padding: 5px 10px;
267 | }
268 | #jump_to a.large {
269 | display: inline-block;
270 | }
271 | #jump_to a.small {
272 | display: none;
273 | }
274 |
275 |
276 |
277 | #background {
278 | position: absolute;
279 | top: 0; bottom: 0;
280 | width: 350px;
281 | background: #fff;
282 | border-right: 1px solid #e5e5ee;
283 | z-index: -1;
284 | }
285 |
286 | ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
287 | padding-left: 40px;
288 | }
289 |
290 | ul.sections > li {
291 | white-space: nowrap;
292 | }
293 |
294 | ul.sections > li > div {
295 | display: inline-block;
296 | }
297 |
298 | ul.sections > li > div.annotation {
299 | max-width: 350px;
300 | min-width: 350px;
301 | min-height: 5px;
302 | padding: 13px;
303 | overflow-x: hidden;
304 | white-space: normal;
305 | vertical-align: top;
306 | text-align: left;
307 | }
308 | ul.sections > li > div.annotation pre {
309 | margin: 15px 0 15px;
310 | padding-left: 15px;
311 | }
312 |
313 | ul.sections > li > div.content {
314 | padding: 13px;
315 | vertical-align: top;
316 | border: none;
317 | -webkit-box-shadow: none;
318 | box-shadow: none;
319 | }
320 |
321 | .pilwrap {
322 | position: relative;
323 | display: inline;
324 | }
325 |
326 | .pilcrow {
327 | font: 12px Arial;
328 | text-decoration: none;
329 | color: #454545;
330 | position: absolute;
331 | top: 3px; left: -20px;
332 | padding: 1px 2px;
333 | opacity: 0;
334 | -webkit-transition: opacity 0.2s linear;
335 | }
336 | .for-h1 .pilcrow {
337 | top: 47px;
338 | }
339 | .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
340 | top: 35px;
341 | }
342 |
343 | ul.sections > li > div.annotation:hover .pilcrow {
344 | opacity: 1;
345 | }
346 | }
347 |
348 | /*---------------------- (> 1025px) ---------------------*/
349 | @media only screen and (min-width: 1025px) {
350 |
351 | body {
352 | font-size: 16px;
353 | line-height: 24px;
354 | }
355 |
356 | #background {
357 | width: 525px;
358 | }
359 | ul.sections > li > div.annotation {
360 | max-width: 525px;
361 | min-width: 525px;
362 | padding: 10px 25px 1px 50px;
363 | }
364 | ul.sections > li > div.content {
365 | padding: 9px 15px 16px 25px;
366 | }
367 | }
368 |
369 | /*---------------------- Syntax Highlighting -----------------------------*/
370 |
371 | td.linenos { background-color: #f0f0f0; padding-right: 10px; }
372 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
373 | /*
374 |
375 | github.com style (c) Vasily Polovnyov
376 |
377 | */
378 |
379 | pre code {
380 | display: block; padding: 0.5em;
381 | color: #000;
382 | background: #f8f8ff
383 | }
384 |
385 | pre .hljs-comment,
386 | pre .hljs-template_comment,
387 | pre .hljs-diff .hljs-header,
388 | pre .hljs-javadoc {
389 | color: #408080;
390 | font-style: italic
391 | }
392 |
393 | pre .hljs-keyword,
394 | pre .hljs-assignment,
395 | pre .hljs-literal,
396 | pre .hljs-css .hljs-rule .hljs-keyword,
397 | pre .hljs-winutils,
398 | pre .hljs-javascript .hljs-title,
399 | pre .hljs-lisp .hljs-title,
400 | pre .hljs-subst {
401 | color: #954121;
402 | /*font-weight: bold*/
403 | }
404 |
405 | pre .hljs-number,
406 | pre .hljs-hexcolor {
407 | color: #40a070
408 | }
409 |
410 | pre .hljs-string,
411 | pre .hljs-tag .hljs-value,
412 | pre .hljs-phpdoc,
413 | pre .hljs-tex .hljs-formula {
414 | color: #219161;
415 | }
416 |
417 | pre .hljs-title,
418 | pre .hljs-id {
419 | color: #19469D;
420 | }
421 | pre .hljs-params {
422 | color: #00F;
423 | }
424 |
425 | pre .hljs-javascript .hljs-title,
426 | pre .hljs-lisp .hljs-title,
427 | pre .hljs-subst {
428 | font-weight: normal
429 | }
430 |
431 | pre .hljs-class .hljs-title,
432 | pre .hljs-haskell .hljs-label,
433 | pre .hljs-tex .hljs-command {
434 | color: #458;
435 | font-weight: bold
436 | }
437 |
438 | pre .hljs-tag,
439 | pre .hljs-tag .hljs-title,
440 | pre .hljs-rules .hljs-property,
441 | pre .hljs-django .hljs-tag .hljs-keyword {
442 | color: #000080;
443 | font-weight: normal
444 | }
445 |
446 | pre .hljs-attribute,
447 | pre .hljs-variable,
448 | pre .hljs-instancevar,
449 | pre .hljs-lisp .hljs-body {
450 | color: #008080
451 | }
452 |
453 | pre .hljs-regexp {
454 | color: #B68
455 | }
456 |
457 | pre .hljs-class {
458 | color: #458;
459 | font-weight: bold
460 | }
461 |
462 | pre .hljs-symbol,
463 | pre .hljs-ruby .hljs-symbol .hljs-string,
464 | pre .hljs-ruby .hljs-symbol .hljs-keyword,
465 | pre .hljs-ruby .hljs-symbol .hljs-keymethods,
466 | pre .hljs-lisp .hljs-keyword,
467 | pre .hljs-tex .hljs-special,
468 | pre .hljs-input_number {
469 | color: #990073
470 | }
471 |
472 | pre .hljs-builtin,
473 | pre .hljs-constructor,
474 | pre .hljs-built_in,
475 | pre .hljs-lisp .hljs-title {
476 | color: #0086b3
477 | }
478 |
479 | pre .hljs-preprocessor,
480 | pre .hljs-pi,
481 | pre .hljs-doctype,
482 | pre .hljs-shebang,
483 | pre .hljs-cdata {
484 | color: #999;
485 | font-weight: bold
486 | }
487 |
488 | pre .hljs-deletion {
489 | background: #fdd
490 | }
491 |
492 | pre .hljs-addition {
493 | background: #dfd
494 | }
495 |
496 | pre .hljs-diff .hljs-change {
497 | background: #0086b3
498 | }
499 |
500 | pre .hljs-chunk {
501 | color: #aaa
502 | }
503 |
504 | pre .hljs-tex .hljs-formula {
505 | opacity: 0.5;
506 | }
507 |
--------------------------------------------------------------------------------
/demo/bridge.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Bridge Demo
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
33 |
34 |
35 |
36 |
37 | 外跳测试
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | SDK相关测试
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | 其它接口测试
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | 城市
100 | back page
101 | 获取服务端时间
102 | 获取客户端时间
103 |
104 | 分享
105 | 设备信息
106 | 地理位置
107 | 新窗口(带叉)
108 | 无头窗口
109 |
110 |
111 |
339 |
340 |
392 |
393 |
394 |
395 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | var path = require('path'),
2 | fs = require('fs-extra'),
3 | os = require('os'),
4 | exec = require('child_process').exec;
5 |
6 | module.exports = function (grunt) {
7 |
8 | var file = grunt.file;
9 | var task = grunt.task;
10 | var pathname = path.basename(__dirname);
11 | var files = doWalk('./');
12 | // files.js 存储项目中的所有js文件
13 | // file.css 存储项目中的所有css文件
14 | // file.less 存储项目中所有less文件
15 |
16 | // ======================= 配置每个任务 ==========================
17 |
18 | grunt.initConfig({
19 |
20 | pkg : grunt.file.readJSON('abc.json'),
21 |
22 | // 配置默认分支
23 | currentBranch: 'master',
24 |
25 |
26 | // 对页面进行清理
27 | clean : {
28 |
29 | build: {
30 | src: 'build/*'
31 | }
32 | },
33 |
34 | /**
35 | * 将src目录中的KISSY文件做编译打包,仅做合并,源文件需要指定名称
36 | * KISSY.add('package/path/to/file',function(S){});
37 | *
38 | * @link https://github.com/daxingplay/grunt-kmc
39 | *
40 | * 如果需要只生成依赖关系表,不做合并
41 | * 在kmc.options中增加两个参数:
42 | * depFilePath: 'build/mods.js',
43 | * comboOnly: true,
44 | * comboMap: true,
45 | */
46 | kmc : {
47 | options: {
48 | packages: [
49 | {
50 | name : 'mpi',
51 | path : '../',
52 | charset: 'utf-8'
53 | }
54 |
55 | ]
56 | },
57 |
58 | main: {
59 | files: [
60 | {
61 | // 这里指定项目根目录下所有文件为入口文件,自定义入口请自行添加
62 | expand: true,
63 | cwd : './',
64 | src : [ '**/*.js', '!**/node_modules/**', '!**/build/**', '!**/demo/**', '!**/doc/**', '!**/guide/**', '!**/tests/**', '!Gruntfile.js'],
65 | dest : 'build/'
66 | }
67 | ]
68 | }
69 | // 若有新任务,请自行添加
70 | /*
71 | "simple-example": {
72 | files: [
73 | {
74 | src: "a.js",
75 | dest: "build/index.js"
76 | }
77 | ]
78 | }
79 | */
80 | },
81 |
82 | // CSS-Combo: 合并项目中所有css,通过@import "other.css" 来处理CSS的依赖关系
83 | css_combo: {
84 | options: {
85 | paths: './'
86 | },
87 | main : {
88 | files: [
89 | {
90 | expand: true,
91 | cwd : 'build',
92 | src : ['**/*.css'],
93 | dest : 'build/',
94 | ext : '.css'
95 | }
96 | ]
97 | }
98 | },
99 |
100 | // 编译LESS为CSS https://github.com/gruntjs/grunt-contrib-less
101 | less : {
102 | options: {
103 | paths: './'
104 | },
105 | main : {
106 | files: [
107 | {
108 | expand: true,
109 | cwd : 'build/',
110 | src : ['**/*.less'],
111 | dest : 'build/',
112 | ext : '.css'
113 | }
114 | ]
115 | }
116 | },
117 |
118 | // 压缩JS https://github.com/gruntjs/grunt-contrib-uglify
119 | uglify : {
120 | options: {
121 | banner : '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd hh:MM:ss") %> */\n',
122 | beautify: {
123 | ascii_only: true
124 | }
125 | },
126 | main : {
127 | files: [
128 | {
129 | expand: true,
130 | cwd : 'build/',
131 | src : ['**/*.js', '!**/*-min.js'],
132 | dest : 'build/',
133 | ext : '-min.js'
134 | }
135 | ]
136 | }
137 | },
138 |
139 | // 压缩CSS https://github.com/gruntjs/grunt-contrib-cssmin
140 | cssmin : {
141 | main: {
142 | files: [
143 | {
144 | expand: true,
145 | cwd : 'build/',
146 | src : ['**/*.css', '!**/*-min.css'],
147 | dest : 'build/',
148 | ext : '-min.css'
149 | }
150 | ]
151 | }
152 | },
153 |
154 | // 发布命令
155 | exec : {
156 | tag : {
157 | command: 'git tag publish/<%= currentBranch %>'
158 | },
159 | publish : {
160 | command: 'git push origin publish/<%= currentBranch %>:publish/<%= currentBranch %>'
161 | },
162 | commit : {
163 | command: 'git commit -m "<%= currentBranch %> - <%= grunt.template.today("yyyy-mm-dd hh:MM:ss") %>"'
164 | },
165 | add : {
166 | command: 'git add .'
167 | },
168 | prepub : {
169 | command: 'git push origin daily/<%= currentBranch %>:daily/<%= currentBranch %>'
170 | },
171 | grunt_publish: {
172 | command: 'grunt default:publish'
173 | },
174 | grunt_prepub : {
175 | command: 'grunt default:prepub'
176 | },
177 | new_branch : {
178 | command: 'git checkout -b daily/<%= currentBranch %>'
179 | }
180 | },
181 |
182 | // 拷贝文件
183 | copy : {
184 | main: {
185 | files: [
186 | {
187 | // src: files.js,
188 | expand: true,
189 | src : ['**/*.html', '**/*.htm', '**/*.js', '**/*.less', '**/*.css', '**/*.png', '**/*.gif', '**/*.jpg', '!**/node_modules/**', '!**/build/', '!**/demo/**', '!**/doc/**', '!**/guide/**', '!**/tests/**', '!Gruntfile.js'],
190 | dest : 'build/',
191 | cwd : './',
192 | filter: 'isFile'
193 | }
194 | ]
195 | }
196 | },
197 | // 替换config中的版本号@@version
198 | replace : {
199 | dist: {
200 | options: {
201 | variables: {
202 | 'version': '<%= pkg.version %>'
203 | },
204 | prefix : '@@'
205 | },
206 | files : [
207 | {
208 | expand: true,
209 | cwd : 'build/',
210 | dest : 'build/',
211 | src : ['**/*']
212 | }
213 | ]
214 | }
215 | }
216 |
217 | // 合并文件
218 | /*
219 | concat: {
220 | dist: {
221 | src: ['from.css'],
222 | dest: 'build/to.css'
223 |
224 | }
225 | },
226 | */
227 |
228 | // YUIDoc: 对build目录中的js文件生成文档,放入doc/中
229 | /*
230 | yuidoc: {
231 | compile: {
232 | name: 'generator-clam',
233 | description: 'A Clam generator for Yeoman',
234 | options: {
235 | paths: 'build/',
236 | outdir: 'doc/'
237 | }
238 | }
239 | }
240 | */
241 |
242 | });
243 |
244 | // ======================= 载入使用到的通过NPM安装的模块 ==========================
245 |
246 | grunt.loadNpmTasks('grunt-contrib-clean');
247 | grunt.loadNpmTasks('grunt-contrib-uglify');
248 | grunt.loadNpmTasks('grunt-css-combo');
249 | grunt.loadNpmTasks('grunt-contrib-less');
250 | grunt.loadNpmTasks('grunt-contrib-cssmin');
251 | grunt.loadNpmTasks('grunt-kmc');
252 | grunt.loadNpmTasks('grunt-exec');
253 | grunt.loadNpmTasks('grunt-contrib-copy');
254 | grunt.loadNpmTasks('grunt-replace');
255 |
256 | // 根据需要打开这些配置
257 | //grunt.loadNpmTasks('grunt-kissy-template');
258 | //grunt.loadNpmTasks('grunt-contrib-connect');
259 | //grunt.loadNpmTasks('grunt-contrib-concat');
260 | //grunt.loadNpmTasks('grunt-contrib-yuidoc');
261 |
262 | // ======================= 注册Grunt 各个操作 ==========================
263 |
264 | /**
265 | * 正式发布
266 | */
267 | grunt.registerTask('publish', 'clam publish...', function () {
268 | task.run('exec:grunt_publish');
269 | });
270 | grunt.registerTask('pub', 'clam publish...', function () {
271 | task.run('exec:grunt_publish');
272 | });
273 | grunt.registerTask('build', 'clam publish...', function () {
274 | //task.run('combohtml');
275 | });
276 |
277 | /**
278 | * 预发布
279 | */
280 | grunt.registerTask('prepub', 'clam pre publish...', function () {
281 | task.run('exec:grunt_prepub');
282 | });
283 |
284 |
285 | /*
286 | * 获取当前库的信息
287 | **/
288 | grunt.registerTask('info', 'clam info...', function () {
289 | var abcJSON = {};
290 | try {
291 | abcJSON = require(path.resolve(process.cwd(), 'abc.json'));
292 | console.log('\n' + abcJSON.repository.url);
293 | } catch (e) {
294 | console.log('未找到abc.json');
295 | }
296 | });
297 |
298 | /*
299 | * 获取当前最大版本号,并创建新分支
300 | **/
301 | grunt.registerTask('newbranch', 'clam newBranch...', function (type) {
302 | var done = this.async();
303 | exec('git branch -a & git tag', function (err, stdout, stderr, cb) {
304 | var r = getBiggestVersion(stdout.match(/\d+\.\d+\.\d+/ig));
305 | if (!r) {
306 | r = '0.0.1';
307 | } else {
308 | r[2]++;
309 | r = r.join('.');
310 | }
311 | grunt.log.write(('新分支:daily/' + r).green);
312 | grunt.config.set('currentBranch', r);
313 | task.run(['exec:new_branch']);
314 | // 回写入 abc.json 的 version
315 | try {
316 | abcJSON = require(path.resolve(process.cwd(), 'abc.json'));
317 | abcJSON.version = r;
318 | fs.writeJSONFile("abc.json", abcJSON, function (err) {
319 | if (err) {
320 | console.log(err);
321 | } else {
322 | console.log("update abc.json.");
323 | }
324 | });
325 | } catch (e) {
326 | console.log('未找到abc.json');
327 | }
328 | done();
329 | });
330 | });
331 |
332 | // ======================= 注册Grunt主流程 ==========================
333 |
334 | return grunt.registerTask('default', 'clam running ...', function (type) {
335 |
336 | var done = this.async();
337 |
338 | // 获取当前分支
339 | exec('git branch', function (err, stdout, stderr, cb) {
340 |
341 | var reg = /\*\s+daily\/(\S+)/,
342 | match = stdout.match(reg);
343 |
344 | if (!match) {
345 | grunt.log.error('当前分支为 master 或者名字不合法(daily/x.y.z),请切换到daily分支'.red);
346 | grunt.log.error('创建新daily分支:grunt newbranch'.yellow);
347 | return;
348 | }
349 | grunt.log.write(('当前分支:' + match[1]).green);
350 | grunt.config.set('currentBranch', match[1]);
351 | done();
352 | });
353 |
354 | // 构建和发布任务
355 | if (!type) {
356 | task.run(['clean:build', 'copy', 'less', 'css_combo', /*'kmc',*/ 'replace', 'uglify', 'cssmin'/*'concat','yuidoc'*/]);
357 | } else if ('publish' === type || 'pub' === type) {
358 | task.run(['exec:tag', 'exec:publish']);
359 | } else if ('prepub' === type) {
360 | task.run(['exec:add', 'exec:commit']);
361 | task.run(['exec:prepub']);
362 | }
363 |
364 | });
365 |
366 | // ======================= 辅助函数 ==========================
367 |
368 | // 遍历当前目录的文件
369 | function walk(uri, files) {
370 |
371 | var stat = fs.lstatSync(uri);
372 | if (stat.isFile() && !/(build|node_modules|demo|doc|\.git|\.+)[\\|\/]/i.test(uri) && !/grunt.+/i.test(uri)) {
373 |
374 | switch (path.extname(uri)) {
375 | case '.css':
376 | files.css.push(uri);
377 | break;
378 | case '.js':
379 | files.js.push(uri);
380 | break;
381 | case '.less':
382 | files.less.push(uri);
383 | break;
384 | case '.scss':
385 | files.scss.push(uri);
386 | break;
387 | default:
388 | files.other.push(uri);
389 | }
390 | }
391 | if (stat.isDirectory()) {
392 | fs.readdirSync(uri).forEach(function (part) {
393 | walk(path.join(uri, part), files);
394 | });
395 | }
396 | }
397 |
398 |
399 | // 得到文件结构的数据结构
400 | function doWalk(rootDir) {
401 | var files = {
402 | css : [],
403 | less : [],
404 | scss : [],
405 | js : [],
406 | other: [] // 暂时没用
407 | };
408 | walk(rootDir, files);
409 | return files;
410 | }
411 |
412 | function getBiggestVersion(A) {
413 | var a = [];
414 | var b = [];
415 | var t = [];
416 | var r = [];
417 | if (!A) {
418 | return [0, 0, 0];
419 | }
420 | for (var i = 0; i < A.length; i++) {
421 | if (A[i].match(/^\d+\.\d+\.\d+$/)) {
422 | var sp = A[i].split('.');
423 | a.push([
424 | Number(sp[0]), Number(sp[1]), Number(sp[2])
425 | ]);
426 | }
427 | }
428 |
429 | var r = findMax(findMax(findMax(a, 0), 1), 2);
430 | return r[0];
431 | }
432 |
433 | // a:二维数组,index,比较第几个
434 | // return:返回保留比较后的结果组成的二维数组
435 | function findMax(a, index) {
436 | var t = [];
437 | var b = [];
438 | var r = [];
439 | for (var i = 0; i < a.length; i++) {
440 | t.push(Number(a[i][index]));
441 | }
442 | var max = Math.max.apply(this, t);
443 | for (var i = 0; i < a.length; i++) {
444 | if (a[i][index] === max) {
445 | b.push(i);
446 | }
447 | }
448 | for (var i = 0; i < b.length; i++) {
449 | r.push(a[b[i]]);
450 | }
451 | return r;
452 | }
453 | };
454 |
--------------------------------------------------------------------------------
/WebView/WebView.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 11DF46E3194CE33F0072FDE3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11DF46E2194CE33F0072FDE3 /* AppDelegate.swift */; };
11 | 11DF46E5194CE33F0072FDE3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11DF46E4194CE33F0072FDE3 /* ViewController.swift */; };
12 | 11DF46E8194CE33F0072FDE3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 11DF46E6194CE33F0072FDE3 /* Main.storyboard */; };
13 | 11DF46EA194CE33F0072FDE3 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 11DF46E9194CE33F0072FDE3 /* Images.xcassets */; };
14 | 11DF46F6194CE3400072FDE3 /* WebViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11DF46F5194CE3400072FDE3 /* WebViewTests.swift */; };
15 | /* End PBXBuildFile section */
16 |
17 | /* Begin PBXContainerItemProxy section */
18 | 11DF46F0194CE3400072FDE3 /* PBXContainerItemProxy */ = {
19 | isa = PBXContainerItemProxy;
20 | containerPortal = 11DF46D5194CE33F0072FDE3 /* Project object */;
21 | proxyType = 1;
22 | remoteGlobalIDString = 11DF46DC194CE33F0072FDE3;
23 | remoteInfo = WebView;
24 | };
25 | /* End PBXContainerItemProxy section */
26 |
27 | /* Begin PBXFileReference section */
28 | 11DF46DD194CE33F0072FDE3 /* WebView.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WebView.app; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 11DF46E1194CE33F0072FDE3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
30 | 11DF46E2194CE33F0072FDE3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
31 | 11DF46E4194CE33F0072FDE3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
32 | 11DF46E7194CE33F0072FDE3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
33 | 11DF46E9194CE33F0072FDE3 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
34 | 11DF46EF194CE3400072FDE3 /* WebViewTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WebViewTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 11DF46F4194CE3400072FDE3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
36 | 11DF46F5194CE3400072FDE3 /* WebViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewTests.swift; sourceTree = ""; };
37 | /* End PBXFileReference section */
38 |
39 | /* Begin PBXFrameworksBuildPhase section */
40 | 11DF46DA194CE33F0072FDE3 /* Frameworks */ = {
41 | isa = PBXFrameworksBuildPhase;
42 | buildActionMask = 2147483647;
43 | files = (
44 | );
45 | runOnlyForDeploymentPostprocessing = 0;
46 | };
47 | 11DF46EC194CE3400072FDE3 /* Frameworks */ = {
48 | isa = PBXFrameworksBuildPhase;
49 | buildActionMask = 2147483647;
50 | files = (
51 | );
52 | runOnlyForDeploymentPostprocessing = 0;
53 | };
54 | /* End PBXFrameworksBuildPhase section */
55 |
56 | /* Begin PBXGroup section */
57 | 11DF46D4194CE33F0072FDE3 = {
58 | isa = PBXGroup;
59 | children = (
60 | 11DF46DF194CE33F0072FDE3 /* WebView */,
61 | 11DF46F2194CE3400072FDE3 /* WebViewTests */,
62 | 11DF46DE194CE33F0072FDE3 /* Products */,
63 | );
64 | sourceTree = "";
65 | };
66 | 11DF46DE194CE33F0072FDE3 /* Products */ = {
67 | isa = PBXGroup;
68 | children = (
69 | 11DF46DD194CE33F0072FDE3 /* WebView.app */,
70 | 11DF46EF194CE3400072FDE3 /* WebViewTests.xctest */,
71 | );
72 | name = Products;
73 | sourceTree = "";
74 | };
75 | 11DF46DF194CE33F0072FDE3 /* WebView */ = {
76 | isa = PBXGroup;
77 | children = (
78 | 11DF46E2194CE33F0072FDE3 /* AppDelegate.swift */,
79 | 11DF46E4194CE33F0072FDE3 /* ViewController.swift */,
80 | 11DF46E6194CE33F0072FDE3 /* Main.storyboard */,
81 | 11DF46E9194CE33F0072FDE3 /* Images.xcassets */,
82 | 11DF46E0194CE33F0072FDE3 /* Supporting Files */,
83 | );
84 | path = WebView;
85 | sourceTree = "";
86 | };
87 | 11DF46E0194CE33F0072FDE3 /* Supporting Files */ = {
88 | isa = PBXGroup;
89 | children = (
90 | 11DF46E1194CE33F0072FDE3 /* Info.plist */,
91 | );
92 | name = "Supporting Files";
93 | sourceTree = "";
94 | };
95 | 11DF46F2194CE3400072FDE3 /* WebViewTests */ = {
96 | isa = PBXGroup;
97 | children = (
98 | 11DF46F5194CE3400072FDE3 /* WebViewTests.swift */,
99 | 11DF46F3194CE3400072FDE3 /* Supporting Files */,
100 | );
101 | path = WebViewTests;
102 | sourceTree = "";
103 | };
104 | 11DF46F3194CE3400072FDE3 /* Supporting Files */ = {
105 | isa = PBXGroup;
106 | children = (
107 | 11DF46F4194CE3400072FDE3 /* Info.plist */,
108 | );
109 | name = "Supporting Files";
110 | sourceTree = "";
111 | };
112 | /* End PBXGroup section */
113 |
114 | /* Begin PBXNativeTarget section */
115 | 11DF46DC194CE33F0072FDE3 /* WebView */ = {
116 | isa = PBXNativeTarget;
117 | buildConfigurationList = 11DF46F9194CE3400072FDE3 /* Build configuration list for PBXNativeTarget "WebView" */;
118 | buildPhases = (
119 | 11DF46D9194CE33F0072FDE3 /* Sources */,
120 | 11DF46DA194CE33F0072FDE3 /* Frameworks */,
121 | 11DF46DB194CE33F0072FDE3 /* Resources */,
122 | );
123 | buildRules = (
124 | );
125 | dependencies = (
126 | );
127 | name = WebView;
128 | productName = WebView;
129 | productReference = 11DF46DD194CE33F0072FDE3 /* WebView.app */;
130 | productType = "com.apple.product-type.application";
131 | };
132 | 11DF46EE194CE3400072FDE3 /* WebViewTests */ = {
133 | isa = PBXNativeTarget;
134 | buildConfigurationList = 11DF46FC194CE3400072FDE3 /* Build configuration list for PBXNativeTarget "WebViewTests" */;
135 | buildPhases = (
136 | 11DF46EB194CE3400072FDE3 /* Sources */,
137 | 11DF46EC194CE3400072FDE3 /* Frameworks */,
138 | 11DF46ED194CE3400072FDE3 /* Resources */,
139 | );
140 | buildRules = (
141 | );
142 | dependencies = (
143 | 11DF46F1194CE3400072FDE3 /* PBXTargetDependency */,
144 | );
145 | name = WebViewTests;
146 | productName = WebViewTests;
147 | productReference = 11DF46EF194CE3400072FDE3 /* WebViewTests.xctest */;
148 | productType = "com.apple.product-type.bundle.unit-test";
149 | };
150 | /* End PBXNativeTarget section */
151 |
152 | /* Begin PBXProject section */
153 | 11DF46D5194CE33F0072FDE3 /* Project object */ = {
154 | isa = PBXProject;
155 | attributes = {
156 | LastUpgradeCheck = 0600;
157 | ORGANIZATIONNAME = "破锣锅";
158 | TargetAttributes = {
159 | 11DF46DC194CE33F0072FDE3 = {
160 | CreatedOnToolsVersion = 6.0;
161 | };
162 | 11DF46EE194CE3400072FDE3 = {
163 | CreatedOnToolsVersion = 6.0;
164 | TestTargetID = 11DF46DC194CE33F0072FDE3;
165 | };
166 | };
167 | };
168 | buildConfigurationList = 11DF46D8194CE33F0072FDE3 /* Build configuration list for PBXProject "WebView" */;
169 | compatibilityVersion = "Xcode 3.2";
170 | developmentRegion = English;
171 | hasScannedForEncodings = 0;
172 | knownRegions = (
173 | en,
174 | Base,
175 | );
176 | mainGroup = 11DF46D4194CE33F0072FDE3;
177 | productRefGroup = 11DF46DE194CE33F0072FDE3 /* Products */;
178 | projectDirPath = "";
179 | projectRoot = "";
180 | targets = (
181 | 11DF46DC194CE33F0072FDE3 /* WebView */,
182 | 11DF46EE194CE3400072FDE3 /* WebViewTests */,
183 | );
184 | };
185 | /* End PBXProject section */
186 |
187 | /* Begin PBXResourcesBuildPhase section */
188 | 11DF46DB194CE33F0072FDE3 /* Resources */ = {
189 | isa = PBXResourcesBuildPhase;
190 | buildActionMask = 2147483647;
191 | files = (
192 | 11DF46E8194CE33F0072FDE3 /* Main.storyboard in Resources */,
193 | 11DF46EA194CE33F0072FDE3 /* Images.xcassets in Resources */,
194 | );
195 | runOnlyForDeploymentPostprocessing = 0;
196 | };
197 | 11DF46ED194CE3400072FDE3 /* Resources */ = {
198 | isa = PBXResourcesBuildPhase;
199 | buildActionMask = 2147483647;
200 | files = (
201 | );
202 | runOnlyForDeploymentPostprocessing = 0;
203 | };
204 | /* End PBXResourcesBuildPhase section */
205 |
206 | /* Begin PBXSourcesBuildPhase section */
207 | 11DF46D9194CE33F0072FDE3 /* Sources */ = {
208 | isa = PBXSourcesBuildPhase;
209 | buildActionMask = 2147483647;
210 | files = (
211 | 11DF46E5194CE33F0072FDE3 /* ViewController.swift in Sources */,
212 | 11DF46E3194CE33F0072FDE3 /* AppDelegate.swift in Sources */,
213 | );
214 | runOnlyForDeploymentPostprocessing = 0;
215 | };
216 | 11DF46EB194CE3400072FDE3 /* Sources */ = {
217 | isa = PBXSourcesBuildPhase;
218 | buildActionMask = 2147483647;
219 | files = (
220 | 11DF46F6194CE3400072FDE3 /* WebViewTests.swift in Sources */,
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | };
224 | /* End PBXSourcesBuildPhase section */
225 |
226 | /* Begin PBXTargetDependency section */
227 | 11DF46F1194CE3400072FDE3 /* PBXTargetDependency */ = {
228 | isa = PBXTargetDependency;
229 | target = 11DF46DC194CE33F0072FDE3 /* WebView */;
230 | targetProxy = 11DF46F0194CE3400072FDE3 /* PBXContainerItemProxy */;
231 | };
232 | /* End PBXTargetDependency section */
233 |
234 | /* Begin PBXVariantGroup section */
235 | 11DF46E6194CE33F0072FDE3 /* Main.storyboard */ = {
236 | isa = PBXVariantGroup;
237 | children = (
238 | 11DF46E7194CE33F0072FDE3 /* Base */,
239 | );
240 | name = Main.storyboard;
241 | sourceTree = "";
242 | };
243 | /* End PBXVariantGroup section */
244 |
245 | /* Begin XCBuildConfiguration section */
246 | 11DF46F7194CE3400072FDE3 /* Debug */ = {
247 | isa = XCBuildConfiguration;
248 | buildSettings = {
249 | ALWAYS_SEARCH_USER_PATHS = NO;
250 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
251 | CLANG_CXX_LIBRARY = "libc++";
252 | CLANG_ENABLE_MODULES = YES;
253 | CLANG_ENABLE_OBJC_ARC = YES;
254 | CLANG_WARN_BOOL_CONVERSION = YES;
255 | CLANG_WARN_CONSTANT_CONVERSION = YES;
256 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257 | CLANG_WARN_EMPTY_BODY = YES;
258 | CLANG_WARN_ENUM_CONVERSION = YES;
259 | CLANG_WARN_INT_CONVERSION = YES;
260 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
261 | CLANG_WARN_UNREACHABLE_CODE = YES;
262 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
263 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
264 | COPY_PHASE_STRIP = NO;
265 | ENABLE_STRICT_OBJC_MSGSEND = YES;
266 | GCC_C_LANGUAGE_STANDARD = gnu99;
267 | GCC_DYNAMIC_NO_PIC = NO;
268 | GCC_OPTIMIZATION_LEVEL = 0;
269 | GCC_PREPROCESSOR_DEFINITIONS = (
270 | "DEBUG=1",
271 | "$(inherited)",
272 | );
273 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
274 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
275 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
276 | GCC_WARN_UNDECLARED_SELECTOR = YES;
277 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
278 | GCC_WARN_UNUSED_FUNCTION = YES;
279 | GCC_WARN_UNUSED_VARIABLE = YES;
280 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
281 | METAL_ENABLE_DEBUG_INFO = YES;
282 | ONLY_ACTIVE_ARCH = YES;
283 | SDKROOT = iphoneos;
284 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
285 | };
286 | name = Debug;
287 | };
288 | 11DF46F8194CE3400072FDE3 /* Release */ = {
289 | isa = XCBuildConfiguration;
290 | buildSettings = {
291 | ALWAYS_SEARCH_USER_PATHS = NO;
292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
293 | CLANG_CXX_LIBRARY = "libc++";
294 | CLANG_ENABLE_MODULES = YES;
295 | CLANG_ENABLE_OBJC_ARC = YES;
296 | CLANG_WARN_BOOL_CONVERSION = YES;
297 | CLANG_WARN_CONSTANT_CONVERSION = YES;
298 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
299 | CLANG_WARN_EMPTY_BODY = YES;
300 | CLANG_WARN_ENUM_CONVERSION = YES;
301 | CLANG_WARN_INT_CONVERSION = YES;
302 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
303 | CLANG_WARN_UNREACHABLE_CODE = YES;
304 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
305 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
306 | COPY_PHASE_STRIP = YES;
307 | ENABLE_NS_ASSERTIONS = NO;
308 | ENABLE_STRICT_OBJC_MSGSEND = YES;
309 | GCC_C_LANGUAGE_STANDARD = gnu99;
310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
312 | GCC_WARN_UNDECLARED_SELECTOR = YES;
313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
314 | GCC_WARN_UNUSED_FUNCTION = YES;
315 | GCC_WARN_UNUSED_VARIABLE = YES;
316 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
317 | METAL_ENABLE_DEBUG_INFO = NO;
318 | SDKROOT = iphoneos;
319 | VALIDATE_PRODUCT = YES;
320 | };
321 | name = Release;
322 | };
323 | 11DF46FA194CE3400072FDE3 /* Debug */ = {
324 | isa = XCBuildConfiguration;
325 | buildSettings = {
326 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
327 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
328 | INFOPLIST_FILE = WebView/Info.plist;
329 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
330 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
331 | PRODUCT_NAME = "$(TARGET_NAME)";
332 | };
333 | name = Debug;
334 | };
335 | 11DF46FB194CE3400072FDE3 /* Release */ = {
336 | isa = XCBuildConfiguration;
337 | buildSettings = {
338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
339 | ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
340 | INFOPLIST_FILE = WebView/Info.plist;
341 | IPHONEOS_DEPLOYMENT_TARGET = 7.1;
342 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
343 | PRODUCT_NAME = "$(TARGET_NAME)";
344 | };
345 | name = Release;
346 | };
347 | 11DF46FD194CE3400072FDE3 /* Debug */ = {
348 | isa = XCBuildConfiguration;
349 | buildSettings = {
350 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WebView.app/WebView";
351 | FRAMEWORK_SEARCH_PATHS = (
352 | "$(SDKROOT)/Developer/Library/Frameworks",
353 | "$(inherited)",
354 | );
355 | GCC_PREPROCESSOR_DEFINITIONS = (
356 | "DEBUG=1",
357 | "$(inherited)",
358 | );
359 | INFOPLIST_FILE = WebViewTests/Info.plist;
360 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
361 | METAL_ENABLE_DEBUG_INFO = YES;
362 | PRODUCT_NAME = "$(TARGET_NAME)";
363 | TEST_HOST = "$(BUNDLE_LOADER)";
364 | };
365 | name = Debug;
366 | };
367 | 11DF46FE194CE3400072FDE3 /* Release */ = {
368 | isa = XCBuildConfiguration;
369 | buildSettings = {
370 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/WebView.app/WebView";
371 | FRAMEWORK_SEARCH_PATHS = (
372 | "$(SDKROOT)/Developer/Library/Frameworks",
373 | "$(inherited)",
374 | );
375 | INFOPLIST_FILE = WebViewTests/Info.plist;
376 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
377 | METAL_ENABLE_DEBUG_INFO = NO;
378 | PRODUCT_NAME = "$(TARGET_NAME)";
379 | TEST_HOST = "$(BUNDLE_LOADER)";
380 | };
381 | name = Release;
382 | };
383 | /* End XCBuildConfiguration section */
384 |
385 | /* Begin XCConfigurationList section */
386 | 11DF46D8194CE33F0072FDE3 /* Build configuration list for PBXProject "WebView" */ = {
387 | isa = XCConfigurationList;
388 | buildConfigurations = (
389 | 11DF46F7194CE3400072FDE3 /* Debug */,
390 | 11DF46F8194CE3400072FDE3 /* Release */,
391 | );
392 | defaultConfigurationIsVisible = 0;
393 | defaultConfigurationName = Release;
394 | };
395 | 11DF46F9194CE3400072FDE3 /* Build configuration list for PBXNativeTarget "WebView" */ = {
396 | isa = XCConfigurationList;
397 | buildConfigurations = (
398 | 11DF46FA194CE3400072FDE3 /* Debug */,
399 | 11DF46FB194CE3400072FDE3 /* Release */,
400 | );
401 | defaultConfigurationIsVisible = 0;
402 | };
403 | 11DF46FC194CE3400072FDE3 /* Build configuration list for PBXNativeTarget "WebViewTests" */ = {
404 | isa = XCConfigurationList;
405 | buildConfigurations = (
406 | 11DF46FD194CE3400072FDE3 /* Debug */,
407 | 11DF46FE194CE3400072FDE3 /* Release */,
408 | );
409 | defaultConfigurationIsVisible = 0;
410 | };
411 | /* End XCConfigurationList section */
412 | };
413 | rootObject = 11DF46D5194CE33F0072FDE3 /* Project object */;
414 | }
415 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A Bridge Between H5 And Native.
3 | * By: Guokai @benbencc
4 | * Created: 2013-7-24
5 | * */
6 |
7 | // 面向Android和iOS的JS调用原理:
8 |
9 | // Android 通过桥(客户端暴露在WebView全局对象下的一个对象,内挂各种API方法)
10 | // 的方式进行调用,如`window['Android_Bridge']['method'](JSON)`。iOS通过自定
11 | // 义Scheme(如`native://method?data=JSON`)方式调用。需要回调的接口需要将函
12 | // 数名称在调用时一并传给客户端,同时将回调函数通过唯一名称挂在全局,待客户端
13 | // 执行回调后移除该全局函数。
14 |
15 | (function() {
16 |
17 | // Bridge是为H5和客户端交互通讯而产生的一个中间件,即一个JavaScript的SDK。负责
18 | // 处理H5和客户端的方法调用、通信及H5页面自身的降级处理(非内嵌在客户端的情况)。
19 |
20 | var Bridge = function() {
21 | this.init.apply(this, arguments);
22 | };
23 |
24 | Bridge.prototype = {
25 | version: '0.0.1',
26 |
27 | // 桥初始化,初始化过程中会:主动探测UA,判断页面所属的生存环境,如果是在客户端中,
28 | // 会探测获取客户端的平台类型和版本号,初始化消息队列,并主动探测网络类型,并同所属
29 | // 环境主动握手交换双方所需的额外基础信息。
30 |
31 | init: function(bridgeName) {
32 | var that = this;
33 |
34 | that.platform = 'h5';
35 | that.bridgeName = bridgeName || 'ali_trip_webview_bridge';
36 | that.bridge = window[that.bridgeName];
37 | that.userAgentDetect.apply(this, arguments);
38 | that.messageQueueInit();
39 | that.deviceInfoDetect();
40 | that.connectionInfoDetect();
41 | that.handShake.apply(this, arguments);
42 |
43 | that.notification.superthat = that;
44 | },
45 |
46 | // 消息队列初始化,针对iOS的实现机制单独做的处理,iOS会主动轮询
47 | // 消息队列中的操作,并批量取回处理。
48 |
49 | messageQueueInit: function() {
50 | var that = this;
51 |
52 | if(that.platform === 'ios') {
53 | window.messageQueue = [];
54 | window.messageQueueFetch = function() {
55 | var response;
56 | response = window.messageQueue.length ? JSON.stringify(window.messageQueue) : '';
57 | window.messageQueue = [];
58 |
59 | if(response) {
60 | return response;
61 | }
62 | };
63 | }
64 | },
65 |
66 | // 握手协议,用于页面同所属生存环境在首次初始化时进行基础信息交换。
67 |
68 | handShake: function(callback) {
69 | var that = this;
70 |
71 | that.pushBack('bridge:', 'ready', {
72 | data: {},
73 | successCallback: function() {
74 | callback && callback();
75 | }
76 | });
77 | },
78 |
79 | // UA探测,用于检测当前页面是否生存在淘宝旅行客户端内,如果在客户端内
80 | // 则继续获取客户端的平台类型及版本号用于后续的操作。
81 |
82 | // User-Agent Format:
83 |
84 | // Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; zh-cn; AliTrip/2.8.0) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile Safari/528.16
85 | // Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI 2 Build/JRO03L; AliTrip/2.8.0) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile Safari/528.16
86 |
87 | // References:
88 |
89 | // http://www.ietf.org/rfc/rfc2616.txt
90 | // http://www.useragentstring.com/
91 |
92 | userAgentDetect: function() {
93 | var that = this;
94 | var ua = navigator.userAgent;
95 | var match = ua.match(/AliTrip[\s\/][\d\.]+/igm);
96 |
97 | if(match) {
98 | that.platform = ua.match(/(iPad|iPhone|iPod)/igm) ? 'ios' : 'android';
99 | that.client.version = parseInt(match[0].match(/[\d\.]+/igm)[0].split('.').join(''));
100 | }
101 | },
102 |
103 | // 设备信息探测,用于检测当前页面的设备信息。
104 |
105 | deviceInfoDetect: function() {
106 | var that = this;
107 |
108 | if(that.platform === 'h5') {
109 | return;
110 | }
111 |
112 | that.pushBack('bridge:', 'client_info', {
113 | successCallback: function(client_info) {
114 | client_info = JSON.parse(client_info || '{}');
115 | client_info.version = that.client.version; //hack, android 3.6.0以下返回的字段缺失
116 | that.client = client_info; //ttid, push_token, device_id, client_version, client_type
117 | }
118 | });
119 | },
120 |
121 | // 网络类型探测,用于检测当前环境的页面类型,如果是生存在浏览器中,
122 | // 则取浏览器的`navigator.connection`属性,如果生存在客户端中,则
123 | // 根据协议接口进行获取,并作为桥的属性后续可被访问到。
124 |
125 | // ```
126 | // {
127 | // "type" : "2",
128 | // "mapping": {
129 | // "UNKNOWN": 0,
130 | // "ETHERNET": 1,
131 | // "WIFI": 2,
132 | // "CELL_2G": 3,
133 | // "CELL_3G": 4,
134 | // "CELL_4G": 5,
135 | // "CELL": 6,
136 | // "NONE": 7,
137 | // }
138 | // }
139 | // ```
140 |
141 | connectionInfoDetect: function() {
142 | var that = this;
143 |
144 | if(that.platform === 'h5') {
145 | that.connection = navigator.connection || {};
146 | return;
147 | }
148 |
149 | that.pushBack('bridge:', 'networktype', {
150 | successCallback: function(conn) {
151 | that.connection.type = conn;
152 | }
153 | });
154 | },
155 |
156 | sendURI: function(uri, newProxy) {
157 | var proxy = this.mClientProxy;
158 |
159 | if (newProxy) {
160 | this.buildProxy(uri);
161 | return this;
162 | }
163 |
164 | if (proxy || (proxy = document.querySelector('#J_MClientProxy'))) {
165 | proxy.attr('src', uri);
166 | } else {
167 | proxy = this.buildProxy(uri);
168 | }
169 |
170 | this.mClientProxy = proxy;
171 |
172 | return this;
173 | },
174 |
175 | buildRandom: function() {
176 | var that = this;
177 | var random = new Date().getTime() + '_' + parseInt(Math.random() * 1000000);
178 |
179 | return random;
180 | },
181 |
182 | buildProxy: function(uri) {
183 | var that = this;
184 | var guid = that.buildRandom();
185 | var iframeString = '';
186 | var proxy = $(iframeString);
187 |
188 | $('body').append(proxy);
189 | return proxy;
190 | },
191 |
192 | buildCallback: function(fn) {
193 | var that = this;
194 | var guid = that.buildRandom();
195 | var callbackName = 'Bridge_Callbacks_' + guid;
196 |
197 | window[callbackName] = (function(cb, callbackName) {
198 | return function() {
199 | cb.apply(this, arguments);
200 | delete window[callbackName];
201 | };
202 | })(fn, callbackName);
203 |
204 | return callbackName;
205 | },
206 |
207 | // 主要API,用于页面同客户端的协议回调,`protocol`协议默认为`native:`,
208 | // 可以省略不传,`host`为协议约定的主体,如`app/beep`,`data`为调用时
209 | // 传递给客户端的所需数据对象,对象内可包含`successCallback`和`failCallback`,
210 | // 鉴于iOS回调的实现机制,`newProxy`参数是为了避免在iOS下多次连续回调
211 | // 造成的消息丢失,当设为`true`时,每次均会创建一个新的`iframe`进行发送。
212 |
213 | pushBack: function(protocol, host, data, newProxy) {
214 | var that = this;
215 | var uri = (protocol || 'native:') + '//' + host + '?params=';
216 | var callbackName;
217 | var args = [].slice.call(arguments);
218 |
219 | if(typeof(protocol) !== 'string' || typeof(host) !== 'string') {
220 | protocol = 'native:';
221 | host = args[0];
222 | data = args[1];
223 | newProxy = args[2];
224 | uri = (protocol || 'native:') + '//' + host + '?params=';
225 | }
226 |
227 | if(that.platform === 'h5') {
228 | return;
229 | }
230 |
231 | data = data || {};
232 |
233 | for(var i in data) {
234 | if(data.hasOwnProperty(i)) {
235 | if(typeof(data[i]) === 'function') {
236 | callbackName = that.buildCallback(data[i]);
237 | data[i] = callbackName;
238 | }
239 |
240 | if(typeof(data[i]) === 'object' && data[i].hasOwnProperty('length')) {
241 | for(var j = 0; j < data[i].length; j++) {
242 | if(typeof(data[i][j]) === 'function') {
243 | callbackName = that.buildCallback(data[i][j]);
244 | data[i][j] = callbackName;
245 | }
246 | }
247 | }
248 |
249 | if(i !== i.replace(/([A-Z])/g,"_$1").toLowerCase()) {
250 | data[i.replace(/([A-Z])/g,"_$1").toLowerCase()] = data[i];
251 | delete data[i];
252 | }
253 | }
254 | }
255 |
256 | if(that.platform === 'android' && that.client.version < 360) {
257 | uri += encodeURIComponent(JSON.stringify(data));
258 |
259 | if(uri.match(/^native:\/\//igm)) {
260 | that.bridge && that.bridge['startNativeService'] && that.bridge['startNativeService'](uri);
261 | }
262 |
263 | if(uri.match(/^page:\/\//igm)) {
264 | that.bridge && that.bridge['startNativePage'] && that.bridge['startNativePage'](uri);
265 | }
266 |
267 | if(uri.match(/^bridge:\/\//igm)) {
268 | that.bridge && that.bridge['startNativeBridge'] && that.bridge['startNativeBridge'](uri);
269 | }
270 |
271 | return;
272 | }
273 |
274 | if(that.platform === 'android' && that.client.version >= 360) {
275 | uri += encodeURIComponent(JSON.stringify(data));
276 | prompt('alitrip-android://' + uri);
277 | return;
278 | }
279 |
280 | if(that.platform === 'ios') {
281 | uri += encodeURIComponent(JSON.stringify(data));
282 | messageQueue.push(uri);
283 | return;
284 | }
285 |
286 | uri += encodeURIComponent(JSON.stringify(data));
287 | that.sendURI(uri, newProxy);
288 | },
289 |
290 | getRequestParam: function(uri, param) {
291 | var value;
292 | uri = uri || window.location.href;
293 | value = uri.match(new RegExp('[\?\&]' + param + '=([^\&]*)(\&?)', 'i'));
294 | return value ? decodeURIComponent(value[1]) : value;
295 | },
296 |
297 | getRequestParams: function(uri) {
298 | var search = location.search.substring(1);
299 | uri = uri || window.location.href;
300 | return search ? JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) {
301 | return key==="" ? value : decodeURIComponent(value);
302 | }) : {};
303 | },
304 |
305 | // 获取URL和客户端传递的所有参数,会优先获取URL中的参数
306 |
307 | getParams: function() {
308 | var that = this;
309 | var params = that.getRequestParams();
310 | var client = that.client;
311 |
312 | for(var i in client) {
313 | if(client.hasOwnProperty(i) && params.hasOwnProperty(i)) {
314 | params[i] = client[i];
315 | }
316 | }
317 |
318 | return params;
319 | },
320 |
321 | notification: {
322 |
323 | // `alert`弹框,对于浏览器的生存环境,仅`message`参数有效,但对于客户端类
324 | // 生存环境,则可以接收回调,以及弹出框的标题和按钮文案,回调中客户端会回
325 | // 传用户相应点击的按钮的索引值。
326 |
327 | alert: function(message, alertCallback, title, buttonLabels) {
328 | var that = this;
329 | var callback = function(ret) {
330 | var buttonIndex = ret.buttonIndex;
331 |
332 | if(Object.prototype.toString.call(alertCallback) === '[object Array]') {
333 | alertCallback[buttonIndex] && alertCallback[buttonIndex].apply(this, arguments);
334 | return;
335 | }
336 |
337 | alertCallback && alertCallback();
338 | };
339 |
340 | if(that.superthat.platform === 'h5') {
341 | alert.apply(null, arguments);
342 | return;
343 | }
344 |
345 | if(typeof(buttonLabels) === 'string') {
346 | buttonLabels = [buttonLabels];
347 | }
348 |
349 | that.superthat.pushBack('bridge:', 'alert', {
350 | message: message,
351 | successCallback: callback,
352 | title: title,
353 | buttonNames: buttonLabels
354 | });
355 | },
356 |
357 | // `confirm`确认对话框,对于浏览器的生存环境,仅`message`和`confirmCallback`
358 | // 参数有效,但对于客户端类生存环境,则可以接收回调,以及弹出框的标题和按钮
359 | // 文案,回调中客户端会回传用户相应点击的按钮的索引值。
360 |
361 | confirm: function(message, confirmCallback, title, buttonLabels) {
362 | var that = this;
363 |
364 | if(that.superthat.platform === 'h5') {
365 | if(confirm.apply(null, arguments)) {
366 | confirmCallback && confirmCallback();
367 | }
368 |
369 | return;
370 | }
371 |
372 | if(typeof(buttonLabels) === 'string') {
373 | buttonLabels = [buttonLabels];
374 | }
375 |
376 | that.superthat.pushBack('bridge:', 'confirm', {
377 | message: message,
378 | title: title,
379 | successCallback: confirmCallback,
380 | buttonNames: buttonLabels
381 | });
382 | },
383 |
384 | // `prompt`提示对话框,对于浏览器的生存环境,仅`message`、`value`和
385 | // `promptCallback`参数有效,但对于客户端类生存环境,则可以接收回调,
386 | // 以及弹出框的标题和按钮文案,回调中客户端会回传用户相应点击的按钮
387 | // 的索引值。
388 |
389 | prompt: function(message, value, promptCallback, title, buttonLabels) {
390 | var that = this;
391 |
392 | if(that.superthat.platform === 'h5') {
393 | prompt.apply(null, arguments);
394 | return;
395 | }
396 |
397 | if(typeof(buttonLabels) === 'string') {
398 | buttonLabels = [buttonLabels];
399 | }
400 |
401 | that.superthat.pushBack('bridge:', 'prompt', {
402 | message: message,
403 | value: value,
404 | successCallback: promptCallback,
405 | title: title,
406 | buttonNames: buttonLabels
407 | });
408 | },
409 |
410 | // 弱提示,用于调用客户端的弱提示进行些许文案的提示。
411 |
412 | toast: function(message, milliseconds) {
413 | var that = this;
414 |
415 | if(that.superthat.platform === 'h5') {
416 | return;
417 | }
418 |
419 | that.superthat.pushBack('bridge:', 'toast', {
420 | message: message,
421 | milliseconds: milliseconds
422 | });
423 | },
424 |
425 | // 蜂鸣,调用客户端的蜂鸣器按照指定的次数进行蜂鸣。
426 |
427 | beep: function(times) {
428 | var that = this;
429 |
430 | that.superthat.pushBack('bridge:', 'beep', {
431 | times: times
432 | });
433 | },
434 |
435 | // 震动,进行设备的震动,持续指定的毫秒时长。
436 |
437 | vibrate: function(milliseconds) {
438 | var that = this;
439 |
440 | that.superthat.pushBack('bridge:', 'vibrate', {
441 | milliseconds: milliseconds
442 | });
443 | }
444 | },
445 |
446 | device: {},
447 |
448 | client: {},
449 |
450 | connection: {},
451 |
452 | // 新开页面打开页面,浏览器生存环境下打开新的标签页,客户端生存环境
453 | // 下则调用系统浏览器打开相应新的页面。
454 |
455 | openBrowser: function(url) {
456 | var that = this;
457 |
458 | if(that.platform === 'h5') {
459 | window.open(url);
460 | return;
461 | }
462 |
463 | that.pushBack('bridge:', 'open_system_browser', {
464 | url: url
465 | });
466 | },
467 |
468 | // 针对iOS,跳转并打开AppStore的相应地址,对于浏览器而言则在新标签
469 | // 中打开对应的应用地址。
470 |
471 | openAppStore: function(url) {
472 | var that = this;
473 |
474 | if(that.platform === 'h5') {
475 | window.open(url);
476 | return;
477 | }
478 |
479 | that.pushBack('bridge:', 'open_app_store', {
480 | url: url
481 | });
482 | },
483 |
484 | // 客户端生存环境下设置WebView顶部的标题显示文案,对于浏览器则直接
485 | // 更改页面的标题文案。子标题为可选参数,只适用在客户端内嵌的情况。
486 |
487 | setTitle: function(title, subtitle) {
488 | var that = this;
489 |
490 | if(that.platform === 'h5') {
491 | document.title = title;
492 | return;
493 | }
494 |
495 | that.pushBack('bridge:', 'set_webview_title', {
496 | title: title,
497 | subtitle: subtitle
498 | });
499 | },
500 |
501 | // 跳转到相应的客户端页面,如果有额外的数据则通过`ext`参数传递给客户端。
502 |
503 | open: function(pagename, ext, successCallback, naviType, animeType) {
504 | var that = this;
505 | var params = {
506 | page_name: pagename,
507 | data: ext || {},
508 | successCallback: successCallback,
509 | naviType: naviType || 0,
510 | animeType: typeof(animeType) !== 'undefined' ? animeType : 4
511 | };
512 |
513 | that.pushBack('page:', 'goto', params);
514 | },
515 |
516 | // 返回上一页,对于浏览器而言,直接调用`history.back`返回上一个历史记录,
517 | // 对于客户端而言则通过协议调用返回上一个WebView打开的相应页面。
518 |
519 | back: function(pagename, ext) {
520 | var that = this;
521 |
522 | if(that.platform === 'h5') {
523 | history.back();
524 | return;
525 | }
526 |
527 | if(pagename) {
528 | that.pushBack('bridge:', 'back', ext);
529 | return;
530 | }
531 |
532 | that.pushBack('bridge:', 'back', ext);
533 | },
534 |
535 | // 关闭当前的客户端页面,如果有额外的数据则通过`ext`参数传递给客户端。
536 |
537 | close: function(ext) {
538 | var that = this;
539 |
540 | if(that.platform === 'h5') {
541 | window.close();
542 | return;
543 | }
544 |
545 | that.pushBack('page:', 'close', ext);
546 | },
547 |
548 | // 客户端极简支付,如果有额外的数据则通过`ext`参数传递给客户端。
549 |
550 | minipay: function(alipayId, nextUrl, ext, successCallback, failCallback) {
551 | var that = this;
552 | var payResult;
553 | var params = {
554 | alipay_id: alipayId,
555 | data: ext || {},
556 | failCallback: failCallback,
557 | successCallback: function(ret) {
558 | try {
559 | payResult = JSON.parse(ret);
560 | } catch(e) {
561 | // iOS客户端3.5版本返回的JSON格式有误,解析会出问题
562 | if(ret.match(/"ResultStatus":"9000"/igm)) {
563 | payResult = {
564 | ResultStatus: '9000'
565 | };
566 | }
567 | }
568 |
569 | // iOS SDK返回的字段名首字母是大写,暂时兼容处理
570 | if(that.platform === 'ios') {
571 | payResult.resultStatus = payResult.ResultStatus;
572 | }
573 |
574 | if(payResult.resultStatus === '9000') {
575 | successCallback && successCallback(payResult);
576 | return;
577 | }
578 |
579 | failCallback && failCallback(payResult);
580 | }
581 | };
582 |
583 | if(that.platform === 'h5' && nextUrl) {
584 | window.location.href = nextUrl;
585 | return;
586 | }
587 |
588 | if(that.client.version < 350 && nextUrl) {
589 | window.location.href = nextUrl;
590 | return;
591 | }
592 |
593 | that.pushBack('bridge:', 'minipay', params);
594 | }
595 | };
596 |
597 | this.Bridge = Bridge;
598 |
599 | }).call(this);
600 |
601 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | index.js
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
index.js
19 |
20 |
21 |
22 |
23 |
24 |
25 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
48 |
面向Android和iOS的JS调用原理:
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
61 |
Android 通过桥(客户端暴露在WebView全局对象下的一个对象,内挂各种API方法)
62 | 的方式进行调用,如window['Android_Bridge']['method'](JSON)。iOS通过自定
63 | 义Scheme(如native://method?data=JSON)方式调用。需要回调的接口需要将函
64 | 数名称在调用时一并传给客户端,同时将回调函数通过唯一名称挂在全局,待客户端
65 | 执行回调后移除该全局函数。
66 |
67 |
68 |
69 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
81 |
Bridge是为H5和客户端交互通讯而产生的一个中间件,即一个JavaScript的SDK。负责
82 | 处理H5和客户端的方法调用、通信及H5页面自身的降级处理(非内嵌在客户端的情况)。
83 |
84 |
85 |
86 |
87 | var Bridge = function () {
88 | this .init.apply(this , arguments );
89 | };
90 |
91 | Bridge.prototype = {
92 | version: '0.0.1' ,
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
103 |
桥初始化,初始化过程中会:主动探测UA,判断页面所属的生存环境,如果是在客户端中,
104 | 会探测获取客户端的平台类型和版本号,初始化消息队列,并主动探测网络类型,并同所属
105 | 环境主动握手交换双方所需的额外基础信息。
106 |
107 |
108 |
109 |
110 | init: function (bridgeName) {
111 | var that = this ;
112 |
113 | that.platform = 'h5' ;
114 | that.bridgeName = bridgeName || 'ali_trip_webview_bridge' ;
115 | that.bridge = window[that.bridgeName];
116 | that.userAgentDetect.apply(this , arguments );
117 | that.messageQueueInit();
118 | that.deviceInfoDetect();
119 | that.connectionInfoDetect();
120 | that.handShake.apply(this , arguments );
121 |
122 | that.notification.superthat = that;
123 | },
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
134 |
消息队列初始化,针对iOS的实现机制单独做的处理,iOS会主动轮询
135 | 消息队列中的操作,并批量取回处理。
136 |
137 |
138 |
139 |
140 | messageQueueInit: function () {
141 | var that = this ;
142 |
143 | if (that.platform === 'ios' ) {
144 | window.messageQueue = [];
145 | window.messageQueueFetch = function () {
146 | var response;
147 | response = window.messageQueue.length ? JSON .stringify(window.messageQueue) : '' ;
148 | window.messageQueue = [];
149 |
150 | if (response) {
151 | return response;
152 | }
153 | };
154 | }
155 | },
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
166 |
握手协议,用于页面同所属生存环境在首次初始化时进行基础信息交换。
167 |
168 |
169 |
170 |
171 | handShake: function (callback) {
172 | var that = this ;
173 |
174 | that.pushBack('bridge:' , 'ready' , {
175 | data: {},
176 | successCallback: function () {
177 | callback && callback();
178 | }
179 | });
180 | },
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
191 |
UA探测,用于检测当前页面是否生存在淘宝旅行客户端内,如果在客户端内
192 | 则继续获取客户端的平台类型及版本号用于后续的操作。
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
205 |
User-Agent Format:
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
218 |
Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_1_2 like Mac OS X; zh-cn; AliTrip/2.8.0) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile Safari/528.16
219 | Mozilla/5.0 (Linux; U; Android 4.1.1; zh-cn; MI 2 Build/JRO03L; AliTrip/2.8.0) AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0 Mobile Safari/528.16
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
232 |
References:
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
249 |
250 |
251 | userAgentDetect: function () {
252 | var that = this ;
253 | var ua = navigator.userAgent;
254 | var match = ua.match(/AliTrip[\s\/][\d\.]+/igm );
255 |
256 | if (match) {
257 | that.platform = ua.match(/(iPad|iPhone|iPod)/igm ) ? 'ios' : 'android' ;
258 | that.client.version = parseInt (match[0 ].match(/[\d\.]+/igm )[0 ].split('.' ).join('' ));
259 | }
260 | },
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
271 |
设备信息探测,用于检测当前页面的设备信息。
272 |
273 |
274 |
275 |
276 | deviceInfoDetect: function () {
277 | var that = this ;
278 |
279 | if (that.platform === 'h5' ) {
280 | return ;
281 | }
282 |
283 | that.pushBack('bridge:' , 'client_info' , {
284 | successCallback: function (client_info) {
285 | client_info = JSON .parse(client_info || '{}' );
286 | client_info.version = that.client.version;
287 | that.client = client_info;
288 | }
289 | });
290 | },
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
301 |
网络类型探测,用于检测当前环境的页面类型,如果是生存在浏览器中,
302 | 则取浏览器的navigator.connection属性,如果生存在客户端中,则
303 | 根据协议接口进行获取,并作为桥的属性后续可被访问到。
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
316 |
{
317 | "type" : "2" ,
318 | "mapping" : {
319 | "UNKNOWN" : 0 ,
320 | "ETHERNET" : 1 ,
321 | "WIFI" : 2 ,
322 | "CELL_2G" : 3 ,
323 | "CELL_3G" : 4 ,
324 | "CELL_4G" : 5 ,
325 | "CELL" : 6 ,
326 | "NONE" : 7 ,
327 | }
328 | }
329 |
330 |
331 |
332 |
333 | connectionInfoDetect: function () {
334 | var that = this ;
335 |
336 | if (that.platform === 'h5' ) {
337 | that.connection = navigator.connection || {};
338 | return ;
339 | }
340 |
341 | that.pushBack('bridge:' , 'networktype' , {
342 | successCallback: function (conn) {
343 | that.connection.type = conn;
344 | }
345 | });
346 | },
347 |
348 | sendURI: function (uri, newProxy) {
349 | var proxy = this .mClientProxy;
350 |
351 | if (newProxy) {
352 | this .buildProxy(uri);
353 | return this ;
354 | }
355 |
356 | if (proxy || (proxy = document.querySelector('#J_MClientProxy' ))) {
357 | proxy.attr('src' , uri);
358 | } else {
359 | proxy = this .buildProxy(uri);
360 | }
361 |
362 | this .mClientProxy = proxy;
363 |
364 | return this ;
365 | },
366 |
367 | buildRandom: function () {
368 | var that = this ;
369 | var random = new Date ().getTime() + '_' + parseInt (Math .random() * 1000000 );
370 |
371 | return random;
372 | },
373 |
374 | buildProxy: function (uri) {
375 | var that = this ;
376 | var guid = that.buildRandom();
377 | var iframeString = '<iframe id="J_MClientProxy_' + guid + '" class="hidden mclient-proxy" style="width:0;height:0;opacity:0;display:none;" src="' + uri + '"></iframe>' ;
378 | var proxy = $(iframeString);
379 |
380 | $('body' ).append(proxy);
381 | return proxy;
382 | },
383 |
384 | buildCallback: function (fn) {
385 | var that = this ;
386 | var guid = that.buildRandom();
387 | var callbackName = 'Bridge_Callbacks_' + guid;
388 |
389 | window[callbackName] = (function (cb, callbackName) {
390 | return function () {
391 | cb.apply(this , arguments );
392 | delete window[callbackName];
393 | };
394 | })(fn, callbackName);
395 |
396 | return callbackName;
397 | },
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
408 |
主要API,用于页面同客户端的协议回调,protocol协议默认为native:,
409 | 可以省略不传,host为协议约定的主体,如app/beep,data为调用时
410 | 传递给客户端的所需数据对象,对象内可包含successCallback和failCallback,
411 | 鉴于iOS回调的实现机制,newProxy参数是为了避免在iOS下多次连续回调
412 | 造成的消息丢失,当设为true时,每次均会创建一个新的iframe进行发送。
413 |
414 |
415 |
416 |
417 | pushBack: function (protocol, host, data, newProxy) {
418 | var that = this ;
419 | var uri = (protocol || 'native:' ) + '//' + host + '?params=' ;
420 | var callbackName;
421 | var args = [].slice.call(arguments );
422 |
423 | if (typeof (protocol) !== 'string' || typeof (host) !== 'string' ) {
424 | protocol = 'native:' ;
425 | host = args[0 ];
426 | data = args[1 ];
427 | newProxy = args[2 ];
428 | uri = (protocol || 'native:' ) + '//' + host + '?params=' ;
429 | }
430 |
431 | if (that.platform === 'h5' ) {
432 | return ;
433 | }
434 |
435 | data = data || {};
436 |
437 | for (var i in data) {
438 | if (data.hasOwnProperty(i)) {
439 | if (typeof (data[i]) === 'function' ) {
440 | callbackName = that.buildCallback(data[i]);
441 | data[i] = callbackName;
442 | }
443 |
444 | if (typeof (data[i]) === 'object' && data[i].hasOwnProperty('length' )) {
445 | for (var j = 0 ; j < data[i].length; j++) {
446 | if (typeof (data[i][j]) === 'function' ) {
447 | callbackName = that.buildCallback(data[i][j]);
448 | data[i][j] = callbackName;
449 | }
450 | }
451 | }
452 |
453 | if (i !== i.replace(/([A-Z])/g ,"_$1" ).toLowerCase()) {
454 | data[i.replace(/([A-Z])/g ,"_$1" ).toLowerCase()] = data[i];
455 | delete data[i];
456 | }
457 | }
458 | }
459 |
460 | if (that.platform === 'android' && that.client.version < 360 ) {
461 | uri += encodeURIComponent (JSON .stringify(data));
462 |
463 | if (uri.match(/^native:\/\//igm )) {
464 | that.bridge && that.bridge['startNativeService' ] && that.bridge['startNativeService' ](uri);
465 | }
466 |
467 | if (uri.match(/^page:\/\//igm )) {
468 | that.bridge && that.bridge['startNativePage' ] && that.bridge['startNativePage' ](uri);
469 | }
470 |
471 | if (uri.match(/^bridge:\/\//igm )) {
472 | that.bridge && that.bridge['startNativeBridge' ] && that.bridge['startNativeBridge' ](uri);
473 | }
474 |
475 | return ;
476 | }
477 |
478 | if (that.platform === 'android' && that.client.version >= 360 ) {
479 | uri += encodeURIComponent (JSON .stringify(data));
480 | prompt('alitrip-android://' + uri);
481 | return ;
482 | }
483 |
484 | if (that.platform === 'ios' ) {
485 | uri += encodeURIComponent (JSON .stringify(data));
486 | messageQueue.push(uri);
487 | return ;
488 | }
489 |
490 | uri += encodeURIComponent (JSON .stringify(data));
491 | that.sendURI(uri, newProxy);
492 | },
493 |
494 | getRequestParam: function (uri, param) {
495 | var value;
496 | uri = uri || window.location.href;
497 | value = uri.match(new RegExp ('[\?\&]' + param + '=([^\&]*)(\&?)' , 'i' ));
498 | return value ? decodeURIComponent (value[1 ]) : value;
499 | },
500 |
501 | getRequestParams: function (uri) {
502 | var search = location.search.substring(1 );
503 | uri = uri || window.location.href;
504 | return search ? JSON .parse('{"' + search.replace(/&/g , '","' ).replace(/=/g ,'":"' ) + '"}' , function (key, value) {
505 | return key==="" ? value : decodeURIComponent (value);
506 | }) : {};
507 | },
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
518 |
获取URL和客户端传递的所有参数,会优先获取URL中的参数
519 |
520 |
521 |
522 |
523 | getParams: function () {
524 | var that = this ;
525 | var params = that.getRequestParams();
526 | var client = that.client;
527 |
528 | for (var i in client) {
529 | if (client.hasOwnProperty(i) && params.hasOwnProperty(i)) {
530 | params[i] = client[i];
531 | }
532 | }
533 |
534 | return params;
535 | },
536 |
537 | notification: {
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
548 |
alert弹框,对于浏览器的生存环境,仅message参数有效,但对于客户端类
549 | 生存环境,则可以接收回调,以及弹出框的标题和按钮文案,回调中客户端会回
550 | 传用户相应点击的按钮的索引值。
551 |
552 |
553 |
554 |
555 | alert: function (message, alertCallback, title, buttonLabels) {
556 | var that = this ;
557 | var callback = function (ret) {
558 | var buttonIndex = ret.buttonIndex;
559 |
560 | if (Object .prototype.toString.call(alertCallback) === '[object Array]' ) {
561 | alertCallback[buttonIndex] && alertCallback[buttonIndex].apply(this , arguments );
562 | return ;
563 | }
564 |
565 | alertCallback && alertCallback();
566 | };
567 |
568 | if (that.superthat.platform === 'h5' ) {
569 | alert.apply(null , arguments );
570 | return ;
571 | }
572 |
573 | if (typeof (buttonLabels) === 'string' ) {
574 | buttonLabels = [buttonLabels];
575 | }
576 |
577 | that.superthat.pushBack('bridge:' , 'alert' , {
578 | message: message,
579 | successCallback: callback,
580 | title: title,
581 | buttonNames: buttonLabels
582 | });
583 | },
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
594 |
confirm确认对话框,对于浏览器的生存环境,仅message和confirmCallback
595 | 参数有效,但对于客户端类生存环境,则可以接收回调,以及弹出框的标题和按钮
596 | 文案,回调中客户端会回传用户相应点击的按钮的索引值。
597 |
598 |
599 |
600 |
601 | confirm: function (message, confirmCallback, title, buttonLabels) {
602 | var that = this ;
603 |
604 | if (that.superthat.platform === 'h5' ) {
605 | if (confirm.apply(null , arguments )) {
606 | confirmCallback && confirmCallback();
607 | }
608 |
609 | return ;
610 | }
611 |
612 | if (typeof (buttonLabels) === 'string' ) {
613 | buttonLabels = [buttonLabels];
614 | }
615 |
616 | that.superthat.pushBack('bridge:' , 'confirm' , {
617 | message: message,
618 | title: title,
619 | successCallback: confirmCallback,
620 | buttonNames: buttonLabels
621 | });
622 | },
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
633 |
prompt提示对话框,对于浏览器的生存环境,仅message、value和
634 | promptCallback参数有效,但对于客户端类生存环境,则可以接收回调,
635 | 以及弹出框的标题和按钮文案,回调中客户端会回传用户相应点击的按钮
636 | 的索引值。
637 |
638 |
639 |
640 |
641 | prompt: function (message, value, promptCallback, title, buttonLabels) {
642 | var that = this ;
643 |
644 | if (that.superthat.platform === 'h5' ) {
645 | prompt.apply(null , arguments );
646 | return ;
647 | }
648 |
649 | if (typeof (buttonLabels) === 'string' ) {
650 | buttonLabels = [buttonLabels];
651 | }
652 |
653 | that.superthat.pushBack('bridge:' , 'prompt' , {
654 | message: message,
655 | value: value,
656 | successCallback: promptCallback,
657 | title: title,
658 | buttonNames: buttonLabels
659 | });
660 | },
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
671 |
弱提示,用于调用客户端的弱提示进行些许文案的提示。
672 |
673 |
674 |
675 |
676 | toast: function (message, milliseconds) {
677 | var that = this ;
678 |
679 | if (that.superthat.platform === 'h5' ) {
680 | return ;
681 | }
682 |
683 | that.superthat.pushBack('bridge:' , 'toast' , {
684 | message: message,
685 | milliseconds: milliseconds
686 | });
687 | },
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
698 |
蜂鸣,调用客户端的蜂鸣器按照指定的次数进行蜂鸣。
699 |
700 |
701 |
702 |
703 | beep: function (times) {
704 | var that = this ;
705 |
706 | that.superthat.pushBack('bridge:' , 'beep' , {
707 | times: times
708 | });
709 | },
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
720 |
震动,进行设备的震动,持续指定的毫秒时长。
721 |
722 |
723 |
724 |
725 | vibrate: function (milliseconds) {
726 | var that = this ;
727 |
728 | that.superthat.pushBack('bridge:' , 'vibrate' , {
729 | milliseconds: milliseconds
730 | });
731 | }
732 | },
733 |
734 | device: {},
735 |
736 | client: {},
737 |
738 | connection: {},
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
749 |
新开页面打开页面,浏览器生存环境下打开新的标签页,客户端生存环境
750 | 下则调用系统浏览器打开相应新的页面。
751 |
752 |
753 |
754 |
755 | openBrowser: function (url) {
756 | var that = this ;
757 |
758 | if (that.platform === 'h5' ) {
759 | window.open(url);
760 | return ;
761 | }
762 |
763 | that.pushBack('bridge:' , 'open_system_browser' , {
764 | url: url
765 | });
766 | },
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
777 |
针对iOS,跳转并打开AppStore的相应地址,对于浏览器而言则在新标签
778 | 中打开对应的应用地址。
779 |
780 |
781 |
782 |
783 | openAppStore: function (url) {
784 | var that = this ;
785 |
786 | if (that.platform === 'h5' ) {
787 | window.open(url);
788 | return ;
789 | }
790 |
791 | that.pushBack('bridge:' , 'open_app_store' , {
792 | url: url
793 | });
794 | },
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
805 |
客户端生存环境下设置WebView顶部的标题显示文案,对于浏览器则直接
806 | 更改页面的标题文案。子标题为可选参数,只适用在客户端内嵌的情况。
807 |
808 |
809 |
810 |
811 | setTitle: function (title, subtitle) {
812 | var that = this ;
813 |
814 | if (that.platform === 'h5' ) {
815 | document.title = title;
816 | return ;
817 | }
818 |
819 | that.pushBack('bridge:' , 'set_webview_title' , {
820 | title: title,
821 | subtitle: subtitle
822 | });
823 | },
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
834 |
跳转到相应的客户端页面,如果有额外的数据则通过ext参数传递给客户端。
835 |
836 |
837 |
838 |
839 | open: function (pagename, ext, successCallback, naviType, animeType) {
840 | var that = this ;
841 | var params = {
842 | page_name: pagename,
843 | data: ext || {},
844 | successCallback: successCallback,
845 | naviType: naviType || 0 ,
846 | animeType: typeof (animeType) !== 'undefined' ? animeType : 4
847 | };
848 |
849 | that.pushBack('page:' , 'goto' , params);
850 | },
851 |
852 |
853 |
854 |
855 |
856 |
857 |
858 |
861 |
返回上一页,对于浏览器而言,直接调用history.back返回上一个历史记录,
862 | 对于客户端而言则通过协议调用返回上一个WebView打开的相应页面。
863 |
864 |
865 |
866 |
867 | back: function (pagename, ext) {
868 | var that = this ;
869 |
870 | if (that.platform === 'h5' ) {
871 | history.back();
872 | return ;
873 | }
874 |
875 | if (pagename) {
876 | that.pushBack('bridge:' , 'back' , ext);
877 | return ;
878 | }
879 |
880 | that.pushBack('bridge:' , 'back' , ext);
881 | },
882 |
883 |
884 |
885 |
886 |
887 |
888 |
889 |
892 |
关闭当前的客户端页面,如果有额外的数据则通过ext参数传递给客户端。
893 |
894 |
895 |
896 |
897 | close: function (ext) {
898 | var that = this ;
899 |
900 | if (that.platform === 'h5' ) {
901 | window.close();
902 | return ;
903 | }
904 |
905 | that.pushBack('page:' , 'close' , ext);
906 | },
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
917 |
客户端极简支付,如果有额外的数据则通过ext参数传递给客户端。
918 |
919 |
920 |
921 |
922 | minipay: function (alipayId, nextUrl, ext, successCallback, failCallback) {
923 | var that = this ;
924 | var payResult;
925 | var params = {
926 | alipay_id: alipayId,
927 | data: ext || {},
928 | failCallback: failCallback,
929 | successCallback: function (ret) {
930 | try {
931 | payResult = JSON .parse(ret);
932 | } catch (e) {
933 |
934 |
935 |
936 |
937 |
938 |
939 |
940 |
943 |
iOS客户端3.5版本返回的JSON格式有误,解析会出问题
944 |
945 |
946 |
947 | if (ret.match(/"ResultStatus":"9000"/igm )) {
948 | payResult = {
949 | ResultStatus: '9000'
950 | };
951 | }
952 | }
953 |
954 |
955 |
956 |
957 |
958 |
959 |
960 |
963 |
iOS SDK返回的字段名首字母是大写,暂时兼容处理
964 |
965 |
966 |
967 | if (that.platform === 'ios' ) {
968 | payResult.resultStatus = payResult.ResultStatus;
969 | }
970 |
971 | if (payResult.resultStatus === '9000' ) {
972 | successCallback && successCallback(payResult);
973 | return ;
974 | }
975 |
976 | failCallback && failCallback(payResult);
977 | }
978 | };
979 |
980 | if (that.platform === 'h5' && nextUrl) {
981 | window.location.href = nextUrl;
982 | return ;
983 | }
984 |
985 | if (that.client.version < 350 && nextUrl) {
986 | window.location.href = nextUrl;
987 | return ;
988 | }
989 |
990 | that.pushBack('bridge:' , 'minipay' , params);
991 | }
992 | };
993 |
994 | this .Bridge = Bridge;
995 |
996 | }).call(this );
997 |
998 |
999 |
1000 |
1001 |
1002 |
1003 |
1004 |
--------------------------------------------------------------------------------