@waline/client 2.4.2 → 2.6.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.
Files changed (57) hide show
  1. package/dist/component.esm.js +1 -1
  2. package/dist/component.esm.js.map +1 -1
  3. package/dist/component.js +1 -1
  4. package/dist/component.js.map +1 -1
  5. package/dist/legacy.d.ts +42 -0
  6. package/dist/legacy.js +1 -1
  7. package/dist/legacy.js.map +1 -1
  8. package/dist/pageview.cjs.js +1 -1
  9. package/dist/pageview.cjs.js.map +1 -1
  10. package/dist/pageview.esm.js +1 -1
  11. package/dist/pageview.esm.js.map +1 -1
  12. package/dist/pageview.js +1 -1
  13. package/dist/pageview.js.map +1 -1
  14. package/dist/shim.d.ts +43 -1
  15. package/dist/shim.esm.d.ts +43 -1
  16. package/dist/shim.esm.js +1 -1
  17. package/dist/shim.esm.js.map +1 -1
  18. package/dist/shim.js +1 -1
  19. package/dist/shim.js.map +1 -1
  20. package/dist/waline.cjs.d.ts +43 -1
  21. package/dist/waline.cjs.js +1 -1
  22. package/dist/waline.cjs.js.map +1 -1
  23. package/dist/waline.css +1 -1
  24. package/dist/waline.css.map +1 -1
  25. package/dist/waline.d.ts +43 -1
  26. package/dist/waline.esm.d.ts +43 -1
  27. package/dist/waline.esm.js +1 -1
  28. package/dist/waline.esm.js.map +1 -1
  29. package/dist/waline.js +1 -1
  30. package/dist/waline.js.map +1 -1
  31. package/package.json +21 -22
  32. package/src/comment.ts +7 -2
  33. package/src/components/CommentBox.vue +121 -4
  34. package/src/components/CommentCard.vue +1 -1
  35. package/src/components/Icons.ts +20 -0
  36. package/src/components/ImageWall.vue +166 -0
  37. package/src/composables/like.ts +1 -1
  38. package/src/config/default.ts +91 -1
  39. package/src/config/i18n/en.ts +2 -0
  40. package/src/config/i18n/generate.ts +2 -0
  41. package/src/config/i18n/jp.ts +8 -0
  42. package/src/config/i18n/pt-BR.ts +8 -0
  43. package/src/config/i18n/ru.ts +8 -0
  44. package/src/config/i18n/vi-VN.ts +8 -0
  45. package/src/config/i18n/zh-CN.ts +2 -0
  46. package/src/config/i18n/zh-TW.ts +2 -0
  47. package/src/init.ts +0 -3
  48. package/src/pageview.ts +2 -1
  49. package/src/styles/card.scss +1 -0
  50. package/src/styles/emoji.scss +112 -94
  51. package/src/styles/gif.scss +70 -0
  52. package/src/styles/index.scss +3 -0
  53. package/src/styles/panel.scss +0 -4
  54. package/src/typings/base.ts +40 -0
  55. package/src/typings/locale.ts +2 -0
  56. package/src/typings/waline.ts +8 -0
  57. package/src/utils/config.ts +5 -2
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=e=>{"AbortError"!==e.name&&console.error(e.message)},t={"Content-Type":"application/json"},r=(e,t="")=>{if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},n=({serverURL:e,lang:t,paths:n,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(n.join(","))}&lang=${t}`,{signal:o}).then((e=>e.json())).then((e=>r(e,"visit count"))).then((e=>Array.isArray(e)?e:[e])),o=({serverURL:e,lang:n,path:o})=>fetch(`${e}/article?lang=${n}`,{method:"POST",headers:t,body:JSON.stringify({path:o})}).then((e=>e.json())).then((e=>r(e,"visit count"))),a=e=>e.dataset.path||e.getAttribute("id"),s=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))};exports.pageviewCount=({serverURL:t,path:r=window.location.pathname,selector:i=".waline-pageview-count",update:l=!0,lang:h="zh-CN"})=>{const c=new AbortController,p=Array.from(document.querySelectorAll(i)),g=e=>{const t=a(e);return null!==t&&r!==t},u=o=>n({serverURL:t,paths:o.map((e=>a(e)||r)),lang:h,signal:c.signal}).then((e=>s(e,o))).catch(e);if(l){const e=p.filter((e=>!g(e))),n=p.filter(g);o({serverURL:t,path:r,lang:h}).then((t=>s(new Array(e.length).fill(t),e))),n.length&&u(n)}else u(p);return c.abort.bind(c)},exports.version="2.4.2";
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});const e=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},t=e=>{"AbortError"!==e.name&&console.error(e.message)},r={"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},o=({serverURL:e,lang:t,paths:r,signal:o})=>fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&lang=${t}`,{signal:o}).then((e=>e.json())).then((e=>n(e,"visit count"))).then((e=>Array.isArray(e)?e:[e])),a=({serverURL:e,lang:t,path:o})=>fetch(`${e}/article?lang=${t}`,{method:"POST",headers:r,body:JSON.stringify({path:o})}).then((e=>e.json())).then((e=>n(e,"visit count"))),s=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))};exports.pageviewCount=({serverURL:r,path:n=window.location.pathname,selector:l=".waline-pageview-count",update:h=!0,lang:c="zh-CN"})=>{const p=new AbortController,g=Array.from(document.querySelectorAll(l)),u=e=>{const t=s(e);return null!==t&&n!==t},d=a=>o({serverURL:e(r),paths:a.map((e=>s(e)||n)),lang:c,signal:p.signal}).then((e=>i(e,a))).catch(t);if(h){const e=g.filter((e=>!u(e))),t=g.filter(u);a({serverURL:r,path:n,lang:c}).then((t=>i(new Array(e.length).fill(t),e))),t.length&&d(t)}else d(g);return p.abort.bind(p)},exports.version="2.6.0";
2
2
  //# sourceMappingURL=pageview.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.cjs.js","sources":["../src/version.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n updatePageviews,\n} 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,\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({ serverURL, path, lang }).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":["errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","serverURL","lang","paths","signal","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","path","method","headers","body","JSON","stringify","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,MCFMA,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,CAAcC,EAA0BN,EAAO,MAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACR,SAASR,iBAAqBM,EAAwBC,UACnDD,EAAwBG,UAI/B,OAAOH,GAgNII,EAAiB,EAC5BC,UAAAA,EACAC,KAAAA,EACAC,MAAAA,EACAC,OAAAA,KAEAC,MACE,GAAGJ,kBAA0BK,mBAC3BH,EAAMI,KAAK,cACHL,IACV,CAAEE,OAAAA,IAEDI,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,iBAEhCY,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAQ5CG,EAAkB,EAC7Bb,UAAAA,EACAC,KAAAA,EACAa,KAAAA,KAEAV,MAAM,GAAGJ,kBAA0BC,IAAQ,CACzCc,OAAQ,OACRC,QAASvB,EACTwB,KAAMC,KAAKC,UAAU,CAAEL,KAAAA,MAEtBP,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,iBCtQxByB,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCqDzCC,EAAqB,CACzBd,EACAe,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYlB,EAAOiB,GAAOE,qCAIT,EAC3B7B,UAAAA,EACAc,KAAAA,EAAOgB,OAAOC,SAASC,SACvBC,SAAAA,EAAW,yBACXC,OAAAA,GAAS,EACTjC,KAAAA,EAAO,YAEP,MAAMkC,EAAa,IAAIC,gBAEjBC,EAAW1B,MAAM2B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUpB,IACd,MAAMqB,EAAQtB,EAASC,GAEvB,OAAiB,OAAVqB,GAAkB5B,IAAS4B,GAG9BtC,EAASiC,GACbtC,EAAe,CACbC,UAAAA,EACAE,MAAOmC,EAASM,KAAKtB,GAAYD,EAASC,IAAYP,IACtDb,KAAAA,EACAE,OAAQgC,EAAWhC,SAElBI,MAAMG,GAAWc,EAAmBd,EAAQ2B,KAC5CO,MAAMzD,GAGX,GAAI+C,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQpB,IAAaoB,EAAOpB,KACtDyB,EAA2BT,EAASI,OAAOA,GAE5C5B,EAAgB,CAAEb,UAAAA,EAAWc,KAAAA,EAAMb,KAAAA,IAAQM,MAAMwC,GACpDvB,EACE,IAAIb,MAAckC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5C,EAAM0C,QAKR1C,EAAMiC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,oBJjHR"}
