@waline/client 2.15.8 → 3.0.0-alpha.2

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 (139) hide show
  1. package/dist/comment.cjs +1 -1
  2. package/dist/comment.cjs.map +1 -1
  3. package/dist/comment.d.cts +2 -1
  4. package/dist/comment.d.mts +2 -1
  5. package/dist/comment.d.ts +2 -1
  6. package/dist/comment.js +1 -1
  7. package/dist/comment.js.map +1 -1
  8. package/dist/comment.mjs +1 -1
  9. package/dist/comment.mjs.map +1 -1
  10. package/dist/component.mjs +8 -1
  11. package/dist/component.mjs.map +1 -1
  12. package/dist/pageview.cjs +1 -1
  13. package/dist/pageview.cjs.map +1 -1
  14. package/dist/pageview.d.cts +2 -1
  15. package/dist/pageview.d.mts +2 -1
  16. package/dist/pageview.d.ts +2 -1
  17. package/dist/pageview.js +1 -1
  18. package/dist/pageview.js.map +1 -1
  19. package/dist/pageview.mjs +1 -1
  20. package/dist/pageview.mjs.map +1 -1
  21. package/dist/shim.cjs +8 -1
  22. package/dist/shim.cjs.map +1 -1
  23. package/dist/shim.d.cts +7 -82
  24. package/dist/shim.d.mts +7 -82
  25. package/dist/shim.mjs +8 -1
  26. package/dist/shim.mjs.map +1 -1
  27. package/dist/waline.cjs +53 -1
  28. package/dist/waline.cjs.map +1 -1
  29. package/dist/waline.css +1 -1
  30. package/dist/waline.css.map +1 -1
  31. package/dist/waline.d.cts +7 -82
  32. package/dist/waline.d.mts +7 -82
  33. package/dist/waline.d.ts +7 -82
  34. package/dist/waline.js +53 -1
  35. package/dist/waline.js.map +1 -1
  36. package/dist/waline.mjs +53 -1
  37. package/dist/waline.mjs.map +1 -1
  38. package/package.json +20 -53
  39. package/dist/api.cjs +0 -2
  40. package/dist/api.cjs.map +0 -1
  41. package/dist/api.d.cts +0 -445
  42. package/dist/api.d.mts +0 -445
  43. package/dist/api.d.ts +0 -445
  44. package/dist/api.mjs +0 -2
  45. package/dist/api.mjs.map +0 -1
  46. package/dist/legacy.umd.d.ts +0 -628
  47. package/dist/legacy.umd.js +0 -2
  48. package/dist/legacy.umd.js.map +0 -1
  49. package/src/api/articleCounter.ts +0 -82
  50. package/src/api/comment.ts +0 -258
  51. package/src/api/commentCount.ts +0 -33
  52. package/src/api/index.ts +0 -7
  53. package/src/api/login.ts +0 -96
  54. package/src/api/pageview.ts +0 -54
  55. package/src/api/recentComment.ts +0 -42
  56. package/src/api/user.ts +0 -44
  57. package/src/api/utils.ts +0 -46
  58. package/src/comment.ts +0 -69
  59. package/src/compact/convert.ts +0 -80
  60. package/src/compact/dropped.ts +0 -35
  61. package/src/compact/index.ts +0 -4
  62. package/src/compact/logger.ts +0 -5
  63. package/src/compact/v1.ts +0 -103
  64. package/src/compact/valine.ts +0 -132
  65. package/src/components/ArticleReaction.vue +0 -159
  66. package/src/components/CommentBox.vue +0 -807
  67. package/src/components/CommentCard.vue +0 -271
  68. package/src/components/Icons.ts +0 -181
  69. package/src/components/ImageWall.vue +0 -169
  70. package/src/components/WalineComment.vue +0 -353
  71. package/src/composables/index.ts +0 -6
  72. package/src/composables/inputs.ts +0 -17
  73. package/src/composables/like.ts +0 -13
  74. package/src/composables/reaction.ts +0 -15
  75. package/src/composables/recaptchaV3.ts +0 -19
  76. package/src/composables/turnstile.ts +0 -50
  77. package/src/composables/userInfo.ts +0 -16
  78. package/src/config/default.ts +0 -93
  79. package/src/config/highlighter.ts +0 -74
  80. package/src/config/i18n/en.ts +0 -53
  81. package/src/config/i18n/generate.ts +0 -58
  82. package/src/config/i18n/index.ts +0 -30
  83. package/src/config/i18n/jp.ts +0 -53
  84. package/src/config/i18n/pt-BR.ts +0 -53
  85. package/src/config/i18n/ru.ts +0 -53
  86. package/src/config/i18n/vi-VN.ts +0 -53
  87. package/src/config/i18n/zh-CN.ts +0 -53
  88. package/src/config/i18n/zh-TW.ts +0 -53
  89. package/src/config/index.ts +0 -3
  90. package/src/entries/api.ts +0 -1
  91. package/src/entries/comment.ts +0 -2
  92. package/src/entries/components.ts +0 -2
  93. package/src/entries/full.ts +0 -7
  94. package/src/entries/init.ts +0 -4
  95. package/src/entries/legacy.ts +0 -31
  96. package/src/entries/pageview.ts +0 -2
  97. package/src/init.ts +0 -112
  98. package/src/pageview.ts +0 -116
  99. package/src/shims-vue.d.ts +0 -6
  100. package/src/styles/base.scss +0 -67
  101. package/src/styles/card.scss +0 -258
  102. package/src/styles/config.scss +0 -52
  103. package/src/styles/emoji.scss +0 -137
  104. package/src/styles/gif.scss +0 -73
  105. package/src/styles/helpers/_svg.scss +0 -51
  106. package/src/styles/highlight.scss +0 -138
  107. package/src/styles/index.scss +0 -12
  108. package/src/styles/layout.scss +0 -105
  109. package/src/styles/meta.scss +0 -82
  110. package/src/styles/normalize.scss +0 -117
  111. package/src/styles/panel.scss +0 -286
  112. package/src/styles/reaction.scss +0 -103
  113. package/src/styles/recent.scss +0 -3
  114. package/src/styles/user-list.scss +0 -159
  115. package/src/typings/base.ts +0 -123
  116. package/src/typings/comment.ts +0 -94
  117. package/src/typings/index.ts +0 -5
  118. package/src/typings/locale.ts +0 -73
  119. package/src/typings/options.ts +0 -44
  120. package/src/typings/waline.ts +0 -251
  121. package/src/utils/config.ts +0 -122
  122. package/src/utils/darkmode.ts +0 -11
  123. package/src/utils/date.ts +0 -71
  124. package/src/utils/email.ts +0 -8
  125. package/src/utils/emoji.ts +0 -73
  126. package/src/utils/error.ts +0 -3
  127. package/src/utils/getRoot.ts +0 -8
  128. package/src/utils/image.ts +0 -10
  129. package/src/utils/index.ts +0 -13
  130. package/src/utils/markdown.ts +0 -41
  131. package/src/utils/markedMathExtension.ts +0 -54
  132. package/src/utils/path.ts +0 -15
  133. package/src/utils/query.ts +0 -2
  134. package/src/utils/userAgent.ts +0 -31
  135. package/src/utils/wordCount.ts +0 -31
  136. package/src/version.ts +0 -3
  137. package/src/widgets/index.ts +0 -2
  138. package/src/widgets/recentComments.ts +0 -93
  139. package/src/widgets/userList.ts +0 -136
