/** * desc: js和客户端的交互模块,尽量封装到对业务逻辑透明,为移植到其他平台做准备 * author: wangyang * date: 2015-04-11 */ //本地回调对象 var NativeCallback; var NativeDataAdapter; define(function (require) { var helper = require('helper'); var config = require('config'); var pingpp = require('pingpp'); //客户端接口类 function NativeApi() { if (typeof NativeApi.instance === 'object') { return NativeApi.instance; } NativeApi.instance = this; this.getUserInfoCallback = null; this.shareCallback = null; this.getSignCallback = null; this.loginCallback = null; this.isWxAppInstalledCallback = null; this.payCallback = null; this.selectAddressCallback = null; this.chooseImageCallback = null; this.uploadImageCallbacks = {}; this.selectPetTypeCallback = null; this.charge = null; this.keyboardEventTimer = 0; this.originalHeight = document.body.scrollHeight; this.keyboardIsOpen = false; this.keyboardIsClose = false; } NativeApi.prototype = { //获取用户信息 getUserInfo: function (callback) { var that = this; var _callback = 'NativeCallback.getUserInfo'; helper.osProxy({ android: function () { var param = { callback: _callback }; var param_str = JSON.stringify(param); window.jsapi.getUserInfo(param_str); }, ios: function () { window.location.href = 'http://callclient?method=getUserInfo&callback=' + _callback; }, wx: function () { //此处有一个循环引用 require('api').getO2oUserInfo({ request_from: 'weixin', user_id: localStorage.getItem('xyhwxUserID') }, function (res) { if (typeof(that.getUserInfoCallback) == 'function') { that.getUserInfoCallback(res); } }); }, cb: function () { require(['../../common/js/ctk-1.0.0'], function (ctk) { ctk.logged({ yes: function (res) { console.log('已登录', res); //判断用户已登录的回调函数,用户的accessToken存放在res.accessToken内 require('api').getO2oUserInfo({ from: 'chubao', accessToken: res.accessToken }, function (res) { if (typeof(that.getUserInfoCallback) == 'function') { that.getUserInfoCallback(res); } }); }, no: function (res) { ctk.login({ phone: '', success: function (res) { require('api').getO2oUserInfo({ from: 'chubao', accessToken: res.accessToken }, function (res) { if (typeof(that.getUserInfoCallback) == 'function') { that.getUserInfoCallback(res); } }); }, fail: function (res) { // alert('login failed!'); } }); } }); }) } }); var callback = typeof (arguments[0]) == 'function' ? arguments[0] : null; this.getUserInfoCallback = callback; }, //从web返回APP back: function () { helper.osProxy({ android: function () { window.jsapi.exitWebView(); }, ios: function () { window.location.href = 'http://callclient?method=exitWebView'; }, wx: function () { require(['../../common/js/jweixin-1.0.0'], function (wx) { wx.closeWindow(); }); } }); }, //登录 login: function (callback) { var that = this; var _callback = 'NativeCallback.login'; helper.osProxy({ android: function () { var param = { callback: _callback }; var param_str = JSON.stringify(param); window.jsapi.goLogin(param_str); }, ios: function () { window.location.href = 'http://callclient?method=goLogin&callback=' + _callback; } }); var callback = typeof (arguments[0]) == 'function' ? arguments[0] : null; this.loginCallback = callback; }, //分享 share: function (title, content, img_url, url, app_id, callback) { var param = { "share_title": title, "share_string": content, "share_img_url": img_url ? encodeURI(img_url) : 'http://common.yiguanjia.club/images/logo.png', "share_url": url ? encodeURI(url) : 'http://common.yiguanjia.club', "share_app_id": app_id }; var _callback = 'NativeCallback.share'; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.doShare(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=doShare¶m=' + param_str + '&callback=' + _callback; } }); var callback = typeof (arguments[5]) == 'function' ? arguments[5] : null; this.shareCallback = callback; }, //参数加密验证 getSign: function (param, callback) { var that = this; for (var x in param) { param[x] = param[x].toString(); } var param = { sign_json_string: param }; var _callback = 'NativeCallback.getSign'; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.sign(param_str); }, //支付 pay: function (params, callback) { var that = this; var param = {}; var _callback = 'NativeCallback.pay'; helper.osProxy({ android: function () { param['charge'] = params.charge; param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.pay(param_str); }, // ios 的可以无视了,本项目暂时用不到 ios: function () { NativeDataAdapter.charge = JSON.stringify(params.charge); param['charge'] = 'NativeDataAdapter.charge'; var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=pay¶m=' + encodeURIComponent(param_str) + '&callback=' + _callback; }, wx: function () { // 这个我就不知道说什么好了,判断当前环境为test的话,走ping++模拟支付 if (config.test) { pingpp.createPayment(params.charge, function (result, err) { if (result == "success") { // 只有微信公众账号 wx_pub 支付成功的结果会在这里返回,其他的支付结果都会跳转到 extra 中对应的 URL。 var res = { success: true }; callback(res); } else if (result == "fail") { // charge 不正确或者微信公众账号支付失败时会在此处返回 console.log(err); } else if (result == "cancel") { // 微信公众账号支付取消支付 console.log(err); } }); }else { // 当前为正式环境,通过url传值到微信支付授权的url,然后在该url下面进行微信支付 /* * 参数说明:带 * 号的为微信支付用到的 * appId:* 公众号名称,由商户传入 * nonceStr:* 随机串 * prepay:预付款ID * signType:* 微信签名方式: * timeStamp:* 时间戳,自1970年以来的秒数 * paySign:* 微信签名 * amount:实付金额 * created:建立 * body:订单名字 * bookingTime:预约时间 * */ var option = params['charge'].credential.wx_pub; // 支付凭据 var prepay = option["package"].replace('prepay_id=', ''); var bookingTime = params.orderInfo.booking_time_str; location.href = '/webapp/o2o/module/pay/index.html?appId=' + option.appId + '&nonceStr=' + option.nonceStr + '&package=' + prepay + '&signType=' + option.signType + '&timeStamp=' + option.timeStamp + '&paySign=' + option.paySign + '&amount=' + params['charge'].amount + '&created=' + params['charge'].created + '&body=' + params['charge'].body + '&bookingTime=' + bookingTime; } } }); var callback = typeof (arguments[1]) == 'function' ? arguments[1] : null; this.payCallback = callback; }, wx: function () { //目前加上参数,绕过验证,后面再加上相关的验证 param['user_id'] = localStorage.getItem('xyhwxUserID'); param['request_from'] = 'weixin'; if (that.getSignCallback) { that.getSignCallback(param); } } }); var callback = typeof (arguments[1]) == 'function' ? arguments[1] : null; this.getSignCallback = callback; }, //支付 pay: function (params, callback) { var that = this; var param = {}; var _callback = 'NativeCallback.pay'; helper.osProxy({ android: function () { param['charge'] = params.charge; param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.pay(param_str); }, ios: function () { NativeDataAdapter.charge = JSON.stringify(params.charge); param['charge'] = 'NativeDataAdapter.charge'; var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=pay¶m=' + encodeURIComponent(param_str) + '&callback=' + _callback; }, wx: function () { if (config.test) { pingpp.createPayment(params.charge, function (result, err) { if (result == "success") { // 只有微信公众账号 wx_pub 支付成功的结果会在这里返回,其他的支付结果都会跳转到 extra 中对应的 URL。 var res = { success: true }; callback(res); } else if (result == "fail") { // charge 不正确或者微信公众账号支付失败时会在此处返回 console.log(err); } else if (result == "cancel") { // 微信公众账号支付取消支付 console.log(err); } }); } else { var option = params['charge'].credential.wx_pub; var prepay = option["package"].replace('prepay_id=', ''); var bookingTime = params.orderInfo.booking_time_str; var isSpecial = params['isSpecial'] location.href = '/webapp/o2o/module/pay/index.html?appId=' + option.appId + '&nonceStr=' + option.nonceStr + '&package=' + prepay + '&signType=' + option.signType + '&timeStamp=' + option.timeStamp + '&paySign=' + option.paySign + '&amount=' + params['charge'].amount + '&created=' + params['charge'].created + '&body=' + params['charge'].body + '&bookingTime=' + bookingTime + '&isSpecial=' + isSpecial; } } }); var callback = typeof (arguments[1]) == 'function' ? arguments[1] : null; this.payCallback = callback; }, //检测是否安装了微信 isWxAppInstalled: function (callback) { var _callback = 'NativeCallback.isWxInstalled'; var param = {}; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.isWxAppInstalled(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=isWxAppInstalled¶m=' + param_str + '&callback=' + _callback; } }); var callback = typeof (arguments[0]) == 'function' ? arguments[0] : null; this.isWxAppInstalledCallback = callback; }, //选择地址 selectAddress: function (callback) { var _callback = 'NativeCallback.selectAddress'; var param = {}; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.selectAddress(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=selectAddress¶m=' + param_str + '&callback=' + _callback; }/*, WX: function() { require('address').getList(function(res){ if (typeof(that.selectAddressCallback) == 'function') { that.selectAddressCallback(res); } }) }*/ }); var callback = typeof (arguments[0]) == 'function' ? arguments[0] : null; this.selectAddressCallback = callback; }, //打开一个链接 openURL: function (new_param) { if (config.appVersion < 2.6) { return false; } var param = { url: 'http://common.yiguanjia.club' }; if (typeof new_param === 'undefined') { new_param = {}; } for (var x in new_param) { if (param.hasOwnProperty(x)) { param[x] = new_param[x]; } } helper.osProxy({ android: function () { var param_str = JSON.stringify(param); window.jsapi.openURL(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=openURL¶m=' + param_str; } }); }, //打开多个页面时页面之间通信 postMessage: function (data) { if (config.appVersion < 2.6) { return false; } var _callback = 'NativeCallback.postMessage'; helper.osProxy({ android: function () { var param = { data: JSON.stringify(data), callback: _callback }; var param_str = JSON.stringify(param); window.jsapi.postMessage(param_str); }, ios: function () { var param = { data: data, callback: _callback }; var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=postMessage¶m=' + param_str; } }); }, //选择图片 chooseImage: function (new_param, callback) { if (config.appVersion < 2.6) { return false; } var param = { sourceType: ['album', 'camera'], count: 9, clip: false, clipSize: 320 }; if (typeof new_param === 'undefined') { new_param = {}; } for (var x in new_param) { if (param.hasOwnProperty(x)) { param[x] = new_param[x]; } } var _callback = 'NativeCallback.chooseImage'; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.chooseImage(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=chooseImage¶m=' + param_str + '&callback=' + _callback; } }); var callback = typeof (arguments[1]) == 'function' ? arguments[1] : null; this.chooseImageCallback = callback; }, //预览图片 previewImage: function (urls, current) { if (!config.isWX && config.appVersion < 2.6) { return false; } var param = { urls: urls, current: current, }; var param_str = JSON.stringify(param); helper.osProxy({ android: function () { window.jsapi.previewImage(param_str); }, ios: function () { window.location.href = 'http://callclient?method=previewImage¶m=' + param_str; }, wx: function () { require(['../../common/js/jweixin-1.0.0'], function (wx) { wx.previewImage({ current: current, // 当前显示图片的http链接 urls: urls // 需要预览的图片http链接列表 }); }); } }); }, //上传图片 uploadImage: function (new_param, callback) { if (config.appVersion < 2.6) { return false; } var param = { localId: '', isShowProgressTips: true, }; if (typeof new_param === 'undefined') { new_param = {}; } for (var x in new_param) { if (param.hasOwnProperty(x)) { param[x] = new_param[x]; } } var _callback = 'NativeCallback.uploadImage'; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.uploadImage(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=uploadImage¶m=' + param_str + '&callback=' + _callback; } }); var callback = typeof (arguments[1]) == 'function' ? arguments[1] : null; this.uploadImageCallbacks[param.localId] = callback; }, // 选择宠物 selectPetType: function (callback) { var _callback = 'NativeCallback.selectPetType'; var param = {}; helper.osProxy({ android: function () { param['callback'] = _callback; var param_str = JSON.stringify(param); window.jsapi.selectPetType(param_str); }, ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=selectPetType¶m=' + param_str + '&callback=' + _callback; } }); var callback = typeof (arguments[0]) == 'function' ? arguments[0] : null; this.selectPetTypeCallback = callback; }, // 拨打电话 call: function (telephone) { var that = this; helper.osProxy({ android: function () { if (config.appVersion < '2.2') { //无法调用拨号,弹出 $(document).trigger('spa:openpanel', ['simpleAlert', { message: '客服热线:' + telephone }]); } else { var param = { telephone: telephone }; var param_str = JSON.stringify(param); window.jsapi.call(param_str); } }, ios: function () { window.location.href = 'tel:' + telephone; } }); }, //ios //开关滑动返回 switchPopGesture: function (flag) { var param = { enable: flag }; helper.osProxy({ ios: function () { var param_str = JSON.stringify(param); window.location.href = 'http://callclient?method=switchPopGesture¶m=' + param_str; } }); }, //安卓 //代理物理返回按钮 delegateBackButton: function (flag, callback) { var param = { enable: flag, callback: 'NativeCallback.back' }; helper.osProxy({ android: function () { var param_str = JSON.stringify(param); window.jsapi.delegateBackButton(param_str); } }); }, //注册键盘事件,由于安卓回调有问题,所以改为js setInterval自定义检测 registerEvent: function (enable) { if (!enable) { if (this.keyboardEventTimer != 0) { clearInterval(this.keyboardEventTimer); } return; } var that = this; this.keyboardIsOpen = false; this.keyboardIsClose = false; helper.osProxy({ android: function () { that.keyboardEventTimer = setInterval(function () { if (that.originalHeight > document.body.scrollHeight) { if (!that.keyboardIsOpen) { that.keyboardIsOpen = true; that.keyboardIsClose = false; $('input, textarea').trigger('openKeyboard'); } } else { if (!that.keyboardIsClose) { that.keyboardIsClose = true; that.keyboardIsOpen = false; $('input, textarea').trigger('closeKeyboard'); } } }, 50); } }); } }; /* * 回调类 */ function CallbackApi() { if (typeof CallbackApi.instance === 'object') { return CallbackApi.instance; } CallbackApi.instance = this; } CallbackApi.prototype = { formatNativeParam: function (res) { var f_res = helper.osProxy({ android: function () { var reg = new RegExp('(\r\n|\r|\n)', 'g'); res = JSON.stringify(res).replace(reg, ''); return JSON.parse(res); }, ios: function () { if (res && res.hasOwnProperty('success')) { if (res.success == '1') { res.success = true; } else { res.success = false; } } return res; } }); return f_res; }, getUserInfo: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.getUserInfoCallback) == 'function') { native_api.getUserInfoCallback(f_res); } }, login: function (res) { //fix by wangyang //ios客户端登陆回调的通知在2.0有点早,改为延时0.1s再重新获取一次用户信息 var f_res = this.formatNativeParam(res); var nativeApi = new NativeApi(); if (nativeApi.loginCallback != null) { setTimeout(function () { nativeApi.getUserInfo(nativeApi.loginCallback); nativeApi.loginCallback(f_res); }, 100); } }, share: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.shareCallback) == 'function') { native_api.shareCallback(f_res); } }, getSign: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.getSignCallback) == 'function') { native_api.getSignCallback(f_res); } }, pay: function (res) { var f_res = this.formatNativeParam(res); if (f_res.success) { f_res.message = '支付成功'; } else { helper.osProxy({ android: function () { if (config.appVersion < '2.2') { if (f_res.message == 'user_cancelled') { f_res.message = '取消支付'; } else { f_res.message = '支付异常,请稍后再试'; } } else { switch (f_res.error_code) { case 'fail': f_res.message = '支付失败'; break; case 'cancel': f_res.message = '取消支付'; break; default: f_res.message = '支付异常,请稍后再试'; break; } } }, ios: function () { if (f_res.error_code == 5) { f_res.message = '取消支付'; } else { f_res.message = '支付异常,请稍后再试'; } } }); } var native_api = new NativeApi(); if (typeof (native_api.payCallback) == 'function') { native_api.payCallback(f_res); } }, selectAddress: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); helper.osProxy({ android: function () { //2.1返回的data是json字符串,特殊处理一下 if (f_res.success && typeof (f_res.data) == 'string') { f_res.data = JSON.parse(f_res.data); } } }); if (typeof (native_api.selectAddressCallback) == 'function') { native_api.selectAddressCallback(f_res); } }, selectPetType: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.selectPetTypeCallback) == 'function') { native_api.selectPetTypeCallback(f_res); } }, chooseImage: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.chooseImageCallback) == 'function') { native_api.chooseImageCallback(f_res); } }, uploadImage: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (res.data.localId && typeof (native_api.uploadImageCallbacks[res.data.localId]) == 'function') { native_api.uploadImageCallbacks[res.data.localId](f_res); } }, isWxInstalled: function (res) { var f_res = this.formatNativeParam(res); var native_api = new NativeApi(); if (typeof (native_api.isWxAppInstalledCallback) == 'function') { native_api.isWxAppInstalledCallback(f_res); } }, //以下回调为js自定义事件,注意,依赖jquery的trigger postMessage: function (res) { var f_res = this.formatNativeParam(res); f_res = helper.osProxy({ android: function () { f_res.data = JSON.parse(f_res.data); return f_res; }, ios: function () { return f_res; } }); require(['$'], function ($) { $(document).trigger('postMessage', f_res); }); }, back: function () { require(['$'], function ($) { $(document).trigger('tapBackButton'); }); } }; /* * 传递数据类,用于ios,某些复杂的json数据不适合通过url传递 * 换一种思路,通过提供一个JS类给OC,然后OC调用js方法反查数据 */ function NativeDataCache() { if (typeof NativeDataCache.instance === 'object') { return NativeDataCache.instance; } NativeDataCache.instance = this; NativeDataCache.charge = ''; } /** * android websocket支持 */ helper.osProxy({ android: function () { if (typeof(WebSocketFactory) == 'undefined') { return false; } // window object var global = window; // WebSocket Object. All listener methods are cleaned up! var WebSocket = global.WebSocket = function (url) { // get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java) this.socket = WebSocketFactory.getInstance(url); // store in registry if (this.socket) { WebSocket.store[this.socket.getId()] = this; } else { throw new Error('Websocket instantiation failed! Address might be wrong.'); } }; // private property WebSocket._keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; // private method for decoding base64 WebSocket._decode = function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); while (i < input.length) { enc1 = this._keyStr.indexOf(input.charAt(i++)); enc2 = this._keyStr.indexOf(input.charAt(i++)); enc3 = this._keyStr.indexOf(input.charAt(i++)); enc4 = this._keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } output = this._utf8_decode(output); return output; } // private method for UTF-8 decoding WebSocket._utf8_decode = function (utftext) { var string = ""; var i = 0; var c = c1 = c2 = 0; while (i < utftext.length) { c = utftext.charCodeAt(i); if (c < 128) { string += String.fromCharCode(c); i++; } else if ((c > 191) && (c < 224)) { c2 = utftext.charCodeAt(i + 1); string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); i += 2; } else { c2 = utftext.charCodeAt(i + 1); c3 = utftext.charCodeAt(i + 2); string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); i += 3; } } return string; } // storage to hold websocket object for later invokation of event methods WebSocket.store = {}; // static event methods to call event methods on target websocket objects WebSocket.onmessage = function (evt) { WebSocket.store[evt._target]['onmessage'].call(global, this._decode(evt._data)); } WebSocket.onopen = function (evt) { WebSocket.store[evt._target]['onopen'].call(global, evt); } WebSocket.onclose = function (evt) { WebSocket.store[evt._target]['onclose'].call(global, evt); } WebSocket.onerror = function (evt) { WebSocket.store[evt._target]['onerror'].call(global, evt); } // instance event methods WebSocket.prototype.send = function (data) { this.socket.send(data); } WebSocket.prototype.close = function () { this.socket.close(); } WebSocket.prototype.getReadyState = function () { this.socket.getReadyState(); } ///////////// Must be overloaded WebSocket.prototype.onopen = function () { throw new Error('onopen not implemented.'); }; // alerts message pushed from server WebSocket.prototype.onmessage = function (msg) { throw new Error('onmessage not implemented.'); }; // alerts message pushed from server WebSocket.prototype.onerror = function (msg) { throw new Error('onerror not implemented.'); }; // alert close event WebSocket.prototype.onclose = function () { throw new Error('onclose not implemented.'); }; } }) NativeDataAdapter = new NativeDataCache(); NativeCallback = new CallbackApi(); return new NativeApi(); })