- // ==UserScript==
- // @name Enhancement Userscript for LIHKG
- // @version 0.6.3
- // @description An Enhancement Userscript for LIHKG
- // @include /https?\:\/\/lihkg\.com/
- // @icon https://www.google.com/s2/favicons?domain=lihkg.com
- // @grant GM_addStyle
- // @license MIT
- // @namespace https://gf.qytechs.cn/users/371179
- // ==/UserScript==
- (function() {
- 'use strict';
-
- GM_addStyle([
-
- `
- /*
- html.skip-drag-upload .EGBBkGyEbfIEpHMLTW84H{
- display:none !important;
- }
- */
-
- /* main textarea */
- ._2ME7dqW8n0YSe687nDnGvI ._3ILx6bqxXMKzVd1P5DRS9F{
- padding-bottom: 40px;
- }
-
-
- /* div 放開圖片即可上載*/
- .EGBBkGyEbfIEpHMLTW84H{
- opacity: 1 !important;
- pointer-events: all !important;
- left: 5%;
- right: 5%;
- top: calc(100% - 35px);
- bottom: 0;
- width: auto;
- height: auto;
- }
-
- /* div Preview ~ div + div 放開圖片即可上載*/
- ._1TdM0E7HcWQwmxXpfa6yJg ~ ._2ME7dqW8n0YSe687nDnGvI .EGBBkGyEbfIEpHMLTW84H{
- visibility: collapse;
- }
-
- `,
-
- // css fix for thread posts positioning
- `
- body ._21IQKhlBjN2jlHS_TVgI3l:after {left:0.4rem}
- body ._21IQKhlBjN2jlHS_TVgI3l .vv9keWAXpwoonDah6rSIU ._3D2lzCKDMcdgEkexZrTSUh{margin-left: -6px;width: 16px;}
- `,
-
- // css fix for like and dislike due to js hack of like count and dislike count (reply posts)
- `
- body label[for*="-dislike-like"] {display:inline-block !important;}
- body label[for*="-like-like"] {display:inline-block !important;}
- body ._3ExaynSI6tUp5h1U50MHtI ._3imUf8qB9LmLpk_t5PjDm4>div:first-child+div:last-child {margin-left:-6px;}
- `,
-
- // css fix for like and dislike due to js hack of like count and dislike count (main thread)
- // empty full space char for maintaining padding when the count is not yet shown
- `
- span[data-tip="正評"]:not([data-score])::after{
- content: " ";
- font-size: .6rem;
- font-weight: 400;
- margin-top: .3rem;
- }
- span[data-tip="負評"]:not([data-score])::after{
- content: " ";
- font-size: .6rem;
- font-weight: 400;
- margin-top: .3rem;
- }
- span[data-tip="正評"],span[data-tip="負評"]{
- padding-top:0px !important;
- }
- `,
-
- // kiwi browser css fix
- `
- @supports not (padding-bottom: env(safe-area-inset-bottom)){
- ._3dwGLtjqTgI2gc9wpc7FuT {
- padding: 1rem .6rem calc(1rem + 0px) calc(.7rem + 0px);
- }
- }
- `,
-
- // po reply - userselect for icons
- `
- html body ._1Ku9qL4qhkBDwAgVLYcQdi[class], html body ._1Ku9qL4qhkBDwAgVLYcQdi[class]:hover {
- user-select:none !important;
- }
- `
- ].map(x => x.trim()).join('\n'))
-
-
- let isNumCheck = function(n) {
- return n > 0 || n < 0 || n === 0
- }
- let postDetails = {}
- let threadDetails = {}
- let pendingRefreshThread = false;
-
- let testBlockElm = function(elm) {
- if (elm && elm.nodeType == 1) {
- switch (elm.tagName) {
- case 'DIV':
- case 'P':
- case 'BLOCKQUOTE':
- return true;
-
- default:
- return false;
-
- }
-
- }
- }
-
-
- document.cssAll = function() {
- return [...document.querySelectorAll.apply(this, arguments)]
- }
-
- function urlConvert(url) {
- let src = url.replace(/\w+\:\/\//, '')
- let replacements = [...src.matchAll(/[\w\.]+/g)].filter((t) => /\./.test(t))
- if (replacements.length > 1) {
- replacements.length--;
-
- }
- replacements.forEach((s) => {
- src = src.replace(s, '')
- })
-
- src = src.replace(/\/+/g, '/')
-
- return src;
-
- }
-
- let emoji = {};
- setTimeout(function() {
- console.log(emoji)
- }, 1500)
-
- setInterval(() => {
-
- document.cssAll('img[src*="lihkg.com"][alt]:not([title])').forEach(function(imgElm) {
- let src = imgElm.getAttribute('src');
- let erc = urlConvert(src)
- let imgAlt = imgElm.getAttribute('alt') || "";
- if (/^[\x20-\x7E]+$/.test(imgAlt) && /\W/.test(imgAlt)) {
- emoji[erc] = imgAlt.trim()
- }
-
- imgElm.setAttribute('title', imgAlt)
-
- })
-
-
- document.cssAll('a[href*="profile/"]:not([href*="//"]):not([title])').forEach(function(aElm) {
- aElm.setAttribute('title', aElm.getAttribute('href'))
- })
-
- document.cssAll('[data-ic~="hkgmoji"]:not([title])>img[src*="lihkg.com"]:not([alt])').forEach(function(imgElm) {
- let src = imgElm.getAttribute('src');
- let erc = urlConvert(src)
- let text = emoji[erc] ? emoji[erc] : "[img]" + erc + "[/img]"
- imgElm.parentNode.setAttribute('title', text)
- imgElm.setAttribute('alt', text)
-
-
- })
-
-
-
- document.cssAll('img[src]:not([alt]),img[src][alt=""]').forEach((el) => {
-
- if (el.getAttribute('alt') || el.getAttribute('title')) return;
-
- let text = '';
- if (el.tagName.toLowerCase() == 'img' && el.getAttribute('data-original')) {
- text = '[img]' + el.getAttribute('data-original') + '[/img]';
- } else if (el.tagName.toLowerCase() == 'img' && el.getAttribute('src')) {
- text = '[img]' + el.getAttribute('src') + '[/img]';
- }
- if (text) el.setAttribute('alt', text)
- if (text) el.setAttribute('title', text)
-
- })
-
-
-
-
- document.cssAll('[data-post-id]:not([hacked])').forEach((el) => {
-
- el.setAttribute('hacked', 'true');
- let post_id = el.getAttribute('data-post-id');
- if (!post_id) return;
-
- //console.log(post_id, postDetails)
- let post_detail = postDetails[post_id]
- if (post_detail) {
- // console.log(55,post_detail)
-
- }
-
- })
-
-
-
- }, 33)
-
-
-
- function refreshingThreadEvent(thread_id) {
-
-
- console.log("refreshingThreadEvent", threadDetails[thread_id])
- if (thread_id && threadDetails[thread_id]) {
-
-
- document.cssAll('span[data-tip="正評"]').forEach((elm) => {
-
- elm.setAttribute('data-score', threadDetails[thread_id]["like_count"]);
- elm.style.paddingTop = '0px';
- })
-
-
- document.cssAll('span[data-tip="負評"]').forEach((elm) => {
-
- elm.setAttribute('data-score', threadDetails[thread_id]["dislike_count"]);
- elm.style.paddingTop = '0px';
- })
-
-
-
- }
-
-
- }
-
-
- let cid_refreshingThread = 0;
-
- function refreshingThreadRunning() {
-
- if (!cid_refreshingThread) return;
-
-
- let titlespan = document.cssAll('a[href^="/category/"]+span');
- if (titlespan.length == 1) {
- let titlespanElm = titlespan[0]
-
- if (!titlespanElm.querySelector('noscript')) {
- titlespanElm.appendChild(document.createElement('noscript'))
-
-
- if (pendingRefreshThread) {
-
- let thread_id = pendingRefreshThread === true ? (/thread\/(\d+)\//.exec(location + "") || [null, null])[1] : pendingRefreshThread
-
- pendingRefreshThread = false;
- clearInterval(cid_refreshingThread);
- cid_refreshingThread = 0;
- refreshingThreadEvent(thread_id)
-
-
- }
-
-
- }
- }
-
- }
-
-
-
-
- let makePlain = false;
-
-
- document.addEventListener("dragstart", function(evt) {
- console.log(evt.target)
- if(!evt || !evt.target) return;
-
- let type = 0
- if(evt.target.nodeType!==1 && evt.target.parentElement/* && evt.target.parentElement.closest('[data-post-id]')*/){
- type=1;
- }
-
- if(evt.target.nodeType ===1){
- if(!evt.target.matches('img[alt][src][title]'))return;
- let alt = evt.target.getAttribute('alt')+'';
- if(!alt)return;
- if(/https?\:\/\//.test(alt))return;
- if(/^[a-zA-Z0-9]+$/.test(alt))return;
- if(/[\u0100-\uFFFF]/.test(alt))return;
- type = 2;
-
- console.log(alt)
-
- evt.dataTransfer.setData('text/plain', alt);
-
- evt.stopPropagation()
- evt.stopImmediatePropagation();
-
- }
- if(type>0){
- evt.dropEffect='copy';
- evt.effectAllowed = "all";
-
- document.documentElement.classList.add('skip-drag-upload')
- }
- }, true);
-
-
-
- function makeRangeFromXY(evt){
-
- let range=null;
- if (document.caretRangeFromPoint) { // Chrome
- range=document.caretRangeFromPoint(evt.clientX,evt.clientY);
- }
- else if (evt.rangeParent) { // Firefox
- range=document.createRange(); range.setStart(evt.rangeParent,evt.rangeOffset);
- }
- return range;
- }
- document.addEventListener("drop", function(evt) {
-
- if(!evt || !evt.target)return;
-
- let node = evt.target;
-
- if(node.nodeType!==1 && node.parentNode && node.parentNode.nodeType===1) node= node.parentNode;
-
- if(node.nodeType===1 &&node.closest('div.ProseMirror[contenteditable]')){
- evt.preventDefault();
- let range = makeRangeFromXY(evt)
- var sel = window.getSelection();
- sel.removeAllRanges(); sel.addRange(range);
-
- let p = sel.anchorNode
- while(p&&p.parentNode){
- if(p.nodeType===1&&p.matches('div.ProseMirror[contenteditable]')){
- p.focus();
- p.classList.add('ProseMirror-focused');
- break;
- }
- p=p.parentNode;
- }
-
- range.collapse(true);
-
- let text = evt.dataTransfer.getData('text/plain')
- document.execCommand('insertHTML',false,text);
-
- //sel.removeAllRanges();
-
- let mRange = window.getSelection().getRangeAt(0);
-
- mRange.setStart(mRange.endContainer,mRange.endOffset-text.length);
- mRange.setEnd(mRange.endContainer,mRange.endOffset);
-
- // nRange.collapse(true);
- // sel.addRange(nRange)
-
-
- }
-
- document.documentElement.classList.remove('skip-drag-upload')
-
- },true)
-
-
- document.addEventListener("dragend", function(evt) {
-
- document.documentElement.classList.remove('skip-drag-upload')
-
- let p = document.querySelector('div.EGBBkGyEbfIEpHMLTW84H[style]')
- if(p){
- p.style.opacity='0';
- p.style.pointerEvents='none';
- }
-
- },true)
-
- /*
- document.addEventListener("dragover", function(evt) {
- if(!evt || !evt.target)return;
- try{
-
- if(!evt.target.matches('[contenteditable], textarea, div.EGBBkGyEbfIEpHMLTW84H, div._1xaNo-2jhq5KooKoBBRKwe '))return;
- }catch(e){return;}
- console.log(evt)
- evt.stopPropagation();
- }, true);
- */
-
-
- let injection = function() {
-
-
- function extractRawURL(thumbnailURL) {
-
- let u = [...thumbnailURL.matchAll(/[\?\&]\w+\=([\x21-\x25\x27-\x3E\x40-\x7E]+)/g)].map(d => d[1])
- if (u.length) {
- let uMaxT = Math.max(...u.map(t => t.length))
- let u0 = u.filter(t => t.length == uMaxT)[0]
-
- if (u0) {
-
- let v0 = null
- try {
- v0 = decodeURIComponent(u0)
- } catch (e) {}
- //console.log(v0,u0)
- if (v0) {
- return v0
- }
-
- }
- }
- return null
- }
-
- if (!JSON._parse && JSON.parse) {
- JSON._parse = JSON.parse
- JSON.parse = function(text, r) {
- /*
- if (text && typeof text == "string" && text.indexOf('display_vote') > 0) {
- text = text.replace(/([\'\"])display_vote[\'\"]\s*:\s*false/gi, '$1display_vote$1:true')
- }
- */
- let res = JSON._parse.apply(this, arguments)
-
- let contentFix = (resObj) => {
- if(!resObj || typeof resObj!='object')return;
- for (let k in resObj) {
- if (typeof resObj[k] == 'object') contentFix(resObj[k]);
- else if (k=='display_vote' && resObj[k]===false){
- resObj[k]=true;
- }
- else if (k == 'msg' && typeof resObj[k] == 'string') {
-
- let msg = resObj[k];
- let replace = false;
- let bmsg=msg
- msg = msg.replace(/(\<img\s+src\=\")(https?\:\/\/i\.lih\.kg\/thumbnail\?[^\"]+)(\"[^\>]+\>)/g, function(s, a, b, c) {
-
- let v0 = extractRawURL(b)
- if (v0) {
- replace = true;
- return s.replace(b,v0).replace(b,v0).replace(b,v0)
-
- /*
- console.log(v0, b)
-
- let v1 = '<img src="' + v0 + '" data-thumbnail-src="' + b + '" />';
-
- return v1
- */
- }
-
- return a + b + c
-
- })
-
- msg = msg.replace(
-
- /<a\s+href=\"(https\:\/\/i\.lih\.kg\/thumbnail\?u=[^\?\s\x00-\x20\x7F-\xFF\"]+)\"[^<>]+>\1<\/a>/g,
- function(_, a) {
-
- let b = extractRawURL(a)
- if (b) {
- replace = true
- return _.replace(a,b).replace(a,b)
- /*
- let a01 = encodeURIComponent(a)
- let a02 = a01.replace(/\%26amp\%3B/gi, '%26')
- let b01 = encodeURIComponent(b)
- let c = _.replace(a, b).replace(a, b).replace(a02, b01).replace(a01, b01)
-
- return c*/
- }
- return _
- })
-
-
- if (replace) {
- console.log(333,bmsg,msg)
- resObj[k] = msg;
-
- }
- }
- }
- }
-
- contentFix(res)
-
- return res;
- }
- }
-
- let api_callback = "uleccyqjstui"
-
- ;
- ((xmlhr, xmlhr_pt) => {
- if (!xmlhr_pt._open) {
- xmlhr_pt._open = xmlhr_pt.open;
-
-
- xmlhr_pt.open = function() {
- // console.log('xmlhr_open', arguments)
- if (/https?\:\/\/[\x20-2E\x30-5B\x5D-\x7E]*lihkg\.com\/[\x20-\x7E]*api[\x20-\x7E]+/.test(arguments[1])) {
- this._url = arguments[1];
-
- console.log('_url', this._url)
- }
- this._open.apply(this, arguments)
- }
- }
-
-
-
- if (!xmlhr_pt._send) {
- xmlhr_pt._send = xmlhr_pt.send;
-
-
- xmlhr_pt.send = function() {
- if (this._url) {
- this.addEventListener('load', function() {
- let resText = this.responseText;
- let jsonObj = null;
- if (resText && typeof resText == 'string') {
- try {
- jsonObj = JSON.parse(resText);
- } catch (e) {}
- }
-
- if (jsonObj) {
- //like_count
-
- let code_num = 0;
-
- if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.item_data && jsonObj.response.item_data.length >= 1 && jsonObj.response.item_data[0]["post_id"]) {
- code_num |= 16;
- }
- if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.thread_id) {
- code_num |= 8;
- }
- // console.log('code', code_num);
- let event = new CustomEvent(api_callback, {
- detail: {
- code: code_num,
- responseJSON: jsonObj
- }
- });
- document.dispatchEvent(event);
-
-
-
- //console.log(jsonObj)
- }
-
- })
- }
- // console.log('xmlhr_send', arguments)
- this._send.apply(this, arguments)
- }
- }
-
-
- })(XMLHttpRequest, XMLHttpRequest.prototype)
-
- }
-
- let jsscript = document.createElement('script');
- jsscript.type = 'text/javascript';
- jsscript.innerHTML = '(' + injection + ')()';
- document.documentElement.appendChild(jsscript)
-
- let api_callback = "uleccyqjstui"
- //data-post-id="5226a9cb7b395fbc182d183a6ee9b35c8adfd2fe"
- document.addEventListener(api_callback, function(e) {
- if (!e || !e.detail) return;
- console.log("API_CALLBACK", e.detail)
- let jsonObj;
- let code_num = e.detail.code
- switch (true) {
-
- case (code_num & 8) == 8: //main thread
-
- case (code_num & 16) == 16: //posts
-
-
- jsonObj = e.detail.responseJSON;
-
-
- if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.item_data && jsonObj.response.item_data.length >= 1 && jsonObj.response.item_data[0]["post_id"]) {
- let reply_post_fx = (reply_item) => {
- if ('dislike_count' in reply_item && 'like_count' in reply_item && reply_item["post_id"]) {
-
- let like_count = +reply_item['like_count']
- let dislike_count = +reply_item['dislike_count']
- let post_id = reply_item['post_id']
-
- if (isNumCheck(like_count) && isNumCheck(dislike_count) && post_id) {
- postDetails[post_id] = {
- 'like_count': like_count,
- 'dislike_count': dislike_count
- }
- }
-
- }
- };
- jsonObj.response.item_data.forEach(reply_post_fx)
- if (jsonObj.response.pinned_post && jsonObj.response.pinned_post["post_id"]) reply_post_fx(jsonObj.response.pinned_post)
-
- }
-
-
-
- if (jsonObj.success == 1 && jsonObj.response && jsonObj.response.thread_id) {
- let thread_fx = (thread_item) => {
- if ('like_count' in thread_item && 'dislike_count' in thread_item && thread_item["thread_id"]) {
-
- let like_count = +thread_item['like_count']
- let dislike_count = +thread_item['dislike_count']
- let thread_id = thread_item['thread_id']
-
- if (isNumCheck(like_count) && isNumCheck(dislike_count) && thread_id) {
- threadDetails[thread_id] = {
- 'like_count': like_count,
- 'dislike_count': dislike_count
- }
- pendingRefreshThread = thread_id;
- if (!cid_refreshingThread) cid_refreshingThread = setInterval(refreshingThreadRunning, 1);
- }
-
- }
- };
- thread_fx(jsonObj.response)
- //console.log(99, threadDetails)
-
- }
-
- //console.log(jsonObj)
- break;
-
-
- default:
- }
-
- });
-
- const makePopup=(obj)=>{
- const {header, content1,content2, placeholder, remarks, btn1, btn2, closeFn, btn1Fn, btn2Fn}=obj;
-
- let mcontent2=content2?`<br>${content2}<br>`:'';
-
- let df = document.createElement('div');
-
-
- df.innerHTML=
- `
- <div class="_34dVbr5A8khk2N65H9Nl-j ">
- <div class="_27su4Zj_qATokwVdWIbEWB ">
- <div class="_1nqRVNQ2PyO3vnAwZIISAJ ">
- <div class="_2b5VMoBy8yIXlX-wC8v57F">${header}</div>
- <div class="_10tWW0o-L-5oSH8lCBl9ai"><i class="i-close"></i></div>
- </div>
- <div class="_27SmIe4FGDNnK7apcCB3W7">
- <div class="_3dbMg7zkkTIVJ5VZ3ygu4-">
- <div style="text-align: center; width: 240px;"><span>${content1}</span>${mcontent2}
- <div><input type="text" placeholder="${placeholder}" class=" _2SVsmVSmKfAWFlZGk_5_L8" value=""
- style="margin: 0.5rem 0px 0px;"><span
- style="font-size: 0.8rem; color: rgb(136, 136, 136);">${remarks}</span></div>
- </div>
- </div>
- </div>
- <div class="_2c5AwJ_0ePFIYub8OFE97J _2F7zIQl_1y5nHpDllTwX17"><a href="#">${btn1}</a><a
- href="#">${btn2}</a></div>
- </div>
- </div>
- `
- df.querySelector('._10tWW0o-L-5oSH8lCBl9ai').addEventListener('click', closeFn)
- let btnFns = [btn1Fn, btn2Fn];
- for(const [idx,a] of [...df.querySelectorAll('a[href*="#"]')].entries()) a.addEventListener('click', btnFns[idx])
- return df.querySelector('div');
-
- }
-
-
- document.addEventListener('keyup',function(evt){
-
- if(evt.code=='KeyB'){
-
- let memberID = getSelection()+""
-
- let m = null
- if(m=/\s*\#(\d+)\s*/.exec(memberID)){
-
-
-
- let mPopup = makePopup({header:'Block', content1:`Memeber ID:${m[1]}`,content2:'', placeholder:'Type the Reason Here', remarks:'Please be careful', btn1:'Cancel', btn2:'Block',
- closeFn:function(){
- mPopup.remove()
- },
- btn1Fn:function(){
- mPopup.remove()
- },
- btn2Fn:function(){
- let reason = mPopup.querySelector('input[type="text"]').value || '';
-
- fetch(`https://lihkg.com/api_v2/user/${m[1]}/block?reason=${reason}`)
- .then(response => response.json())
- .then(data => {
- if(data&& 'success' in data) alert( data.success?'成功':'失敗');
- console.log(data)
- });
-
- mPopup.remove()
- },
-
- })
- document.querySelector('body').appendChild(mPopup)
-
- }
-
-
-
-
- }else if(evt.code=='KeyU'){
-
- let memberID = getSelection()+""
-
- let m = null
- if(m=/\s*\#(\d+)\s*/.exec(memberID)){
- fetch(`https://lihkg.com/api_v2/user/${m[1]}/unblock`)
- .then(response => response.json())
- .then(data => {
- if(data&& 'success' in data) alert( data.success?'成功':'失敗');
- console.log(data)
- });
- }
-
-
-
-
-
- }
-
- })
-
-
- // Your code here...
- })();