package/dist/pageview.cjs CHANGED
@@ -1,2 +1,2 @@
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=navigator.language})=>{const g=new AbortController,h=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:g.signal}).then((e=>s(e,n))).catch(a);if(p){const t=h.filter((e=>!y(e))),a=h.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(h);return g.abort.bind(g)},exports.version="2.15.8";
1
+ "use strict";const d="3.0.0-alpha.2",m={"Content-Type":"application/json"},g=e=>`${e.replace(/\/?$/,"/")}api/`,$=(e,t="")=>{if(typeof e=="object"&&e.errno)throw new TypeError(`${t} failed with ${e.errno}: ${e.errmsg}`);return e},f=({serverURL:e,lang:t,paths:r,type:a,signal:n})=>fetch(`${g(e)}article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${t}`,{signal:n}).then(o=>o.json()),w=({serverURL:e,lang:t,path:r,type:a,action:n})=>fetch(`${g(e)}article?lang=${t}`,{method:"POST",headers:m,body:JSON.stringify({path:r,type:a,action:n})}).then(o=>o.json()).then(o=>$(o,"Update counter").data),U=({serverURL:e,lang:t,paths:r,signal:a})=>f({serverURL:e,lang:t,paths:r,type:["time"],signal:a}).then(n=>Array.isArray(n)?n:[n]),R=e=>w({...e,type:"time",action:"inc"}),L=(e="")=>e.replace(/\/$/u,""),j=e=>/^(https?:)?\/\//.test(e),u=e=>{const t=L(e);return j(t)?t:`https://${t}`},A=e=>{e.name!=="AbortError"&&console.error(e.message)},v=e=>e.dataset.path||null,y=(e,t)=>{t.forEach((r,a)=>{r.innerText=e[a].toString()})},b=({serverURL:e,path:t=window.location.pathname,selector:r=".waline-pageview-count",update:a=!0,lang:n=navigator.language})=>{const o=new AbortController,l=Array.from(document.querySelectorAll(r)),c=i=>{const s=v(i);return s!==null&&t!==s},h=i=>U({serverURL:u(e),paths:i.map(s=>v(s)||t),lang:n,signal:o.signal}).then(s=>y(s,i)).catch(A);if(a){const i=l.filter(p=>!c(p)),s=l.filter(c);R({serverURL:u(e),path:t,lang:n}).then(([p])=>y(new Array(i.length).fill(p),i)),s.length&&h(s)}else h(l);return o.abort.bind(o)};exports.pageviewCount=b,exports.version=d;
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 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.js';\nimport { type BaseAPIOptions } from './utils.js';\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 { type BaseAPIOptions, JSON_HEADERS } from './utils.js';\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 { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\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/index.js';\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\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 = navigator.language,\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","navigator","language","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,YCQ+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,SDlB1DC,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GCuBkC,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,SDhCxCc,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QELCO,EAAgBzB,IAC3B,MAAM0B,ECtC0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDqCTC,CAAmB7B,GAElC,MCpCA,kBAAkB8B,KDoCAJ,GAAUA,EAAS,WAAWA,GAAQ,EElD7CK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQtB,MAAQqB,EAAQE,aAAa,MCgDzCC,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,OAAOmD,UAAUC,aAEjB,MAAMC,EAAa,IAAIC,gBAEjBC,EAAW3C,MAAM4C,KAErBC,SAASC,iBAA8BT,IAGnCU,EAAUtB,IACd,MAAMuB,EAAQxB,EAASC,GAEvB,OAAiB,OAAVuB,GAAkB5C,IAAS4C,CAAK,EAGnCxD,EAASmD,GACbzD,EAAY,CACVC,UAAWyB,EAAazB,GACxBE,MAAOsD,EAASM,KAAKxB,GAAYD,EAASC,IAAYrB,IACtDhB,OACAE,OAAQmD,EAAWnD,SAElBK,MAAMI,GAAW6B,EAAmB7B,EAAQ4C,KAC5CO,MAAMhC,GAGX,GAAIoB,EAAQ,CACV,MAAMa,EAAiBR,EAASI,QAAQtB,IAAasB,EAAOtB,KACtD2B,EAA2BT,EAASI,OAAOA,GAE5C7C,EAAe,CAClBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAM0D,GACPzB,EACE,IAAI5B,MAAcmD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB9D,EAAM4D,EAEd,MAGM5D,EAAMmD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW,kBRhHnB"}
1
+ {"version":3,"file":"pageview.cjs","sources":["../src/version.ts","../../api/dist/api.mjs","../src/utils/path.ts","../src/utils/config.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","const m={\"Content-Type\":\"application/json\"},s=t=>`${t.replace(/\\/?$/,\"/\")}api/`,h=(t,n=\"\")=>{if(typeof t==\"object\"&&t.errno)throw new TypeError(`${n} failed with ${t.errno}: ${t.errmsg}`);return t},p=({serverURL:t,lang:n,paths:o,type:a,signal:e})=>fetch(`${s(t)}article?path=${encodeURIComponent(o.join(\",\"))}&type=${encodeURIComponent(a.join(\",\"))}&lang=${n}`,{signal:e}).then(r=>r.json()),d=({serverURL:t,lang:n,path:o,type:a,action:e})=>fetch(`${s(t)}article?lang=${n}`,{method:\"POST\",headers:m,body:JSON.stringify({path:o,type:a,action:e})}).then(r=>r.json()).then(r=>h(r,\"Update counter\").data),$=({serverURL:t,lang:n,path:o,page:a,pageSize:e,sortBy:r,signal:c,token:i})=>{const l={};return i&&(l.Authorization=`Bearer ${i}`),fetch(`${s(t)}comment?path=${encodeURIComponent(o)}&pageSize=${e}&page=${a}&lang=${n}&sortBy=${r}`,{signal:c,headers:l}).then(g=>g.json()).then(g=>h(g,\"Get comment data\").data)},u=({serverURL:t,lang:n,token:o,comment:a})=>{const e={\"Content-Type\":\"application/json\"};return o&&(e.Authorization=`Bearer ${o}`),fetch(`${s(t)}comment?lang=${n}`,{method:\"POST\",headers:e,body:JSON.stringify(a)}).then(r=>r.json())},y=({serverURL:t,lang:n,token:o,objectId:a})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"DELETE\",headers:{Authorization:`Bearer ${o}`}}).then(e=>e.json()).then(e=>h(e,\"Delete comment\")),U=({serverURL:t,lang:n,token:o,objectId:a,comment:e})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"PUT\",headers:{...m,Authorization:`Bearer ${o}`},body:JSON.stringify(e)}).then(r=>r.json()).then(r=>h(r,\"Update comment\")),f=({serverURL:t,lang:n,paths:o,signal:a})=>fetch(`${s(t)}comment?type=count&url=${encodeURIComponent(o.join(\",\"))}&lang=${n}`,{signal:a}).then(e=>e.json()).then(e=>h(e,\"Get comment count\").data),R=({lang:t,serverURL:n})=>{const o=(window.innerWidth-450)/2,a=(window.innerHeight-450)/2,e=window.open(`${n.replace(/\\/$/,\"\")}/ui/login?lng=${encodeURIComponent(t)}`,\"_blank\",`width=450,height=450,left=${o},top=${a},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`);return e?.postMessage({type:\"TOKEN\",data:null},\"*\"),new Promise(r=>{const c=({data:i})=>{!i||typeof i!=\"object\"||i.type!==\"userInfo\"||i.data.token&&(e?.close(),window.removeEventListener(\"message\",c),r(i.data))};window.addEventListener(\"message\",c)})},j=({serverURL:t,lang:n,paths:o,signal:a})=>p({serverURL:t,lang:n,paths:o,type:[\"time\"],signal:a}).then(e=>Array.isArray(e)?e:[e]),v=t=>d({...t,type:\"time\",action:\"inc\"}),w=({serverURL:t,lang:n,count:o,signal:a,token:e})=>{const r={};return e&&(r.Authorization=`Bearer ${e}`),fetch(`${s(t)}comment?type=recent&count=${o}&lang=${n}`,{signal:a,headers:r}).then(c=>c.json())},C=({serverURL:t,signal:n,pageSize:o,lang:a})=>fetch(`${s(t)}user?pageSize=${o}&lang=${a}`,{signal:n}).then(e=>e.json()).then(e=>h(e,\"user list\")).then(e=>e.data);export{u as addComment,y as deleteComment,f as fetchCommentCount,p as getArticleCounter,$ as getComment,j as getPageview,w as getRecentComment,C as getUserList,R as login,d as updateArticleCounter,U as updateComment,v as updatePageview};\n//# sourceMappingURL=api.mjs.map\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","import { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\n : [],\n commentSorting,\n ...more,\n});\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 || null;\n","import { getPageview, updatePageview } from '@waline/api';\n\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\n */\n lang?: string;\n}\n\nexport { type WalineAbort } from './typings/index.js';\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 = navigator.language,\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","s","t","h","n","p","o","e","r","d","j","v","removeEndingSplash","content","isLinkHttp","link","getServerURL","serverURL","result","errorHandler","err","getQuery","element","renderVisitorCount","counts","countElements","index","pageviewCount","path","selector","update","lang","controller","elements","filter","query","fetch","getPageview","normalElements","elementsNeedstoBeFetched","updatePageview","count"],"mappings":"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"}
@@ -42,6 +42,7 @@ interface WalinePageviewCountOptions {
42
42
  */
43
43
  lang?: string;
44
44
  }