1
+ {"version":3,"file":"pageview.cjs.js","sources":["../src/version.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","import {\n defaultLang,\n defaultLocales,\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 extends Required<Omit<WalineProps, 'wordLimit'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n // emoji: Promise<EmojiConfig>;\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.0.1/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 ...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 ...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","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n getServerURL,\n updatePageviews,\n} 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({ serverURL, path, lang }).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":["getServerURL","serverURL","result","content","replace","removeEndingSplash","test","errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","lang","paths","signal","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","path","method","headers","body","JSON","stringify","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,MC4BMA,EAAgBC,IAC3B,MAAMC,ECrB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDoBTC,CAAmBJ,GAElC,MCnBA,kBAAkBK,KDmBAJ,GAAUA,EAAS,WAAWA,KEjCrCK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,CAAcC,EAA0BN,EAAO,MAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACR,SAASR,iBAAqBM,EAAwBC,UACnDD,EAAwBG,UAI/B,OAAOH,GAgNII,EAAiB,EAC5BlB,YACAmB,OACAC,QACAC,YAEAC,MACE,GAAGtB,kBAA0BuB,mBAC3BH,EAAMI,KAAK,cACHL,IACV,CAAEE,WAEDI,MAAMC,GAASA,EAAKC,SACpBF,MAAMX,GAASD,EAAWC,EAAM,iBAEhCW,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAQ5CG,EAAkB,EAC7B/B,YACAmB,OACAa,UAEAV,MAAM,GAAGtB,kBAA0BmB,IAAQ,CACzCc,OAAQ,OACRC,QAAStB,EACTuB,KAAMC,KAAKC,UAAU,CAAEL,WAEtBP,MAAMC,GAASA,EAAKC,SACpBF,MAAMX,GAASD,EAAWC,EAAM,iBCtQxBwB,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCsDzCC,EAAqB,CACzBd,EACAe,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYlB,EAAOiB,GAAOE,qCAIT,EAC3B/C,YACAgC,OAAOgB,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTjC,OAAO,YAEP,MAAMkC,EAAa,IAAIC,gBAEjBC,EAAW1B,MAAM2B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUpB,IACd,MAAMqB,EAAQtB,EAASC,GAEvB,OAAiB,OAAVqB,GAAkB5B,IAAS4B,GAG9BtC,EAASiC,GACbrC,EAAe,CACblB,UAAWD,EAAaC,GACxBoB,MAAOmC,EAASM,KAAKtB,GAAYD,EAASC,IAAYP,IACtDb,OACAE,OAAQgC,EAAWhC,SAElBI,MAAMG,GAAWc,EAAmBd,EAAQ2B,KAC5CO,MAAMxD,GAGX,GAAI8C,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQpB,IAAaoB,EAAOpB,KACtDyB,EAA2BT,EAASI,OAAOA,GAE5C5B,EAAgB,CAAE/B,YAAWgC,OAAMb,SAAQM,MAAMwC,GACpDvB,EACE,IAAIb,MAAckC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB5C,EAAM0C,QAKR1C,EAAMiC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB,oBNlHR"}
@@ -1,2 +1,2 @@
1
- const e="2.4.2",t=e=>{"AbortError"!==e.name&&console.error(e.message)},r={"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},a=({serverURL:e,lang:t,paths:r,signal:a})=>fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&lang=${t}`,{signal:a}).then((e=>e.json())).then((e=>n(e,"visit count"))).then((e=>Array.isArray(e)?e:[e])),o=({serverURL:e,lang:t,path:a})=>fetch(`${e}/article?lang=${t}`,{method:"POST",headers:r,body:JSON.stringify({path:a})}).then((e=>e.json())).then((e=>n(e,"visit count"))),l=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))},s=({serverURL:e,path:r=window.location.pathname,selector:n=".waline-pageview-count",update:s=!0,lang:h="zh-CN"})=>{const c=new AbortController,g=Array.from(document.querySelectorAll(n)),p=e=>{const t=l(e);return null!==t&&r!==t},f=n=>a({serverURL:e,paths:n.map((e=>l(e)||r)),lang:h,signal:c.signal}).then((e=>i(e,n))).catch(t);if(s){const t=g.filter((e=>!p(e))),n=g.filter(p);o({serverURL:e,path:r,lang:h}).then((e=>i(new Array(t.length).fill(e),t))),n.length&&f(n)}else f(g);return c.abort.bind(c)};export{s as pageviewCount,e as version};
1
+ const e="2.6.0",t=e=>{const t=((e="")=>e.replace(/\/$/u,""))(e);return/^(https?:)?\/\//.test(t)?t:`https://${t}`},r=e=>{"AbortError"!==e.name&&console.error(e.message)},n={"Content-Type":"application/json"},a=(e,t="")=>{if("object"==typeof e&&e.errno)throw new TypeError(`Fetch ${t} failed with ${e.errno}: ${e.errmsg}`);return e},o=({serverURL:e,lang:t,paths:r,signal:n})=>fetch(`${e}/article?path=${encodeURIComponent(r.join(","))}&lang=${t}`,{signal:n}).then((e=>e.json())).then((e=>a(e,"visit count"))).then((e=>Array.isArray(e)?e:[e])),l=({serverURL:e,lang:t,path:r})=>fetch(`${e}/article?lang=${t}`,{method:"POST",headers:n,body:JSON.stringify({path:r})}).then((e=>e.json())).then((e=>a(e,"visit count"))),s=e=>e.dataset.path||e.getAttribute("id"),i=(e,t)=>{t.forEach(((t,r)=>{t.innerText=e[r].toString()}))},h=({serverURL:e,path:n=window.location.pathname,selector:a=".waline-pageview-count",update:h=!0,lang:c="zh-CN"})=>{const p=new AbortController,g=Array.from(document.querySelectorAll(a)),u=e=>{const t=s(e);return null!==t&&n!==t},f=a=>o({serverURL:t(e),paths:a.map((e=>s(e)||n)),lang:c,signal:p.signal}).then((e=>i(e,a))).catch(r);if(h){const t=g.filter((e=>!u(e))),r=g.filter(u);l({serverURL:e,path:n,lang:c}).then((e=>i(new Array(t.length).fill(e),t))),r.length&&f(r)}else f(g);return p.abort.bind(p)};export{h as pageviewCount,e as version};
2
2
  //# sourceMappingURL=pageview.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.esm.js","sources":["../src/version.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n updatePageviews,\n} 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,\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({ serverURL, path, lang }).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","errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","serverURL","lang","paths","signal","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","path","method","headers","body","JSON","stringify","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,QCFVC,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,CAAcC,EAA0BN,EAAO,MAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACR,SAASR,iBAAqBM,EAAwBC,UACnDD,EAAwBG,UAI/B,OAAOH,GAgNII,EAAiB,EAC5BC,UAAAA,EACAC,KAAAA,EACAC,MAAAA,EACAC,OAAAA,KAEAC,MACE,GAAGJ,kBAA0BK,mBAC3BH,EAAMI,KAAK,cACHL,IACV,CAAEE,OAAAA,IAEDI,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,iBAEhCY,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAQ5CG,EAAkB,EAC7Bb,UAAAA,EACAC,KAAAA,EACAa,KAAAA,KAEAV,MAAM,GAAGJ,kBAA0BC,IAAQ,CACzCc,OAAQ,OACRC,QAASvB,EACTwB,KAAMC,KAAKC,UAAU,CAAEL,KAAAA,MAEtBP,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,iBCtQxByB,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCqDzCC,EAAqB,CACzBd,EACAe,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYlB,EAAOiB,GAAOE,eAIzBC,EAAgB,EAC3B9B,UAAAA,EACAc,KAAAA,EAAOiB,OAAOC,SAASC,SACvBC,SAAAA,EAAW,yBACXC,OAAAA,GAAS,EACTlC,KAAAA,EAAO,YAEP,MAAMmC,EAAa,IAAIC,gBAEjBC,EAAW3B,MAAM4B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB7B,IAAS6B,GAG9BvC,EAASkC,GACbvC,EAAe,CACbC,UAAAA,EACAE,MAAOoC,EAASM,KAAKvB,GAAYD,EAASC,IAAYP,IACtDb,KAAAA,EACAE,OAAQiC,EAAWjC,SAElBI,MAAMG,GAAWc,EAAmBd,EAAQ4B,KAC5CO,MAAM1D,GAGX,GAAIgD,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C7B,EAAgB,CAAEb,UAAAA,EAAWc,KAAAA,EAAMb,KAAAA,IAAQM,MAAMyC,GACpDxB,EACE,IAAIb,MAAcmC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB7C,EAAM2C,QAKR3C,EAAMkC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB"}
1
+ {"version":3,"file":"pageview.esm.js","sources":["../src/version.ts","../src/utils/config.ts","../src/utils/path.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","import {\n defaultLang,\n defaultLocales,\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 extends Required<Omit<WalineProps, 'wordLimit'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n // emoji: Promise<EmojiConfig>;\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.0.1/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 ...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 ...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","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n getServerURL,\n updatePageviews,\n} 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({ serverURL, path, lang }).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","getServerURL","serverURL","result","content","replace","removeEndingSplash","test","errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","lang","paths","signal","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","path","method","headers","body","JSON","stringify","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,QC4BVC,EAAgBC,IAC3B,MAAMC,ECrB0B,EAACC,EAAU,KAC3CA,EAAQC,QAAQ,OAAQ,IDoBTC,CAAmBJ,GAElC,MCnBA,kBAAkBK,KDmBAJ,GAAUA,EAAS,WAAWA,KEjCrCK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,CAAcC,EAA0BN,EAAO,MAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACR,SAASR,iBAAqBM,EAAwBC,UACnDD,EAAwBG,UAI/B,OAAOH,GAgNII,EAAiB,EAC5BlB,YACAmB,OACAC,QACAC,YAEAC,MACE,GAAGtB,kBAA0BuB,mBAC3BH,EAAMI,KAAK,cACHL,IACV,CAAEE,WAEDI,MAAMC,GAASA,EAAKC,SACpBF,MAAMX,GAASD,EAAWC,EAAM,iBAEhCW,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,KAQ5CG,EAAkB,EAC7B/B,YACAmB,OACAa,UAEAV,MAAM,GAAGtB,kBAA0BmB,IAAQ,CACzCc,OAAQ,OACRC,QAAStB,EACTuB,KAAMC,KAAKC,UAAU,CAAEL,WAEtBP,MAAMC,GAASA,EAAKC,SACpBF,MAAMX,GAASD,EAAWC,EAAM,iBCtQxBwB,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCsDzCC,EAAqB,CACzBd,EACAe,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYlB,EAAOiB,GAAOE,eAIzBC,EAAgB,EAC3BhD,YACAgC,OAAOiB,OAAOC,SAASC,SACvBC,WAAW,yBACXC,UAAS,EACTlC,OAAO,YAEP,MAAMmC,EAAa,IAAIC,gBAEjBC,EAAW3B,MAAM4B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB7B,IAAS6B,GAG9BvC,EAASkC,GACbtC,EAAe,CACblB,UAAWD,EAAaC,GACxBoB,MAAOoC,EAASM,KAAKvB,GAAYD,EAASC,IAAYP,IACtDb,OACAE,OAAQiC,EAAWjC,SAElBI,MAAMG,GAAWc,EAAmBd,EAAQ4B,KAC5CO,MAAMzD,GAGX,GAAI+C,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C7B,EAAgB,CAAE/B,YAAWgC,OAAMb,SAAQM,MAAMyC,GACpDxB,EACE,IAAIb,MAAcmC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB7C,EAAM2C,QAKR3C,EAAMkC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB"}
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.4.2";const t=e=>{"AbortError"!==e.name&&console.error(e.message)},n={"Content-Type":"application/json"},o=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if("object"==typeof e&&e.errno)throw new TypeError("Fetch ".concat(t," failed with ").concat(e.errno,": ").concat(e.errmsg));return e},r=e=>{let{serverURL:t,lang:n,paths:r,signal:a}=e;return fetch("".concat(t,"/article?path=").concat(encodeURIComponent(r.join(",")),"&lang=").concat(n),{signal:a}).then((e=>e.json())).then((e=>o(e,"visit count"))).then((e=>Array.isArray(e)?e:[e]))},a=e=>{let{serverURL:t,lang:r,path:a}=e;return fetch("".concat(t,"/article?lang=").concat(r),{method:"POST",headers:n,body:JSON.stringify({path:a})}).then((e=>e.json())).then((e=>o(e,"visit count")))},i=e=>e.dataset.path||e.getAttribute("id"),l=(e,t)=>{t.forEach(((t,n)=>{t.innerText=e[n].toString()}))};e.pageviewCount=e=>{let{serverURL:n,path:o=window.location.pathname,selector:s=".waline-pageview-count",update:c=!0,lang:h="zh-CN"}=e;const f=new AbortController,p=Array.from(document.querySelectorAll(s)),d=e=>{const t=i(e);return null!==t&&o!==t},g=e=>r({serverURL:n,paths:e.map((e=>i(e)||o)),lang:h,signal:f.signal}).then((t=>l(t,e))).catch(t);if(c){const e=p.filter((e=>!d(e))),t=p.filter(d);a({serverURL:n,path:o,lang:h}).then((t=>l(new Array(e.length).fill(t),e))),t.length&&g(t)}else g(p);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.6.0";const t=e=>{const t=function(){return(arguments.length>0&&void 0!==arguments[0]?arguments[0]:"").replace(/\/$/u,"")}(e);return/^(https?:)?\/\//.test(t)?t:"https://".concat(t)},n=e=>{"AbortError"!==e.name&&console.error(e.message)},r={"Content-Type":"application/json"},o=function(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"";if("object"==typeof e&&e.errno)throw new TypeError("Fetch ".concat(t," failed with ").concat(e.errno,": ").concat(e.errmsg));return e},a=e=>{let{serverURL:t,lang:n,paths:r,signal:a}=e;return fetch("".concat(t,"/article?path=").concat(encodeURIComponent(r.join(",")),"&lang=").concat(n),{signal:a}).then((e=>e.json())).then((e=>o(e,"visit count"))).then((e=>Array.isArray(e)?e:[e]))},i=e=>{let{serverURL:t,lang:n,path:a}=e;return fetch("".concat(t,"/article?lang=").concat(n),{method:"POST",headers:r,body:JSON.stringify({path:a})}).then((e=>e.json())).then((e=>o(e,"visit count")))},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:r,path:o=window.location.pathname,selector:c=".waline-pageview-count",update:h=!0,lang:p="zh-CN"}=e;const f=new AbortController,u=Array.from(document.querySelectorAll(c)),d=e=>{const t=l(e);return null!==t&&o!==t},g=e=>a({serverURL:t(r),paths:e.map((e=>l(e)||o)),lang:p,signal:f.signal}).then((t=>s(t,e))).catch(n);if(h){const e=u.filter((e=>!d(e))),t=u.filter(d);i({serverURL:r,path:o,lang:p}).then((t=>s(new Array(e.length).fill(t),e))),t.length&&g(t)}else g(u);return f.abort.bind(f)}}));
2
2
  //# sourceMappingURL=pageview.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pageview.js","sources":["../src/version.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n updatePageviews,\n} 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,\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({ serverURL, path, lang }).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":["errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","_ref","serverURL","lang","paths","signal","fetch","concat","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","_ref2","path","method","headers","body","JSON","stringify","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","_ref3","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"sXAEuB,QCFhB,MAAMA,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,SAAcC,GAA0C,IAAhBN,yDAAO,GAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACCR,SAAAA,OAAAA,0BAAqBM,EAAwBC,MACnDD,MAAAA,OAAAA,EAAwBG,SAI/B,OAAOH,GAgNII,EAAiBC,IAAA,IAACC,UAC7BA,EAD6BC,KAE7BA,EAF6BC,MAG7BA,EAH6BC,OAI7BA,GAJ4BJ,EAAA,OAM5BK,MAAK,GAAAC,OACAL,EADA,kBAAAK,OAC0BC,mBAC3BJ,EAAMK,KAAK,MAFV,UAAAF,OAGOJ,GACV,CAAEE,OAAAA,IAEDK,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,iBAEhCc,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,MAQ5CG,EAAkBC,IAAA,IAACf,UAC9BA,EAD8BC,KAE9BA,EAF8Be,KAG9BA,GAH6BD,EAAA,OAK7BX,MAAK,GAAAC,OAAIL,EAAJ,kBAAAK,OAA8BJ,GAAQ,CACzCgB,OAAQ,OACRC,QAAS1B,EACT2B,KAAMC,KAAKC,UAAU,CAAEL,KAAAA,MAEtBR,MAAMC,GAASA,EAAKC,SACpBF,MAAMd,GAASD,EAAWC,EAAM,kBCtQxB4B,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCqDzCC,EAAqB,CACzBf,EACAgB,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYnB,EAAOkB,GAAOE,+BAITC,IAMiB,IANhBhC,UAC5BA,EAD4BgB,KAE5BA,EAAOiB,OAAOC,SAASC,SAFKC,SAG5BA,EAAW,yBAHiBC,OAI5BA,GAAS,EAJmBpC,KAK5BA,EAAO,SACqC+B,EAC5C,MAAMM,EAAa,IAAIC,gBAEjBC,EAAW5B,MAAM6B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB7B,IAAS6B,GAG9BzC,EAASoC,GACb1C,EAAe,CACbE,UAAAA,EACAE,MAAOsC,EAASM,KAAKvB,GAAYD,EAASC,IAAYP,IACtDf,KAAAA,EACAE,OAAQmC,EAAWnC,SAElBK,MAAMG,GAAWe,EAAmBf,EAAQ6B,KAC5CO,MAAM7D,GAGX,GAAImD,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C9B,EAAgB,CAAEd,UAAAA,EAAWgB,KAAAA,EAAMf,KAAAA,IAAQO,MAAM0C,GACpDxB,EACE,IAAId,MAAcoC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB/C,EAAM6C,QAKR7C,EAAMoC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB"}
1
+ {"version":3,"file":"pageview.js","sources":["../src/version.ts","../src/utils/path.ts","../src/utils/config.ts","../src/utils/error.ts","../src/utils/fetch.ts","../src/utils/query.ts","../src/pageview.ts"],"sourcesContent":["declare const VERSION: string;\n\nexport const version = VERSION;\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 {\n defaultLang,\n defaultLocales,\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 extends Required<Omit<WalineProps, 'wordLimit'>> {\n locale: WalineLocale;\n wordLimit: [number, number] | false;\n // emoji: Promise<EmojiConfig>;\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.0.1/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 ...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 ...more,\n});\n","export const errorHandler = (err: Error): void => {\n if (err.name !== 'AbortError') console.error(err.message);\n};\n","import type { WalineComment, WalineCommentData } from '../typings';\n\nexport interface FetchErrorData {\n errno: number;\n errmsg: string;\n}\n\nconst JSON_HEADERS: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n};\n\nconst errorCheck = <T = unknown>(data: T | FetchErrorData, name = ''): 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\nexport interface FetchCountOptions {\n serverURL: string;\n lang: string;\n paths: string[];\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchCommentCount = ({\n serverURL,\n lang,\n paths,\n signal,\n token,\n}: FetchCountOptions): Promise<number[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return (\n fetch(\n `${serverURL}/comment?type=count&url=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<number | number[]>)\n .then((data) => errorCheck(data, 'comment count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]))\n );\n};\nexport interface FetchRecentOptions {\n serverURL: string;\n lang: string;\n count: number;\n signal: AbortSignal;\n token?: string;\n}\n\nexport const fetchRecentComment = ({\n serverURL,\n lang,\n count,\n signal,\n token,\n}: FetchRecentOptions): Promise<WalineComment[]> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?type=recent&count=${count}&lang=${lang}`, {\n signal,\n headers,\n })\n .then((resp) => resp.json() as Promise<WalineComment[]>)\n .then((data) => errorCheck(data, 'recent comment'));\n};\n\nexport interface FetchListOptions {\n serverURL: string;\n path: string;\n page: number;\n pageSize: number;\n signal: AbortSignal;\n token?: string;\n lang: string;\n}\n\nexport interface FetchListResult {\n count: number;\n data: WalineComment[];\n totalPages: number;\n}\n\nexport const fetchCommentList = ({\n serverURL,\n lang,\n path,\n page,\n pageSize,\n signal,\n token,\n}: FetchListOptions): Promise<FetchListResult> => {\n const headers: Record<string, string> = {};\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(\n `${serverURL}/comment?path=${encodeURIComponent(\n path\n )}&pageSize=${pageSize}&page=${page}&lang=${lang}`,\n { signal, headers }\n )\n .then((resp) => resp.json() as Promise<FetchListResult>)\n .then((data) => errorCheck(data, 'comment list'));\n};\n\nexport interface PostCommentOptions {\n serverURL: string;\n lang: string;\n token?: string;\n comment: WalineCommentData;\n}\n\nexport interface PostCommentResponse {\n data?: WalineComment;\n errmsg?: string;\n}\n\nexport const postComment = ({\n serverURL,\n lang,\n token,\n comment,\n}: PostCommentOptions): Promise<PostCommentResponse> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n };\n\n if (token) headers.Authorization = `Bearer ${token}`;\n\n return fetch(`${serverURL}/comment?lang=${lang}`, {\n method: 'POST',\n headers,\n body: JSON.stringify(comment),\n }).then((resp) => resp.json() as Promise<PostCommentResponse>);\n};\n\nexport interface DeleteCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: string | number;\n}\n\nexport const deleteComment = ({\n serverURL,\n lang,\n token,\n objectId,\n}: DeleteCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'DELETE',\n headers,\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface LikeCommentOptions {\n serverURL: string;\n lang: string;\n objectId: number | string;\n like: boolean;\n}\n\nexport const likeComment = ({\n serverURL,\n lang,\n objectId,\n like,\n}: LikeCommentOptions): Promise<void> =>\n fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers: JSON_HEADERS,\n body: JSON.stringify({ like }),\n }).then((resp) => resp.json() as Promise<void>);\n\nexport interface UpdateCommentOptions {\n serverURL: string;\n lang: string;\n token: string;\n objectId: number | string;\n status?: 'approved' | 'waiting' | 'spam';\n sticky?: number;\n}\n\nexport const updateComment = ({\n serverURL,\n lang,\n token,\n objectId,\n ...data\n}: UpdateCommentOptions): Promise<void> => {\n const headers: Record<string, string> = {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n return fetch(`${serverURL}/comment/${objectId}?lang=${lang}`, {\n method: 'PUT',\n headers,\n body: JSON.stringify(data),\n }).then((resp) => resp.json() as Promise<void>);\n};\n\nexport interface FetchPageviewsOptions {\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}: FetchPageviewsOptions): Promise<number[]> =>\n fetch(\n `${serverURL}/article?path=${encodeURIComponent(\n paths.join(',')\n )}&lang=${lang}`,\n { signal }\n )\n .then((resp) => resp.json() as Promise<number[] | number>)\n .then((data) => errorCheck(data, 'visit count'))\n // TODO: Improve this API\n .then((counts) => (Array.isArray(counts) ? counts : [counts]));\n\nexport interface UpdatePageviewsOptions {\n serverURL: string;\n lang: string;\n path: string;\n}\n\nexport const updatePageviews = ({\n serverURL,\n lang,\n path,\n}: UpdatePageviewsOptions): Promise<number> =>\n fetch(`${serverURL}/article?lang=${lang}`, {\n method: 'POST',\n headers: JSON_HEADERS,\n body: JSON.stringify({ path }),\n })\n .then((resp) => resp.json() as Promise<number>)\n .then((data) => errorCheck(data, 'visit count'));\n","export const getQuery = (element: HTMLElement): string | null =>\n element.dataset.path || element.getAttribute('id');\n","import {\n errorHandler,\n fetchPageviews,\n getQuery,\n getServerURL,\n updatePageviews,\n} 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({ serverURL, path, lang }).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":["getServerURL","serverURL","result","replace","removeEndingSplash","test","concat","errorHandler","err","name","console","error","message","JSON_HEADERS","errorCheck","data","errno","TypeError","errmsg","fetchPageviews","_ref","lang","paths","signal","fetch","encodeURIComponent","join","then","resp","json","counts","Array","isArray","updatePageviews","_ref2","path","method","headers","body","JSON","stringify","getQuery","element","dataset","getAttribute","renderVisitorCount","countElements","forEach","index","innerText","toString","_ref3","window","location","pathname","selector","update","controller","AbortController","elements","from","document","querySelectorAll","filter","query","map","catch","normalElements","elementsNeedstoBeFetched","count","length","fill","abort","bind"],"mappings":"sXAEuB,QCQhB,MCoBMA,EAAgBC,IAC3B,MAAMC,EDrB0B,WAAA,8DAAW,IACnCC,QAAQ,OAAQ,ICoBTC,CAAmBH,GAElC,MDnBA,kBAAkBI,KCmBAH,GAAUA,EAArB,WAAAI,OAAyCJ,ICjCrCK,EAAgBC,IACV,eAAbA,EAAIC,MAAuBC,QAAQC,MAAMH,EAAII,UCM7CC,EAAuC,CAE3C,eAAgB,oBAGZC,EAAa,SAAcC,GAA0C,IAAhBN,yDAAO,GAChE,GAAoB,iBAATM,GAAsBA,EAAwBC,MACvD,MAAM,IAAIC,UACCR,SAAAA,OAAAA,0BAAqBM,EAAwBC,MACnDD,MAAAA,OAAAA,EAAwBG,SAI/B,OAAOH,GAgNII,EAAiBC,IAAA,IAACnB,UAC7BA,EAD6BoB,KAE7BA,EAF6BC,MAG7BA,EAH6BC,OAI7BA,GAJ4BH,EAAA,OAM5BI,MAAK,GAAAlB,OACAL,EADA,kBAAAK,OAC0BmB,mBAC3BH,EAAMI,KAAK,MAFV,UAAApB,OAGOe,GACV,CAAEE,WAEDI,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,iBAEhCY,MAAMG,GAAYC,MAAMC,QAAQF,GAAUA,EAAS,CAACA,MAQ5CG,EAAkBC,IAAA,IAACjC,UAC9BA,EAD8BoB,KAE9BA,EAF8Bc,KAG9BA,GAH6BD,EAAA,OAK7BV,MAAK,GAAAlB,OAAIL,EAAJ,kBAAAK,OAA8Be,GAAQ,CACzCe,OAAQ,OACRC,QAASxB,EACTyB,KAAMC,KAAKC,UAAU,CAAEL,WAEtBR,MAAMC,GAASA,EAAKC,SACpBF,MAAMZ,GAASD,EAAWC,EAAM,kBCtQxB0B,EAAYC,GACvBA,EAAQC,QAAQR,MAAQO,EAAQE,aAAa,MCsDzCC,EAAqB,CACzBf,EACAgB,KAEAA,EAAcC,SAAQ,CAACL,EAASM,KAC9BN,EAAQO,UAAYnB,EAAOkB,GAAOE,+BAITC,IAMiB,IANhBlD,UAC5BA,EAD4BkC,KAE5BA,EAAOiB,OAAOC,SAASC,SAFKC,SAG5BA,EAAW,yBAHiBC,OAI5BA,GAAS,EAJmBnC,KAK5BA,EAAO,SACqC8B,EAC5C,MAAMM,EAAa,IAAIC,gBAEjBC,EAAW5B,MAAM6B,KAErBC,SAASC,iBAA8BP,IAGnCQ,EAAUrB,IACd,MAAMsB,EAAQvB,EAASC,GAEvB,OAAiB,OAAVsB,GAAkB7B,IAAS6B,GAG9BxC,EAASmC,GACbxC,EAAe,CACblB,UAAWD,EAAaC,GACxBqB,MAAOqC,EAASM,KAAKvB,GAAYD,EAASC,IAAYP,IACtDd,OACAE,OAAQkC,EAAWlC,SAElBI,MAAMG,GAAWe,EAAmBf,EAAQ6B,KAC5CO,MAAM3D,GAGX,GAAIiD,EAAQ,CACV,MAAMW,EAAiBR,EAASI,QAAQrB,IAAaqB,EAAOrB,KACtD0B,EAA2BT,EAASI,OAAOA,GAE5C9B,EAAgB,CAAEhC,YAAWkC,OAAMd,SAAQM,MAAM0C,GACpDxB,EACE,IAAId,MAAcoC,EAAeG,QAAQC,KAAKF,GAC9CF,KAKAC,EAAyBE,QACtB9C,EAAM4C,QAKR5C,EAAMmC,GAGb,OAAOF,EAAWe,MAAMC,KAAKhB"}
package/dist/shim.d.ts CHANGED
@@ -37,6 +37,40 @@ interface WalineEmojiInfo {
37
37
  items: string[];
38
38
  }
39
39
  declare type WalineEmojiMaps = Record<string, string>;
40
+ interface WalineSearchResult extends Record<string, unknown> {
41
+ /**
42
+ * Image link
43
+ */
44
+ src: string;
45
+ /**
46
+ * Image title, optional
47
+ */
48
+ title?: string;
49
+ /**
50
+ * Image preview link, optional
51
+ *
52
+ * @default src
53
+ */
54
+ preview?: string;
55
+ }
56
+ interface WalineSearchOptions {
57
+ /**
58
+ * Search action
59
+ */
60
+ search: (word: string) => Promise<WalineSearchResult[]>;
61
+ /**
62
+ * Default search action
63
+ *
64
+ * @default () => search('')
65
+ */
66
+ default?: () => Promise<WalineSearchResult[]>;
67
+ /**
68
+ * Fetch more action
69
+ *
70
+ * @default (word) => search(word)
71
+ */
72
+ more?: (word: string, currectCount: number) => Promise<WalineSearchResult[]>;
73
+ }
40
74
  declare type WalineMeta = 'nick' | 'mail' | 'link';
41
75
  declare type WalineImageUploader = (image: File) => Promise<string>;
42
76
  declare type WalineHighlighter = (code: string, lang: string) => string;
@@ -145,6 +179,8 @@ interface WalineLocale extends WalineDateLocale, WalineLevelLocale {
145
179
  word: string;
146
180
  wordHint: string;
147
181
  anonymous: string;
182
+ gif: string;
183
+ gifSearchPlaceholder: string;
148
184
  }
149
185
 
150
186
  interface WalineProps {
@@ -278,6 +314,12 @@ interface WalineProps {
278
314
  * @default ['//unpkg.com/@waline/emojis@1.0.1/weibo']
279
315
  */
280
316
  emoji?: (string | WalineEmojiInfo)[] | false;
317
+ /**
318
+ * 设置搜索功能
319
+ *
320
+ * Customize Search feature
321
+ */
322
+ search?: WalineSearchOptions | false;
281
323
  /**
282
324
  * 代码高亮
283
325
  *
@@ -524,4 +566,4 @@ interface WalineRecentCommentsResult {
524
566
  }
525
567
  declare const RecentComments: ({ el, serverURL, count, lang, }: WalineRecentCommentsOptions) => Promise<WalineRecentCommentsResult>;
526
568
 
527
- export { RecentComments, WalineAbort, WalineComment, WalineCommentCountOptions, WalineCommentData, WalineCommentStatus, WalineDateLocale, WalineEmojiInfo, WalineEmojiMaps, WalineHighlighter, WalineImageUploader, WalineInitOptions, WalineInstance, WalineLevelLocale, WalineLocale, WalineMeta, WalinePageviewCountOptions, WalineProps, WalineRecentCommentsOptions, WalineRecentCommentsResult, WalineTexRenderer, commentCount, defaultLocales, init, pageviewCount, version };
569
+ export { RecentComments, WalineAbort, WalineComment, WalineCommentCountOptions, WalineCommentData, WalineCommentStatus, WalineDateLocale, WalineEmojiInfo, WalineEmojiMaps, WalineHighlighter, WalineImageUploader, WalineInitOptions, WalineInstance, WalineLevelLocale, WalineLocale, WalineMeta, WalinePageviewCountOptions, WalineProps, WalineRecentCommentsOptions, WalineRecentCommentsResult, WalineSearchOptions, WalineSearchResult, WalineTexRenderer, commentCount, defaultLocales, init, pageviewCount, version };
@@ -37,6 +37,40 @@ interface WalineEmojiInfo {
37
37
  items: string[];
38
38
  }
39
39
  declare type WalineEmojiMaps = Record<string, string>;
40
+ interface WalineSearchResult extends Record<string, unknown> {
41
+ /**
42
+ * Image link
43
+ */
44
+ src: string;
45
+ /**
46
+ * Image title, optional
47
+ */
48
+ title?: string;
49
+ /**
50
+ * Image preview link, optional
51
+ *
52
+ * @default src
53
+ */
54
+ preview?: string;
55
+ }
56
+ interface WalineSearchOptions {
57
+ /**
58
+ * Search action
59
+ */
60
+ search: (word: string) => Promise<WalineSearchResult[]>;
61
+ /**
62
+ * Default search action
63
+ *
64
+ * @default () => search('')
65
+ */
66
+ default?: () => Promise<WalineSearchResult[]>;
67
+ /**
68
+ * Fetch more action
69
+ *
70
+ * @default (word) => search(word)
71
+ */
72
+ more?: (word: string, currectCount: number) => Promise<WalineSearchResult[]>;
73
+ }
40
74
  declare type WalineMeta = 'nick' | 'mail' | 'link';
41
75
  declare type WalineImageUploader = (image: File) => Promise<string>;
42
76
  declare type WalineHighlighter = (code: string, lang: string) => string;
@@ -145,6 +179,8 @@ interface WalineLocale extends WalineDateLocale, WalineLevelLocale {
145
179
  word: string;
146
180
  wordHint: string;
147
181
  anonymous: string;
182
+ gif: string;
183
+ gifSearchPlaceholder: string;
148
184
  }
149
185
 
150
186
  interface WalineProps {
@@ -278,6 +314,12 @@ interface WalineProps {
278
314
  * @default ['//unpkg.com/@waline/emojis@1.0.1/weibo']
279
315
  */
280
316
  emoji?: (string | WalineEmojiInfo)[] | false;
317
+ /**
318
+ * 设置搜索功能
319
+ *
320
+ * Customize Search feature
321
+ */
322
+ search?: WalineSearchOptions | false;
281
323
  /**
282
324
  * 代码高亮
283
325
  *
@@ -524,4 +566,4 @@ interface WalineRecentCommentsResult {
524
566
  }
525
567
  declare const RecentComments: ({ el, serverURL, count, lang, }: WalineRecentCommentsOptions) => Promise<WalineRecentCommentsResult>;
526
568
 
527
- export { RecentComments, WalineAbort, WalineComment, WalineCommentCountOptions, WalineCommentData, WalineCommentStatus, WalineDateLocale, WalineEmojiInfo, WalineEmojiMaps, WalineHighlighter, WalineImageUploader, WalineInitOptions, WalineInstance, WalineLevelLocale, WalineLocale, WalineMeta, WalinePageviewCountOptions, WalineProps, WalineRecentCommentsOptions, WalineRecentCommentsResult, WalineTexRenderer, commentCount, defaultLocales, init, pageviewCount, version };
569
+ export { RecentComments, WalineAbort, WalineComment, WalineCommentCountOptions, WalineCommentData, WalineCommentStatus, WalineDateLocale, WalineEmojiInfo, WalineEmojiMaps, WalineHighlighter, WalineImageUploader, WalineInitOptions, WalineInstance, WalineLevelLocale, WalineLocale, WalineMeta, WalinePageviewCountOptions, WalineProps, WalineRecentCommentsOptions, WalineRecentCommentsResult, WalineSearchOptions, WalineSearchResult, WalineTexRenderer, commentCount, defaultLocales, init, pageviewCount, version };