// ==UserScript== // @name Replace and highlight // @namespace wordreplace // @version 2015.08.21 // @include *://boards.4chan.org/* // @grant unsafeWindow // @grant GM_getValue // @grant GM_setValue // @description Your own censor list for 4chan // @downloadURL none // ==/UserScript== 'use strict'; // Create element function function element(){ var parent var toreturn={} for(var i=0;i200){ var end=200 }else{ var end=replies.length } for(var i=0;iEdit]','Your own censor list',1], wronlyin:['Instead of replacing everything, replace only in:','',1], wrbody:['Comment body','',1,1], wrsubject:['Subject','',1,1], wrfilename:['Filename','',1,1], wrposter:['Poster name','',0,1], wrtitle:['Page title','',1,1], wrremwbr:['Remove all <wbr> tags from the posts','<wbr> tags are inserted every 35 characters in long words, which may prevent some patterns from working',1], wrtooltip:['Reveal original line in a tooltip when hovered over','',0] } unsafeWindow.wordreplace=function(event,action){ var norefresh=0 var order={on:1,pattern:0,case:1,replace:0,js:1,caps:1,mark:1,board:0} if(!action){ wr_temp=JSON.parse(JSON.stringify(wr_strings)) var menu=element( document.body, ['div',{ class:'UIPanel', id:'wr-body', onclick:closepanel }, ['div',{ class:'extPanel reply', style:'width:600px;margin-left:-300px' }, ['div',{ class:'panelHeader' }, 'Replace and highlight words', ['span', ['img',{ alt:'Close', title:'Close', class:'pointer', src:unsafeWindow.Main.icons.cross }] ] ], ['table',{ style:'text-align:center' }, ['thead', ['tr', ['th'], ['th','On'], ['th','Pattern'], ['th','Case'], ['th','Replace'], ['th','JS'], ['th','Caps'], ['th','Mark'], ['th','Board'], ['th','Del'] ] ], ['tbody',{ id:'wr-list' }], ], ['div',{ style:'float:left' }, ['input',{ type:'button', value:'Add', onclick:function(event){ wordreplace(event,'add') } }], ['input',{ type:'button', value:'Import/Export', onclick:function(event){ wordreplace(event,'port') } }] ], ['div',{ style:'float:right' }, ['input',{ type:'button', value:'Save', onclick:function(event){ wordreplace(event,'save') } }], ['input',{ type:'button', value:'Save and close', onclick:function(event){ wordreplace(event,'save') document.getElementById('wr-body').click() } }] ], ['table',{ style:'width:100%' }, ['tr',{ style:'vertical-align:top' }, ['td', ['textarea#input',{ id:'wr-input', paceholder:'Test it here', style:'width:280px;height:50px' }] ], ['td',{ style:'vertical-align:top;text-align:left' }, ['div',{ id:'wr-output', style:'width:294px;word-wrap:break-word;overflow:hidden' }] ] ] ] ], ['style','\ #wr-body input[type="text"],#wr-body .wr-replace{\ font:13px monospace;\ width:135px;\ }\ #wr-body .wr-board{\ width:60px!important;\ }\ #wr-body textarea.wr-replace{\ height:50px;\ resize:none!important;\ overflow:hidden;\ cursor:text;\ }\ #wr-body [error],#wr-body .js{\ vertical-align:top;\ }\ #wr-body [error]::after{\ content:attr(error);\ display:block;\ position:absolute;\ overflow:hidden;\ left:0;\ text-overflow:ellipsis;\ width:100%;\ white-space:nowrap;\ margin-top:25px;\ height:20px;\ font-size:12px;\ color:#d00\ }\ #wr-body [error] .wr-replace{\ margin-bottom:20px!important;\ }\ #wr-body .js[error]::after{\ margin-top:60px;\ }'] ] ) menu.input.onkeydown=menu.input.onkeyup=menu.input.onchange=menu.input.onclick=function(event){ wordreplace(event,'type') } } var list=document.getElementById('wr-list') var trs=list.getElementsByTagName('tr') if(action!='imported'){ for(var i=0;i0){ var temp=wr_temp[id] wr_temp[id]=wr_temp[id-1] wr_temp[id-1]=temp }else{ norefresh=1 } break } case 'del':{ var id=event.target.parentNode.parentNode.id.match(/filter-(\d+)/)[1]*1 wr_temp.splice(id,1) break } case 'save':{ for(var i=wr_temp.length;i--;){ if(!wr_temp[i].pattern&&!wr_temp[i].replace){ wr_temp.splice(i,1) } } wr_strings=wr_temp.slice() GM_setValue('wordreplace',JSON.stringify(wr_temp)) norefresh=1 break } case 'type': case 'imported':{ var input=document.getElementById('wr-input').value input=input.replace(/&/g,'&').replace(//g,'>').replace(/(>>\d+|>>>\/\w+\/\d*)/g,'$1').replace(/\n/g,'
').replace(/\[(\/)?spoiler\]/g,'<$1s>').replace(/^(>[^\n]*)$/gm,'$1') var output=document.getElementById('wr-output') output.innerHTML=input output.classList.remove('replacedtext') wordreplacing(output,1,wr_temp) if(action!='imported'&&!event.target.classList.contains('wr-js')){ norefresh=1 } break } case 'port':{ var port=element( document.body, ['div#panel',{ class:'UIPanel', onclick:closepanel }, ['div',{ class:'extPanel reply', style:'text-align:center' }, ['div',{ class:'panelHeader' }, 'Import/Export patterns', ['span', ['img',{ alt:'Close', title:'Close', class:'pointer', src:unsafeWindow.Main.icons.cross }] ] ], ['textarea#txt',{ style:'width:100%;height:100px;box-sizing:border-box', value:JSON.stringify(wr_temp) }], ['div',{ style:'float:left' }, ['input',{ type:'button', value:'Save to file', onclick:function(){ var txt=port.txt.value txt=btoa(encodeURIComponent(txt).replace(/%([0-9A-F]{2})/g,function(a,b){ return String.fromCharCode('0x'+b) })) element( ['a#link',{ href:'data:application/json;base64,'+txt, download:'wordreplace.json' }] ).link.click() } }], ['input',{ type:'button', value:'Load from file', onclick:function(){ port.file.click() } }], ], ['div',{ style:'float:right' }, ['input',{ type:'button', value:'Replace all', onclick:function(event){ var temp try{ temp=fromimport(JSON.parse(port.txt.value)) }catch(e){ alert('This is not a valid JSON') } if(temp){ wr_temp=temp wordreplace(event,'imported',1) port.panel.click() } } }], ['input',{ type:'button', value:'Import and append', onclick:function(event){ var temp try{ temp=fromimport(JSON.parse(port.txt.value)) }catch(e){ alert('This is not a valid JSON') } if(temp){ wr_temp=wr_temp.concat(temp) wordreplace(event,'type',1) port.panel.click() } } }] ], ['form', ['input#file',{ type:'file', style:'display:none', onchange:function(event){ if(event.target.files.length){ var reader=new FileReader() reader.onload=function(read){ port.txt.value=read.target.result event.target.parentNode.reset() } reader.readAsText(event.target.files[0]) } } }] ] ] ] ) norefresh=1 break } case 'editjs':{ var target=event.target var editor=element( document.body, ['div',{ class:'UIPanel', id:'wr-body', onclick:function(event){ if(event.target.className=='UIPanel'||event.target.tagName=='IMG'){ target.value=editor.txt.value closepanel(event) wordreplace(event,'type') } } }, ['div',{ class:'extPanel reply', style:'width:600px;margin-left:-300px' }, ['div',{ class:'panelHeader' }, 'Edit function', ['span', ['img',{ alt:'Close', title:'Close', class:'pointer', src:unsafeWindow.Main.icons.cross }] ] ], ['textarea#txt',{ value:target.value, onkeydown:function(event){ if(event.keyCode==9){ //Tab event.preventDefault() var input=event.target var start=input.selectionStart input.value=input.value.slice(0,start)+'\t'+input.value.slice(input.selectionEnd) input.setSelectionRange(start+1,start+1) } }, style:'width:100%;height:500px;box-sizing:border-box;font:13px monospace' }] ] ] ) editor.txt.setSelectionRange(0,0) norefresh=1 break } } if(!norefresh){ list.innerHTML='' if(!wr_temp.length){ wr_temp=[{on:1,caps:1}] } for(var i in wr_temp){ var table=element( list, ['tr#tr',{ id:'filter-'+i }, ['td', ['span',{ class:'pointer', onclick:function(event){ wordreplace(event,'up') } },'\u2191'] ] ] ) for(var j in order){ var tagname='input' var js=j=='replace'&&wr_temp[i].js if(js){ tagname='textarea' } var td=element( table.tr, ['td', [tagname+'#input',{ class:'wr-'+j, onchange:function(event){ wordreplace(event,'type') } }] ] ) if(order[j]){ td.input.type='checkbox' if(j=='on'&&wr_temp[i][j]==undefined){ td.input.checked=1 }else{ td.input.checked=wr_temp[i][j] } }else{ if(js){ table.tr.classList.add('js') td.input.readOnly=1 td.input.onclick=function(event){ wordreplace(event,'editjs') } }else{ td.input.type='text' } td.input.value=wr_temp[i][j]||'' } } element( table.tr, ['td', ['span',{ class:'pointer', onclick:function(event){ wordreplace(event,'del') } },'\xD7'] ] ) } } for(var i in wr_temp){ var errors=[] try{ var a=RegExp(wr_temp[i].pattern) }catch(e){ errors.push('Error in regex: '+e.message.replace(/.*:.*: /,'')) } if(wr_temp[i].js){ try{ eval('!function(){'+wr_temp[i].replace+'}') }catch(e){ errors.push('Error in function: '+e.message) } } if(errors.length){ document.getElementById('filter-'+i).setAttribute('error',errors.join(' | ')) }else{ document.getElementById('filter-'+i).removeAttribute('error') } } } },1000) } function closepanel(event){ if(event.target.className=='UIPanel'){ event.target.parentNode.removeChild(event.target) } if(event.target.tagName=='IMG'){ var target=event.target.parentNode.parentNode.parentNode.parentNode target.parentNode.removeChild(target) } } function fromimport(json){ var order={on:1,pattern:0,case:1,replace:0,js:1,caps:1,mark:1,board:0} try{ if(json.patterns){ json=json.patterns } var temp=[] for(var i in json){ temp[i]={} for(var j in order){ if(json[i][j]==undefined){ if(j=='on'||j=='caps'){ temp[i][j]=1 }else{ if(order[j]){ temp[i][j]=0 }else{ temp[i][j]='' } } }else{ temp[i][j]=json[i][j] } } } return temp }catch(e){ alert('This JSON cannot be imported because it is corrupt') console.log(e.message) } } //Word replace function wordreplacing(post,main,strings){ if(!post||!post.classList){ return } if(post.classList.contains('replacedtext')){ return } post.classList.add('replacedtext') if(main){ var comments=[post] }else{ var props=[] if(thesettings.wronlyin){ if(thesettings.wrbody){ props.push('blockquote') } if(thesettings.wrsubject){ props.push('.subject') } if(thesettings.wrfilename){ props.push('.fileText>a') } if(thesettings.wrposter){ props.push('.nameBlock') } }else{ props=['.nameBlock','.fileText>a','.subject','blockquote'] } if(!props.length){ return } var comments=post.querySelectorAll(props.join(',')) } for(var i=0;i/g,'>'),strings) if(out!=null){ if(thesettings.wrtooltip||/[<>]/.test(out)){ var newspan=element( ['span#span',{ innerHTML:out }] ) if(thesettings.wrtooltip){ newspan.span.title=currentnode.nodeValue } currentnode.parentNode.insertBefore(newspan.span,currentnode) currentnode.parentNode.removeChild(currentnode) }else{ currentnode.nodeValue=out.replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&') } } } } } function teststring(text,replaces){ if(text.length){ var oldtext=text if(replaces){ var patterns=replaces }else{ var patterns=wr_strings } for(var i in patterns){ try{ if( patterns[i].on&& patterns[i].pattern&&( !patterns[i].board|| patterns[i].board.toLowerCase().replace(/[,;]/g,' ').replace(/[^a-z\d\s]/g,'').trim().split(/\s+/).indexOf(currentboard)+1 ) ){ var replacewith=patterns[i].replace+'' if(patterns[i].js){ replacewith=function(){ return eval('(function(){'+patterns[i].replace+'}).apply(undefined,arguments)') } }else if(patterns[i].mark){ replacewith=''+(replacewith||'$&')+'' } if(patterns[i].caps){ var replacestring=replacewith replacewith=function(){ if(patterns[i].js){ var returned=replacestring.apply(undefined,arguments) var s=[returned,arguments[0]] }else{ var args=arguments var s=[replacestring,args[0]] s[0]=s[0].replace(/\$&/g,'$$0').replace(/\$(\d+)/g,function(){ var num=arguments[1]*1 return args[num]||'' }) } var al=s[0].length var bl=s[1].length if(al>bl){ var l=bl }else{ var l=al } if(l<2){ return s[0] } s=s.map(function(a){ return [a.slice(0,l-l/2),a.slice(l-l/2,-l/2),a.slice(-l/2)] }) for(var j=0;j<3;j+=2){ s[0][j]=s[0][j].toLowerCase().split('') for(var k=0;k/g,'>')) if(newtitle!=null){ document.title=newtitle.replace(/[\r\n]/g,'').replace(/<[^>]*>/g,'').replace(/>/g,'>').replace(/</g,'<').replace(/&/g,'&') } } if(activepage=='index'||activepage=='thread'){ if(thesettings.wordreplace){ updateposts() document.addEventListener('4chanParsingDone',updateposts) } if(unsafeWindow.Main){ updatesettings() }else{ document.addEventListener('4chanMainInit',updatesettings) } } if(activepage=='catalog'&&thesettings.wordreplace){ for(var i in unsafeWindow.catalog.threads){ var props=[] if(thesettings.wronlyin){ if(thesettings.wrbody){ props.push('teaser') } if(thesettings.wrsubject){ props.push('sub') } if(thesettings.wrfilename){ props.push('file') } if(thesettings.wrposter){ props.push('author') } }else{ props=['author','file','sub','teaser'] } props.forEach(function(prop){ var out=teststring(unsafeWindow.catalog.threads[i][prop]) if(out!=null){ unsafeWindow.catalog.threads[i][prop]=out } if(prop=='author'&&unsafeWindow.catalog.threads[i].lr.author){ var out=teststring(unsafeWindow.catalog.threads[i].lr.author) if(out!=null){ unsafeWindow.catalog.threads[i].lr.author=out } } }) } document.getElementById('size-ctrl').dispatchEvent(new Event('change')) } }