45
+
45
46
  declare const pageviewCount: ({ serverURL, path, selector, update, lang, }: WalinePageviewCountOptions) => WalineAbort;
46
47
 
47
- export { type WalinePageviewCountOptions, pageviewCount, version };
48
+ export { type WalineAbort, type WalinePageviewCountOptions, pageviewCount, version };
@@ -42,6 +42,7 @@ interface WalinePageviewCountOptions {
42
42
  */
43
43
  lang?: string;
44
44
  }
45
+
45
46
  declare const pageviewCount: ({ serverURL, path, selector, update, lang, }: WalinePageviewCountOptions) => WalineAbort;
46
47
 
47
- export { type WalinePageviewCountOptions, pageviewCount, version };
48
+ export { type WalineAbort, type WalinePageviewCountOptions, pageviewCount, version };
@@ -42,6 +42,7 @@ interface WalinePageviewCountOptions {
42
42
  */
43
43
  lang?: string;
44
44
  }
45
+
45
46
  declare const pageviewCount: ({ serverURL, path, selector, update, lang, }: WalinePageviewCountOptions) => WalineAbort;
46
47
 
47
- export { type WalinePageviewCountOptions, pageviewCount, version };
48
+ export { type WalineAbort, type WalinePageviewCountOptions, pageviewCount, version };
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;const t={"Content-Type":"application/json"},n=e=>(e=>{let{serverURL:n,lang:r,path:o,type:a,action:i}=e;return fetch(`${n}/article?lang=${r}`,{method:"POST",headers:t,body:JSON.stringify({path:o,type:a,action:i})}).then((e=>e.json()))})({...e,type:"time",action:"inc"}),r=e=>{const t=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").replace(/\/$/u,"")}(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},o=e=>{"AbortError"!==e.name&&console.error(e.message)},a=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))};e.pageviewCount=e=>{let{serverURL:t,path:l=window.location.pathname,selector:s=".waline-pageview-count",update:p=!0,lang:c=navigator.language}=e;const g=new AbortController,h=Array.from(document.querySelectorAll(s)),f=e=>{const t=a(e);return null!==t&&l!==t},u=e=>(e=>{let{serverURL:t,lang:n,paths:r,signal:o}=e;return(e=>{let{serverURL:t,lang:n,paths:r,type:o,signal:a}=e;return fetch(`${t}/article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(o.join(","))}&lang=${n}`,{signal:a}).then((e=>e.json()))})({serverURL:t,lang:n,paths:r,type:["time"],signal:o}).then((e=>Array.isArray(e)?e:[e]))})({serverURL:r(t),paths:e.map((e=>a(e)||l)),lang:c,signal:g.signal}).then((t=>i(t,e))).catch(o);if(p){const e=h.filter((e=>!f(e))),o=h.filter(f);n({serverURL:r(t),path:l,lang:c}).then((t=>i(new Array(e.length).fill(t),e))),o.length&&u(o)}else u(h);return g.abort.bind(g)},e.version="2.15.8"}));
1
+ (function(l,p){typeof exports=="object"&&typeof module<"u"?p(exports):typeof define=="function"&&define.amd?define(["exports"],p):(l=typeof globalThis<"u"?globalThis:l||self,p(l.Waline={}))})(this,function(l){"use strict";const p="3.0.0-alpha.2",v={"Content-Type":"application/json"},g=e=>`${e.replace(/\/?$/,"/")}api/`,$=(e,t="")=>{if(typeof e=="object"&&e.errno)throw new TypeError(`${t} failed with ${e.errno}: ${e.errmsg}`);return e},w=({serverURL:e,lang:t,paths:r,type:o,signal:n})=>fetch(`${g(e)}article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(o.join(","))}&lang=${t}`,{signal:n}).then(a=>a.json()),U=({serverURL:e,lang:t,path:r,type:o,action:n})=>fetch(`${g(e)}article?lang=${t}`,{method:"POST",headers:v,body:JSON.stringify({path:r,type:o,action:n})}).then(a=>a.json()).then(a=>$(a,"Update counter").data),R=({serverURL:e,lang:t,paths:r,signal:o})=>w({serverURL:e,lang:t,paths:r,type:["time"],signal:o}).then(n=>Array.isArray(n)?n:[n]),L=e=>U({...e,type:"time",action:"inc"}),j=(e="")=>e.replace(/\/$/u,""),b=e=>/^(https?:)?\/\//.test(e),d=e=>{const t=j(e);return b(t)?t:`https://${t}`},A=e=>{e.name!=="AbortError"&&console.error(e.message)},f=e=>e.dataset.path||null,u=(e,t)=>{t.forEach((r,o)=>{r.innerText=e[o].toString()})},C=({serverURL:e,path:t=window.location.pathname,selector:r=".waline-pageview-count",update:o=!0,lang:n=navigator.language})=>{const a=new AbortController,c=Array.from(document.querySelectorAll(r)),y=s=>{const i=f(s);return i!==null&&t!==i},m=s=>R({serverURL:d(e),paths:s.map(i=>f(i)||t),lang:n,signal:a.signal}).then(i=>u(i,s)).catch(A);if(o){const s=c.filter(h=>!y(h)),i=c.filter(y);L({serverURL:d(e),path:t,lang:n}).then(([h])=>u(new Array(s.length).fill(h),s)),i.length&&m(i)}else m(c);return a.abort.bind(a)};l.pageviewCount=C,l.version=p});
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 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.js';\nimport { type BaseAPIOptions } from './utils.js';\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 { type BaseAPIOptions, JSON_HEADERS } from './utils.js';\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 { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\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/index.js';\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\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 = navigator.language,\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","updatePageview","options","serverURL","lang","path","type","action","fetch","method","headers","body","JSON","stringify","then","resp","json","getServerURL","result","content","replace","removeEndingSplash","test","errorHandler","err","name","console","error","message","getQuery","element","dataset","getAttribute","renderVisitorCount","s","counts","countElements","forEach","index","innerText","toString","_exports","pageviewCount","_ref4","language","window","location","pathname","selector","update","navigator","controller","AbortController","elements","Array","from","document","querySelectorAll","filter","query","_ref","paths","signal","a","_ref2","encodeURIComponent","join","isArray","getPageview","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind","version"],"mappings":"4WAEO,MC8BMA,EAAuC,CAElD,eAAgB,oBCYLC,EACXC,GCuBkC,CAClCC,IAAAA,IAAAA,UAAAA,EACAC,KACAC,EAAAA,KAAAA,EACAC,KACAC,EAAAA,OAAAA,GAAAA,EAAAA,OAEAC,MAASL,GAAAA,kBAA0BC,IAAQ,CACzCK,OAAQ,OACRC,QAASV,EACTW,KAAMC,KAAKC,UAAU,CAAER,KAAMC,EAAAA,KAAAA,EAAMC,OAClCO,MAAAA,MAAMC,GAA0BA,EAAKC,QDhCnB,ECqBa,CDrBb,IAChBd,EACHI,KAAM,OACNC,OAAQ,QELCU,EAAgBd,IAC3B,MAAMe,ECtC2BC,WACjCA,OADiCA,UAAAA,OAAAA,QAAAA,IAAAA,UAAAA,GAAAA,UAAAA,GAAU,IACnCC,QAAQ,OAAQ,GDqCTC,CCtCkBF,CDsCChB,GAElC,MCpCA,kBAAkBmB,KDoCAJ,GAAUA,EAAoBA,WAAAA,GAAQ,EElD7CK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQ1B,MAAQyB,EAAQE,aAAa,MCgDzCC,EAAqBC,CACzBC,EACAC,KAEAA,EAAcC,SAAQ,CAACP,EAASQ,KAC9BR,EAAQS,UAAYJ,EAAOG,GAAOE,UAAU,GAC5C,EA2DsCC,EAAAC,cAxDbC,IAKVC,IAJjBzC,UACAE,EAAAA,KAAAA,EAAOwC,OAAOC,SAASC,SACvBC,SAAAA,EAAW,yBACXC,OAAAA,GAAAA,EACA7C,KAAAA,EAAO8C,UAAUN,UAAAA,EAEjB,MAAMO,EAAa,IAAIC,gBAEjBC,EAAWC,MAAMC,KAErBC,SAASC,iBAA8BT,IAGnCU,EAAU5B,IACd,MAAM6B,EAAQ9B,EAASC,GAEvB,OAAiB,OAAV6B,GAAkBtD,IAASsD,CAAK,EAGnCnD,EAAS6C,GN3DUO,KAAA,IACzBzD,UAAAA,EACAC,KACAyD,EAAAA,MAAAA,EACAC,OCQ+BC,GAAAH,EAAA,MAAA,CAAAI,IAAA,IAC/B7D,UACAC,EAAAA,KAAAA,EACAyD,MACAvD,EAAAA,KAAAA,EACAwD,OAEAtD,GAAAA,EAAAA,OAAAA,MACKL,GAAAA,kBAA0B8D,mBAC3BJ,EAAMK,KAAK,cACHD,mBAAmB3D,EAAK4D,KAAK,cAAc9D,IACrD,CAAE0D,OACFhD,IAAAA,MAAMC,GAA6CA,EAAKC,QDlBxC,ECMa,CDNb,CAChBb,UAAAA,EACAC,KACAyD,EAAAA,MAAAA,EACAvD,KAAM,CAAC,QACPwD,OAAAA,IAGChD,MAAMqB,GAAYmB,MAAMa,QAAQhC,GAAUA,EAAS,CAACA,IAAAA,EM8CrDiC,CAAY,CACVjE,UAAWc,EAAad,GACxB0D,MAAOR,EAASgB,KAAKvC,GAAYD,EAASC,IAAYzB,IACtDD,KAAAA,EACA0D,OAAQX,EAAWW,SAElBhD,MAAMqB,GAAWF,EAAmBE,EAAQkB,KAC5CiB,MAAM/C,GAGX,GAAI0B,EAAQ,CACV,MAAMsB,EAAiBlB,EAASK,QAAQ5B,IAAa4B,EAAO5B,KACtD0C,EAA2BnB,EAASK,OAAOA,GAE5CzD,EAAe,CAClBE,UAAWc,EAAad,GACxBE,KAAAA,EACAD,KACCU,IAAAA,MAAM2D,GACPxC,EACE,IAAIqB,MAAciB,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtBlE,EAAMgE,EAEd,MAGMhE,EAAM6C,GAGb,OAAOF,EAAWyB,MAAMC,KAAK1B,EAAW,EAAAV,EAAAqC,QRhHnB,QQgHmB"}
1
+ {"version":3,"file":"pageview.js","sources":["../src/version.ts","../../api/dist/api.mjs","../src/utils/path.ts","../src/utils/config.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","const m={\"Content-Type\":\"application/json\"},s=t=>`${t.replace(/\\/?$/,\"/\")}api/`,h=(t,n=\"\")=>{if(typeof t==\"object\"&&t.errno)throw new TypeError(`${n} failed with ${t.errno}: ${t.errmsg}`);return t},p=({serverURL:t,lang:n,paths:o,type:a,signal:e})=>fetch(`${s(t)}article?path=${encodeURIComponent(o.join(\",\"))}&type=${encodeURIComponent(a.join(\",\"))}&lang=${n}`,{signal:e}).then(r=>r.json()),d=({serverURL:t,lang:n,path:o,type:a,action:e})=>fetch(`${s(t)}article?lang=${n}`,{method:\"POST\",headers:m,body:JSON.stringify({path:o,type:a,action:e})}).then(r=>r.json()).then(r=>h(r,\"Update counter\").data),$=({serverURL:t,lang:n,path:o,page:a,pageSize:e,sortBy:r,signal:c,token:i})=>{const l={};return i&&(l.Authorization=`Bearer ${i}`),fetch(`${s(t)}comment?path=${encodeURIComponent(o)}&pageSize=${e}&page=${a}&lang=${n}&sortBy=${r}`,{signal:c,headers:l}).then(g=>g.json()).then(g=>h(g,\"Get comment data\").data)},u=({serverURL:t,lang:n,token:o,comment:a})=>{const e={\"Content-Type\":\"application/json\"};return o&&(e.Authorization=`Bearer ${o}`),fetch(`${s(t)}comment?lang=${n}`,{method:\"POST\",headers:e,body:JSON.stringify(a)}).then(r=>r.json())},y=({serverURL:t,lang:n,token:o,objectId:a})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"DELETE\",headers:{Authorization:`Bearer ${o}`}}).then(e=>e.json()).then(e=>h(e,\"Delete comment\")),U=({serverURL:t,lang:n,token:o,objectId:a,comment:e})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"PUT\",headers:{...m,Authorization:`Bearer ${o}`},body:JSON.stringify(e)}).then(r=>r.json()).then(r=>h(r,\"Update comment\")),f=({serverURL:t,lang:n,paths:o,signal:a})=>fetch(`${s(t)}comment?type=count&url=${encodeURIComponent(o.join(\",\"))}&lang=${n}`,{signal:a}).then(e=>e.json()).then(e=>h(e,\"Get comment count\").data),R=({lang:t,serverURL:n})=>{const o=(window.innerWidth-450)/2,a=(window.innerHeight-450)/2,e=window.open(`${n.replace(/\\/$/,\"\")}/ui/login?lng=${encodeURIComponent(t)}`,\"_blank\",`width=450,height=450,left=${o},top=${a},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`);return e?.postMessage({type:\"TOKEN\",data:null},\"*\"),new Promise(r=>{const c=({data:i})=>{!i||typeof i!=\"object\"||i.type!==\"userInfo\"||i.data.token&&(e?.close(),window.removeEventListener(\"message\",c),r(i.data))};window.addEventListener(\"message\",c)})},j=({serverURL:t,lang:n,paths:o,signal:a})=>p({serverURL:t,lang:n,paths:o,type:[\"time\"],signal:a}).then(e=>Array.isArray(e)?e:[e]),v=t=>d({...t,type:\"time\",action:\"inc\"}),w=({serverURL:t,lang:n,count:o,signal:a,token:e})=>{const r={};return e&&(r.Authorization=`Bearer ${e}`),fetch(`${s(t)}comment?type=recent&count=${o}&lang=${n}`,{signal:a,headers:r}).then(c=>c.json())},C=({serverURL:t,signal:n,pageSize:o,lang:a})=>fetch(`${s(t)}user?pageSize=${o}&lang=${a}`,{signal:n}).then(e=>e.json()).then(e=>h(e,\"user list\")).then(e=>e.data);export{u as addComment,y as deleteComment,f as fetchCommentCount,p as getArticleCounter,$ as getComment,j as getPageview,w as getRecentComment,C as getUserList,R as login,d as updateArticleCounter,U as updateComment,v as updatePageview};\n//# sourceMappingURL=api.mjs.map\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","import { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\n : [],\n commentSorting,\n ...more,\n});\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 || null;\n","import { getPageview, updatePageview } from '@waline/api';\n\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\n */\n lang?: string;\n}\n\nexport { type WalineAbort } from './typings/index.js';\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 = navigator.language,\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","m","s","t","h","n","p","o","a","e","r","d","j","v","removeEndingSplash","content","isLinkHttp","link","getServerURL","serverURL","result","errorHandler","err","getQuery","element","renderVisitorCount","counts","countElements","index","pageviewCount","path","selector","update","lang","controller","elements","filter","query","fetch","getPageview","normalElements","elementsNeedstoBeFetched","updatePageview","count"],"mappings":"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"}
package/dist/pageview.mjs CHANGED
@@ -1,2 +1,2 @@
1
- const e="2.15.8",t={"Content-Type":"application/json"},n=({serverURL:e,lang:t,paths:n,signal:a})=>(({serverURL:e,lang:t,paths:n,type:a,signal:r})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${t}`,{signal:r}).then((e=>e.json())))({serverURL:e,lang:t,paths:n,type:["time"],signal:a}).then((e=>Array.isArray(e)?e:[e])),a=e=>(({serverURL:e,lang:n,path:a,type:r,action:o})=>fetch(`${e}/article?lang=${n}`,{method:"POST",headers:t,body:JSON.stringify({path:a,type:r,action:o})}).then((e=>e.json())))({...e,type:"time",action:"inc"}),r=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:c=navigator.language})=>{const h=new AbortController,g=Array.from(document.querySelectorAll(i)),y=e=>{const n=l(e);return null!==n&&t!==n},d=a=>n({serverURL:r(e),paths:a.map((e=>l(e)||t)),lang:c,signal:h.signal}).then((e=>s(e,a))).catch(o);if(p){const n=g.filter((e=>!y(e))),o=g.filter(y);a({serverURL:r(e),path:t,lang:c}).then((e=>s(new Array(n.length).fill(e),n))),o.length&&d(o)}else d(g);return h.abort.bind(h)};export{i as pageviewCount,e as version};
1
+ const u="3.0.0-alpha.2",v={"Content-Type":"application/json"},g=e=>`${e.replace(/\/?$/,"/")}api/`,$=(e,t="")=>{if(typeof e=="object"&&e.errno)throw new TypeError(`${t} failed with ${e.errno}: ${e.errmsg}`);return e},f=({serverURL:e,lang:t,paths:r,type:a,signal:n})=>fetch(`${g(e)}article?path=${encodeURIComponent(r.join(","))}&type=${encodeURIComponent(a.join(","))}&lang=${t}`,{signal:n}).then(o=>o.json()),U=({serverURL:e,lang:t,path:r,type:a,action:n})=>fetch(`${g(e)}article?lang=${t}`,{method:"POST",headers:v,body:JSON.stringify({path:r,type:a,action:n})}).then(o=>o.json()).then(o=>$(o,"Update counter").data),w=({serverURL:e,lang:t,paths:r,signal:a})=>f({serverURL:e,lang:t,paths:r,type:["time"],signal:a}).then(n=>Array.isArray(n)?n:[n]),R=e=>U({...e,type:"time",action:"inc"}),L=(e="")=>e.replace(/\/$/u,""),j=e=>/^(https?:)?\/\//.test(e),y=e=>{const t=L(e);return j(t)?t:`https://${t}`},A=e=>{e.name!=="AbortError"&&console.error(e.message)},d=e=>e.dataset.path||null,m=(e,t)=>{t.forEach((r,a)=>{r.innerText=e[a].toString()})},b=({serverURL:e,path:t=window.location.pathname,selector:r=".waline-pageview-count",update:a=!0,lang:n=navigator.language})=>{const o=new AbortController,i=Array.from(document.querySelectorAll(r)),c=l=>{const s=d(l);return s!==null&&t!==s},h=l=>w({serverURL:y(e),paths:l.map(s=>d(s)||t),lang:n,signal:o.signal}).then(s=>m(s,l)).catch(A);if(a){const l=i.filter(p=>!c(p)),s=i.filter(c);R({serverURL:y(e),path:t,lang:n}).then(([p])=>m(new Array(l.length).fill(p),l)),s.length&&h(s)}else h(i);return o.abort.bind(o)};export{b as pageviewCount,u 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 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.js';\nimport { type BaseAPIOptions } from './utils.js';\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 { type BaseAPIOptions, JSON_HEADERS } from './utils.js';\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 { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\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/index.js';\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\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 = navigator.language,\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","navigator","language","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,YCQ+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,SDlB1DC,CAAkB,CAChBX,YACAC,OACAC,QACAE,KAAM,CAAC,QACPD,WAGCK,MAAMI,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAa5CG,EACXC,GCuBkC,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,SDhCxCc,CAAqB,IAChBR,EACHZ,KAAM,OACNc,OAAQ,QELCO,EAAgBzB,IAC3B,MAAM0B,ECtC0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDqCTC,CAAmB7B,GAElC,MCpCA,kBAAkB8B,KDoCAJ,GAAUA,EAAS,WAAWA,GAAQ,EElD7CK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,QAAQ,ECD9CC,EAAYC,GACvBA,EAAQC,QAAQtB,MAAQqB,EAAQE,aAAa,MCgDzCC,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,OAAOoD,UAAUC,aAEjB,MAAMC,EAAa,IAAIC,gBAEjBC,EAAW5C,MAAM6C,KAErBC,SAASC,iBAA8BT,IAGnCU,EAAUvB,IACd,MAAMwB,EAAQzB,EAASC,GAEvB,OAAiB,OAAVwB,GAAkB7C,IAAS6C,CAAK,EAGnCzD,EAASoD,GACb1D,EAAY,CACVC,UAAWyB,EAAazB,GACxBE,MAAOuD,EAASM,KAAKzB,GAAYD,EAASC,IAAYrB,IACtDhB,OACAE,OAAQoD,EAAWpD,SAElBK,MAAMI,GAAW6B,EAAmB7B,EAAQ6C,KAC5CO,MAAMjC,GAGX,GAAIqB,EAAQ,CACV,MAAMa,EAAiBR,EAASI,QAAQvB,IAAauB,EAAOvB,KACtD4B,EAA2BT,EAASI,OAAOA,GAE5C9C,EAAe,CAClBf,UAAWyB,EAAazB,GACxBiB,OACAhB,SACCO,MAAM2D,GACP1B,EACE,IAAI5B,MAAcoD,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB/D,EAAM6D,EAEd,MAGM7D,EAAMoD,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,EAAW"}
1
+ {"version":3,"file":"pageview.mjs","sources":["../src/version.ts","../../api/dist/api.mjs","../src/utils/path.ts","../src/utils/config.ts","../src/utils/error.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","const m={\"Content-Type\":\"application/json\"},s=t=>`${t.replace(/\\/?$/,\"/\")}api/`,h=(t,n=\"\")=>{if(typeof t==\"object\"&&t.errno)throw new TypeError(`${n} failed with ${t.errno}: ${t.errmsg}`);return t},p=({serverURL:t,lang:n,paths:o,type:a,signal:e})=>fetch(`${s(t)}article?path=${encodeURIComponent(o.join(\",\"))}&type=${encodeURIComponent(a.join(\",\"))}&lang=${n}`,{signal:e}).then(r=>r.json()),d=({serverURL:t,lang:n,path:o,type:a,action:e})=>fetch(`${s(t)}article?lang=${n}`,{method:\"POST\",headers:m,body:JSON.stringify({path:o,type:a,action:e})}).then(r=>r.json()).then(r=>h(r,\"Update counter\").data),$=({serverURL:t,lang:n,path:o,page:a,pageSize:e,sortBy:r,signal:c,token:i})=>{const l={};return i&&(l.Authorization=`Bearer ${i}`),fetch(`${s(t)}comment?path=${encodeURIComponent(o)}&pageSize=${e}&page=${a}&lang=${n}&sortBy=${r}`,{signal:c,headers:l}).then(g=>g.json()).then(g=>h(g,\"Get comment data\").data)},u=({serverURL:t,lang:n,token:o,comment:a})=>{const e={\"Content-Type\":\"application/json\"};return o&&(e.Authorization=`Bearer ${o}`),fetch(`${s(t)}comment?lang=${n}`,{method:\"POST\",headers:e,body:JSON.stringify(a)}).then(r=>r.json())},y=({serverURL:t,lang:n,token:o,objectId:a})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"DELETE\",headers:{Authorization:`Bearer ${o}`}}).then(e=>e.json()).then(e=>h(e,\"Delete comment\")),U=({serverURL:t,lang:n,token:o,objectId:a,comment:e})=>fetch(`${s(t)}comment/${a}?lang=${n}`,{method:\"PUT\",headers:{...m,Authorization:`Bearer ${o}`},body:JSON.stringify(e)}).then(r=>r.json()).then(r=>h(r,\"Update comment\")),f=({serverURL:t,lang:n,paths:o,signal:a})=>fetch(`${s(t)}comment?type=count&url=${encodeURIComponent(o.join(\",\"))}&lang=${n}`,{signal:a}).then(e=>e.json()).then(e=>h(e,\"Get comment count\").data),R=({lang:t,serverURL:n})=>{const o=(window.innerWidth-450)/2,a=(window.innerHeight-450)/2,e=window.open(`${n.replace(/\\/$/,\"\")}/ui/login?lng=${encodeURIComponent(t)}`,\"_blank\",`width=450,height=450,left=${o},top=${a},scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no`);return e?.postMessage({type:\"TOKEN\",data:null},\"*\"),new Promise(r=>{const c=({data:i})=>{!i||typeof i!=\"object\"||i.type!==\"userInfo\"||i.data.token&&(e?.close(),window.removeEventListener(\"message\",c),r(i.data))};window.addEventListener(\"message\",c)})},j=({serverURL:t,lang:n,paths:o,signal:a})=>p({serverURL:t,lang:n,paths:o,type:[\"time\"],signal:a}).then(e=>Array.isArray(e)?e:[e]),v=t=>d({...t,type:\"time\",action:\"inc\"}),w=({serverURL:t,lang:n,count:o,signal:a,token:e})=>{const r={};return e&&(r.Authorization=`Bearer ${e}`),fetch(`${s(t)}comment?type=recent&count=${o}&lang=${n}`,{signal:a,headers:r}).then(c=>c.json())},C=({serverURL:t,signal:n,pageSize:o,lang:a})=>fetch(`${s(t)}user?pageSize=${o}&lang=${a}`,{signal:n}).then(e=>e.json()).then(e=>h(e,\"user list\")).then(e=>e.data);export{u as addComment,y as deleteComment,f as fetchCommentCount,p as getArticleCounter,$ as getComment,j as getPageview,w as getRecentComment,C as getUserList,R as login,d as updateArticleCounter,U as updateComment,v as updatePageview};\n//# sourceMappingURL=api.mjs.map\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","import { decodePath, isLinkHttp, removeEndingSplash } from './path.js';\nimport {\n DEFAULT_EMOJI,\n DEFAULT_LANG,\n DEFAULT_LOCALES,\n DEFAULT_REACTION,\n defaultUploadImage,\n defaultHighlighter,\n defaultTeXRenderer,\n getDefaultSearchOptions,\n getMeta,\n} from '../config/index.js';\nimport {\n type WalineEmojiInfo,\n type WalineEmojiMaps,\n type WalineLocale,\n type WalineProps,\n} from '../typings/index.js';\n\nexport interface WalineEmojiConfig {\n tabs: Pick<WalineEmojiInfo, 'name' | 'icon' | 'items'>[];\n map: WalineEmojiMaps;\n}\n\nexport interface WalineConfig\n extends Required<\n Omit<\n WalineProps,\n | 'emoji'\n | 'imageUploader'\n | 'highlighter'\n | 'texRenderer'\n | 'wordLimit'\n | 'reaction'\n | 'search'\n >\n > {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n reaction: string[];\n emoji: Exclude<WalineProps['emoji'], boolean | undefined>;\n highlighter: Exclude<WalineProps['highlighter'], true | undefined>;\n imageUploader: Exclude<WalineProps['imageUploader'], true | undefined>;\n texRenderer: Exclude<WalineProps['texRenderer'], true | undefined>;\n search: Exclude<WalineProps['search'], true | undefined>;\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 | boolean | 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 = typeof navigator === 'undefined' ? 'en-US' : navigator.language,\n locale,\n emoji = DEFAULT_EMOJI,\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 turnstileKey = '',\n commentSorting = 'latest',\n ...more\n}: WalineProps): WalineConfig => ({\n serverURL: getServerURL(serverURL),\n path: decodePath(path),\n locale: {\n ...(DEFAULT_LOCALES[lang] || DEFAULT_LOCALES[DEFAULT_LANG]),\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: Object.keys(DEFAULT_LOCALES).includes(lang) ? lang : 'en-US',\n dark,\n emoji: typeof emoji === 'boolean' ? (emoji ? DEFAULT_EMOJI : []) : emoji,\n pageSize,\n login,\n copyright,\n search:\n search === false\n ? false\n : typeof search === 'object'\n ? search\n : getDefaultSearchOptions(lang),\n recaptchaV3Key,\n turnstileKey,\n reaction: Array.isArray(reaction)\n ? reaction\n : reaction === true\n ? DEFAULT_REACTION\n : [],\n commentSorting,\n ...more,\n});\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 || null;\n","import { getPageview, updatePageview } from '@waline/api';\n\nimport { type WalineAbort } from './typings/index.js';\nimport { errorHandler, getQuery, getServerURL } from './utils/index.js';\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 navigator.language\n */\n lang?: string;\n}\n\nexport { type WalineAbort } from './typings/index.js';\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 = navigator.language,\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","m","s","t","h","n","p","o","e","r","d","j","v","removeEndingSplash","content","isLinkHttp","link","getServerURL","serverURL","result","errorHandler","err","getQuery","element","renderVisitorCount","counts","countElements","index","pageviewCount","path","selector","update","lang","controller","elements","filter","query","fetch","getPageview","normalElements","elementsNeedstoBeFetched","updatePageview","count"],"mappings":"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"}