Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@autoxbc
Last active December 1, 2019 09:23
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save autoxbc/6d80da78a9e976f91e22554b464305f8 to your computer and use it in GitHub Desktop.
Save autoxbc/6d80da78a9e976f91e22554b464305f8 to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name V2EX with Filter
// @author autoxbc
// @version 1.0
// @match *://*.v2ex.com/*
// @grant GM_addStyle
// ==/UserScript==
'use strict';
( () => {
const buttons = document.querySelector('.topic_buttons');
if(!buttons){
return;
}
const style = `
.ago + .small.fade {
color:#ED090D !important;
}
`;
GM_addStyle(style);
// 删除多余空格
[...buttons.childNodes].forEach( e => {
if( e.nodeType === 3 ){
e.nodeValue = e.nodeValue.trim();
}
} );
// 添加 优评排序 & 内容过滤框
const [ , good , filter , input ] = [
,'<a>优评</a>'
,'<a>过滤</a>'
,'<input type="text" hidden="hidden" />'
].map( e => html2node(e) );
[ good , filter ].forEach( e => {
e.href = '#;';
e.className = 'tb';
} );
const cells = document.querySelectorAll('.box > .cell[id^="r_"]');
const box = cells[0].parentNode ;
const info = box.querySelector('.cell:not([id])');
good.reset = () => [ ...cells , box , info , good ].forEach( e => e.removeAttribute('style') );
good.onclick = () => {
filter.reset();
if( box.hasAttribute('style') )
{
good.reset();
} else {
const maxOrder = [...cells].reduce( (pre,cell) => {
const order = [ ...cell.querySelectorAll('span.fade') ]
.reduce( (pre,cur) => {
const order = /\d+/.exec(cur.textContent);
return Math.max( pre , order );
},0);
cell.style = order ? `order:${ order };` : 'display:none;';
return Math.max( pre , order );
},0);
box.style = `
display:flex;
flex-direction:column-reverse;
`;
info.style = `order:${ maxOrder+1 };`;
good.style = 'font-weight:700;';
}
return false ;
};
filter.title = `
普通字符串:abc
正则字面量:/abc/i
排除符号:@@abc , @@/abc/i
注意:每次过滤在上一次结果上叠加
如想进行全新过滤,双击输入框重置
`.replace( /\t/g ,'').trim();
filter.reset = () => {
input.setAttribute('hidden','hidden');
input.ondblclick();
};
filter.onclick = () => {
good.reset();
if( input.hasAttribute('hidden') )
{
input.removeAttribute('hidden');
input.focus();
} else {
filter.reset();
}
return false ;
};
input.ondblclick = () => {
input.value = '';
input.onchange();
};
input.onchange = () => {
const { value } = input ;
if( value === '' )
{
nodeFilter();
return;
}
const cvtRegExp = str => {
if( /^\/(.+)\/(\w*)$/.test(str) )
{
const { $1 , $2 } = RegExp ;
return new RegExp( $1 , $2 );
} else {
return str ;
}
};
if( /^@@/.test(value) ){
nodeFilter( cvtRegExp( value.substr(2) ) , true );
} else {
nodeFilter( cvtRegExp(value) );
}
};
buttons.append( good , filter , input );
} )();
function html2node(str)
{
const div = document.createElement('div');
div.innerHTML = str.trim();
if( isSame( 1 , div.childNodes.length , div.children.length ) ){
return div.childNodes[0];
} else {
return div ;
}
}
function isSame(...args)
{
const callee = isSame ;
const { length } = args ;
if( length === 0 ){
return true;
}
if( length === 1 ){
const ele = args[0];
if( Array.isArray(ele) ){
return callee(...ele);
} else {
return true ;
}
}
if( length > 1 ){
return new Set(args).size === 1 ;
}
}
function nodeFilter(...args)
{
const callee = nodeFilter ;
callee.last = callee.last || new Set();
const { last } = callee ;
if( args.length === 0 )
{
last.forEach( e => e.hidden = false );
last.clear();
return;
}
let [ findReg , reverse = false , nodeSelector , fineSelector ] = args ;
// 附带规则,用于增强指定网站的过滤能力
[
[ 'v2ex.com' , '.box > .cell[id^="r_"]' ] ,
].some( ([ host , _nodeSelector , _fineSelector ]) => {
if( !nodeSelector && location.href.includes(host) )
{
nodeSelector = _nodeSelector ;
fineSelector = _fineSelector ;
return true ;
}
} );
[ ...document.querySelectorAll(nodeSelector) ].forEach( e => {
const target = e.querySelector(fineSelector) || e ;
if( contains( target.textContent , findReg ) !== reverse ){
return;
}
e.hidden = true;
last.add(e);
} );
}
function contains( str , arg ){
if( isString(arg) )
return str.includes(arg);
if( isRegExp(arg) )
return arg.test(str);
return false ;
}
function isRegExp(reg)
{
return Object.prototype.toString.call(reg) === '[object RegExp]';
}
function isString(str)
{
return !!str && typeof str === 'string' && str.constructor === String ;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment