{o.value=c})}catch{i.emit(l.SHOW_MSG,{type:"error",text:"Base64解码失败!不是标准数据!"})}}return(c,r)=>Vue.withDirectives((Vue.openBlock(),Vue.createElementBlock("div",{class:"base64_tooltip",style:Vue.normalizeStyle(n),onClick:p,ref_key:"tooltip",ref:e},[o.value?(Vue.openBlock(),Vue.createElementBlock("div",Ft,[Vue.createElementVNode("span",null,Vue.toDisplayString(o.value),1),Vue.createElementVNode("div",{class:"button",onClick:d},"点击复制")])):(Vue.openBlock(),Vue.createElementBlock(Vue.Fragment,{key:0},[Vue.createTextVNode(" Base64解码:"+Vue.toDisplayString(a.value)+" ",1),jt],64))],4)),[[Vue.vShow,s.value]])}},Wt=_(zt,[["__scopeId","data-v-61690a22"]]);const Ut={name:"home",provide(){return{isDev:Vue.computed(()=>!1),isLogin:Vue.computed(()=>!!window.win().user.username),pageType:Vue.computed(()=>this.pageType),clone:window.win().clone,post:Vue.computed(()=>this.current),allReplyUsers:Vue.computed(()=>Array.from(new Set(this.current.replies.map(t=>t.username))))}},components:{PostDetail:wt,Post:It,Msg:$t,Base64Tooltip:Wt},data(){return{loading:window.win().pageType==="post",loadMore:!1,pageType:window.win().pageType,isNight:window.win().isNight,msgList:[],show:!1,showConfig:!1,current:window.win().initPost,list:[],config:window.win().config}},computed:{isDev(){return!1},showList(){return this.pageType==="home"||this.pageType==="recent"||this.pageType==="nodePage"}},watch:{"current.replies":{handler(t,e){if(t.length){this.current.replyCount=t.length;let s=window.parse.createNestedList(t,this.current.allReplyUsers);s&&(this.current.nestedReplies=s)}else this.current.replyCount=0,this.current.nestedReplies=[];if(this.list){let s=this.list.findIndex(a=>a.id===this.current.id);s>-1&&(this.list[s].replyCount=t.length)}},deep:!0},config:{handler(t){let e={[window.win().user.username??"default"]:t};window.win().localStorage.setItem("v2ex-config",JSON.stringify(e)),window.win().config=t},deep:!0},"config.viewType"(t){t&&(t==="card"?$(".post-item").each(function(){$(this).addClass("preview")}):$(".post-item").each(function(){$(this).removeClass("preview")}))}},created(){window.win().cb=this.winCb,this.list=window.win().postList,this.config=window.win().config,this.config.autoOpenDetail&&this.pageType==="post"&&(this.loading=!0,window.win().doc.body.style.overflow="hidden",this.show=!0),window.win().canParseV2exPage&&this.showList,$(window.win().doc).on("click","a",t=>{let{href:e,id:s}=window.parse.parseA(t.currentTarget);if(s&&this.config.clickPostItemOpenDetail){let a=this.list.findIndex(o=>o.id==s);if(a>-1){let o=this.list[a],n=`
`;return o.headerTemplate=n,this.getPostDetail(o),t.preventDefault(),!1}}}),$(window.win().doc).on("click",".toggle",t=>{let e=t.currentTarget.dataset.id,s=window.win().query(`.id_${e}`);s.classList.contains("preview")?s.classList.remove("preview"):s.classList.add("preview")}),this.initEvent()},beforeUnmount(){i.clear()},methods:{async winCb({type:t,value:e}){console.log("回调的类型",t,e),t==="list"&&(this.list=e),t==="postContent"&&(this.current=Object.assign(this.clone(window.win().initPost),this.clone(e))),t==="postReplies"&&(this.current=Object.assign(this.current,this.clone(e)),this.loading=!1)},clone(t){return window.win().clone(t)},initEvent(){i.on(l.CHANGE_COMMENT_THANK,t=>{const{id:e,type:s}=t;let a=this.current.replies.findIndex(o=>o.id===e);a>-1&&(this.current.replies[a].isThanked=s==="add",s==="add"?this.current.replies[a].thankCount++:this.current.replies[a].thankCount--)}),i.on(l.CHANGE_POST_THANK,t=>{const{id:e,type:s}=t;this.current.isThanked=s==="add",s==="add"?this.current.thankCount++:this.current.thankCount--;let a=this.list.findIndex(o=>o.id===e);a>-1&&(this.list[a].isThanked=s==="add",s==="add"?this.list[a].thankCount++:this.list[a].thankCount++)}),i.on(l.REMOVE,t=>{let e=this.current.replies.findIndex(a=>a.floor===t);e>-1&&this.current.replies.splice(e,1);let s=this.list.findIndex(a=>a.id===this.current.id);s>-1&&(this.list[s]=Object.assign(this.list[s],t))}),i.on(l.SHOW_MSG,t=>{this.msgList.push({...t,id:Date.now()})}),i.on(l.IGNORE,()=>{this.show=!1;let t=this.list.findIndex(e=>e.id===this.current.id);t>-1&&this.list.splice(t,1),this.current=this.clone(window.win().initPost)}),i.on(l.MERGE,t=>{this.current=Object.assign(this.current,t);let e=this.list.findIndex(s=>s.id===this.current.id);e>-1&&(this.list[e]=Object.assign(this.list[e],t))}),i.on(l.ADD_REPLY,t=>{this.current.replies.push(t)}),i.on(l.REFRESH_ONCE,async t=>{if(t){if(typeof t=="string"){let e=t.match(/var once = "([\d]+)";/);if(e&&e[1]){this.current.once=Number(e[1]);return}}if(typeof t=="number"){this.current.once=t;return}}window.win().fetchOnce().then(e=>{this.current.once=e})})},removeMsg(t){let e=this.msgList.findIndex(s=>s.id===t);e>-1&&this.msgList.splice(e,1)},async getPostDetail(t,e){this.show=!0;let s=window.win().url+"/t/"+t.id;window.win().doc.body.style.overflow="hidden",window.win().history.pushState({},0,t.href??s),this.current=Object.assign(this.clone(window.win().initPost),{RightbarHTML:this.current.RightbarHTML},this.clone(t)),this.current.replies.length||(this.loading=!0);let a=await window.win().fetch(s+"?p=1");if(a.status===404)return i.emit(l.SHOW_MSG,{type:"error",text:"主题未找到"}),this.loading=!1;if(a.redirected)return i.emit(l.SHOW_MSG,{type:"error",text:"没有权限"}),this.loading=!1;let o=await a.text();if(o.search("你要查看的页面需要先登录")>-1)return i.emit(l.SHOW_MSG,{type:"error",text:"你要查看的页面需要先登录"}),this.loading=!1;let d=o.match(/]*>([\s\S]+?)<\/body>/g),u=$(d[0]);if(this.current=await window.parse.getPostDetail(this.current,u,o),this.current.replies.length){let p=this.list.findIndex(c=>c.id==t.id);p>-1&&(this.list[p].replies=this.current.replies,this.list[p].nestedReplies=this.current.nestedReplies,this.list[p].once=this.current.once,this.list[p].createDate=this.current.createDate)}this.loading=!1,console.log("当前帖子",this.current)}}},Kt={key:0,class:"nav flex flex-end"},Zt=Vue.createElementVNode("span",null,"配置",-1),Yt=[Zt],qt={class:"radio-group2"},Xt={key:1,class:"my-box flex f14 open-post",style:{margin:"1rem 0 0 0",padding:"1rem"}},Jt={class:"flex"},Qt={class:"msgs"},eo={key:2,class:"setting"},to={class:"wrapper"},oo=Vue.createElementVNode("div",{class:"title"}," 脚本配置 ",-1),no=Vue.createElementVNode("div",{class:"sub-title"}," 配置自动保存到本地,下次打开依然生效 ",-1),so={class:"option"},io=Vue.createElementVNode("span",null,"列表帖子展示方式:",-1),lo={class:"radio-group2"},ro={class:"option"},ao=Vue.createElementVNode("span",null,"回复展示方式:",-1),co={class:"radio-group2"},uo={class:"option"},po=Vue.createElementVNode("span",null,"帖子界面自动打开详情弹框 :",-1),mo=Vue.createElementVNode("div",{class:"notice"}," 单独打开这种地址 https://v2ex.com/t/xxxx 时, 是否自动打开详情弹框 ",-1),ho={class:"option"},Vo=Vue.createElementVNode("span",null,"点击列表的帖子,打开详情弹框 :",-1),fo=Vue.createElementVNode("div",{class:"notice"}," 若关闭此项,点击列表的帖子时,不会打开弹框,会跳转网页 ",-1),vo={class:"option"},wo=Vue.createElementVNode("span",null,"点击两侧空白处关闭帖子详情:",-1),go=Vue.createElementVNode("div",{class:"jieshao"}," 如只想要列表预览功能,可关闭 ”点击列表的帖子,打开详情弹框“,”帖子界面自动打开详情弹框“ ",-1);function _o(t,e,s,a,o,n){const d=Vue.resolveComponent("PostDetail"),u=Vue.resolveComponent("Msg"),p=Vue.resolveComponent("Base64Tooltip");return Vue.openBlock(),Vue.createElementBlock("div",{class:Vue.normalizeClass(["app-home",[o.pageType,o.isNight?"isNight":""]])},[n.showList?(Vue.openBlock(),Vue.createElementBlock("div",Kt,[Vue.createElementVNode("div",{class:"nav-item",onClick:e[0]||(e[0]=c=>o.showConfig=!0)},Yt),Vue.createElementVNode("div",qt,[Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.viewType==="table"?"active":""]),onClick:e[1]||(e[1]=c=>o.config.viewType="table")},"表格 ",2),Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.viewType==="card"?"active":""]),onClick:e[2]||(e[2]=c=>o.config.viewType="card")},"卡片 ",2)])])):Vue.createCommentVNode("",!0),o.pageType==="post"?(Vue.openBlock(),Vue.createElementBlock("div",Xt,[Vue.createElementVNode("div",Jt,[Vue.createTextVNode(" 自动打开详情弹框 : "),Vue.createElementVNode("div",{class:Vue.normalizeClass(["switch",{active:o.config.autoOpenDetail}]),onClick:e[3]||(e[3]=c=>o.config.autoOpenDetail=!o.config.autoOpenDetail)},null,2)]),Vue.createElementVNode("div",{class:Vue.normalizeClass(["button",{loading:o.loading}]),onClick:e[4]||(e[4]=c=>o.show=!0)}," 点击显示详情弹框 ",2)])):Vue.createCommentVNode("",!0),Vue.createVNode(d,{modelValue:o.show,"onUpdate:modelValue":e[5]||(e[5]=c=>o.show=c),isNight:o.isNight,displayType:o.config.commentDisplayType,"onUpdate:displayType":e[6]||(e[6]=c=>o.config.commentDisplayType=c),closePostDetailBySpace:o.config.closePostDetailBySpace,loading:o.loading},null,8,["modelValue","isNight","displayType","closePostDetailBySpace","loading"]),Vue.createElementVNode("div",Qt,[(Vue.openBlock(!0),Vue.createElementBlock(Vue.Fragment,null,Vue.renderList(o.msgList,c=>(Vue.openBlock(),Vue.createBlock(u,{key:c.id,type:c.type,text:c.text,onClose:r=>n.removeMsg(c.id)},null,8,["type","text","onClose"]))),128))]),Vue.createVNode(p),o.showConfig?(Vue.openBlock(),Vue.createElementBlock("div",eo,[Vue.createElementVNode("div",{class:"mask",onClick:e[7]||(e[7]=c=>o.showConfig=!o.showConfig)}),Vue.createElementVNode("div",to,[oo,no,Vue.createElementVNode("div",so,[io,Vue.createElementVNode("div",lo,[Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.viewType==="table"?"active":""]),onClick:e[8]||(e[8]=c=>o.config.viewType="table")},"表格 ",2),Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.viewType==="card"?"active":""]),onClick:e[9]||(e[9]=c=>o.config.viewType="card")},"卡片 ",2)])]),Vue.createElementVNode("div",ro,[ao,Vue.createElementVNode("div",co,[Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.commentDisplayType===0?"active":""]),onClick:e[10]||(e[10]=c=>o.config.commentDisplayType=0)},"楼中楼 ",2),Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.commentDisplayType===1?"active":""]),onClick:e[11]||(e[11]=c=>o.config.commentDisplayType=1)},"感谢最多 ",2),Vue.createElementVNode("div",{class:Vue.normalizeClass(["radio",o.config.commentDisplayType===2?"active":""]),onClick:e[12]||(e[12]=c=>o.config.commentDisplayType=2)},"V2原版 ",2)])]),Vue.createElementVNode("div",uo,[po,Vue.createElementVNode("div",{class:Vue.normalizeClass(["switch",{active:o.config.autoOpenDetail}]),onClick:e[13]||(e[13]=c=>o.config.autoOpenDetail=!o.config.autoOpenDetail)},null,2)]),mo,Vue.createElementVNode("div",ho,[Vo,Vue.createElementVNode("div",{class:Vue.normalizeClass(["switch",{active:o.config.clickPostItemOpenDetail}]),onClick:e[14]||(e[14]=c=>o.config.clickPostItemOpenDetail=!o.config.clickPostItemOpenDetail)},null,2)]),fo,Vue.createElementVNode("div",vo,[wo,Vue.createElementVNode("div",{class:Vue.normalizeClass(["switch",{active:o.config.closePostDetailBySpace}]),onClick:e[15]||(e[15]=c=>o.config.closePostDetailBySpace=!o.config.closePostDetailBySpace)},null,2)]),go])])):Vue.createCommentVNode("",!0)],2)}const ko=_(Ut,[["render",_o]]);let N;window.win().isFrame?N=$("#app",window.win().doc)[0]:N=window.win().query("#app");window.win().vue&&window.win().vue.unmount();let B=Vue.createApp(ko);window.win().vue=B;window.win().appNode=N;B.config.unwrapInjectedRef=!0;B.mount(N);
//# sourceMappingURL=index-596fb18f.js.map
})
if (window.top !== window.self) {
window.win = () => window.top
window.win().url = 'https://v2ex.com'
window.win().isFrame = true
//直接使用v2的jquery,因为v2对jquery作了修改,加了一些header,缺少这些header发送请求会报403
window.$ = window.win().$
} else {
window.win = () => window
//这里必须一致。不然会报跨域
window.win().url = location.origin
window.win().isFrame = false
}
window.win().initPost = {
replies: [],
nestedReplies: [],
username: '',
title: '',
id: '',
type: 'post',
once: '',
replyCount: 0,
clickCount: 0,
thankCount: 0,
collectCount: 0,
isFavorite: false,
isIgnore: false,
isThanked: false,
isReport: false,
RightbarHTML: '',
}
window.win().doc = window.win().document
window.win().query = (v) => window.win().document.querySelector(v)
window.win().clone = (val) => JSON.parse(JSON.stringify(val))
window.win().user = {}
window.win().pageType = ''
window.win().pageData = {pageNo: 1}
window.win().config = {
autoOpenDetail: true,
clickPostItemOpenDetail: true,
// newTabOpen: true,
closePostDetailBySpace: true,//点击空白处关闭详情
viewType: 'card',
commentDisplayType: 0
}
window.win().isNight = $('.Night').length === 1
window.win().cb = null
window.win().postList = []
window.parse = {
//解析帖子内容
async parsePostContent(post = {}, body, htmlText) {
let once = htmlText.match(/var once = "([\d]+)";/)
// console.log(once)
if (once && once[1]) {
post.once = once[1]
}
post.isReport = htmlText.includes('你已对本主题进行了报告')
let topic_buttons = body.find('.topic_buttons')
if (topic_buttons.length) {
let favoriteNode = topic_buttons.find('.tb:first')
if (favoriteNode.length) {
post.isFavorite = favoriteNode[0].innerText === '取消收藏'
}
let ignoreNode = topic_buttons.find('.tb:nth-child(3)')
if (ignoreNode.length) {
post.isIgnore = ignoreNode[0].innerText === '取消忽略'
}
//
let thankNode = topic_buttons.find('#topic_thank .tb')
if (!thankNode.length) {
post.isThanked = true
}
let topic_stats = topic_buttons.find('.topic_stats')
//topic_stats = $(`9569 次点击 ∙ 28 人收藏 ∙ 1 人感谢
`)
//收藏数、感谢数
if (topic_stats.length) {
let text = topic_stats[0].innerText
let reg1 = text.matchAll(/([\d]+)[\s]*人收藏/g)
let collectCountReg = [...reg1]
if (collectCountReg.length) {
post.collectCount = Number(collectCountReg[0][1])
}
// console.log([...collectCountReg])
let reg2 = text.matchAll(/([\d]+)[\s]*人感谢/g)
let thankCountReg = [...reg2]
if (thankCountReg.length) {
post.thankCount = Number(thankCountReg[0][1])
}
let reg3 = text.matchAll(/([\d]+)[\s]*次点击/g)
let clickCountReg = [...reg3]
if (clickCountReg.length) {
post.clickCount = Number(clickCountReg[0][1])
}
// console.log([...thankCountReg])
}
}
let Rightbar = body.find('#Rightbar')
if (Rightbar.length) {
post.RightbarHTML = Rightbar[0].innerHTML
//让修改背景的样式失效
post.RightbarHTML = post.RightbarHTML.replace('type="text/css"', 'type="text/css1"')
}
// console.log('基本信息', post)
body.find('.topic_buttons').remove()
let header = body.find('#Main .box').first()
post.headerTemplate = `${header.html()} `
return post
},
//获取帖子所有回复
async getPostAllReplies(post = {}, body, htmlText, pageNo = 1) {
if (body.find('#no-comments-yet').length) {
return post
}
let box = body.find('#Main > .box')[1]
let cells = box.querySelectorAll('.cell')
post.fr = cells[0].querySelector('.cell .fr').innerHTML
cells = Array.from(cells)
//获取创建时间
let snow = cells[0].querySelector('.snow')
post.createDate = snow?.nextSibling.nodeValue.trim() || ''
let repliesMap = []
if (cells[1].id) {
repliesMap.push({i: pageNo, replyList: this.parsePageReplies(cells.slice(1))})
let replies = this.getAllReply(repliesMap)
post.replies = replies
post.replyCount = replies.length
post.allReplyUsers = Array.from(new Set(replies.map(v => v.username)))
let nestedList = this.createNestedList(replies, post.allReplyUsers)
if (nestedList) post.nestedReplies = nestedList
return post
} else {
let promiseList = []
// console.log(this.current.repliesMap)
return new Promise((resolve, reject) => {
repliesMap.push({i: pageNo, replyList: this.parsePageReplies(cells.slice(2, cells.length - 1))})
let pages = cells[1].querySelectorAll('a')
pages = Array.from(pages)
// console.log(pages)
let url = window.win().url + '/t/' + post.id
for (let i = 0; i < pages.length; i++) {
let currentPageNo = Number(pages[i].innerText)
if (currentPageNo == pageNo) continue
promiseList.push(this.fetchPostOtherPageReplies(url + '?p=' + currentPageNo, currentPageNo))
}
Promise.allSettled(promiseList).then(
(results) => {
results.filter((result) => result.status === "fulfilled").map(v => repliesMap.push(v.value))
let replies = this.getAllReply(repliesMap)
post.replies = replies
post.replyCount = replies.length
post.allReplyUsers = Array.from(new Set(replies.map(v => v.username)))
let nestedList = this.createNestedList(replies, post.allReplyUsers)
if (nestedList) post.nestedReplies = nestedList
resolve(post)
}
);
})
}
},
//请求帖子其他页的回复
fetchPostOtherPageReplies(url, pageNo) {
return new Promise(resolve => {
$.get(url).then(res => {
let s = res.match(/]*>([\s\S]+?)<\/body>/g)
let box = $(s[0]).find('#Main .box')[1]
let cells = box.querySelectorAll('.cell')
cells = Array.from(cells)
resolve({i: pageNo, replyList: this.parsePageReplies(cells.slice(2, cells.length - 1))})
})
})
},
//解析页面的回复
parsePageReplies(nodes) {
let replyList = []
nodes.forEach((node, index) => {
if (!node.id) return
let item = {
thankCount: 0,
isThanked: false,
isOp: false,
id: node.id.replace('r_', '')
}
let reply_content = node.querySelector('.reply_content')
// console.log('reply_content',reply_content)
item.reply_content = reply_content.innerHTML
item.reply_text = reply_content.innerText
let {users, floor} = this.parseReplyContent(item.reply_content)
item.replyUsers = users
item.replyFloor = floor
if (index === 5) {
// console.log(item)
// console.log(reply_content.innerText)
// console.log(reply_content.innerHTML)
}
let ago = node.querySelector('.ago')
item.date = ago.innerText
let userNode = node.querySelector('strong a')
item.username = userNode.innerText
let avatar = node.querySelector('td img')
item.avatar = avatar.src
let no = node.querySelector('.no')
item.floor = Number(no.innerText)
let thank_area = node.querySelector('.thank_area')
if (thank_area) {
item.isThanked = thank_area.classList.contains('thanked')
}
let small = node.querySelector('.small')
if (small) {
item.thankCount = Number(small.innerText)
}
let op = node.querySelector('.op')
if (op) {
item.isOp = true
}
// console.log('item', item)
replyList.push(item)
})
return replyList
},
//解析回复内容,解析出@用户,回复楼层。用于后续生成嵌套楼层
parseReplyContent(str) {
if (!str) return
let users = []
let getUsername = (userStr) => {
let endIndex = userStr.indexOf('">')
if (endIndex > -1) {
let user = userStr.substring(0, endIndex)
if (!users.find(i => i === user)) {
users.push(user)
}
}
}
// str = `@ #4 @Eiden1 @Eiden21 #11 这也是执行阶段,所谓的安装也是程序业务的 setup 。
windows 、Android 并没有系统级的 CD-KEY 。`
let floorReg = /@/g
let hasFloor = str.matchAll(floorReg)
let res = [...hasFloor]
// console.log('总匹配', res)
let floor = -1
if (res.length) {
floor = Number(res[0][1])
}
let has = str.matchAll(userReg)
let res2 = [...has]
// console.log('总匹配', res2)
if (res2.length > 1) {
res2.map(item => {
getUsername(item[1])
})
}
if (res2.length === 1) {
getUsername(res2[0][1])
}
// console.log('用户', users)
// console.log('楼层', floor)
return {users, floor}
},
//获取帖子详情
async getPostDetail(post = {}, body, htmlText, pageNo = 1) {
post = await this.parsePostContent(post, body, htmlText)
return await this.getPostAllReplies(post, body, htmlText, pageNo)
},
getAllReply(repliesMap = []) {
return repliesMap.sort((a, b) => a.i - b.i).reduce((pre, i) => {
pre = pre.concat(i.replyList)
return pre
}, [])
},
//生成嵌套回复
createNestedList(allList = []) {
if (!allList.length) return []
if ((Date.now() - window.win().lastCallDate) < 1000) {
// console.log('短时间内,重复调用,因为监听了replies,所以打开时会触发两次。第二次不管他')
return false
}
// console.log('cal-createNestedList', Date.now())
let list = JSON.parse(JSON.stringify(allList))
let nestedList = []
list.map((item, index) => {
let startList = list.slice(0, index)
//用于918489这种情况,@不存在的人
let startReplyUsers = Array.from(new Set(startList.map(v => v.username)))
let endList = list.slice(index + 1)
if (index === 0) {
item.level = 0
nestedList.push(this.findChildren(item, endList, list))
} else {
if (!item.isUse) {
//是否是一级回复
let isOneLevelReply = false
// if (item.replyFloor === -1) {
// isOneLevelReply = true
// }
if (item.replyUsers.length) {
if (item.replyUsers.length > 1) {
isOneLevelReply = true
} else {
isOneLevelReply = !startReplyUsers.find(v => v === item.replyUsers[0]);
}
} else {
isOneLevelReply = true
}
if (isOneLevelReply) {
item.level === 0
nestedList.push(this.findChildren(item, endList, list))
}
}
}
})
// console.log('replies长度', allList)
// console.log('nestedList长度', nestedList)
window.win().lastCallDate = Date.now()
return nestedList
},
//查找子回复
findChildren(item, endList, all) {
const fn = (child, endList2, parent) => {
child.level = parent.level + 1
let rIndex = all.findIndex(v => v.floor === child.floor)
if (rIndex > -1) {
all[rIndex].isUse = true
}
parent.children.push(this.findChildren(child, endList2, all))
}
// console.log('endList', endList)
item.children = []
// if (item.floor ==8)debugger
for (let i = 0; i < endList.length; i++) {
let currentItem = endList[i]
//如果已被使用,直接跳过
if (currentItem.isUse) continue
let endList2 = endList.slice(i + 1)
if (currentItem.replyUsers.length === 1) {
//如果是下一条是同一人的回复,那么跳出循环
if (currentItem.username === item.username) {
//自己回复自己的特殊情况
if (currentItem.replyUsers[0] === item.username) {
fn(currentItem, endList2, item)
}
return item
} else {
if (currentItem.replyUsers[0] === item.username) {
fn(currentItem, endList2, item)
}
}
} else {
//下一条是同一人的回复,并且均未@人。直接跳过
if (currentItem.username === item.username) return item
}
}
return item
},
findChildren2(item, endList, all) {
const fn = (child, endList2, parent) => {
child.level = parent.level + 1
let rIndex = all.findIndex(v => v.floor === child.floor)
if (rIndex > -1) {
all[rIndex].isUse = true
}
parent.children.push(this.findChildren(child, endList2, all))
}
// console.log('endList', endList)
item.children = []
// if (item.floor ==8)debugger
for (let i = 0; i < endList.length; i++) {
let currentItem = endList[i]
//如果已被使用,直接跳过
if (currentItem.isUse) continue
let endList2 = endList.slice(i + 1)
//注: 2022-10-26,回复被忽略、用户被ban,会占据原来的楼层,以楼层号来判断会出问题
// if (currentItem.replyFloor !== -1) {
// if (currentItem.replyFloor === item.floor) {
// fn(currentItem, endList2, item)
// }
// }
if (currentItem.replyUsers.length) {
// //找出自己最近一条正常回复,以那条为搜索终点
// let rIndex = endList.findIndex(v => v.username === item.username && (currentItem.replyUsers.length === 0 && currentItem.replyFloor === -1))
// if (rIndex > -1) {
// endList2 = endList.slice(i + 1, rIndex)
// }
//如果是下一条是同一人的回复,那么跳出循环。children从下一条开始找
if (currentItem.username === item.username) {
//自己回复自己的特殊情况
if (currentItem.replyUsers[0] === item.username) {
fn(currentItem, endList2, item)
continue
}
//有种特殊情况,就是自己连着评论了两条。然后后面的人以#号的方式 回复了自己第一条。
//如果检测到下条是自己的回复,直接略过的话就会丢失第一条的所有楼中楼回复
//具体如下
// 自己:1楼,在for的时候,如果碰到 2楼不能直接跳过,直接略过的话就会丢失第一条的所有楼中楼回复,即3楼。所以应该以3楼为起点
//进行楼中楼查找,查找完了再跳过
// 自己:2楼
// 别人:指定回复1楼
// endList2.map((v, vi) => {
// if (v.replyFloor === item.floor) {
// fn(v, endList.slice(vi + 1), item)
// }
// })
break
} else {
if (currentItem.replyUsers[0] === item.username) {
fn(currentItem, endList2, item)
}
}
} else {
//下一条是同一人的回复,并且均未@人。直接跳过
if (currentItem.username === item.username) return item
}
}
return item
},
//解析其他页面
parseOtherPage(html, pageType) {
let bodyText = html.match(/]*>([\s\S]+?)<\/body>/g)
let body = $(bodyText[0])
if (pageType === 'recent') {
let list = body.find('.item')
let res = this.parsePagePostList(Array.from(list))
let page = body.find('.cell:last').html()
return {...res, page}
}
if (pageType === 'nodePage') {
let topics = body.find('#TopicsNode')
let list = topics.children()
let res = this.parsePagePostList(Array.from(list))
let page = topics.next().html()
return {...res, page}
}
},
//解析页面帖子列表
parsePagePostList(list, box) {
list.forEach(itemDom => {
let item = window.win().clone(window.win().initPost)
let item_title = itemDom.querySelector('.item_title a')
let {href, id} = window.parse.parseA(item_title)
item.id = id
item.href = href
item.url = window.win().location.origin + '/api/topics/show.json?id=' + item.id
itemDom.classList.add('post-item')
itemDom.classList.add(`id_${id}`)
window.win().postList.push(item)
})
Promise.allSettled(window.win().postList.map(item => $.get(item.url))).then(
res => {
let ok = res.filter((r) => r.status === "fulfilled").map(v => v.value[0])
// let fail = res.filter((r) => r.status === "rejected")
box.style.boxShadow = 'unset'
box.style.background = 'unset'
if (window.win().config.viewType === 'card') {
list.forEach(itemDom => {
itemDom.classList.add('preview')
})
}
ok.map(postItem => {
let itemDom = box.querySelector(`.id_${postItem.id}`)
//添加切换按钮
let td = itemDom.querySelector('td:nth-child(4)')
td.style.position = 'relative'
let toggle = document.createElement('div')
toggle.dataset['id'] = postItem.id
toggle.classList.add('toggle')
toggle.innerText = '点击展开/收起'
td.append(toggle)
let index = window.win().postList.findIndex(v => v.id == postItem.id)
if (index > -1) {
let obj = window.win().postList[index]
window.win().postList[index] = Object.assign({}, obj, postItem)
if (postItem.content_rendered) {
let a = document.createElement('a')
a.href = obj.href
a.classList.add('post-content')
let div = document.createElement('div')
div.innerHTML = postItem.content_rendered
a.append(div)
// console.log(div.clientHeight)
itemDom.append(a)
}
}
})
cbChecker({type: 'list', value: window.win().postList})
}
)
},
parseA(a) {
let href = a.href
let id
if (href.includes('/t/')) {
if (href.includes('#')) {
id = href.substring(href.indexOf('/t/') + 3, href.indexOf('#'))
} else {
id = href.substring(href.indexOf('/t/') + 3,)
}
}
return {href, id}
}
}
let style2 = `
html, body {
font-size: 62.5%;
}
.post-item {
background: white;
}
.post-item > .post-content {
height: 0;
margin-top: 0;
}
.post-item:hover .toggle {
display: flex;
}
.toggle {
position: absolute;
right: 0;
top: 0.5rem;
width: 8rem;
height: 100%;
display: flex;
justify-content: flex-end;
align-items: flex-end;
cursor: pointer;
font-size: 1.2rem;
color: #4d5256;
display: none;
}
.preview {
margin: 1rem 0;
border: 1px solid #e2e2e2;
border-radius: 0.4rem;
cursor: pointer;
}
.preview:hover {
border: 1px solid #968b8b;
}
.preview > .post-content {
height: unset !important;
margin-top: 0.5rem !important;
}
.post-content {
margin-top: 0.5rem;
display: block;
max-height: 20rem;
overflow: hidden;
text-decoration: unset !important;
line-break: anywhere;
}
.post-content:link {
color: black;
}
.post-content:visited {
color: #afb9c1 !important;
}
.Night .post-item {
background: #18222d !important;
}
.Night .preview {
border: 1px solid #3b536e;
}
.Night .preview > .post-content:link {
color: #d1d5d9;
}
.Night .preview > .post-content:visited {
color: #393f4e !important;
}
`
let addStyle2 = document.createElement("style");
addStyle2.rel = "stylesheet";
addStyle2.type = "text/css";
addStyle2.innerHTML = style2
$(window.win().doc.head).append(addStyle2)
async function sleep(time) {
return new Promise(resolve => {
// console.log('等待vue加载完成,第' + count + '次', Date.now())
setTimeout(resolve, time)
})
}
async function cbChecker(val, count = 0) {
if (window.win().cb) {
window.win().cb(val)
} else {
while ((!window.win().cb) && count < 20) {
await sleep(500)
count++
}
window.win().cb && window.win().cb(val)
}
}
// console.log('type===', window.win().pageType, 'pageData===', window.win().pageData)
// console.log('postList', window.win().postList)
// console.log('init', window.win().init)\
window.win().canParseV2exPage = true
if (window.win().canParseV2exPage) {
let location2 = window.win().location
if (location2.pathname === '/') {
window.win().pageType = 'home'
} else if (location2.href.match(/.com\/?tab=/)) {
window.win().pageType = 'home'
} else if (location2.href.match(/.com\/go\//)) {
if (!location2.href.includes('/links')) {
window.win().pageType = 'nodePage'
}
} else if (location2.href.match(/.com\/recent/)) {
window.win().pageType = 'recent'
} else {
let r = location2.href.match(/.com\/t\/([\d]+)/)
if (r) {
window.win().pageType = 'post'
window.win().pageData.id = r[1]
if (location2.search) {
let pr = location2.href.match(/\?p=([\d]+)/)
if (pr) window.win().pageData.pageNo = Number(pr[1])
}
}
}
let top2 = window.win().query('.tools .top:nth-child(2)').text
if (top2 !== '注册') {
window.win().user = {
username: top2,
avatar: $('#Rightbar .box .avatar', window.win().doc).attr('src')
}
}
//获取默认配置
let configStr = window.win().localStorage.getItem('v2ex-config')
if (configStr) {
let configObj = JSON.parse(configStr)
configObj = configObj[window.win().user.username ?? 'default']
if (configObj) {
window.win().config = Object.assign(window.win().config, configObj)
}
}
let $section = document.createElement('section')
$section.id = 'app'
let box
let list
switch (window.win().pageType) {
case 'nodePage':
box = window.win().doc.querySelectorAll('#Wrapper #Main .box')
let topics = box[1].querySelector('#TopicsNode')
list = topics.querySelectorAll('.cell')
list[0].before($section)
window.parse.parsePagePostList(list, box[1])
break
case 'recent':
case 'home':
box = window.win().query('#Wrapper #Main .box')
list = box.querySelectorAll('.item')
list[0].before($section)
window.parse.parsePagePostList(list, box)
break
case 'post':
box = window.win().query('#Wrapper #Main .box')
box.after($section)
window.parse.parsePostContent(
{id: window.win().pageData.id},
$(window.win().doc.body),
window.win().doc.documentElement.outerHTML
).then(async res => {
// console.log('详情页-基本信息解析完成', new Date())
window.win().pageData.post = res
await cbChecker({type: 'postContent', value: res}, 0)
})
window.parse.getPostAllReplies(
{id: window.win().pageData.id},
$(window.win().doc.body),
window.win().doc.documentElement.outerHTML,
window.win().pageData.pageNo
).then(async res => {
// console.log('详情页-回复解析完成', new Date())
window.win().pageData.post = Object.assign(window.win().pageData.post, res)
await cbChecker({type: 'postReplies', value: res}, 0)
})
break
default:
console.error('未知页面')
break
}
}
})();