/** * The variable used in DisqusJS * * DisqusJS Mode * @param {string} disqusjs.mode = dsqjs | disqus - Set which mode to use, should store and get in localStorage * @param {string} disqusjs.sortType = popular | asc(oldest first) | desc(latest first) - Set which sort type to use, should store and get in localStorage * * DisqusJS Config * @param {string} disqusjs.config.shortname - The disqus shortname * @param {string} disqusjs.config.siteName - The Forum Name * @param {string} disqusjs.config.identifier - The identifier of the page * @param {string} disqusjs.config.title - The title of the page * @param {string} disqusjs.config.url - The url of the page * @param {string} disqusjs.config.api - Where to get data * @param {string} disqusjs.config.apikey - The apikey used to request Disqus API * @param {number} disqusjs.config.nesting - The max nesting level of Disqus comment * @param {string} disqusjs.config.nocomment - The msg when there is no comment * @param {string} disqusjs.config.admin - The disqus forum admin username * @param {string} disqusjs.config.adminLabel - The disqus moderator badge text * * DisqusJS Info * @param {string} disqusjs.page.id = The thread id, used at next API call * @param {string} disqusjs.page.next = The cursor of next page of list * @param {boolean} disqusjs.page.isClosed - Whether the comment is closed * @param {number} disqusjs.page.length - How many comment in the thread */ function DisqusJS(config) { ((window, document, localStorage, fetch, Promise) => { // 封装一下基于 Object.asign 的方法 function _extends(...args) { _extends = Object.assign || function (target) { for (const source of arguments) { for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; } return _extends.apply(this, args); }; const $$ = (elementID) => document.getElementById(elementID); /** * msg - 提示信息 * * @param {string} url */ const msg = (str) => { const msgEl = $$('dsqjs-msg'); if (msgEl) msgEl.innerHTML = str; } // 定义一些常量 const CLICK = 'click'; const DISQUS_CONTAINER_EL_ID = 'disqus_thread'; const DSQJS_STORAGE_KEY_SORT_TYPE = 'dsqjs_sort' /** * htmlTpl - DisqusJS 的 HTML 模板片段 * * msg: DisqusJS 提示信息模板 * footer: 尾部信息模板 */ /* target="_blank" rel="external nofollow noopener noreferrer" */ const HTML_TPL_A_ATTR = `target="_blank" rel="external nofollow noopener noreferrer"`; /*
*/ const HTML_TPL_EL_MSG = ``; /* */ const HTML_TPL_EL_FOOTER = ``; /*评论列表加载中...
正在切换排序方式...
'; // 把 加载更多评论 隐藏起来 $loadMoreBtn.classList.add('dsqjs-hide'); getComment(); } // 处理传入的 cursor const cursorParam = (cursor === '') ? '' : `&cursor=${cursor}`; // 在发起请求前禁用 加载更多评论 按钮防止重复调用 $loadMoreBtn.classList.add('dsqjs-disabled') /* * 获取评论列表 * * API Docs: https://disqus.com/api/docs/posts/list/ * API URI: /3.0/posts/list.json?forum=[shortname]&thread=[thread id]&api_key=[apikey] * * https://github.com/SukkaW/DisqusJS/issues/6 * 可以使用 include=deleted 来获得已被删除评论列表 * * https://blog.fooleap.org/disqus-api-comments-order-by-desc.html * 处理评论嵌套问题,使用了一个隐藏 API /threads/listPostsThreaded * 用法和 /threads/listPosts 相似,和 /threads/post 的区别也只有 include 字段不同 * 这个能够返回已删除评论,所以也不需要 include=deleted 了 * sort 字段提供三个取值: * - desc (降序) * - asc (升序) * - popular(最热) * 这个 API 的问题在于被嵌套的评论总是降序,看起来很不习惯 * * 每次加载翻页评论的时候合并数据并进行重排序 * 用户切换排序方式的时候直接取出进行重新渲染 */ const sortCommentParseDate = ({ createdAt }) => Date.parse(new Date(createdAt)); const sortCommentparentAsc = (a, b) => { if (a.parent && b.parent) { return sortCommentParseDate(a) - sortCommentParseDate(b); } else { return 0; } }; const url = `${disqusjs.config.api}3.0/threads/listPostsThreaded?forum=${disqusjs.config.shortname}&thread=${disqusjs.page.id}${cursorParam}&api_key=${apikey()}&order=${disqusjs.sortType}`; _get(url).then(({ data }) => { if (data.code === 0 && data.response.length > 0) { // 解禁 加载更多评论 $loadMoreBtn.classList.remove('dsqjs-disabled'); // 将获得的评论数据和当前页面已有的评论数据合并 disqusjs.page.comment.push(...data.response) // 将所有的子评论进行降序排列 disqusjs.page.comment.sort(sortCommentparentAsc); // 用当前页面的所有评论数据进行渲染 renderComment(disqusjs.page.comment); // 为排序按钮们委托事件 for (const i of $orderRadio) { i.addEventListener('change', switchSortType); } for (const i of $loadHideCommentInDisqus) { i.addEventListener(CLICK, checkDisqus); }; if (data.cursor.hasNext) { // 将 cursor.next 存入 disqusjs 变量中供不能传参的不匿名函数使用 disqusjs.page.next = data.cursor.next; // 确保 加载更多评论按钮 文字正常 $loadMoreBtn.innerHTML = '加载更多评论' // 显示 加载更多评论 按钮 $loadMoreBtn.classList.remove('dsqjs-hide'); $loadMoreBtn.addEventListener(CLICK, getMoreComment); } else { // 没有更多评论了,确保按钮隐藏 $loadMoreBtn.classList.add('dsqjs-hide'); } } else if (data.code === 0 && data.response.length === 0) { // 当前没有评论,显示提示信息 msg(`你可能无法访问 Disqus,已启用评论基础模式。${HTML_TPL_EL_ASK_FOR_FULL}`) $$('dsqjs-post-container').innerHTML = `${disqusjs.config.nocomment}
` assignClickEventForAskForFulButton(); $$('dsqjs-force-disqus').addEventListener(CLICK, useDsqjs); } else { throw new Error; } }).catch(getCommentError); } /* * parseCommentData(data) - 解析评论列表 * * @param {Array} data - 评论列表 JSON * @return {Array} - 解析后的评论列表数据 */ const parseCommentData = (data) => { let topLevelComments = []; let childComments = []; let commentJSON = (comment) => ({ comment, author: comment.author.name, // 如果不设置 admin 会返回 undefined,所以需要嘴一个判断 isPrimary: (disqusjs.config.admin ? (comment.author.username === disqusjs.config.admin) : false), children: getChildren(+comment.id), /* * Disqus 改变了 Private API 的行为 * https://github.com/fooleap/disqus-php-api/issues/44 * 默认隐藏更多的评论,通过 hasMore 字段判断 */ // 将 hasMore 字段提升到上层字段中 hasMore: comment.hasMore }); const getChildren = (id) => { // 如果没有子评论,就不需要解析子评论了 if (childComments.length === 0) return null; const list = []; for (const comment of childComments) { if (comment.parent === id) { list.unshift(commentJSON(comment)); } } return (list.length) ? list : null } data.forEach((comment) => { // 如果没有 comment.parent 说明是第一级评论 const c = comment.parent ? childComments : topLevelComments; c.push(comment); }); return topLevelComments.map(comment => commentJSON(comment)); } /* * renderCommentData(data) - 渲染评论列表 * * @param {Array} data - 从 getComment() 获取到的 JSON */ const renderComment = (data) => { /* * processData(data) - 处理评论列表 * * @param {Array} data - 解析后的评论列表 JSON */ const processData = (data) => { // 处理 Disqus Profile URL if (data.comment.author.profileUrl) { /* Avatar Element切换至 完整 Disqus 模式 显示更多回复
` } html += `切换至 完整 Disqus 模式 显示更多回复
` } html += `