{"version":3,"file":"quicklink.mjs","sources":["../src/prefetch.js","../src/request-idle-callback.js","../src/index.js"],"sourcesContent":["const preFetched = {};\n\n/**\n * Checks if a feature on `link` is natively supported.\n * Examples of features include `prefetch` and `preload`.\n * @param {string} feature - name of the feature to test\n * @return {Boolean} whether the feature is supported\n */\nfunction support(feature) {\n  const link = document.createElement('link');\n  return link.relList && link.relList.supports && link.relList.supports(feature);\n}\n\n/**\n * Fetches a given URL using `<link rel=prefetch>`\n * @param {string} url - the URL to fetch\n * @return {Object} a Promise\n */\nfunction linkPrefetchStrategy(url) {\n  return new Promise((resolve, reject) => {\n    const link = document.createElement(`link`);\n    link.rel = `prefetch`;\n    link.href = url;\n\n    link.onload = resolve;\n    link.onerror = reject;\n\n    document.head.appendChild(link);\n  });\n};\n\n/**\n * Fetches a given URL using XMLHttpRequest\n * @param {string} url - the URL to fetch\n * @return {Object} a Promise\n */\nfunction xhrPrefetchStrategy(url) {\n  return new Promise((resolve, reject) => {\n    const req = new XMLHttpRequest();\n\n    req.open(`GET`, url, req.withCredentials=true);\n\n    req.onload = () => {\n      (req.status === 200) ? resolve() : reject();\n    };\n\n    req.send();\n  });\n}\n\n/**\n * Fetches a given URL using the Fetch API. Falls back\n * to XMLHttpRequest if the API is not supported.\n * @param {string} url - the URL to fetch\n * @return {Object} a Promise\n */\nfunction highPriFetchStrategy(url) {\n  // TODO: Investigate using preload for high-priority\n  // fetches. May have to sniff file-extension to provide\n  // valid 'as' values. In the future, we may be able to\n  // use Priority Hints here.\n  //\n  // As of 2018, fetch() is high-priority in Chrome\n  // and medium-priority in Safari.\n  return self.fetch == null\n    ? xhrPrefetchStrategy(url)\n    : fetch(url, {credentials: `include`});\n}\n\nconst supportedPrefetchStrategy = support('prefetch')\n  ? linkPrefetchStrategy\n  : xhrPrefetchStrategy;\n\n/**\n * Prefetch a given URL with an optional preferred fetch priority\n * @param {String} url - the URL to fetch\n * @param {Boolean} isPriority - if is \"high\" priority\n * @param {Object} conn - navigator.connection (internal)\n * @return {Object} a Promise\n */\nfunction prefetcher(url, isPriority, conn) {\n  if (preFetched[url]) {\n    return;\n  }\n\n  if (conn = navigator.connection) {\n    // Don't prefetch if the user is on 2G. or 3G or if Save-Data is enabled..\n    const effType = (conn.effectiveType || '');\n    if (effType.includes('2g') || effType.includes('3g') || conn.saveData) return;\n  }\n\n  // Wanna do something on catch()?\n  return (isPriority ? highPriFetchStrategy : supportedPrefetchStrategy)(url).then(() => {\n    preFetched[url] = true;\n  }, error=> console.warn(error));\n};\n\nexport default prefetcher;\n","/**\n * Copyright 2018 Google Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n**/\n\n// RIC and shim for browsers setTimeout() without it\nconst requestIdleCallback = requestIdleCallback ||\n  function (cb) {\n    const start = Date.now();\n    return setTimeout(function () {\n      cb({\n        didTimeout: false,\n        timeRemaining: function () {\n          return Math.max(0, 50 - (Date.now() - start));\n        },\n      });\n    }, 0);\n  };\n\nexport default requestIdleCallback;\n","/*\n * @Author: 率小火汁Jsonz\n * @Date: 2019-02-25 17:33:09\n * @Last Modified by: 率小火汁Jsonz\n * @Last Modified time: 2019-02-27 17:00:29\n * @Description: prefetch module\n * 使用:\n * - 默认情况\n * quicklink(options);\n * @options timeoutFn 执行监听绑定的函数，默认是 requestIdleCallback\n * @options timeout 执行监听的最后期限\n * @options el 在该组件下寻找a标签监听\n * @options allowed 允许的域名，默认是当前域名\n * @options ignores 要忽略的链接\n * @options urls 直接对urls进行预加载\n *\n * - 移除单个监听\n * manualRemovePreFetch(dom, link)\n * @dom 移除监听的dom\n * @link 移除监听的link，不传会取 dom.href\n *\n * - 批量移除监听\n * batchManualRemove(dom)\n * @dom 移除传入的dom or document 下面所有找到的a标签的监听\n *\n * - 设置全局配置 quicklink 会读取全局配置\n * setQuicklinkOptions(options)\n *\n * - 获取全局配置\n * getQuicklinkOptions\n *\n * - 手动设置监听某个dom\n * manualPreFetch(options)\n * @options dom 要监听的dom，没有强制要求是什么类型\n * @options isForce 是否强制，如果传true，直接preFetch而不是监听再 preFetch\n * @options link 执行prefetch的link\n * @options priority 传true就用FetchAPI，不明所以\n * @return 返回一个可以取消监听的函数\n *\n*/\nimport 'intersection-observer';\nimport prefetch from './prefetch';\nimport requestIdleCallback from './request-idle-callback';\nimport noop from './noop';\n\n// prefetch list\nconst toPrefetch = new Set();\n\n// 默认的全局配置\nconst quicklinkOptions = {\n  ignores: [],\n  priority: false,\n  allowed: [location.hostname],\n  timeout: 4e3,\n  timeoutFn: requestIdleCallback,\n};\n\n/**\n * 获取全局的 quicklinkOptions\n * @return {Object}\n */\nexport function getQuicklinkOptions() {\n  return quicklinkOptions;\n}\n\n/**\n * 设置全局的 quicklinkOptions\n * @param {Object} options\n * @return {Object} new options\n */\nexport function setQuicklinkOptions(options) {\n  Object.assign(quicklinkOptions, options);\n  return quicklinkOptions;\n}\n\n/**\n * 默认监控\n * 1. 触发回调后，判断是否进入用户界面\n * 2. 在 prefetch 列表里面的才执行该操作做\n * 3. 取消监听并执行预加载\n */\nconst defaultObserver = new IntersectionObserver(entries=> {\n  entries.forEach(entry=> {\n    if (!entry.isIntersecting) return;\n\n    const dom = entry.target;\n    const link = dom.dataset.href || dom.href;\n\n    if (!toPrefetch.has(link)) return;\n    defaultObserver.unobserve(dom);\n    prefetcher(link, quicklinkOptions.priority);\n  });\n});\n\n\n/**\n * @param {String} url\n * @param {Boolean} priority\n */\nfunction prefetcher(url, priority= false) {\n  toPrefetch.delete(url);\n  prefetch(new URL(url, location.href).toString(), priority);\n}\n\n/**\n * @param {String} link\n * @param {Mixed} filter\n * @return {Boolean}\n */\nfunction isIgnored(link, filter) {\n  return Array.isArray(filter)\n    ? filter.some(x=> isIgnored(link, x))\n    : (filter.test || filter).call(filter, link);\n}\n\n/**\n * 手动触发dom监听或者直接触发preload\n * 1. 如果isForce是true，直接调用, return\n * 2. 如果 toPrefetch 有，则不需要处理\n * 3. 如果没有，则手动添加到 observer 监听\n * @param {Object} options\n * @return {Function} 返回手动移除的函数，isForce 没有这种操作\n */\nexport function manualPreFetch({dom, isForce, link, priority}={}) {\n  if (!dom) {\n    console.warn('@arguments: dom is require');\n    return noop;\n  }\n  const href = link || dom.href;\n  dom.dataset.href = href;\n\n  if (isForce) {\n    prefetch(new URL(href, location.href).toString, priority);\n    return noop;\n  }\n  if (toPrefetch.has(href) || isIgnored(href, quicklinkOptions.ignores)) return noop;\n\n  defaultObserver.observe(dom);\n  toPrefetch.add(href);\n\n  return ()=> manualRemovePreFetch(dom, href);\n}\n\n/**\n * 手动移除监听\n * @param {Object} dom\n * @param {String} link\n */\nexport function manualRemovePreFetch(dom, link) {\n  if (!dom) return;\n  link = link || dom.dataset.href || dom.href;\n  defaultObserver.unobserve(dom);\n  toPrefetch.delete(link);\n}\n\n/**\n * 批量移除监听\n * @param {Object} dom\n */\nexport function batchManualRemove(dom) {\n  Array.from((dom || document).querySelectorAll('a'), manualRemovePreFetch);\n}\n\n/**\n * 默认的函数\n * @param {*} options\n */\nexport default function (options= {}) {\n  const userOptions = Object.assign({}, quicklinkOptions, options);\n\n  const {timeoutFn, timeout, el, allowed, ignores, urls} = userOptions;\n\n  timeoutFn(()=> {\n    if (urls) urls.forEach(prefetcher);\n    else {\n      Array.from((el || document).querySelectorAll('a'), dom=> {\n        if (!allowed.length || allowed.includes(dom.hostname)) {\n          defaultObserver.observe(dom);\n          isIgnored(dom.href, ignores) || toPrefetch.add(dom.href);\n        }\n      });\n    }\n  }, {timeout});\n}\n\n"],"names":["const","preFetched","xhrPrefetchStrategy","url","Promise","resolve","reject","req","XMLHttpRequest","open","withCredentials","onload","status","send","feature","link","supportedPrefetchStrategy","document","createElement","relList","supports","rel","href","onerror","head","appendChild","prefetcher","isPriority","conn","navigator","connection","effType","effectiveType","includes","saveData","self","fetch","credentials","then","error","console","warn","requestIdleCallback","cb","start","Date","now","setTimeout","didTimeout","timeRemaining","Math","max","toPrefetch","Set","quicklinkOptions","ignores","priority","allowed","location","hostname","timeout","timeoutFn","getQuicklinkOptions","setQuicklinkOptions","options","Object","assign","defaultObserver","IntersectionObserver","entries","forEach","entry","isIntersecting","dom","target","dataset","has","unobserve","delete","prefetch","URL","toString","isIgnored","filter","Array","isArray","some","x","test","call","manualPreFetch","ref","noop","isForce","observe","add","manualRemovePreFetch","batchManualRemove","from","querySelectorAll","userOptions","urls","el","length"],"mappings":"8BAAAA,IAAMC,EAAa,GAoCnB,SAASC,EAAoBC,UACpB,IAAIC,iBAASC,EAASC,OACrBC,EAAM,IAAIC,eAEhBD,EAAIE,KAAM,MAAMN,EAAKI,EAAIG,iBAAgB,GAEzCH,EAAII,kBACc,MAAfJ,EAAIK,OAAkBP,IAAYC,KAGrCC,EAAIM,SAuBRb,IA7DiBc,EACTC,EA4DFC,GA7DWF,EA6DyB,YA5DlCC,EAAOE,SAASC,cAAc,SACxBC,SAAWJ,EAAKI,QAAQC,UAAYL,EAAKI,QAAQC,SAASN,GAQxE,SAA8BX,UACrB,IAAIC,iBAASC,EAASC,OACrBS,EAAOE,SAASC,cAAe,QACrCH,EAAKM,IAAO,WACZN,EAAKO,KAAOnB,EAEZY,EAAKJ,OAASN,EACdU,EAAKQ,QAAUjB,EAEfW,SAASO,KAAKC,YAAYV,MA4C1Bb,GASJ,SAASwB,EAAWvB,EAAKwB,EAAYC,OAC/B3B,EAAWE,OAIXyB,EAAOC,UAAUC,WAAY,KAEzBC,EAAWH,EAAKI,eAAiB,MACnCD,EAAQE,SAAS,OAASF,EAAQE,SAAS,OAASL,EAAKM,SAAU,cAIjEP,EApCV,SAA8BxB,UAQP,MAAdgC,KAAKC,MACRlC,EAAoBC,GACpBiC,MAAMjC,EAAK,CAACkC,YAAc,aA0BcrB,GAA2Bb,GAAKmC,gBAC1ErC,EAAWE,IAAO,YACjBoC,UAAQC,QAAQC,KAAKF,MC7E1BvC,IAAM0C,EAAsBA,GAC1B,SAAUC,OACFC,EAAQC,KAAKC,aACZC,WAAW,WAChBJ,EAAG,CACDK,YAAY,EACZC,cAAe,kBACNC,KAAKC,IAAI,EAAG,IAAMN,KAAKC,MAAQF,QAGzC,mBCmBDQ,EAAa,IAAIC,IAGjBC,EAAmB,CACvBC,QAAS,GACTC,UAAU,EACVC,QAAS,CAACC,SAASC,UACnBC,QAAS,IACTC,UAAWnB,GAOb,SAAgBoB,WACPR,EAQF,SAASS,EAAoBC,UAClCC,OAAOC,OAAOZ,EAAkBU,GACzBV,EASTtD,IAAMmE,EAAkB,IAAIC,8BAAqBC,GAC/CA,EAAQC,iBAAQC,MACTA,EAAMC,oBAELC,EAAMF,EAAMG,OACZ3D,EAAO0D,EAAIE,QAAQrD,MAAQmD,EAAInD,KAEhC8B,EAAWwB,IAAI7D,KACpBoD,EAAgBU,UAAUJ,GAC1B/C,EAAWX,EAAMuC,EAAiBE,gBAStC,SAAS9B,EAAWvB,EAAKqD,mBAAU,GACjCJ,EAAW0B,OAAO3E,GAClB4E,EAAS,IAAIC,IAAI7E,EAAKuD,SAASpC,MAAM2D,WAAYzB,GAQnD,SAAS0B,EAAUnE,EAAMoE,UAChBC,MAAMC,QAAQF,GACjBA,EAAOG,cAAKC,UAAIL,EAAUnE,EAAMwE,MAC/BJ,EAAOK,MAAQL,GAAQM,KAAKN,EAAQpE,GAWpC,SAAS2E,EAAeC,kBAA+B,sDACvDlB,SACHjC,QAAQC,KAAK,8BACNmD,MAEHtE,EAAOP,GAAQ0D,EAAInD,YACzBmD,EAAIE,QAAQrD,KAAOA,EAEfuE,GACFd,EAAS,IAAIC,IAAI1D,EAAMoC,SAASpC,MAAM2D,SAAUzB,GACzCoC,GAELxC,EAAWwB,IAAItD,IAAS4D,EAAU5D,EAAMgC,EAAiBC,SAAiBqC,GAE9EzB,EAAgB2B,QAAQrB,GACxBrB,EAAW2C,IAAIzE,qBAEH0E,EAAqBvB,EAAKnD,KAQjC,SAAS0E,EAAqBvB,EAAK1D,GACnC0D,IACL1D,EAAOA,GAAQ0D,EAAIE,QAAQrD,MAAQmD,EAAInD,KACvC6C,EAAgBU,UAAUJ,GAC1BrB,EAAW0B,OAAO/D,IAOb,SAASkF,EAAkBxB,GAChCW,MAAMc,MAAMzB,GAAOxD,UAAUkF,iBAAiB,KAAMH,kBAOtD,SAAyBhC,kBAAS,QAC1BoC,EAAcnC,OAAOC,OAAO,GAAIZ,EAAkBU,4CAIxDH,0BACMwC,EAAMA,EAAK/B,QAAQ5C,GAErB0D,MAAMc,MAAMI,GAAMrF,UAAUkF,iBAAiB,cAAM1B,GAC5ChB,EAAQ8C,SAAU9C,EAAQxB,SAASwC,EAAId,YAC1CQ,EAAgB2B,QAAQrB,GACxBS,EAAUT,EAAInD,KAAMiC,IAAYH,EAAW2C,IAAItB,EAAInD,UAIxD"}