@waline/client 2.13.1 → 2.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/api.cjs +1 -1
  2. package/dist/api.cjs.map +1 -1
  3. package/dist/api.d.cts +60 -34
  4. package/dist/api.d.mts +60 -34
  5. package/dist/api.d.ts +60 -34
  6. package/dist/api.mjs +1 -1
  7. package/dist/api.mjs.map +1 -1
  8. package/dist/comment.cjs +1 -1
  9. package/dist/comment.cjs.map +1 -1
  10. package/dist/comment.d.cts +1 -1
  11. package/dist/comment.d.mts +1 -1
  12. package/dist/comment.d.ts +1 -1
  13. package/dist/comment.js +40 -45
  14. package/dist/comment.js.map +1 -1
  15. package/dist/comment.mjs +1 -1
  16. package/dist/comment.mjs.map +1 -1
  17. package/dist/component.mjs +1 -1
  18. package/dist/component.mjs.map +1 -1
  19. package/dist/legacy.umd.d.ts +13 -3
  20. package/dist/legacy.umd.js +1 -1
  21. package/dist/legacy.umd.js.map +1 -1
  22. package/dist/pageview.cjs +1 -1
  23. package/dist/pageview.cjs.map +1 -1
  24. package/dist/pageview.js +44 -49
  25. package/dist/pageview.js.map +1 -1
  26. package/dist/pageview.mjs +1 -1
  27. package/dist/pageview.mjs.map +1 -1
  28. package/dist/shim.cjs +1 -1
  29. package/dist/shim.cjs.map +1 -1
  30. package/dist/shim.d.cts +15 -9
  31. package/dist/shim.d.mts +15 -9
  32. package/dist/shim.mjs +1 -1
  33. package/dist/shim.mjs.map +1 -1
  34. package/dist/waline.cjs +1 -1
  35. package/dist/waline.cjs.map +1 -1
  36. package/dist/waline.css +1 -1
  37. package/dist/waline.css.map +1 -1
  38. package/dist/waline.d.cts +15 -9
  39. package/dist/waline.d.mts +15 -9
  40. package/dist/waline.d.ts +15 -9
  41. package/dist/waline.js +442 -450
  42. package/dist/waline.js.map +1 -1
  43. package/dist/waline.mjs +1 -1
  44. package/dist/waline.mjs.map +1 -1
  45. package/package.json +1 -1
  46. package/src/api/articleCounter.ts +3 -7
  47. package/src/api/comment.ts +60 -49
  48. package/src/api/commentCount.ts +0 -2
  49. package/src/api/pageview.ts +2 -2
  50. package/src/api/recentComment.ts +2 -5
  51. package/src/api/user.ts +2 -4
  52. package/src/api/utils.ts +17 -10
  53. package/src/comment.ts +1 -1
  54. package/src/compact/convert.ts +1 -1
  55. package/src/components/CommentBox.vue +14 -11
  56. package/src/components/Waline.vue +35 -34
  57. package/src/pageview.ts +3 -3
  58. package/src/styles/index.scss +2 -2
  59. package/src/styles/{nomalize.scss → normalize.scss} +0 -0
  60. package/src/styles/panel.scss +1 -1
  61. package/src/styles/{userlist.scss → user-list.scss} +0 -0
  62. package/src/typings/base.ts +5 -1
  63. package/src/typings/comment.ts +1 -5
  64. package/src/typings/waline.ts +13 -2
  65. package/src/utils/config.ts +3 -1
  66. package/src/utils/date.ts +3 -3
