专门用于微信公众平台的Javascript API

1 /**!
  2  * 微信内置浏览器的Javascript API,功能包括:
  3  *
  4  * 1、分享到微信朋友圈
  5  * 2、分享给微信好友
  6  * 3、分享到腾讯微博
  7  * 4、新的分享接口,包含朋友圈、好友、微博的分享(for iOS)
  8  * 5、隐藏/显示右上角的菜单入口
  9  * 6、隐藏/显示底部浏览器工具栏
 10  * 7、获取当前的网络状态
 11  * 8、调起微信客户端的图片播放组件
 12  * 9、关闭公众平台Web页面
 13  * 10、判断当前网页是否在微信内置浏览器中打开
 14  * 11、增加打开扫描二维码
 15  * 12、支持WeixinApi的错误监控
 16  * 13、检测应用程序是否已经安装(需要官方开通权限)
 17  * 14、发送电子邮件
 18  * 15、禁止用户分享
 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);
posted @ 2015-03-31 13:32  左正  阅读(337)  评论(0编辑  收藏  举报