@waline/client 2.11.3 → 2.12.0

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.
package/dist/pageview.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});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},r=({serverURL:e,lang:r,paths:n,signal:a})=>(({serverURL:e,lang:r,paths:n,type:a,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${r}`,{signal:o}).then((e=>e.json())).then((e=>t(e,"article count"))))({serverURL:e,lang:r,paths:n,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e])),n=r=>(({serverURL:r,lang:n,path:a,type:o,action:s})=>fetch(`${r}/article?lang=${n}`,{method:"POST",headers:e,body:JSON.stringify({path:a,type:o,action:s})}).then((e=>e.json())).then((e=>t(e,"article count"))))({...r,type:"time"}),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"),l=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))};exports.pageviewCount=({serverURL:e,path:t=window.location.pathname,selector:i=".waline-pageview-count",update:p=!0,lang:c="zh-CN"})=>{const h=new AbortController,g=Array.from(document.querySelectorAll(i)),u=e=>{const r=s(e);return null!==r&&t!==r},y=n=>r({serverURL:a(e),paths:n.map((e=>s(e)||t)),lang:c,signal:h.signal}).then((e=>l(e,n))).catch(o);if(p){const r=g.filter((e=>!u(e))),o=g.filter(u);n({serverURL:a(e),path:t,lang:c}).then((e=>l(new Array(r.length).fill(e),r))),o.length&&y(o)}else y(g);return h.abort.bind(h)},exports.version="2.11.3";
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});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},r=({serverURL:e,lang:r,paths:n,signal:a})=>(({serverURL:e,lang:r,paths:n,type:a,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${r}`,{signal:o}).then((e=>e.json())).then((e=>t(e,"article count"))))({serverURL:e,lang:r,paths:n,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e])),n=r=>(({serverURL:r,lang:n,path:a,type:o,action:s})=>fetch(`${r}/article?lang=${n}`,{method:"POST",headers:e,body:JSON.stringify({path:a,type:o,action:s})}).then((e=>e.json())).then((e=>t(e,"article count"))))({...r,type:"time"}),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"),l=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))};exports.pageviewCount=({serverURL:e,path:t=window.location.pathname,selector:i=".waline-pageview-count",update:p=!0,lang:c="zh-CN"})=>{const h=new AbortController,g=Array.from(document.querySelectorAll(i)),u=e=>{const r=s(e);return null!==r&&t!==r},y=n=>r({serverURL:a(e),paths:n.map((e=>s(e)||t)),lang:c,signal:h.signal}).then((e=>l(e,n))).catch(o);if(p){const r=g.filter((e=>!u(e))),o=g.filter(u);n({serverURL:a(e),path:t,lang:c}).then((e=>l(new Array(r.length).fill(e),r))),o.length&&y(o)}else y(g);return h.abort.bind(h)},exports.version="2.12.0";
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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 = getDefaultSearchOptions(),\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,\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","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":"oEAEO,MCGMA,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CAAS,ECZLK,EAAiB,EAC5BC,YACAC,OACAC,QACAC,YCHiC,GACjCH,YACAC,OACAC,QACAE,OACAD,YAIAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MACEC,GACCA,EAAKC,SAERF,MAAMd,GAASD,EAAWC,EAAM,mBDdnCiB,CAAoB,CAClBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAW5CG,EACXC,GCIkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,mBDfnC8B,CAAqB,IAChBR,EACHZ,KAAM,SEPGqB,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,EAAe,CACbC,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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","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":"oEAEO,MCGMA,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CAAS,ECZLK,EAAiB,EAC5BC,YACAC,OACAC,QACAC,YCHiC,GACjCH,YACAC,OACAC,QACAE,OACAD,YAIAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MACEC,GACCA,EAAKC,SAERF,MAAMd,GAASD,EAAWC,EAAM,mBDdnCiB,CAAoB,CAClBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAW5CG,EACXC,GCIkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,mBDfnC8B,CAAqB,IAChBR,EACHZ,KAAM,SEPGqB,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,EAAe,CACbC,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"}
package/dist/pageview.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){if("function"==typeof define&&define.amd)define("Waline",["exports"],t);else if("undefined"!=typeof exports)t(exports);else{var n={exports:{}};t(n.exports),e.Waline=n.exports}}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:this,(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.pageviewCount=void 0;e.version="2.11.3";const t={"Content-Type":"application/json"},n=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},r=e=>{let{serverURL:t,lang:r,paths:o,signal:a}=e;return(e=>{let{serverURL:t,lang:r,paths:o,type:a,signal:i}=e;return fetch(`${t}/article?path=${encodeURIComponent(o.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${r}`,{signal:i}).then((e=>e.json())).then((e=>n(e,"article count")))})({serverURL:t,lang:r,paths:o,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e]))},o=e=>(e=>{let{serverURL:r,lang:o,path:a,type:i,action:l}=e;return fetch(`${r}/article?lang=${o}`,{method:"POST",headers:t,body:JSON.stringify({path:a,type:i,action:l})}).then((e=>e.json())).then((e=>n(e,"article count")))})({...e,type:"time"}),a=e=>{const t=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").replace(/\/$/u,"")}(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},i=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()}))};e.pageviewCount=e=>{let{serverURL:t,path:n=window.location.pathname,selector:p=".waline-pageview-count",update:h=!0,lang:c="zh-CN"}=e;const f=new AbortController,g=Array.from(document.querySelectorAll(p)),u=e=>{const t=l(e);return null!==t&&n!==t},d=e=>r({serverURL:a(t),paths:e.map((e=>l(e)||n)),lang:c,signal:f.signal}).then((t=>s(t,e))).catch(i);if(h){const e=g.filter((e=>!u(e))),r=g.filter(u);o({serverURL:a(t),path:n,lang:c}).then((t=>s(new Array(e.length).fill(t),e))),r.length&&d(r)}else d(g);return f.abort.bind(f)}}));
1
+ !function(e,t){if("function"==typeof define&&define.amd)define("Waline",["exports"],t);else if("undefined"!=typeof exports)t(exports);else{var n={exports:{}};t(n.exports),e.Waline=n.exports}}("undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:this,(function(e){Object.defineProperty(e,"__esModule",{value:!0}),e.version=e.pageviewCount=void 0;e.version="2.12.0";const t={"Content-Type":"application/json"},n=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},r=e=>{let{serverURL:t,lang:r,paths:o,signal:a}=e;return(e=>{let{serverURL:t,lang:r,paths:o,type:a,signal:i}=e;return fetch(`${t}/article?path=${encodeURIComponent(o.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${r}`,{signal:i}).then((e=>e.json())).then((e=>n(e,"article count")))})({serverURL:t,lang:r,paths:o,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e]))},o=e=>(e=>{let{serverURL:r,lang:o,path:a,type:i,action:l}=e;return fetch(`${r}/article?lang=${o}`,{method:"POST",headers:t,body:JSON.stringify({path:a,type:i,action:l})}).then((e=>e.json())).then((e=>n(e,"article count")))})({...e,type:"time"}),a=e=>{const t=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").replace(/\/$/u,"")}(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},i=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()}))};e.pageviewCount=e=>{let{serverURL:t,path:n=window.location.pathname,selector:p=".waline-pageview-count",update:h=!0,lang:c="zh-CN"}=e;const f=new AbortController,g=Array.from(document.querySelectorAll(p)),u=e=>{const t=l(e);return null!==t&&n!==t},d=e=>r({serverURL:a(t),paths:e.map((e=>l(e)||n)),lang:c,signal:f.signal}).then((t=>s(t,e))).catch(i);if(h){const e=g.filter((e=>!u(e))),r=g.filter(u);o({serverURL:a(t),path:n,lang:c}).then((t=>s(new Array(e.length).fill(t),e))),r.length&&d(r)}else d(g);return f.abort.bind(f)}}));
2
2
  //# 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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 = getDefaultSearchOptions(),\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,\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","_ref3","serverURL","lang","paths","signal","_ref","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","counts","Array","isArray","updatePageviews","options","_ref2","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","replace","removeEndingSplash","test","errorHandler","err","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","_ref4","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"sXAEuB,SCGhB,MAAMA,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,SACxBC,GAEK,IADLC,yDAAO,GAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CACR,ECbYK,EAAiBC,IAAA,IAACC,UAC7BA,EAD6BC,KAE7BA,EAF6BC,MAG7BA,EAH6BC,OAI7BA,GAJ4BJ,EAAA,MCCKK,KAAA,IAACJ,UAClCA,EADkCC,KAElCA,EAFkCC,MAGlCA,EAHkCG,KAIlCA,EAJkCF,OAKlCA,GALiCC,EAAA,OASjCE,MACK,GAAAN,kBAA0BO,mBAC3BL,EAAMM,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcP,IACrD,CAAEE,WAEDM,MACEC,GACCA,EAAKC,SAERF,MAAMhB,GAASD,EAAWC,EAAM,kBAnBF,EDKjCmB,CAAoB,CAClBZ,YACAC,OACAC,QACAG,KAAM,CAAC,QACPF,WAGCM,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,IAd3B,EAyBjBG,EACXC,GCIkCC,KAAA,IAAClB,UACnCA,EADmCC,KAEnCA,EAFmCkB,KAGnCA,EAHmCd,KAInCA,EAJmCe,OAKnCA,GALkCF,EAAA,OAOlCZ,MAAM,GAAGN,kBAA0BC,IAAQ,CACzCoB,OAAQ,OACRC,QAAS/B,EACTgC,KAAMC,KAAKC,UAAU,CAAEN,OAAMd,OAAMe,aAElCX,MAAMC,GAASA,EAAKC,SACpBF,MAAMhB,GAASD,EAAWC,EAAM,kBAbD,EDFlCiC,CAAqB,IAChBT,EACHZ,KAAM,SEPGsB,EAAgB3B,IAC3B,MAAM4B,ECvB0B,WAAA,8DAAW,IACnCC,QAAQ,OAAQ,GADQ,CDuBjBC,CAAmB9B,GAElC,MCrBA,kBAAkB+B,KDqBAH,GAAUA,EAAoB,WAAAA,GAAhD,EEnCWI,EAAgBC,IACV,eAAbA,EAAIvC,MAAuBwC,QAAQC,MAAMF,EAAIG,QAAlB,ECDpBC,EAAYC,GACvBA,EAAQC,QAAQpB,MAAQmB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB5B,EACA6B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYhC,EAAO+B,GAAOE,UAAlC,GADF,kBAK2BC,IAMiB,IANhB/C,UAC5BA,EAD4BmB,KAE5BA,EAAO6B,OAAOC,SAASC,SAFKC,SAG5BA,EAAW,yBAHiBC,OAI5BA,GAAS,EAJmBnD,KAK5BA,EAAO,SACqC8C,EAC5C,MAAMM,EAAa,IAAIC,gBAEjBC,EAAWzC,MAAM0C,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkBzC,IAASyC,CAAlC,EAGItD,EAASiD,GACbzD,EAAe,CACbE,UAAW2B,EAAa3B,GACxBE,MAAOqD,EAASM,KAAKvB,GAAYD,EAASC,IAAYnB,IACtDlB,OACAE,OAAQkD,EAAWlD,SAElBM,MAAMI,GAAW4B,EAAmB5B,EAAQ0C,KAC5CO,MAAM9B,GAGX,GAAIoB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C3C,EAAgB,CACnBhB,UAAW2B,EAAa3B,GACxBmB,OACAlB,SACCQ,MAAMwD,GACPxB,EACE,IAAI3B,MAAciD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5D,EAAM0D,EAEd,MAGM1D,EAAMiD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAA7B"}
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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","_ref3","serverURL","lang","paths","signal","_ref","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","counts","Array","isArray","updatePageviews","options","_ref2","path","action","method","headers","body","JSON","stringify","updateArticleCounter","getServerURL","result","replace","removeEndingSplash","test","errorHandler","err","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","_ref4","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"sXAEuB,SCGhB,MAAMA,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,SACxBC,GAEK,IADLC,yDAAO,GAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CACR,ECbYK,EAAiBC,IAAA,IAACC,UAC7BA,EAD6BC,KAE7BA,EAF6BC,MAG7BA,EAH6BC,OAI7BA,GAJ4BJ,EAAA,MCCKK,KAAA,IAACJ,UAClCA,EADkCC,KAElCA,EAFkCC,MAGlCA,EAHkCG,KAIlCA,EAJkCF,OAKlCA,GALiCC,EAAA,OASjCE,MACK,GAAAN,kBAA0BO,mBAC3BL,EAAMM,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcP,IACrD,CAAEE,WAEDM,MACEC,GACCA,EAAKC,SAERF,MAAMhB,GAASD,EAAWC,EAAM,kBAnBF,EDKjCmB,CAAoB,CAClBZ,YACAC,OACAC,QACAG,KAAM,CAAC,QACPF,WAGCM,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,IAd3B,EAyBjBG,EACXC,GCIkCC,KAAA,IAAClB,UACnCA,EADmCC,KAEnCA,EAFmCkB,KAGnCA,EAHmCd,KAInCA,EAJmCe,OAKnCA,GALkCF,EAAA,OAOlCZ,MAAM,GAAGN,kBAA0BC,IAAQ,CACzCoB,OAAQ,OACRC,QAAS/B,EACTgC,KAAMC,KAAKC,UAAU,CAAEN,OAAMd,OAAMe,aAElCX,MAAMC,GAASA,EAAKC,SACpBF,MAAMhB,GAASD,EAAWC,EAAM,kBAbD,EDFlCiC,CAAqB,IAChBT,EACHZ,KAAM,SEPGsB,EAAgB3B,IAC3B,MAAM4B,ECvB0B,WAAA,8DAAW,IACnCC,QAAQ,OAAQ,GADQ,CDuBjBC,CAAmB9B,GAElC,MCrBA,kBAAkB+B,KDqBAH,GAAUA,EAAoB,WAAAA,GAAhD,EEnCWI,EAAgBC,IACV,eAAbA,EAAIvC,MAAuBwC,QAAQC,MAAMF,EAAIG,QAAlB,ECDpBC,EAAYC,GACvBA,EAAQC,QAAQpB,MAAQmB,EAAQE,aAAa,MCiDzCC,EAAqB,CACzB5B,EACA6B,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYhC,EAAO+B,GAAOE,UAAlC,GADF,kBAK2BC,IAMiB,IANhB/C,UAC5BA,EAD4BmB,KAE5BA,EAAO6B,OAAOC,SAASC,SAFKC,SAG5BA,EAAW,yBAHiBC,OAI5BA,GAAS,EAJmBnD,KAK5BA,EAAO,SACqC8C,EAC5C,MAAMM,EAAa,IAAIC,gBAEjBC,EAAWzC,MAAM0C,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkBzC,IAASyC,CAAlC,EAGItD,EAASiD,GACbzD,EAAe,CACbE,UAAW2B,EAAa3B,GACxBE,MAAOqD,EAASM,KAAKvB,GAAYD,EAASC,IAAYnB,IACtDlB,OACAE,OAAQkD,EAAWlD,SAElBM,MAAMI,GAAW4B,EAAmB5B,EAAQ0C,KAC5CO,MAAM9B,GAGX,GAAIoB,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C3C,EAAgB,CACnBhB,UAAW2B,EAAa3B,GACxBmB,OACAlB,SACCQ,MAAMwD,GACPxB,EACE,IAAI3B,MAAciD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5D,EAAM0D,EAEd,MAGM1D,EAAMiD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAA7B"}
package/dist/pageview.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const e="2.11.3",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"}),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)},s=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))},h=({serverURL:e,path:t=window.location.pathname,selector:n=".waline-pageview-count",update:h=!0,lang:p="zh-CN"})=>{const c=new AbortController,g=Array.from(document.querySelectorAll(n)),y=e=>{const n=s(e);return null!==n&&t!==n},u=n=>r({serverURL:o(e),paths:n.map((e=>s(e)||t)),lang:p,signal:c.signal}).then((e=>i(e,n))).catch(l);if(h){const n=g.filter((e=>!y(e))),r=g.filter(y);a({serverURL:o(e),path:t,lang:p}).then((e=>i(new Array(n.length).fill(e),n))),r.length&&u(r)}else u(g);return c.abort.bind(c)};export{h as pageviewCount,e as version};
1
+ const e="2.12.0",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"}),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)},s=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))},h=({serverURL:e,path:t=window.location.pathname,selector:n=".waline-pageview-count",update:h=!0,lang:p="zh-CN"})=>{const c=new AbortController,g=Array.from(document.querySelectorAll(n)),y=e=>{const n=s(e);return null!==n&&t!==n},u=n=>r({serverURL:o(e),paths:n.map((e=>s(e)||t)),lang:p,signal:c.signal}).then((e=>i(e,n))).catch(l);if(h){const n=g.filter((e=>!y(e))),r=g.filter(y);a({serverURL:o(e),path:t,lang:p}).then((e=>i(new Array(n.length).fill(e),n))),r.length&&u(r)}else u(g);return c.abort.bind(c)};export{h 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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 = getDefaultSearchOptions(),\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,\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","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,SCGVC,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CAAS,ECZLK,EAAiB,EAC5BC,YACAC,OACAC,QACAC,YCHiC,GACjCH,YACAC,OACAC,QACAE,OACAD,YAIAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MACEC,GACCA,EAAKC,SAERF,MAAMd,GAASD,EAAWC,EAAM,mBDdnCiB,CAAoB,CAClBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAW5CG,EACXC,GCIkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,mBDfnC8B,CAAqB,IAChBR,EACHZ,KAAM,SEPGqB,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,EAAe,CACbC,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 FetchErrorData {\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 | FetchErrorData,\n name = ''\n): T => {\n if (typeof data === 'object' && (data as FetchErrorData).errno)\n throw new TypeError(\n `Fetch ${name} failed with ${(data as FetchErrorData).errno}: ${\n (data as FetchErrorData).errmsg\n }`\n );\n\n return data as T;\n};\n","import { fetchArticleCounter, updateArticleCounter } from './articleCounter';\n\ninterface FetchPageviewOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n}\n\nexport const fetchPageviews = ({\n serverURL,\n lang,\n paths,\n signal,\n}: FetchPageviewOptions): Promise<number[]> =>\n fetchArticleCounter({\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 {\n serverURL: string;\n lang: string;\n path: string;\n action?: 'inc' | 'desc';\n}\n\nexport const updatePageviews = (\n options: UpdatePageviewOptions\n): Promise<number> =>\n updateArticleCounter({\n ...options,\n type: 'time',\n });\n","import { JSON_HEADERS, errorCheck } from './utils';\n\nexport interface FetchArticleCounterOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n type: string[];\n}\n\nexport const fetchArticleCounter = ({\n serverURL,\n lang,\n paths,\n type,\n signal,\n}: FetchArticleCounterOptions): Promise<\n Record<string, number>[] | Record<string, number> | number[] | number\n> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&type=${encodeURIComponent(type.join(','))}&lang=${lang}`,\n { signal }\n )\n .then(\n (resp) =>\n resp.json() as Promise<Record<string, number>[] | number[] | number>\n )\n .then((data) => errorCheck(data, 'article count'));\n\nexport interface UpdateArticleCounterOptions {\n serverURL: string;\n lang: string;\n path: string;\n type: string;\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) => resp.json() as Promise<number>)\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 { fetchPageviews, 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 fetchPageviews({\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","fetchPageviews","serverURL","lang","paths","signal","type","fetch","encodeURIComponent","join","then","resp","json","fetchArticleCounter","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,SCGVC,EAAuC,CAElD,eAAgB,oBAGLC,EAAa,CACxBC,EACAC,EAAO,MAEP,GAAoB,iBAATD,GAAsBA,EAAwBE,MACvD,MAAM,IAAIC,UACR,SAASF,iBAAqBD,EAAwBE,UACnDF,EAAwBI,UAI/B,OAAOJ,CAAS,ECZLK,EAAiB,EAC5BC,YACAC,OACAC,QACAC,YCHiC,GACjCH,YACAC,OACAC,QACAE,OACAD,YAIAE,MACE,GAAGL,kBAA0BM,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmBF,EAAKG,KAAK,cAAcN,IACrD,CAAEE,WAEDK,MACEC,GACCA,EAAKC,SAERF,MAAMd,GAASD,EAAWC,EAAM,mBDdnCiB,CAAoB,CAClBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAW5CG,EACXC,GCIkC,GAClChB,YACAC,OACAgB,OACAb,OACAc,YAEAb,MAAM,GAAGL,kBAA0BC,IAAQ,CACzCkB,OAAQ,OACRC,QAAS5B,EACT6B,KAAMC,KAAKC,UAAU,CAAEN,OAAMb,OAAMc,aAElCV,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,mBDfnC8B,CAAqB,IAChBR,EACHZ,KAAM,SEPGqB,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,EAAe,CACbC,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"}