package/dist/pageview.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";const e={"Content-Type":"application/json"},t=(e,t="")=>{if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},n=({serverURL:e,lang:n,paths:r,signal:a})=>(({serverURL:e,lang:n,paths:r,type:a,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${n}`,{signal:o}).then((e=>e.json())).then((e=>t(e,"article count"))))({serverURL:e,lang:n,paths:r,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e])),r=n=>(({serverURL:n,lang:r,path:a,type:o,action:s})=>fetch(`${n}/article?lang=${r}`,{method:"POST",headers:e,body:JSON.stringify({path:a,type:o,action:s})}).then((e=>e.json())).then((e=>t(e,"article count"))))({...n,type:"time",action:"inc"}),a=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},o=e=>{"AbortError"!==e.name&&console.error(e.message)},s=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))};exports.pageviewCount=({serverURL:e,path:t=window.location.pathname,selector:l=".waline-pageview-count",update:c=!0,lang:p="zh-CN"})=>{const h=new AbortController,g=Array.from(document.querySelectorAll(l)),y=e=>{const n=s(e);return null!==n&&t!==n},u=r=>n({serverURL:a(e),paths:r.map((e=>s(e)||t)),lang:p,signal:h.signal}).then((e=>i(e,r))).catch(o);if(c){const n=g.filter((e=>!y(e))),o=g.filter(y);r({serverURL:a(e),path:t,lang:p}).then((e=>i(new Array(n.length).fill(e),n))),o.length&&u(o)}else u(g);return h.abort.bind(h)},exports.version="2.13.1";
1
+ "use strict";const e={"Content-Type":"application/json"},t=({serverURL:e,lang:t,paths:n,signal:r})=>(({serverURL:e,lang:t,paths:n,type:r,signal:a})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(r.join(","))}&lang=${t}`,{signal:a}).then((e=>e.json())))({serverURL:e,lang:t,paths:n,type:["time"],signal:r}).then((e=>Array.isArray(e)?e:[e])),n=t=>(({serverURL:t,lang:n,path:r,type:a,action:o})=>fetch(`${t}/article?lang=${n}`,{method:"POST",headers:e,body:JSON.stringify({path:r,type:a,action:o})}).then((e=>e.json())))({...t,type:"time",action:"inc"}),r=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},a=e=>{"AbortError"!==e.name&&console.error(e.message)},o=e=>e.dataset.path||e.getAttribute("id"),s=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))};exports.pageviewCount=({serverURL:e,path:i=window.location.pathname,selector:l=".waline-pageview-count",update:p=!0,lang:c="zh-CN"})=>{const h=new AbortController,g=Array.from(document.querySelectorAll(l)),y=e=>{const t=o(e);return null!==t&&i!==t},u=n=>t({serverURL:r(e),paths:n.map((e=>o(e)||i)),lang:c,signal:h.signal}).then((e=>s(e,n))).catch(a);if(p){const t=g.filter((e=>!y(e))),a=g.filter(y);n({serverURL:r(e),path:i,lang:c}).then((e=>s(new Array(t.length).fill(e),t))),a.length&&u(a)}else u(g);return h.abort.bind(h)},exports.version="2.14.1";
2
2
  //# sourceMappingURL=pageview.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.cjs","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface APIErrorResponse {\n errno: number;\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T = unknown>(\n data: T | APIErrorResponse,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as APIErrorResponse).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as APIErrorResponse).errno}: ${\n (data as APIErrorResponse).errmsg\n }`\n );\n\n return data as T;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then((resp) => <Promise<GetArticleCounterResponse>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n })\n .then((resp) => <Promise<number>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search || getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageviews, updatePageviews } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageviews({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageviews({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["JSON_HEADERS","errorCheck","data","name","errno","TypeError","errmsg","getPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","getArticleCounter","counts","Array","isArray","updatePageviews","options","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"aAEO,MCmBMA,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAA0BE,MACzD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAA0BE,UACrDF,EAA0BI,UAIjC,OAAOJ,CAAS,EClBLK,EAAe,EAC1BC,YACAC,OACAC,QACAC,YCS+B,GAC/BH,YACAC,OACAC,QACAE,OACAD,YAEAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MAAMC,GAA6CA,EAAKC,SACxDF,MAAMd,GAASD,EAAWC,EAAM,mBDrBnCiB,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GC0BkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAA0BA,EAAKC,SACrCF,MAAMd,GAASD,EAAWC,EAAM,mBDrCnC8B,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QEpBCO,EAAgBzB,IAC3B,MAAM0B,ECvB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDsBTC,CAAmB7B,GAElC,MCrBA,kBAAkB8B,KDqBAJ,GAAUA,EAAS,WAAWA,GAAQ,EEnC7CK,EAAgBC,IACV,eAAbA,EAAIrC,MAAuBsC,QAAQC,MAAMF,EAAIG,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQrB,MAAQoB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB5B,EACA6B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYhC,EAAO+B,GAAOE,UAAU,GAC5C,wBAGyB,EAC3B7C,YACAiB,OAAO6B,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTjD,OAAO,YAEP,MAAMkD,EAAa,IAAIC,gBAEjBC,EAAWxC,MAAMyC,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUpB,IACd,MAAMqB,EAAQtB,EAASC,GAEvB,OAAiB,OAAVqB,GAAkBzC,IAASyC,CAAK,EAGnCrD,EAASgD,GACbtD,EAAa,CACXC,UAAWyB,EAAazB,GACxBE,MAAOmD,EAASM,KAAKtB,GAAYD,EAASC,IAAYpB,IACtDhB,OACAE,OAAQgD,EAAWhD,SAElBK,MAAMI,GAAW4B,EAAmB5B,EAAQyC,KAC5CO,MAAM7B,GAGX,GAAImB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQpB,IAAaoB,EAAOpB,KACtDyB,EAA2BT,EAASI,OAAOA,GAE5C1C,EAAgB,CACnBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAMuD,GACPvB,EACE,IAAI3B,MAAcgD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB3D,EAAMyD,EAEd,MAGMzD,EAAMgD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW,kBRjHnB"}
1
+ {"version":3,"file":"pageview.cjs","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface ErrorStatusResponse {\n /**\n * 错误代码\n *\n * Error number\n */\n errno: number;\n\n /**\n * 错误消息\n *\n * Error msg\n */\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T extends ErrorStatusResponse>(\n data: T,\n name = ''\n): T => {\n if (typeof data === 'object' && data.errno)\n throw new TypeError(`${name} failed with ${data.errno}: ${data.errmsg}`);\n\n return data;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageview = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageview = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n ).then((resp) => <Promise<GetArticleCounterResponse>>resp.json());\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n }).then((resp) => <Promise<number>>resp.json());\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search ?? getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n commentSorting,\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageview, updatePageview } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageview({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageview({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["JSON_HEADERS","getPageview","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","getArticleCounter","counts","Array","isArray","updatePageview","options","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","name","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"aAEO,MC8BMA,EAAuC,CAElD,eAAgB,oBCfLC,EAAc,EACzBC,YACAC,OACAC,QACAC,YCS+B,GAC/BH,YACAC,OACAC,QACAE,OACAD,YAEAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WACFK,MAAMC,GAA6CA,EAAKC,SDnB1DC,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GCwBkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAStB,EACTuB,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAClCV,MAAMC,GAA0BA,EAAKC,SDjCxCc,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QEpBCO,EAAgBzB,IAC3B,MAAM0B,ECvB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDsBTC,CAAmB7B,GAElC,MCrBA,kBAAkB8B,KDqBAJ,GAAUA,EAAS,WAAWA,GAAQ,EEnC7CK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQtB,MAAQqB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB7B,EACA8B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYjC,EAAOgC,GAAOE,UAAU,GAC5C,wBAGyB,EAC3B9C,YACAiB,OAAO8B,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTlD,OAAO,YAEP,MAAMmD,EAAa,IAAIC,gBAEjBC,EAAWzC,MAAM0C,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUpB,IACd,MAAMqB,EAAQtB,EAASC,GAEvB,OAAiB,OAAVqB,GAAkB1C,IAAS0C,CAAK,EAGnCtD,EAASiD,GACbvD,EAAY,CACVC,UAAWyB,EAAazB,GACxBE,MAAOoD,EAASM,KAAKtB,GAAYD,EAASC,IAAYrB,IACtDhB,OACAE,OAAQiD,EAAWjD,SAElBK,MAAMI,GAAW6B,EAAmB7B,EAAQ0C,KAC5CO,MAAM9B,GAGX,GAAIoB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQpB,IAAaoB,EAAOpB,KACtDyB,EAA2BT,EAASI,OAAOA,GAE5C3C,EAAe,CAClBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAMwD,GACPvB,EACE,IAAI5B,MAAciD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5D,EAAM0D,EAEd,MAGM1D,EAAMiD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW,kBRjHnB"}
package/dist/pageview.js CHANGED
@@ -17,111 +17,106 @@
17
17
  value: true
18
18
  });
19
19
  _exports.version = _exports.pageviewCount = void 0;
20
- const e = "2.13.1",
20
+ const e = "2.14.1",
21
21
  t = {
22
22
  "Content-Type": "application/json"
23
23
  },
24
- n = function (e) {
25
- let t = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
26
- if ("object" == typeof e && e.errno) throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);
27
- return e;
28
- },
29
- r = _ref => {
24
+ n = _ref => {
30
25
  let {
31
26
  serverURL: e,
32
27
  lang: t,
33
- paths: r,
34
- signal: a
28
+ paths: n,
29
+ signal: r
35
30
  } = _ref;
36
31
  return (_ref2 => {
37
32
  let {
38
33
  serverURL: e,
39
34
  lang: t,
40
- paths: r,
41
- type: a,
42
- signal: o
35
+ paths: n,
36
+ type: r,
37
+ signal: a
43
38
  } = _ref2;
44
- return fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${t}`, {
45
- signal: o
46
- }).then(e => e.json()).then(e => n(e, "article count"));
39
+ return fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(r.join(","))}&lang=${t}`, {
40
+ signal: a
41
+ }).then(e => e.json());
47
42
  })({
48
43
  serverURL: e,
49
44
  lang: t,
50
- paths: r,
45
+ paths: n,
51
46
  type: ["time"],
52
- signal: a
47
+ signal: r
53
48
  }).then(e => Array.isArray(e) ? e : [e]);
54
49
  },
55
- a = e => (_ref3 => {
50
+ r = e => (_ref3 => {
56
51
  let {
57
52
  serverURL: e,
58
- lang: r,
59
- path: a,
60
- type: o,
61
- action: l
53
+ lang: n,
54
+ path: r,
55
+ type: a,
56
+ action: o
62
57
  } = _ref3;
63
- return fetch(`${e}/article?lang=${r}`, {
58
+ return fetch(`${e}/article?lang=${n}`, {
64
59
  method: "POST",
65
60
  headers: t,
66
61
  body: JSON.stringify({
67
- path: a,
68
- type: o,
69
- action: l
62
+ path: r,
63
+ type: a,
64
+ action: o
70
65
  })
71
- }).then(e => e.json()).then(e => n(e, "article count"));
66
+ }).then(e => e.json());
72
67
  })({
73
68
  ...e,
74
69
  type: "time",
75
70
  action: "inc"
76
71
  }),
77
- o = e => {
72
+ a = e => {
78
73
  const t = function () {
79
74
  let e = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
80
75
  return e.replace(/\/$/u, "");
81
76
  }(e);
82
77
  return /^(https?:)?\/\//.test(t) ? t : `https://${t}`;
83
78
  },
84
- l = e => {
79
+ o = e => {
85
80
  "AbortError" !== e.name && console.error(e.message);
86
81
  },
87
- i = e => e.dataset.path || e.getAttribute("id"),
82
+ l = e => e.dataset.path || e.getAttribute("id"),
88
83
  s = (e, t) => {
89
84
  t.forEach((t, n) => {
90
85
  t.innerText = e[n].toString();
91
86
  });
92
87
  },
93
- c = _ref4 => {
88
+ i = _ref4 => {
94
89
  let {
95
90
  serverURL: e,
96
91
  path: t = window.location.pathname,
97
- selector: n = ".waline-pageview-count",
98
- update: c = !0,
92
+ selector: i = ".waline-pageview-count",
93
+ update: p = !0,
99
94
  lang: h = "zh-CN"
100
95
  } = _ref4;
101
- const p = new AbortController(),
102
- g = Array.from(document.querySelectorAll(n)),
96
+ const c = new AbortController(),
97
+ g = Array.from(document.querySelectorAll(i)),
103
98
  y = e => {
104
- const n = i(e);
99
+ const n = l(e);
105
100
  return null !== n && t !== n;
106
101
  },
107
- u = n => r({
108
- serverURL: o(e),
109
- paths: n.map(e => i(e) || t),
102
+ d = r => n({
103
+ serverURL: a(e),
104
+ paths: r.map(e => l(e) || t),
110
105
  lang: h,
111
- signal: p.signal
112
- }).then(e => s(e, n)).catch(l);
113
- if (c) {
106
+ signal: c.signal
107
+ }).then(e => s(e, r)).catch(o);
108
+ if (p) {
114
109
  const n = g.filter(e => !y(e)),
115
- r = g.filter(y);
116
- a({
117
- serverURL: o(e),
110
+ o = g.filter(y);
111
+ r({
112
+ serverURL: a(e),
118
113
  path: t,
119
114
  lang: h
120
- }).then(e => s(new Array(n.length).fill(e), n)), r.length && u(r);
121
- } else u(g);
122
- return p.abort.bind(p);
115
+ }).then(e => s(new Array(n.length).fill(e), n)), o.length && d(o);
116
+ } else d(g);
117
+ return c.abort.bind(c);
123
118
  };
124
- _exports.pageviewCount = c;
119
+ _exports.pageviewCount = i;
125
120
  _exports.version = e;
126
121
  });
127
122
  //# sourceMappingURL=pageview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.js","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface APIErrorResponse {\n errno: number;\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T = unknown>(\n data: T | APIErrorResponse,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as APIErrorResponse).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as APIErrorResponse).errno}: ${\n (data as APIErrorResponse).errmsg\n }`\n );\n\n return data as T;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then((resp) => <Promise<GetArticleCounterResponse>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n })\n .then((resp) => <Promise<number>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search || getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageviews, updatePageviews } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageviews({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageviews({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["version","JSON_HEADERS","errorCheck","data","name","errno","TypeError","errmsg","getPageviews","serverURL","lang","paths","signal","a","type","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","options","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","pageviewCount","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"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"}
1
+ {"version":3,"file":"pageview.js","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface ErrorStatusResponse {\n /**\n * 错误代码\n *\n * Error number\n */\n errno: number;\n\n /**\n * 错误消息\n *\n * Error msg\n */\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T extends ErrorStatusResponse>(\n data: T,\n name = ''\n): T => {\n if (typeof data === 'object' && data.errno)\n throw new TypeError(`${name} failed with ${data.errno}: ${data.errmsg}`);\n\n return data;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageview = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageview = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n ).then((resp) => <Promise<GetArticleCounterResponse>>resp.json());\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n }).then((resp) => <Promise<number>>resp.json());\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search ?? getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n commentSorting,\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageview, updatePageview } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageview({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageview({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["version","JSON_HEADERS","getPageview","serverURL","lang","paths","signal","r","type","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageview","options","path","action","method","headers","body","JSON","stringify","getServerURL","result","content","replace","test","errorHandler","err","name","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","pageviewCount","window","location","pathname","selector","i","update","p","h","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"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"}
package/dist/pageview.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const e="2.13.1",t={"Content-Type":"application/json"},n=(e,t="")=>{if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},r=({serverURL:e,lang:t,paths:r,signal:a})=>(({serverURL:e,lang:t,paths:r,type:a,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${t}`,{signal:o}).then((e=>e.json())).then((e=>n(e,"article count"))))({serverURL:e,lang:t,paths:r,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e])),a=e=>(({serverURL:e,lang:r,path:a,type:o,action:l})=>fetch(`${e}/article?lang=${r}`,{method:"POST",headers:t,body:JSON.stringify({path:a,type:o,action:l})}).then((e=>e.json())).then((e=>n(e,"article count"))))({...e,type:"time",action:"inc"}),o=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},l=e=>{"AbortError"!==e.name&&console.error(e.message)},i=e=>e.dataset.path||e.getAttribute("id"),s=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))},c=({serverURL:e,path:t=window.location.pathname,selector:n=".waline-pageview-count",update:c=!0,lang:h="zh-CN"})=>{const p=new AbortController,g=Array.from(document.querySelectorAll(n)),y=e=>{const n=i(e);return null!==n&&t!==n},u=n=>r({serverURL:o(e),paths:n.map((e=>i(e)||t)),lang:h,signal:p.signal}).then((e=>s(e,n))).catch(l);if(c){const n=g.filter((e=>!y(e))),r=g.filter(y);a({serverURL:o(e),path:t,lang:h}).then((e=>s(new Array(n.length).fill(e),n))),r.length&&u(r)}else u(g);return p.abort.bind(p)};export{c as pageviewCount,e as version};
1
+ const e="2.14.1",t={"Content-Type":"application/json"},n=({serverURL:e,lang:t,paths:n,signal:r})=>(({serverURL:e,lang:t,paths:n,type:r,signal:a})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(r.join(","))}&lang=${t}`,{signal:a}).then((e=>e.json())))({serverURL:e,lang:t,paths:n,type:["time"],signal:r}).then((e=>Array.isArray(e)?e:[e])),r=e=>(({serverURL:e,lang:n,path:r,type:a,action:o})=>fetch(`${e}/article?lang=${n}`,{method:"POST",headers:t,body:JSON.stringify({path:r,type:a,action:o})}).then((e=>e.json())))({...e,type:"time",action:"inc"}),a=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},o=e=>{"AbortError"!==e.name&&console.error(e.message)},l=e=>e.dataset.path||e.getAttribute("id"),s=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))},i=({serverURL:e,path:t=window.location.pathname,selector:i=".waline-pageview-count",update:p=!0,lang:h="zh-CN"})=>{const c=new AbortController,g=Array.from(document.querySelectorAll(i)),y=e=>{const n=l(e);return null!==n&&t!==n},d=r=>n({serverURL:a(e),paths:r.map((e=>l(e)||t)),lang:h,signal:c.signal}).then((e=>s(e,r))).catch(o);if(p){const n=g.filter((e=>!y(e))),o=g.filter(y);r({serverURL:a(e),path:t,lang:h}).then((e=>s(new Array(n.length).fill(e),n))),o.length&&d(o)}else d(g);return c.abort.bind(c)};export{i as pageviewCount,e as version};
2
2
  //# sourceMappingURL=pageview.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.mjs","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface APIErrorResponse {\n errno: number;\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T = unknown>(\n data: T | APIErrorResponse,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as APIErrorResponse).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as APIErrorResponse).errno}: ${\n (data as APIErrorResponse).errmsg\n }`\n );\n\n return data as T;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then((resp) => <Promise<GetArticleCounterResponse>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n })\n .then((resp) => <Promise<number>>resp.json())\n .then((data) => errorCheck(data, 'article count'));\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search || getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageviews, updatePageviews } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageviews({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageviews({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["version","JSON_HEADERS","errorCheck","data","name","errno","TypeError","errmsg","getPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","getArticleCounter","counts","Array","isArray","updatePageviews","options","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","pageviewCount","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"AAEO,MAAMA,EAAU,SCmBVC,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAA0BE,MACzD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAA0BE,UACrDF,EAA0BI,UAIjC,OAAOJ,CAAS,EClBLK,EAAe,EAC1BC,YACAC,OACAC,QACAC,YCS+B,GAC/BH,YACAC,OACAC,QACAE,OACAD,YAEAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MAAMC,GAA6CA,EAAKC,SACxDF,MAAMd,GAASD,EAAWC,EAAM,mBDrBnCiB,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GC0BkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAA0BA,EAAKC,SACrCF,MAAMd,GAASD,EAAWC,EAAM,mBDrCnC8B,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QEpBCO,EAAgBzB,IAC3B,MAAM0B,ECvB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDsBTC,CAAmB7B,GAElC,MCrBA,kBAAkB8B,KDqBAJ,GAAUA,EAAS,WAAWA,GAAQ,EEnC7CK,EAAgBC,IACV,eAAbA,EAAIrC,MAAuBsC,QAAQC,MAAMF,EAAIG,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQrB,MAAQoB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB5B,EACA6B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYhC,EAAO+B,GAAOE,UAAU,GAC5C,EAGSC,EAAgB,EAC3B9C,YACAiB,OAAO8B,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTlD,OAAO,YAEP,MAAMmD,EAAa,IAAIC,gBAEjBC,EAAWzC,MAAM0C,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB1C,IAAS0C,CAAK,EAGnCtD,EAASiD,GACbvD,EAAa,CACXC,UAAWyB,EAAazB,GACxBE,MAAOoD,EAASM,KAAKvB,GAAYD,EAASC,IAAYpB,IACtDhB,OACAE,OAAQiD,EAAWjD,SAElBK,MAAMI,GAAW4B,EAAmB5B,EAAQ0C,KAC5CO,MAAM9B,GAGX,GAAIoB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C3C,EAAgB,CACnBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAMwD,GACPxB,EACE,IAAI3B,MAAciD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5D,EAAM0D,EAEd,MAGM1D,EAAMiD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW"}
1
+ {"version":3,"file":"pageview.mjs","sources":["../src/version.ts","../src/api/utils.ts","../src/api/pageview.ts","../src/api/articleCounter.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export interface BaseAPIOptions {\n /**\n * Waline 服务端地址\n *\n * Waline serverURL\n */\n serverURL: string;\n\n /**\n * 错误信息所使用的语言\n *\n * Language used in error text\n */\n lang: string;\n}\n\nexport interface ErrorStatusResponse {\n /**\n * 错误代码\n *\n * Error number\n */\n errno: number;\n\n /**\n * 错误消息\n *\n * Error msg\n */\n errmsg: string;\n}\n\nexport const JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nexport const errorCheck = <T extends ErrorStatusResponse>(\n data: T,\n name = ''\n): T => {\n if (typeof data === 'object' && data.errno)\n throw new TypeError(`${name} failed with ${data.errno}: ${data.errmsg}`);\n\n return data;\n};\n","import { getArticleCounter, updateArticleCounter } from './articleCounter';\nimport type { BaseAPIOptions } from './utils';\n\ninterface GetPageviewOptions extends BaseAPIOptions {\n /**\n * 待获取页面的 path\n *\n * Path of pages\n */\n paths: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport const getPageview = ({\n serverURL,\n lang,\n paths,\n signal,\n}: GetPageviewOptions): Promise<number[]> =>\n getArticleCounter({\n serverURL,\n lang,\n paths,\n type: ['time'],\n signal,\n })\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts])) as Promise<\n number[]\n >;\n\nexport interface UpdatePageviewOptions extends BaseAPIOptions {\n /**\n * 待更新页面的 path\n *\n * Path of pages\n */\n path: string;\n}\n\nexport const updatePageview = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n action: 'inc',\n });\n","import { JSON_HEADERS } from './utils';\nimport type { BaseAPIOptions } from './utils';\n\nexport interface GetArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待获取计数器的 path\n *\n * Path of counters\n */\n paths: string[];\n\n /**\n * 待获取计数器的类型\n *\n * Counter type to be fetched\n */\n type: string[];\n\n /**\n * 取消请求的信号\n *\n * AbortSignal to cancel request\n */\n signal?: AbortSignal;\n}\n\nexport type GetArticleCounterResponse =\n | Record<string, number>[]\n | Record<string, number>\n | number[]\n | number;\n\nexport const getArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: GetArticleCounterOptions): Promise<GetArticleCounterResponse> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n ).then((resp) => <Promise<GetArticleCounterResponse>>resp.json());\n\nexport interface UpdateArticleCounterOptions extends BaseAPIOptions {\n /**\n * 待更新计数器的 path\n *\n * Path of counter to be updated\n */\n path: string;\n\n /**\n * 待更新计数器的类型\n *\n * Counter type to be updated\n */\n type: string;\n\n /**\n * 更新操作\n *\n * Update operation\n *\n * @default 'inc'\n */\n action?: 'inc' | 'desc';\n}\n\nexport const updateArticleCounter = ({\n serverURL,\n lang,\n path,\n type,\n action,\n}: UpdateArticleCounterOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path, type, action }),\n }).then((resp) => <Promise<number>>resp.json());\n","import {\n defaultLang,\n defaultLocales,\n defaultReaction,\n defaultUploadImage,\n defaultHighlighter,\n defaultTexRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config';\n\nimport { decodePath, isLinkHttp, removeEndingSplash } from './path';\n\nimport type {\n WalineEmojiInfo,\n WalineEmojiMaps,\n WalineLocale,\n WalineProps,\n} from '../typings';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<Omit<WalineProps, 'wordLimit' | 'reaction'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n}\n\nexport const getServerURL = (serverURL: string): string => {\n const result = removeEndingSplash(serverURL);\n\n return isLinkHttp(result) ? result : `https://${result}`;\n};\n\nconst getWordLimit = (\n wordLimit: WalineProps['wordLimit']\n): [number, number] | false =>\n Array.isArray(wordLimit) ? wordLimit : wordLimit ? [0, wordLimit] : false;\n\nconst fallback = <T = unknown>(\n value: T | false | undefined,\n fallback: T\n): T | false =>\n typeof value === 'function' ? value : value === false ? false : fallback;\n\nexport const getConfig = ({\n serverURL,\n\n path = location.pathname,\n lang = defaultLang,\n locale,\n emoji = ['//unpkg.com/@waline/emojis@1.1.0/weibo'],\n meta = ['nick', 'mail', 'link'],\n requiredMeta = [],\n dark = false,\n pageSize = 10,\n wordLimit,\n imageUploader,\n highlighter,\n texRenderer,\n copyright = true,\n login = 'enable',\n search,\n reaction,\n recaptchaV3Key = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(defaultLocales[lang] || defaultLocales[defaultLang]),\n ...(typeof locale === 'object' ? locale : {}),\n } as WalineLocale,\n wordLimit: getWordLimit(wordLimit),\n meta: getMeta(meta),\n requiredMeta: getMeta(requiredMeta),\n imageUploader: fallback(imageUploader, defaultUploadImage),\n highlighter: fallback(highlighter, defaultHighlighter),\n texRenderer: fallback(texRenderer, defaultTexRenderer),\n lang,\n dark,\n emoji,\n pageSize,\n login,\n copyright,\n search: search ?? getDefaultSearchOptions(lang),\n recaptchaV3Key,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? defaultReaction\n : [],\n commentSorting,\n ...more,\n});\n","export const decodePath = (path: string): string => {\n try {\n path = decodeURI(path);\n } catch (err) {\n // ignore error\n }\n\n return path;\n};\n\nexport const removeEndingSplash = (content = ''): string =>\n content.replace(/\\/$/u, '');\n\nexport const isLinkHttp = (link: string): boolean =>\n /^(https?:)?\\/\\//.test(link);\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import { getPageview, updatePageview } from './api';\nimport { errorHandler, getQuery, getServerURL } from './utils';\n\nimport type { WalineAbort } from './typings';\n\nexport interface WalinePageviewCountOptions {\n /**\n * Waline 服务端地址\n *\n * Waline server url\n */\n serverURL: string;\n\n /**\n * 浏览量 CSS 选择器\n *\n * Pageview CSS selector\n *\n * @default '.waline-pageview-count'\n */\n selector?: string;\n\n /**\n * 需要更新和获取的路径\n *\n * Path to be fetched and updated\n *\n * @default window.location.pathname\n */\n path?: string;\n\n /**\n * 是否在查询时更新 path 的浏览量\n *\n * Whether update pageviews when fetching path result\n *\n * @default true\n */\n update?: boolean;\n\n /**\n * 错误提示消息所使用的语言\n *\n * Language of error message\n *\n * @default 'zh-CN'\n */\n lang?: string;\n}\n\nconst renderVisitorCount = (\n counts: number[],\n countElements: HTMLElement[]\n): void => {\n countElements.forEach((element, index) => {\n element.innerText = counts[index].toString();\n });\n};\n\nexport const pageviewCount = ({\n serverURL,\n path = window.location.pathname,\n selector = '.waline-pageview-count',\n update = true,\n lang = 'zh-CN',\n}: WalinePageviewCountOptions): WalineAbort => {\n const controller = new AbortController();\n\n const elements = Array.from(\n // pageview selectors\n document.querySelectorAll<HTMLElement>(selector)\n );\n\n const filter = (element: HTMLElement): boolean => {\n const query = getQuery(element);\n\n return query !== null && path !== query;\n };\n\n const fetch = (elements: HTMLElement[]): Promise<void> =>\n getPageview({\n serverURL: getServerURL(serverURL),\n paths: elements.map((element) => getQuery(element) || path),\n lang,\n signal: controller.signal,\n })\n .then((counts) => renderVisitorCount(counts, elements))\n .catch(errorHandler);\n\n // we should update pageviews\n if (update) {\n const normalElements = elements.filter((element) => !filter(element));\n const elementsNeedstoBeFetched = elements.filter(filter);\n\n void updatePageview({\n serverURL: getServerURL(serverURL),\n path,\n lang,\n }).then((count) =>\n renderVisitorCount(\n new Array<number>(normalElements.length).fill(count),\n normalElements\n )\n );\n\n // if we should fetch count of other pages\n if (elementsNeedstoBeFetched.length) {\n void fetch(elementsNeedstoBeFetched);\n }\n }\n // we should not update pageviews\n else {\n void fetch(elements);\n }\n\n return controller.abort.bind(controller);\n};\n"],"names":["version","JSON_HEADERS","getPageview","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","getArticleCounter","counts","Array","isArray","updatePageview","options","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","name","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","pageviewCount","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"AAEO,MAAMA,EAAU,SC8BVC,EAAuC,CAElD,eAAgB,oBCfLC,EAAc,EACzBC,YACAC,OACAC,QACAC,YCS+B,GAC/BH,YACAC,OACAC,QACAE,OACAD,YAEAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WACFK,MAAMC,GAA6CA,EAAKC,SDnB1DC,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GCwBkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAStB,EACTuB,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAClCV,MAAMC,GAA0BA,EAAKC,SDjCxCc,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QEpBCO,EAAgBzB,IAC3B,MAAM0B,ECvB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDsBTC,CAAmB7B,GAElC,MCrBA,kBAAkB8B,KDqBAJ,GAAUA,EAAS,WAAWA,GAAQ,EEnC7CK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQtB,MAAQqB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB7B,EACA8B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYjC,EAAOgC,GAAOE,UAAU,GAC5C,EAGSC,EAAgB,EAC3B/C,YACAiB,OAAO+B,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTnD,OAAO,YAEP,MAAMoD,EAAa,IAAIC,gBAEjBC,EAAW1C,MAAM2C,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB3C,IAAS2C,CAAK,EAGnCvD,EAASkD,GACbxD,EAAY,CACVC,UAAWyB,EAAazB,GACxBE,MAAOqD,EAASM,KAAKvB,GAAYD,EAASC,IAAYrB,IACtDhB,OACAE,OAAQkD,EAAWlD,SAElBK,MAAMI,GAAW6B,EAAmB7B,EAAQ2C,KAC5CO,MAAM/B,GAGX,GAAIqB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C5C,EAAe,CAClBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAMyD,GACPxB,EACE,IAAI5B,MAAckD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB7D,EAAM2D,EAEd,MAGM3D,EAAMkD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW"}