1 /**! 2 * 微信內置瀏覽器的Javascript API,功能包括: 3 * 4 * 一、分享到微信朋友圈 5 * 二、分享給微信好友 6 * 三、分享到騰訊微博 7 * 四、新的分享接口,包含朋友圈、好友、微博的分享(for iOS) 8 * 五、隱藏/顯示右上角的菜單入口 9 * 六、隱藏/顯示底部瀏覽器工具欄 10 * 七、獲取當前的網絡狀態 11 * 八、調起微信客戶端的圖片播放組件 12 * 九、關閉公衆平臺Web頁面 13 * 十、判斷當前網頁是否在微信內置瀏覽器中打開 14 * 十一、增長打開掃描二維碼 15 * 十二、支持WeixinApi的錯誤監控 16 * 1三、檢測應用程序是否已經安裝(須要官方開通權限) 17 * 1四、發送電子郵件 18 * 1五、禁止用戶分享 19 * 20 * @author guanguoxiang(http://www.ggxapp.com) 21 */ 22 (function (window) { 23 24 "use strict"; 25 26 /** 27 * 定義WeixinApi 28 */ 29 var WeixinApi = { 30 version: 4.1 31 }; 32 33 // 將WeixinApi暴露到window下:全局可以使用,對舊版本向下兼容 34 window.WeixinApi = WeixinApi; 35 36 /////////////////////////// CommonJS ///////////////////////////////// 37 if (typeof define === 'function' && (define.amd || define.cmd)) { 38 if (define.amd) { 39 // AMD 規範,for:requirejs 40 define(function () { 41 return WeixinApi; 42 }); 43 } else if (define.cmd) { 44 // CMD 規範,for:seajs 45 define(function (require, exports, module) { 46 module.exports = WeixinApi; 47 }); 48 } 49 } 50 51 /** 52 * 對象簡單繼承,後面的覆蓋前面的,繼承深度:deep=1 53 * @private 54 */ 55 var _extend = function () { 56 var result = {}, obj, k; 57 for (var i = 0, len = arguments.length; i < len; i++) { 58 obj = arguments[i]; 59 if (typeof obj === 'object') { 60 for (k in obj) { 61 obj[k] && (result[k] = obj[k]); 62 } 63 } 64 } 65 return result; 66 }; 67 68 /** 69 * 內部私有方法,分享用 70 * @private 71 */ 72 var _share = function (cmd, data, callbacks) { 73 callbacks = callbacks || {}; 74 75 // 分享過程當中的一些回調 76 var progress = function (resp) { 77 switch (true) { 78 // 用戶取消 79 case /\:cancel$/i.test(resp.err_msg) : 80 callbacks.cancel && callbacks.cancel(resp); 81 break; 82 // 發送成功 83 case /\:(confirm|ok)$/i.test(resp.err_msg): 84 callbacks.confirm && callbacks.confirm(resp); 85 break; 86 // fail 發送失敗 87 case /\:fail$/i.test(resp.err_msg) : 88 default: 89 callbacks.fail && callbacks.fail(resp); 90 break; 91 } 92 // 不管成功失敗都會執行的回調 93 callbacks.all && callbacks.all(resp); 94 }; 95 96 // 執行分享,並處理結果 97 var handler = function (theData, argv) { 98 99 // 加工一下數據 100 if (cmd.menu == 'menu:share:timeline' || 101 (cmd.menu == 'general:share' && argv.shareTo == 'timeline')) { 102 103 var title = theData.title; 104 theData.title = theData.desc || title; 105 theData.desc = title || theData.desc; 106 } 107 108 // 新的分享接口,單獨處理 109 if (cmd.menu === 'general:share') { 110 // 若是是收藏操做,而且在wxCallbacks中配置了favorite爲false,則不執行回調 111 if (argv.shareTo == 'favorite' || argv.scene == 'favorite') { 112 if (callbacks.favorite === false) { 113 return argv.generalShare(theData, function () { 114 }); 115 } 116 } 117 if (argv.shareTo === 'timeline') { 118 WeixinJSBridge.invoke('shareTimeline', theData, progress); 119 } else if (argv.shareTo === 'friend') { 120 WeixinJSBridge.invoke('sendAppMessage', theData, progress); 121 } else if (argv.shareTo === 'QQ') { 122 WeixinJSBridge.invoke('shareQQ', theData, progress); 123 }else if (argv.shareTo === 'weibo') { 124 WeixinJSBridge.invoke('shareWeibo', theData, progress); 125 } 126 } else { 127 WeixinJSBridge.invoke(cmd.action, theData, progress); 128 } 129 }; 130 131 // 監聽分享操做 132 WeixinJSBridge.on(cmd.menu, function (argv) { 133 callbacks.dataLoaded = callbacks.dataLoaded || new Function(); 134 if (callbacks.async && callbacks.ready) { 135 WeixinApi["_wx_loadedCb_"] = callbacks.dataLoaded; 136 if (WeixinApi["_wx_loadedCb_"].toString().indexOf("_wx_loadedCb_") > 0) { 137 WeixinApi["_wx_loadedCb_"] = new Function(); 138 } 139 callbacks.dataLoaded = function (newData) { 140 callbacks.__cbkCalled = true; 141 var theData = _extend(data, newData); 142 theData.img_url = theData.imgUrl || theData.img_url; 143 delete theData.imgUrl; 144 WeixinApi["_wx_loadedCb_"](theData); 145 handler(theData, argv); 146 }; 147 // 而後就緒 148 if (!(argv && (argv.shareTo == 'favorite' || argv.scene == 'favorite') && callbacks.favorite === false)) { 149 callbacks.ready && callbacks.ready(argv, data); 150 // 若是設置了async爲true,可是在ready方法中並無手動調用dataLoaded方法,則自動觸發一次 151 if (!callbacks.__cbkCalled) { 152 callbacks.dataLoaded({}); 153 callbacks.__cbkCalled = false; 154 } 155 } 156 } else { 157 // 就緒狀態 158 var theData = _extend(data); 159 if (!(argv && (argv.shareTo == 'favorite' || argv.scene == 'favorite') && callbacks.favorite === false)) { 160 callbacks.ready && callbacks.ready(argv, theData); 161 } 162 handler(theData, argv); 163 } 164 }); 165 }; 166 167 /** 168 * 分享到微信朋友圈 169 * @param {Object} data 待分享的信息 170 * @p-config {String} appId 公衆平臺的appId(服務號可用) 171 * @p-config {String} imgUrl 圖片地址 172 * @p-config {String} link 連接地址 173 * @p-config {String} desc 描述 174 * @p-config {String} title 分享的標題 175 * 176 * @param {Object} callbacks 相關回調方法 177 * @p-config {Boolean} async ready方法是否須要異步執行,默認false 178 * @p-config {Function} ready(argv, data) 就緒狀態 179 * @p-config {Function} dataLoaded(data) 數據加載完成後調用,async爲true時有用,也能夠爲空 180 * @p-config {Function} cancel(resp) 取消 181 * @p-config {Function} fail(resp) 失敗 182 * @p-config {Function} confirm(resp) 成功 183 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 184 */ 185 WeixinApi.shareToTimeline = function (data, callbacks) { 186 _share({ 187 menu: 'menu:share:timeline', 188 action: 'shareTimeline' 189 }, { 190 "appid": data.appId ? data.appId : '', 191 "img_url": data.imgUrl, 192 "link": data.link, 193 "desc": data.desc, 194 "title": data.title, 195 "img_width": "640", 196 "img_height": "640" 197 }, callbacks); 198 }; 199 200 /** 201 * 發送給微信上的好友 202 * @param {Object} data 待分享的信息 203 * @p-config {String} appId 公衆平臺的appId(服務號可用) 204 * @p-config {String} imgUrl 圖片地址 205 * @p-config {String} link 連接地址 206 * @p-config {String} desc 描述 207 * @p-config {String} title 分享的標題 208 * 209 * @param {Object} callbacks 相關回調方法 210 * @p-config {Boolean} async ready方法是否須要異步執行,默認false 211 * @p-config {Function} ready(argv, data) 就緒狀態 212 * @p-config {Function} dataLoaded(data) 數據加載完成後調用,async爲true時有用,也能夠爲空 213 * @p-config {Function} cancel(resp) 取消 214 * @p-config {Function} fail(resp) 失敗 215 * @p-config {Function} confirm(resp) 成功 216 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 217 */ 218 WeixinApi.shareToFriend = function (data, callbacks) { 219 _share({ 220 menu: 'menu:share:appmessage', 221 action: 'sendAppMessage' 222 }, { 223 "appid": data.appId ? data.appId : '', 224 "img_url": data.imgUrl, 225 "link": data.link, 226 "desc": data.desc, 227 "title": data.title, 228 "img_width": "640", 229 "img_height": "640" 230 }, callbacks); 231 }; 232 233 234 /** 235 * 分享到騰訊微博 236 * @param {Object} data 待分享的信息 237 * @p-config {String} link 連接地址 238 * @p-config {String} desc 描述 239 * 240 * @param {Object} callbacks 相關回調方法 241 * @p-config {Boolean} async ready方法是否須要異步執行,默認false 242 * @p-config {Function} ready(argv, data) 就緒狀態 243 * @p-config {Function} dataLoaded(data) 數據加載完成後調用,async爲true時有用,也能夠爲空 244 * @p-config {Function} cancel(resp) 取消 245 * @p-config {Function} fail(resp) 失敗 246 * @p-config {Function} confirm(resp) 成功 247 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 248 */ 249 WeixinApi.shareToWeibo = function (data, callbacks) { 250 _share({ 251 menu: 'menu:share:weibo', 252 action: 'shareWeibo' 253 }, { 254 "content": data.desc, 255 "url": data.link 256 }, callbacks); 257 }; 258 259 /** 260 * 新的分享接口 261 * @param {Object} data 待分享的信息 262 * @p-config {String} appId 公衆平臺的appId(服務號可用) 263 * @p-config {String} imgUrl 圖片地址 264 * @p-config {String} link 連接地址 265 * @p-config {String} desc 描述 266 * @p-config {String} title 分享的標題 267 * 268 * @param {Object} callbacks 相關回調方法 269 * @p-config {Boolean} async ready方法是否須要異步執行,默認false 270 * @p-config {Function} ready(argv, data) 就緒狀態 271 * @p-config {Function} dataLoaded(data) 數據加載完成後調用,async爲true時有用,也能夠爲空 272 * @p-config {Function} cancel(resp) 取消 273 * @p-config {Function} fail(resp) 失敗 274 * @p-config {Function} confirm(resp) 成功 275 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 276 */ 277 WeixinApi.generalShare = function (data, callbacks) { 278 _share({ 279 menu: 'general:share' 280 }, { 281 "appid": data.appId ? data.appId : '', 282 "img_url": data.imgUrl, 283 "link": data.link, 284 "desc": data.desc, 285 "title": data.title, 286 "img_width": "640", 287 "img_height": "640" 288 }, callbacks); 289 }; 290 291 /** 292 * 設置頁面禁止分享:包括朋友圈、好友、騰訊微博、qq 293 * @param callback 294 */ 295 WeixinApi.disabledShare = function (callback) { 296 callback = callback || function () { 297 alert('當前頁面禁止分享!'); 298 }; 299 ['menu:share:timeline', 'menu:share:appmessage', 'menu:share:qq', 300 'menu:share:weibo', 'general:share'].forEach(function (menu) { 301 WeixinJSBridge.on(menu, function () { 302 callback(); 303 return false; 304 }); 305 }); 306 }; 307 308 /** 309 * 加關注(此功能只是暫時先加上,不過由於權限限制問題,不能用,若是你的站點是部署在*.qq.com下,也許可行) 310 * @param {String} appWeixinId 微信公衆號ID 311 * @param {Object} callbacks 回調方法 312 * @p-config {Function} fail(resp) 失敗 313 * @p-config {Function} confirm(resp) 成功 314 */ 315 WeixinApi.addContact = function (appWeixinId, callbacks) { 316 callbacks = callbacks || {}; 317 WeixinJSBridge.invoke("addContact", { 318 webtype: "1", 319 username: appWeixinId 320 }, function (resp) { 321 var success = !resp.err_msg || "add_contact:ok" == resp.err_msg 322 || "add_contact:added" == resp.err_msg; 323 if (success) { 324 callbacks.success && callbacks.success(resp); 325 } else { 326 callbacks.fail && callbacks.fail(resp); 327 } 328 }) 329 }; 330 331 /** 332 * 調起微信Native的圖片播放組件。 333 * 這裏必須對參數進行強檢測,若是參數不合法,直接會致使微信客戶端crash 334 * 335 * @param {String} curSrc 當前播放的圖片地址 336 * @param {Array} srcList 圖片地址列表 337 */ 338 WeixinApi.imagePreview = function (curSrc, srcList) { 339 if (!curSrc || !srcList || srcList.length == 0) { 340 return; 341 } 342 WeixinJSBridge.invoke('imagePreview', { 343 'current': curSrc, 344 'urls': srcList 345 }); 346 }; 347 348 /** 349 * 顯示網頁右上角的按鈕 350 */ 351 WeixinApi.showOptionMenu = function () { 352 WeixinJSBridge.call('showOptionMenu'); 353 }; 354 355 356 /** 357 * 隱藏網頁右上角的按鈕 358 */ 359 WeixinApi.hideOptionMenu = function () { 360 WeixinJSBridge.call('hideOptionMenu'); 361 }; 362 363 /** 364 * 顯示底部工具欄 365 */ 366 WeixinApi.showToolbar = function () { 367 WeixinJSBridge.call('showToolbar'); 368 }; 369 370 /** 371 * 隱藏底部工具欄 372 */ 373 WeixinApi.hideToolbar = function () { 374 WeixinJSBridge.call('hideToolbar'); 375 }; 376 377 /** 378 * 返回以下幾種類型: 379 * 380 * network_type:wifi wifi網絡 381 * network_type:edge 非wifi,包含3G/2G 382 * network_type:fail 網絡斷開鏈接 383 * network_type:wwan 2g或者3g 384 * 385 * 使用方法: 386 * WeixinApi.getNetworkType(function(networkType){ 387 * 388 * }); 389 * 390 * @param callback 391 */ 392 WeixinApi.getNetworkType = function (callback) { 393 if (callback && typeof callback == 'function') { 394 WeixinJSBridge.invoke('getNetworkType', {}, function (e) { 395 // 在這裏拿到e.err_msg,這裏面就包含了全部的網絡類型 396 callback(e.err_msg); 397 }); 398 } 399 }; 400 401 /** 402 * 關閉當前微信公衆平臺頁面 403 * @param {Object} callbacks 回調方法 404 * @p-config {Function} fail(resp) 失敗 405 * @p-config {Function} success(resp) 成功 406 */ 407 WeixinApi.closeWindow = function (callbacks) { 408 callbacks = callbacks || {}; 409 WeixinJSBridge.invoke("closeWindow", {}, function (resp) { 410 switch (resp.err_msg) { 411 // 關閉成功 412 case 'close_window:ok': 413 callbacks.success && callbacks.success(resp); 414 break; 415 416 // 關閉失敗 417 default : 418 callbacks.fail && callbacks.fail(resp); 419 break; 420 } 421 }); 422 }; 423 424 /** 425 * 當頁面加載完畢後執行,使用方法: 426 * WeixinApi.ready(function(Api){ 427 * // 從這裏只用Api便是WeixinApi 428 * }); 429 * @param readyCallback 430 */ 431 WeixinApi.ready = function (readyCallback) { 432 433 /** 434 * 加一個鉤子,同時解決Android和iOS下的分享問題 435 * @private 436 */ 437 var _hook = function () { 438 var _WeixinJSBridge = {}; 439 Object.keys(WeixinJSBridge).forEach(function (key) { 440 _WeixinJSBridge[key] = WeixinJSBridge[key]; 441 }); 442 Object.keys(WeixinJSBridge).forEach(function (key) { 443 if (typeof WeixinJSBridge[key] === 'function') { 444 WeixinJSBridge[key] = function () { 445 try { 446 var args = arguments.length > 0 ? arguments[0] : {}, 447 runOn3rd_apis = args.__params ? args.__params.__runOn3rd_apis || [] : []; 448 ['menu:share:timeline', 'menu:share:appmessage', 'menu:share:weibo', 449 'menu:share:qq', 'general:share'].forEach(function (menu) { 450 runOn3rd_apis.indexOf(menu) === -1 && runOn3rd_apis.push(menu); 451 }); 452 } catch (e) { 453 } 454 return _WeixinJSBridge[key].apply(WeixinJSBridge, arguments); 455 }; 456 } 457 }); 458 }; 459 460 if (readyCallback && typeof readyCallback == 'function') { 461 var Api = this; 462 var wxReadyFunc = function () { 463 _hook(); 464 readyCallback(Api); 465 }; 466 if (typeof window.WeixinJSBridge == "undefined") { 467 if (document.addEventListener) { 468 document.addEventListener('WeixinJSBridgeReady', wxReadyFunc, false); 469 } else if (document.attachEvent) { 470 document.attachEvent('WeixinJSBridgeReady', wxReadyFunc); 471 document.attachEvent('onWeixinJSBridgeReady', wxReadyFunc); 472 } 473 } else { 474 wxReadyFunc(); 475 } 476 } 477 }; 478 479 /** 480 * 判斷當前網頁是否在微信內置瀏覽器中打開 481 */ 482 WeixinApi.openInWeixin = function () { 483 return /MicroMessenger/i.test(navigator.userAgent); 484 }; 485 486 /* 487 * 打開掃描二維碼 488 * @param {Object} callbacks 回調方法 489 * @p-config {Boolean} needResult 是否直接獲取分享後的內容 490 * @p-config {String} desc 掃描二維碼時的描述 491 * @p-config {Function} fail(resp) 失敗 492 * @p-config {Function} success(resp) 成功 493 */ 494 WeixinApi.scanQRCode = function (callbacks) { 495 callbacks = callbacks || {}; 496 WeixinJSBridge.invoke("scanQRCode", { 497 needResult: callbacks.needResult ? 1 : 0, 498 desc: callbacks.desc || 'WeixinApi Desc' 499 }, function (resp) { 500 switch (resp.err_msg) { 501 // 打開掃描器成功 502 case 'scanQRCode:ok': 503 case 'scan_qrcode:ok': 504 callbacks.success && callbacks.success(resp); 505 break; 506 507 // 打開掃描器失敗 508 default : 509 callbacks.fail && callbacks.fail(resp); 510 break; 511 } 512 }); 513 }; 514 515 /** 516 * 檢測應用程序是否已安裝 517 * by mingcheng 2014-10-17 518 * 519 * @param {Object} data 應用程序的信息 520 * @p-config {String} packageUrl 應用註冊的自定義前綴,如 xxx:// 就取 xxx 521 * @p-config {String} packageName 應用的包名 522 * 523 * @param {Object} callbacks 相關回調方法 524 * @p-config {Function} fail(resp) 失敗 525 * @p-config {Function} success(resp) 成功,若是有對應的版本信息,則寫入到 resp.version 中 526 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 527 */ 528 WeixinApi.getInstallState = function (data, callbacks) { 529 callbacks = callbacks || {}; 530 531 WeixinJSBridge.invoke("getInstallState", { 532 "packageUrl": data.packageUrl || "", 533 "packageName": data.packageName || "" 534 }, function (resp) { 535 var msg = resp.err_msg, match = msg.match(/state:yes_?(.*)$/); 536 if (match) { 537 resp.version = match[1] || ""; 538 callbacks.success && callbacks.success(resp); 539 } else { 540 callbacks.fail && callbacks.fail(resp); 541 } 542 543 callbacks.all && callbacks.all(resp); 544 }); 545 }; 546 547 /** 548 * 發送郵件 549 * @param {Object} data 郵件初始內容 550 * @p-config {String} subject 郵件標題 551 * @p-config {String} body 郵件正文 552 * 553 * @param {Object} callbacks 相關回調方法 554 * @p-config {Function} fail(resp) 失敗 555 * @p-config {Function} success(resp) 成功 556 * @p-config {Function} all(resp) 不管成功失敗都會執行的回調 557 */ 558 WeixinApi.sendEmail = function (data, callbacks) { 559 callbacks = callbacks || {}; 560 WeixinJSBridge.invoke("sendEmail", { 561 "title": data.subject, 562 "content": data.body 563 }, function (resp) { 564 if (resp.err_msg === 'send_email:sent') { 565 callbacks.success && callbacks.success(resp); 566 } else { 567 callbacks.fail && callbacks.fail(resp); 568 } 569 callbacks.all && callbacks.all(resp); 570 }) 571 }; 572 573 /** 574 * 開啓Api的debug模式,好比出了個什麼錯誤,能alert告訴你,而不是一直很苦逼的在想哪兒出問題了 575 * @param {Function} callback(error) 出錯後的回調,默認是alert 576 */ 577 WeixinApi.enableDebugMode = function (callback) { 578 /** 579 * @param {String} errorMessage 錯誤信息 580 * @param {String} scriptURI 出錯的文件 581 * @param {Long} lineNumber 出錯代碼的行號 582 * @param {Long} columnNumber 出錯代碼的列號 583 */ 584 window.onerror = function (errorMessage, scriptURI, lineNumber, columnNumber) { 585 586 // 有callback的狀況下,將錯誤信息傳遞到options.callback中 587 if (typeof callback === 'function') { 588 callback({ 589 message: errorMessage, 590 script: scriptURI, 591 line: lineNumber, 592 column: columnNumber 593 }); 594 } else { 595 // 其餘狀況,都以alert方式直接提示錯誤信息 596 var msgs = []; 597 msgs.push("額,代碼有錯。。。"); 598 msgs.push("\n錯誤信息:", errorMessage); 599 msgs.push("\n出錯文件:", scriptURI); 600 msgs.push("\n出錯位置:", lineNumber + '行,' + columnNumber + '列'); 601 alert(msgs.join('')); 602 } 603 } 604 }; 605 606 })(window);