All files / app callUp.js

93.1% Statements 27/29
66.67% Branches 12/18
75% Functions 3/4
93.1% Lines 27/29

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 39 40 41 42 43 44 45 46 47 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 101 102 103 104 105 106 107                                                                          1x 1x     2x                                   2x   2x     1x 1x 1x 1x 1x         1x 1x 1x 1x     2x 2x 1x     2x 1x     2x 2x         2x 2x 2x   2x 2x           1x  
/**
 * 此方法用于呼起本地客户端,呼起失败时,跳转到客户端下载地址或者中间页
 * - 首先需要客户端提供一个协议地址 protocol
 * - 然后通过浏览器访问该地址或者 iframe 访问该协议地址来触发客户端的打开动作
 * - 在设定好的超时时间 (waiting) 到达时进行检查
 * - 由于 IOS 下,跳转到 APP,页面 JS 会被阻止执行
 * - 所以如果超时时间大大超过了预期时间范围,可断定 APP 已被打开,此时触发 onTimeout 回调事件函数
 * - 对于 IOS,此时如果从 APP 返回页面,就可以通过 waitingLimit 时间差来判断是否要执行 fallback 回调事件函数
 * - Android 下,跳转到 APP,页面 JS 会继续执行
 * - 此时无论 APP 是否已打开,都会触发 onFallback 事件与 fallback 回调事件函数
 * - fallback 默认操作是跳转到 fallbackUrl 客户端下载地址或者中间页地址
 * - 这样对于没有安装 APP 的移动端,页面会在超时事件发生时,直接跳转到 fallbackUrl
 * @method app/callUp
 * @param {Object} options
 * @param {String} options.protocol 客户端APP呼起协议地址
 * @param {String} options.fallbackUrl 客户端下载地址或者中间页地址
 * @param {Function} options.action 自定义呼起客户端的方式
 * @param {Number} [options.startTime=new Date().getTime()] 呼起客户端的开始时间(ms),以时间数值作为参数
 * @param {Number} [options.waiting=800] 呼起超时等待时间(ms)
 * @param {Number} [options.waitingLimit=50] 超时后检查回调是否在此时间限制内触发(ms)
 * @param {Function} [options.fallback=function () { window.location = fallbackUrl; }] 默认回退操作
 * @param {Function} [options.onFallback=null] 呼起操作未能成功执行时触发的回调事件函数
 * @param {Function} [options.onTimeout=null] 呼起超时触发的回调事件函数
 * @example
 * var $callUp = require('@spore-ui/kit/packages/app/callUp');
 * $callUp({
 *   startTime: Date.now(),
 *   waiting: 800,
 *   waitingLimit: 50,
 *   protocol : scheme,
 *   fallbackUrl : download,
 *   onFallback : function () {
 *     // should download
 *   }
 * });
 */
 
var $assign = require('../obj/assign');
var $browser = require('../env/browser');
 
function callUp(options) {
  var conf = $assign({
    protocol: '',
    fallbackUrl: '',
    action: null,
    startTime: new Date().getTime(),
    waiting: 800,
    waitingLimit: 50,
    fallback: function (fallbackUrl) {
      // 在一定时间内无法唤起客户端,跳转下载地址或到中间页
      window.location = fallbackUrl;
    },
    onFallback: null,
    onTimeout: null,
  }, options);
 
  var wId;
  var iframe;
 
  Iif (typeof conf.action === 'function') {
    conf.action();
  } else if ($browser().chrome) {
    // chrome下iframe无法唤起Android客户端,这里使用window.open
    // 另一个方案参考 https://developers.google.com/chrome/mobile/docs/intents
    var win = window.open(conf.protocol);
    wId = setInterval(function () {
      Eif (typeof win === 'object') {
        clearInterval(wId);
        win.close();
      }
    }, 10);
  } else {
    // 创建iframe
    iframe = document.createElement('iframe');
    iframe.style.display = 'none';
    iframe.src = conf.protocol;
    document.body.appendChild(iframe);
  }
 
  setTimeout(function () {
    if (wId) {
      clearInterval(wId);
    }
 
    if (iframe) {
      document.body.removeChild(iframe);
    }
 
    Eif (typeof conf.onTimeout === 'function') {
      conf.onTimeout();
    }
 
    // ios下,跳转到APP,页面JS会被阻止执行。
    // 因此如果超时时间大大超过了预期时间范围,可断定APP已被打开。
    Eif (new Date().getTime() - conf.startTime < conf.waiting + conf.waitingLimit) {
      Eif (typeof conf.onFallback === 'function') {
        conf.onFallback();
      }
      Eif (typeof conf.fallback === 'function') {
        conf.fallback(conf.fallbackUrl);
      }
    }
  }, conf.waiting);
}
 
module.exports = callUp;