// ==UserScript==
// @name 4chan widescreen
// @namespace meh
// @description Presents more images and threads at once using columns.
// @include http://boards.4chan.org/*
// @exclude http://boards.4chan.org/f/*
// @require http://code.jquery.com/jquery-1.5.1.min.js
// @require http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.10/jquery-ui.min.js
// ==/UserScript==
const DEBUG = false;
const RESIZE_DELAY = 400;
const ID_HOLDER = 'navtop';
const ID_TOP_MENU = 'header';
const HREF_NO_HISTORY = 'javascript:void(0)';
const DEF_THUMB_SIZE = 5; // 50%
const DEF_TEXT_SIZE = 12; // px
const DEF_NUM_COLUMNS = 0;// 0 == auto
const BASE_ZINDEX = 999;
const GMT_4chan = -4;
var columns;
var scrollTopBackup;
var topSpace, bottomSpace;
var pages;
var timerResize;
var formHtml, formHtmlNext;
var isX;
var imgReply, imgPopup;
// auto settings
const MIN_COLS = 3;
var colSettings = [
{cols:4, resWidth:960},
{cols:5, resWidth:1300}
];
var thumbSettings = [
{resize:5, resWidth:960},
{resize:4, resWidth:1300},
{resize:5, resWidth:1600},
{resize:7, resWidth:1800}
];
//if not a thread page
if(!isThreadPage()){
//add toggle link
$('a:contains("@")')
.parent()
.after(
$('<a href="javascript:void(0)" id="wstogglelink">')
.html('ws')
.css({cursor:'pointer'})
.click(toggle)
)
.after(' / ');
//autorun
if(GM_getValue('autorun')==true)
toggle();
}
function toggle(evt){
var div = getById('divHoriz');
img();
if(div){//off
GM_setValue('autorun',false);
window.removeEventListener('resize', resize, false);
$(document).unbind('keyup.keys');
document.body.scrollTop = scrollTopBackup;
document.body.style.overflow = 'auto';
$('#divHoriz').remove();
$('#divBgHoriz').remove();
$('div.logo').css('visibility','visible');
$(pages).remove();
$('#divMenu').remove();
$('#divPageLeft').remove();
$('#divPageRight').remove();
$('#divTempIsolateScript').remove();
$('#divTempLoadNextPage').remove();
closeForm();
$('.resizedThumb').remove();
$('form[name=post]').unbind('submit.postform');
}else{//on
GM_setValue('autorun',true);
scrollTopBackup = document.body.scrollTop;
document.body.scrollTop = 0;
document.body.style.overflow = 'hidden';
isX = ($('#imgControls').length > 0);
if(GM_getValue('opt_isolate_script') || isX){ // solve incompatibilities with other scripts
if(!formHtml){//avoids making new request when window resized
$('<div id="divBgHoriz">').css({ position:'fixed', top: $('#wstogglelink').offset().top + $('#wstogglelink').outerHeight(), left:0,width:window.innerWidth,height:window.innerHeight,backgroundColor:getStyle(document.body,'background-color')}).appendTo(document.body);
GM_xmlhttpRequest({
method: 'GET',
url: location.href,
onload: function(resp){
if(resp.status==200){
formHtml = null;
formHtml = resp.responseText.match(/<form[^>]+name\=\"delform\"[^>]*>([\s\S]+)<\/form>/)[1];
create( formHtml );
}
}
});
}else{
create(formHtml);
}
}else{
create(null);
}
window.addEventListener('resize', resize, false);
$('div.logo').css('visibility','hidden');
$(document).bind('keyup.keys', keyupHandler);
$('form[name=post]').bind('submit.postform',onSubmitFormPost)
}
if(evt)
evt.target.blur();
}
function onSubmitFormPost(evt){
var form = $('#divFormPosttt form[name=post]');
var upfile = form.find('input[name=upfile]');
var str, divform, divBlock;
//submit page without GM_xmlhttpRequest, if there's a file to upload
if(upfile.val() != ''){
return true;
}
//grab form input data
str = '';
form.find('input,textarea').each(function(i,el){
if(el.getAttribute('name'))
str += ((str==''?'':'&')) + el.getAttribute('name') +'='+ el.value;
});
debug('action:'+form.attr('action'));
debug('data: '+str);
// hide form before sending request
$('<div id="divBlockForm">')
.css({width:$('#divFormPosttt').outerWidth(), minWidth:$('#divFormPosttt').outerWidth(), height:$('#divFormPosttt').outerHeight(), minHeight:$('#divFormPosttt').outerHeight(), verticalAlign:'middle', display:'table-cell', fontSize:'16px', color:'#789922'})
.html('<center>\> posting ...</center>')
.appendTo($('#divFormPosttt'));
form.hide();
// post form request
GM_xmlhttpRequest({
method: "POST",
url: form.attr('action'),
headers: {
"Content-type" : "application/x-www-form-urlencoded",
"Referer" : location.href
},
data: encodeURI(str),
onload: function(resp) {
$('#divFormPosttt form[name=post]').show();
$('#divBlockForm').remove();
debug(resp.status+': '+resp.statusText)
debug(resp.responseText)
if(resp.status==200){
var msg = createElement('div',null,null,resp.responseText.match(/\<table[\s\S]+\<\/table\>/)[0]).textContent;
if(msg.search(/Post successful/) > -1){
closeForm({target:document.getElementById('closeFormmm')});
}else{
alert( (msg.search(/Return/) > -1 ? msg.replace(/Return/,'') : msg) );
unsafeWindow.Recaptcha.reload();
}
}else{
alert(resp.status+': '+resp.statusText)
}
}
});
return false;
}
function create(html){
var page;
//remove any embed
try{ $('embed').remove(); }catch(e){}
//div background
if($('#divBgHoriz').length==0)
document.body.appendChild(createElement('div', {id:'divBgHoriz'}));
//main div
document.body.appendChild(createElement('div', {id:'divHoriz', tabindex:1}));
getById('divHoriz').appendChild(createElement('table', {id:'tableHoriz'},null,'<tr id="trHoriz"></tr>'));
getById('divHoriz').focus();
document.body.scrollTop = 0;
//define num of columns
if(GM_getValue('opt_numcolumns') && GM_getValue('opt_numcolumns')!=0){
columns = GM_getValue('opt_numcolumns');
}else{
columns = MIN_COLS;
for(var i=0; i<colSettings.length; i++){
if(window.innerWidth >= colSettings[i].resWidth){
columns = colSettings[i].cols;
}
}
}
// isolate script from others
if(!html){
page = $('form[name="delform"]');
debug('creating... normal mode')
}else{
page = $('<div id="divTempIsolateScript">').css('display','none').html(formHtml).appendTo(document.body);
debug('creating... isolated mode')
}
//pages
pages = createElement('div');
pages.appendChild(xp('//table[@class="pages"]//td')[1].cloneNode(true));
pages.setAttribute('class', 'tablePages');
document.body.appendChild(pages);
//menu
$(document.body)
.append(
$('<div id="divMenu">')
.append(
$('<a id="linkClose">')
.css({cursor:'pointer'})
.html('\>close')
.click(toggle)
.mouseenter(function(evt){
$('#divMenuHide').show();
$('#divMenu').addClass('shadowww').css('font-size','12px');
})
)
.append(
$('<div id="divMenuHide">')
.hide()
.append( $('<a id="linkOptions">').css({cursor:'pointer'}).html('\>options').click(options) )
.append('<br>')
.append( $('<a id="linkNewThread">').css({cursor:'pointer'}).html('\>new thread').click(postForm) )
.append('<br>')
.mouseleave(function(evt){
$('#divMenuHide').hide();
$('#divMenu').removeClass('shadowww').css('font-size','11px');;
})
)
)
//create threads
createThreads(page,false);
debug('create() > calling applyOptionsAndEvents()...')
applyOptionsAndEvents();
if(GM_getValue('opt_showonlyop') && GM_getValue('opt_loadnextpage')){
loadNextPage();
}
}
function createThreads(page, isNextPage){
var td, el, cont, id, divThread;
cont = 0;
page.children('span[id^="nothread"]').each(function(i,x){
//count td
if(cont==columns)
cont = 0;
//thread id
id = x.getAttribute('id').match(/\d+$/)[0];
// create div
if($('#divHoriz #'+id).length == 0){
divThread = createElement('div', {id:id});
divThread.setAttribute('class','divThread'+((isNextPage==true)?' nextpageee':''));
el = x;
while(el && el.previousSibling && el.previousSibling.nodeName!='HR'){
el=el.previousSibling;
}
if(el && !el.previousSibling){
divThread.appendChild(el.cloneNode(true));
}
while(el && el.nextSibling && el.nextSibling.nodeName != 'HR')
{
divThread.appendChild(el.nextSibling.cloneNode(true));
el = el.nextSibling;
}
el = getByTag('img',divThread)[0].parentNode;
//append div thread to td
try{ td = xp('child::td',getById('trHoriz'))[cont]; }catch(e){}
if(!td || td=='undefined')
td = getById('trHoriz').appendChild(createElement('td',{class:'tdHoriz'}));
cont++;
td.appendChild(divThread);
td.style.zIndex = BASE_ZINDEX+cont;//old code
}else{
debug('duplicate thread #'+id)
}
});
}
function applyOptionsAndEvents(){
//cleanup
$('#divHoriz input').remove();
$('td .doubledash').remove();
$('#divHoriz a:contains(Reply)').each(function(i,el){
var reply = $(el);
reply.parent().contents().filter(function(){return this.nodeType == 3;}).remove();
reply.remove();
});
//show only OP's post
if(GM_getValue('opt_showonlyop')){
$('.tdHoriz table').hide();
}
//poster name
if(!GM_getValue('opt_show_poster')){
$('#divHoriz span.postername').hide();
$('#divHoriz span.postertrip').hide();
$('#divHoriz span.commentpostername').hide();
}
//post title
if(!GM_getValue('opt_show_title')){
$('#divHoriz span.filetitle').hide();
$('#divHoriz span.replytitle').hide();
}
//post number
if(!GM_getValue('opt_show_threadnumber')){
$('#divHoriz a.quotejs').hide();
}
//file info
if(!GM_getValue('opt_show_filesize')){
$('#divHoriz span.filesize').hide();
}
//ommited posts info
if(!GM_getValue('opt_show_omittedposts')){
$('#divHoriz span.omittedposts').hide();
}
//remove <br> from posts
$('#divHoriz td.reply').each(function(i,el){
var comment = $(el).children('blockquote');
comment.html(comment.html().replace(/\<br\>\s*\<br\>/ig,'<br>'))
if( comment.prev().get(0).nodeName == 'A' && comment.prev().get(0).href && comment.prev().get(0).href.search(/http\:\/\/images.4chan\.org/) > -1)
comment = comment.prev();
comment.prevAll().filter('br,a').remove();
});
//wrap date in a span tag
$('.divThread td.reply').each(function(i,td){
td.innerHTML = td.innerHTML.replace(/(\d+\/\d+\/\d+\(\S+\)\d+\:\d+)/g, '<span class="posttime">$1</span>');
});
//post date
if(!GM_getValue('opt_show_date')) {
$('#divHoriz .posttime').hide();
}else if(GM_getValue('opt_showrelativedate')){
toggleRelativeDate(true)
}
//thread shadow
if(!GM_getValue('opt_hideShadows')){
$('#divHoriz .divThread').removeClass('shadowww').addClass('shadowww');
}
//thumbnail's resizing and events
var resize = getThumbSize();
resize = parseFloat(resize/10);
$('#divHoriz img')
.each(function(i,el){
var jqel = $(el);
if(jqel.parents('.divThreadButtons').length==0 && jqel.attr('src').search(/sticky/) < 0 && jqel.attr('src').search(/closed/) < 0){
jqel.css({
width: parseInt(el.getAttribute('width'))*resize,
height: parseInt(el.getAttribute('height'))*resize
})
.mouseenter(function(evt) {
if(GM_getValue('opt_resize_thumbnail')){
var t = $(evt.target);
var os = t.offset();
var clone;
$('.resizedThumb').parent().remove();
clone = t.clone(false)
.addClass('resizedThumb')
.css({
position: 'absolute',
zIndex: BASE_ZINDEX+998,
border:'1px solid #789922',
top: os.top,
left: os.left - 20,
width: '',
height: ''
})
.mouseleave(function(evt){
$(evt.target).parent().remove();
//$('.resizedThumb').remove();
})
.appendTo(document.body)
.wrap(
$('<a>')
.attr({target:'_blank', href:t.parent().attr('href')})
.click(function(evt){
$('.resizedThumb').parent().remove();
})
);
//out of bounds
if(clone.height() + clone.offset().top > window.innerHeight)
clone.css({top: window.innerHeight - clone.height()});
if(clone.width() + clone.offset().left > window.innerWidth)
clone.css({left: window.innerWidth - clone.width()});
}
})
}
})
//css() uses num of columns
debug('applyOptions() calling css()...')
css();
//pagination edge buttons
if( $('#divPageLeft').length == 0 && $('#divPageRight').length == 0 ){
var currentPage = 0;
try{ currentPage = parseInt(location.href.match(/(\d+)$/)[1]) }catch(e){}
if(currentPage > 0){
$(document.body).append(
$('<div id="divPageLeft">')
.append(
$('<table>').addClass('tablePagination').html('<tr><td><center>'+(currentPage-1)+'</center></tr></td>')
)
.mouseenter(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
$('#divPageLeft').css('opacity','1')
})
.mouseleave(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
$('#divPageLeft').css('opacity','0')
})
.click(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
paginate(37);
})
);
}
if(currentPage < $('.tablePages').text().match(/\[(\d+)\]\s*$/)[1] ){
$(document.body).append(
$('<div id="divPageRight">')
.append(
$('<table>').addClass('tablePagination').html('<tr><td><center>'+(currentPage+1)+'</center></tr></td>')
)
.mouseenter(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
$('#divPageRight').css('opacity','0.9')//.animate({width:'100px'}, 'fast')
})
.mouseleave(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
$('#divPageRight').css('opacity','0')
})
.click(function(evt){
if(GM_getValue('opt_hidePaginationEdges'))
return;
paginate(39);
})
);
}
}
//posts buttons
$('#divHoriz .divThread:not(.threadDone) blockquote').each(function(i,el){
if($(el).parent().find('.divThreadButtons').length==0){
$(el).parent().prepend(
//quick reply buton
$('<div>')
.addClass('divThreadButtons')
.append(
imgReply
.clone(true)
.attr({id:el.parentNode.getAttribute('id')})
.click(replyThread)
)
)
}
});
//main thread buttons
$('.divThread:not(.threadDone) > .divThreadButtons').each(function(i,el){
$(el).prepend(
$('<a>')
.attr({ href:'res/'+el.parentNode.getAttribute('id'), target:'_blank'})
.append( imgPopup.clone(true) )
)
//$('<a>GoTo</a>').click(goTo).attr({href:'javascript:void(0)', threadUrl:a.href})
});
$('#divHoriz .divThread').removeClass('threadDone').addClass('threadDone');
//cleanup
$('#divTempIsolateScript').remove();
$('#divTempLoadNextPage').remove();
}
function img(){
if(!imgReply){
imgReply = $(createElement('img', {src: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAAI0lEQVR42mPYsGHDfwZCYJAoQhHHpwguB+Pgw3idQdC9yAoA8aVAPGs+tj4AAAAASUVORK5CYII%3D', title:'quick reply'})).css({cursor:'pointer'});
imgPopup = $(createElement('img', {src:'data:image/gif;base64,R0lGODlhCQAJANEAAP///4CAgAAAAMDAwCH5BAkZAAAALAAAAAAJAAkAAAIUhI8my8ccwhBIxFmDehaNE4VaQhYAOw%3D%3D', title:'open thread in a new tab'}));
}
}
function loadNextPage(){
var currentPage;
var nextUrl;
try{
currentPage = parseInt(location.href.match(/(\d+)\/?$/)[1]);
}catch(e){
currentPage = 0;
}
if(currentPage==0)
nextUrl = location.href + '1';
else
nextUrl = location.href.replace(/\d+\/?$/, (currentPage+1));
if(!formHtmlNext){ //avoids requests when resizing and toggling
debug('loadNextPage() > requesting next page')
GM_xmlhttpRequest({
method: 'GET',
url: nextUrl,
onload: function(resp){
if(resp.status==200){
formHtmlNext = null;
formHtmlNext = resp.responseText.match(/<form[^>]+name\=\"delform\"[^>]*>([\s\S]+)<\/form>/)[1];
page = $('<div id="divTempLoadNextPage">').css('display','none').html(formHtmlNext).appendTo(document.body)
createThreads(page,true);
debug('loadNextPage() > GM_xmlhttpRequest() > onload > calling applyOptionsAndEvents()...')
applyOptionsAndEvents();
}
}
});
}else{
page = $('<div id="divTempLoadNextPage">').css('display','none').html(formHtmlNext).appendTo(document.body);
createThreads(page,true);
debug('loadNextPage() > formHtmlNext==true > calling applyOptionsAndEvents()...')
applyOptionsAndEvents();
}
}
function recentMenu(evt){
}
function replyThread(evt){
var ta, divform;
var t = $(evt.target);
var id = t.attr('id');
var thread = t.parents('.divThread');
var threadoffset = thread.offset();
var spaceLeft, spaceRight;
debug('replyThread() > id: '+id)
//show form
postForm(evt);
divform = $('#divFormPosttt');
ta = $('#divFormPosttt textarea[name=com]');
//position form horizontally
spaceLeft = threadoffset.left;
spaceRight = window.innerWidth - threadoffset.left - thread.outerWidth();
if(spaceRight >= spaceLeft*0.8 || spaceRight >= divform.outerWidth()*0.9 )
divform.css({ right:'', left: threadoffset.left + thread.outerWidth() +7}); // right side
else
divform.css({ right:'', left: threadoffset.left - divform.outerWidth() -7}); // left side
//position form vertically
divform.css({top:threadoffset.top});
if(window.innerHeight - threadoffset.top < (divform.outerHeight()*0.7)){
divform.css({top:window.innerHeight - (divform.outerHeight()*0.8)})
}
debug(divform.css('top'))
if(divform.css('top').search(/^-/) > -1){//negative top value ("-33px")
divform.css({top:0});
}
//set selected thread
if(divform.attr('threadid') != thread.attr('id')){
debug('replyThread() > '+divform.attr('threadid')+' != '+thread.attr('id'))
//new thread selected, remove previous quotes (format ">>12653625")
ta.val(ta.val().replace(/\>\>\d+/ig, ''));
divform.find('input[name=resto]').remove();
divform.find('form').prepend($('<input>').attr({type:'hidden', value:thread.attr('id'), name:'resto'}));
$('#spanFormPostTopBarTitle').html('Reply to thread <a class="quotelink" href="res/'+thread.attr('id')+'" target="_blank">\>\>'+divform.find('input[name=resto]').val()+'</a>')
}
divform.attr({threadid: thread.attr('id')});
//add thread number to textarea
ta.val(ta.val()+'>>'+id);
setCaretToPos(ta.get(0), ta.val().length)
//highlight thread or reply
$('.classHighlightThread').removeClass('classHighlightThread');
t.parents('.divThread').addClass('classHighlightThread');
}
function closeForm(evt){
debug('closeForm()...')
if($('#divFormPosttt').lenght==0 || evt==undefined || !evt.target || evt.target.getAttribute('id')!='closeFormmm'){
debug('closeForm() > divForm not found or close link not clicked by the user')
return;
}
//clear fields
$('#divFormPosttt').find('input[name=sub]').val('');
$('#divFormPosttt').find('textarea[name=com]').val('');
$('#recaptcha_response_field').val('');
$('#divFormPosttt').find('input[name=upfile]').val('');
//remove thread reference
$('#divFormPosttt').find('input[name=resto]').remove();
//re-append form to original pos
$('#spanFormPostOriginalPlace').before($('#formPosttt'));
//undo highlight
$('.classHighlightThread').removeClass('classHighlightThread')
//remove div form
$('#divFormPosttt').remove();
}
function postForm(evt){
var t = $(evt.target);
var form = $('form[name="post"]');
var divForm = $('#divFormPosttt');
var placeholder;
//create div to append form
if(divForm.length==0){
debug('postForm() > creating divForm...')
divForm = $('<div id="divFormPosttt">')
.append(
$('<div id="divFormPostTopBar">')
.css({width:'100%', cursor:'move', backgroundColor:'#789922', color:'#000000', display:'inline-block'})
.append(
$('<a id="closeFormmm">close</a>')
.css({float:'right', cursor:'pointer', margin:'1px 3px'})
.click(closeForm)
)
.append(
$('<span id="spanFormPostTopBarTitle">')
.css({margin:'1px 3px'})
)
)
.css({
position: 'absolute',
backgroundColor: $(document.body).css('background-color'),
zIndex: BASE_ZINDEX + 999,
padding: 0,
margin: 0,
border:'1px solid #789922'
})
.addClass('shadowww')
.appendTo(document.body)
.draggable({
handle:'#divFormPostTopBar',
start: function(event, ui){
$('#divFormPosttt').css({right:'',top:''});
}
})
.hide();
//save form original position
placeholder = $('#spanFormPostOriginalPlace');
if(placeholder.length==0)
placeholder = $('<span id="spanFormPostOriginalPlace">').hide();
form.before( placeholder );
//append form to div
form.attr('id','formPosttt').appendTo( divForm );
}
divForm.show()
//remove reply info if it's new thread
if(t.parents('#divMenu').length > 0)
$('#spanFormPostTopBarTitle').html('New thread');
if(divForm.attr('threadid')!=undefined && divForm.attr('threadid') != t.parents('.divThread').attr('id')){
debug("1. postForm() > "+divForm.attr('threadid') +' != '+ t.parents('.divThread').attr('id'))
divForm.removeAttr('threadid').find('input[name=resto]').remove();
}else{
debug("2. postForm() > "+divForm.attr('threadid') +' != '+ t.parents('.divThread').attr('id'))
}
//position div form
t = $('#linkNewThread');
divForm.css({
top: t.offset().top + t.outerHeight(),
right: window.innerWidth - t.offset().left - t.outerWidth(),
left:''
});
//get new captcha
unsafeWindow.Recaptcha.reload();
}
function getThumbSize(){
var res = (GM_getValue('opt_thumbsize') ? GM_getValue('opt_thumbsize') : DEF_THUMB_SIZE);
if(res == -1){//auto
res = DEF_THUMB_SIZE;
for(var i=0; i<thumbSettings.length; i++){
if(window.innerWidth > thumbSettings[i].resWidth)
res = thumbSettings[i].resize;
}
}
return res;
}
function distributeThreads(){
var threads;
var data = [];
var reverse = false;
var t, j, obj, tds, freeHeight, bestTD;
if(GM_getValue('opt_showonlyop') && GM_getValue('opt_loadnextpage') && $('#divHoriz .nextpageee').length > 0){
//distribute next page threads
threads = $('#divHoriz .nextpageee');
debug('distributeThreads() > next page threads = '+threads.length)
}else{
//distribute current page threads
threads = $('#divHoriz .divThread').not('.nextpageee');
debug('distributeThreads() > current page threads = '+threads.length)
}
//sort threads by height in data[]
for(var i=0; i<threads.length; i++){
t = $(threads[i]);
obj = {thread:t, height:t.outerHeight()}
//insertion order by height
j=0;
while(j<data.length && obj.height >= data[j].height)
j++
data.splice(j,0,obj);//insert obj at j
}
debug('distributeThreads() > data.length = '+data.length)
tds = $('.tdHoriz');
tds.removeAttr('freeheight');//cleanup
while(data.length > 0){
//insert on empty tds first
for(var i=0; i<tds.length; i++){
if(data[0] && tds.eq(i).attr('freeheight')==undefined){
tds.eq(i).append(data[0].thread);
tds.eq(i).attr('freeheight', (parseInt(window.innerHeight) - parseInt(topSpace) - parseInt(data[0].height)));
data.splice(0,1);
}
}
if(data[0]==undefined)
return;
if(!reverse){
data = data.reverse();
reverse = true;
}
//insert on higher free space (data[0] is the higher thread)
bestTD = 0;
for(var i=0; i<tds.length; i++){
if(parseInt(tds.eq(i).attr('freeheight')) > parseInt(tds.eq(bestTD).attr('freeheight'))){
bestTD = i;
}
}
tds.eq(bestTD).append(data[0].thread);
tds.eq(bestTD).attr('freeheight', (parseInt(tds.eq(bestTD).attr('freeheight')) - parseInt(data[0].height)));
data.splice(0,1);
}
}
function options(evt){
if($('#options').length == 0){
$('<div id="options">')
.appendTo(document.body)
.css({position:'absolute', top:'0px', right:'0px', zIndex:BASE_ZINDEX+999, backgroundColor: $(document.body).css('background-color'), color: 'black', border:'1px solid #789922', padding: '13px', minWidth:'200px'})
.mouseleave(function(){
$('#options').hide()
})
.append(' Number of columns: ')
.append(
$('<select id="comboNumColumns">')
.css({fontSize:'10px'})
.html('<option value=0>auto</option> <option value=3>3</option> <option value=4>4</option> <option value=5>5</option> <option value=6>6</option> <option value=7>7</option> <option value=8>8</option> <option value=9>9</option> <option value=10>10</option>')
.val((GM_getValue('opt_numcolumns') ? GM_getValue('opt_numcolumns') : DEF_NUM_COLUMNS))
.change(function(evt){
GM_setValue('opt_numcolumns', parseInt($(evt.target).val()));
columns = parseInt($(evt.target).val());
toggle();
toggle();
css();
})
)
.append('<br>')
.append(' Text size: ')
.append(
$('<select id="comboTextSize">')
.css({fontSize:'10px'})
.html('<option value=9>9px</option> <option value=10>10px</option> <option value=11>11px</option> <option value=12>12px</option> <option value=13>13px</option> <option value=14>14px</option> <option value=16>16px</option> <option value=20>20px</option>')
.val((GM_getValue('opt_textsize') ? GM_getValue('opt_textsize') : DEF_TEXT_SIZE))
.change(function(evt){
GM_setValue('opt_textsize', parseInt($(evt.target).val()));
css();
})
)
.append('<br>')
.append(' Thumbnail size: ')
.append(
$('<select id="comboThumbSize">')
.css({fontSize:'10px'})
.html('<option value=-1>auto</option><option value=3>30%</option> <option value=4>40%</option> <option value=5>50%</option> <option value=6>60%</option> <option value=7>70%</option> <option value=8>80%</option> <option value=9>90%</option> <option value=10>100%</option>')
.val( (GM_getValue('opt_thumbsize') ? GM_getValue('opt_thumbsize') : DEF_THUMB_SIZE))
.change(function(evt){
var resize = parseInt($(evt.target).val());
GM_setValue('opt_thumbsize', resize);
if(resize==-1){//auto
resize = getThumbSize();
}
resize = parseFloat(resize/10);
$('#divHoriz img')
.each(function(i,el){
if($(el).attr('src').search(/sticky/) < 0 && $(el).attr('src').search(/closed/) < 0 ){
$(el).css({width: parseInt(el.getAttribute('width')) * resize, height: parseInt(el.getAttribute('height')) * resize})
}
})
})
)
.append('<br>')
.append(
$('<input type="checkbox" id="optResizeThumbnail">')
.attr({checked:GM_getValue('opt_resize_thumbnail')})
.click( function(evt){
GM_setValue('opt_resize_thumbnail',evt.target.checked);
})
)
.append(' Resize thumbnails on mouse over')
.append('<br>')
.append(
$('<input type="checkbox" id="optSolveIncompatibilities">')
.attr({checked:GM_getValue('opt_isolate_script')})
.click( function(evt){
GM_setValue('opt_isolate_script',evt.target.checked);
})
)
.append(' Isolate script (solve incompatibilities)')
.append('<br>')
.append(
$('<input type="checkbox" id="optCheckUseArrowKeysToPaginate">')
.attr({checked:GM_getValue('opt_useArrowKeysToPaginate')})
.click( function(evt){
GM_setValue('opt_useArrowKeysToPaginate',evt.target.checked);
})
)
.append(' Use arrow keys to change pages')
.append('<br>')
.append(
$('<input type="checkbox" id="optCheckHidePagination">')
.attr({checked:GM_getValue('opt_hidePaginationEdges')})
.click( function(evt){
GM_setValue('opt_hidePaginationEdges',evt.target.checked);
})
)
.append(' Hide pagination edges')
.append('<br>')
.append(
$('<input type="checkbox" id="optCheckHideShadows">')
.attr({checked:GM_getValue('opt_hideShadows')})
.click( function(evt){
GM_setValue('opt_hideShadows', evt.target.checked);
if(!evt.target.checked)
$('#divHoriz .divThread').removeClass('shadowww').addClass('shadowww');
else
$('#divHoriz .divThread').removeClass('shadowww');
})
)
.append(' Hide shadows')
.append('<br>')
.append(
$('<input type="checkbox" id="optCheckShowOnlyOP">')
.attr({checked:GM_getValue('opt_showonlyop')})
.click( function(evt){
GM_setValue('opt_showonlyop',evt.target.checked);
if(evt.target.checked){
//show only OP
$('.tdHoriz table').hide();
$('#optSpanLoadNextPage').show();
if(GM_getValue('opt_loadnextpage')){
if($('.nextpageee').length==0)
loadNextPage()
else
$('.nextpageee').show();
}
}else{
//show replies
$('#optSpanLoadNextPage').hide();
$('.tdHoriz table').show();
$('.nextpageee').hide();
}
debug('opt_showonlyop > calling distributeThreads()...')
distributeThreads();
})
)
.append(' Show only original post<br>')
.append(
$('<span>')
.attr({id:'optSpanLoadNextPage', checked:GM_getValue('opt_loadnextpage')})
.css({marginLeft:'19px', display:(GM_getValue('opt_showonlyop')?'box':'none')})
.append(
$('<input type="checkbox" id="optCheckLoadNextPage">')
.attr({checked:GM_getValue('opt_loadnextpage')})
.click(function(evt){
GM_setValue('opt_loadnextpage', evt.target.checked);
if(evt.target.checked){
if($('.nextpageee').length==0)
loadNextPage()
else
$('.nextpageee').show();
}else{
$('.nextpageee').hide();
}
})
)
.append(' Load next page<br>')
)
.append('<hr>')
.append(
$('<input type="checkbox" id="optCheckShowPoster">')
.attr({checked:GM_getValue('opt_show_poster')})
.click( function(evt){
GM_setValue('opt_show_poster',evt.target.checked);
if(evt.target.checked){
$('#divHoriz span.postername').show();
$('#divHoriz span.postertrip').show();
$('#divHoriz span.commentpostername').show();
}else{
$('#divHoriz span.postername').hide();
$('#divHoriz span.postertrip').hide();
$('#divHoriz span.commentpostername').hide();
}
})
)
.append(' Show post names<br>')
.append(
$('<input type="checkbox" id="optCheckShowTitle">')
.attr({checked:GM_getValue('opt_show_title')})
.click( function(evt){
GM_setValue('opt_show_title',evt.target.checked);
if(evt.target.checked){
$('#divHoriz span.filetitle').show();
$('#divHoriz span.replytitle').show();
}else{
$('#divHoriz span.filetitle').hide();
$('#divHoriz span.replytitle').hide();
}
})
)
.append(' Show post titles<br>')
.append(
$('<input type="checkbox" id="optCheckShowFileSize">')
.attr({checked:GM_getValue('opt_show_filesize')})
.click( function(evt){
GM_setValue('opt_show_filesize',evt.target.checked);
if(evt.target.checked){
$('#divHoriz span.filesize').show();
}else{
$('#divHoriz span.filesize').hide();
}
})
)
.append(' Show file info<br>')
.append(
$('<input type="checkbox" id="optCheckShowThreadNumber">')
.attr({checked:GM_getValue('opt_show_threadnumber')})
.click( function(evt){
GM_setValue('opt_show_threadnumber',evt.target.checked);
if(evt.target.checked){
$('#divHoriz a.quotejs').show();
}else{
$('#divHoriz a.quotejs').hide();
}
})
)
.append(' Show post number<br>')
.append(
$('<input type="checkbox" id="optCheckShowDate">')
.attr({checked:GM_getValue('opt_show_date')})
.click( function(evt){
GM_setValue('opt_show_date',evt.target.checked);
if(evt.target.checked){
$('#divHoriz .posttime').show();
$('#optSpanRelativeDate').show();
}else{
$('#divHoriz .posttime').hide();
$('#optSpanRelativeDate').hide();
}
})
)
.append(' Show date<br>')
.append(
$('<span>')
.attr({id:'optSpanRelativeDate', checked:GM_getValue('opt_showrelativedate')})
.css({marginLeft:'19px', display:(GM_getValue('opt_show_date')?'box':'none')})
.append(
$('<input type="checkbox" id="optCheckRelativeDate">')
.attr({checked:GM_getValue('opt_showrelativedate')})
.click(function(evt){
GM_setValue('opt_showrelativedate', evt.target.checked);
toggleRelativeDate(evt.target.checked);
})
)
.append(' Calculate relative time using ')
.append(
$('<select>')
.attr({id:'optComboDateGMT'})
.css({marginLeft:'4px', fontSize:'10px'})
.html('<option value=-12>GMT-12</option><option value=-11>GMT-11</option><option value=-10>GMT-10</option><option value=-9>GMT-9</option><option value=-8>GMT-8</option><option value=-7>GMT-7</option><option value=-6>GMT-6</option><option value=-5>GMT-5</option><option value=-4>GMT-4</option><option value=-3>GMT-3</option><option value=-2>GMT-2</option><option value=-1>GMT-1</option><option value=0>GMT 0</option><option value=1>GMT+1</option><option value=2>GMT+2</option><option value=3>GMT+3</option><option value=4>GMT+4</option><option value=5>GMT+5</option><option value=6>GMT+6</option><option value=7>GMT+7</option><option value=8>GMT+8</option><option value=9>GMT+9</option><option value=10>GMT+10</option><option value=11>GMT+11</option><option value=12>GMT+12</option>')
.val((GM_getValue('opt_gmt') ? GM_getValue('opt_gmt') : GMT_4chan))
.change(function(evt){
GM_setValue('opt_gmt',$('#optComboDateGMT').val())
toggleRelativeDate(false);
$('#divHoriz .posttime').removeAttr('originaldate').removeAttr('relativedate');
toggleRelativeDate(true);
})
)
.append('<br>')
)
.append(
$('<input type="checkbox" id="optCheckShowOmittedPosts">')
.attr({checked:GM_getValue('opt_show_omittedposts')})
.click( function(evt){
GM_setValue('opt_show_omittedposts',evt.target.checked);
if(evt.target.checked){
$('#divHoriz span.omittedposts').show();
}else{
$('#divHoriz span.omittedposts').hide();
}
})
)
.append(' Show omitted posts info<br>')
}else{
$('#options').show();
}
}
function toggleRelativeDate(on){
if(on==true){
//show relative date
$('#divHoriz .posttime').each(function(i,el){
if(!el.getAttribute('originalDate') && !el.getAttribute('relativeDate')){
// calculate relativeDate, store in attributes
var hour, origDate;
var arr = el.innerHTML.match(/(\d+)\/(\d+)\/(\d+)[^\d]+(\d+)\:(\d+)/); // 01/19/11(Wed)02:27
var gmt = (GM_getValue('opt_gmt') ? GM_getValue('opt_gmt') : GMT_4chan);
hour = parseInt(arr[4],10);
if(gmt > GMT_4chan){
hour += Math.abs(gmt - GMT_4chan);
}else if(gmt < GMT_4chan){
hour -= Math.abs(gmt - GMT_4chan);
}else{
hour = (new Date()).getHours();
}
origDate = new Date(20+arr[3], arr[1]-1, arr[2], hour, arr[5]);// new Date(year, month, day, hours, minutes, seconds, milliseconds);
el.setAttribute('originalDate', el.innerHTML);
el.setAttribute('relativeDate', getRelativeDate(origDate));
}
el.innerHTML = el.getAttribute('relativeDate');
});
}else{
//show original date
$('#divHoriz .posttime').each(function(i,el){
el.innerHTML = el.getAttribute('originalDate');
});
}
}
// http://snipplr.com/view/10290/
function getRelativeDate(d){
var dateFunc = new Date();
var timeSince = dateFunc.getTime() - d;
var inSeconds = timeSince / 1000;
var inMinutes = timeSince / 1000 / 60;
var inHours = timeSince / 1000 / 60 / 60;
var inDays = timeSince / 1000 / 60 / 60 / 24;
var inYears = timeSince / 1000 / 60 / 60 / 24 / 365;
var ret = "";
// in seconds
if(timeSince < 0 || Math.round(inSeconds) == 1){
ret += "1 second ago";
}
else if(inMinutes < 1.01){
ret += Math.round(inSeconds) + " seconds ago";
}
// in minutes
else if(Math.round(inMinutes) == 1){
ret += "1 minute ago";
}
else if(inHours < 1.01){
ret += Math.round(inMinutes) + " minutes ago";
}
// in hours
else if(Math.round(inHours) == 1){
ret += "1 hour ago";
}
else if(inDays < 1.01){
ret += Math.round(inHours) + " hours ago";
}
// in days
else if(Math.round(inDays) == 1){
ret += "1 day ago";
}
else if(inYears < 1.01){
ret += Math.round(inDays) + " days ago";
}
// in years
else if(Math.round(inYears) == 1){
ret += "1 year ago";
}
else
{
ret += Math.round(inYears) + " years ago";
}
return ret+'.';
}
function keyupHandler(evt){
//escape key, close ws
if(evt.keyCode == 27){
toggle();
return;
}
// left/right arrow
if( (evt.keyCode == 37 || evt.keyCode == 39) && GM_getValue('opt_useArrowKeysToPaginate') && !isFormPostBeingUsed()){
paginate(evt.keyCode);
}
}
function isFormPostBeingUsed(){
if( $('#divFormPosttt').length > 0 && $('#divFormPosttt').find('form').length > 0 ){
return true;
}else{
return false;
}
}
function goTo(evt){
toggle();
var href = evt.target.getAttribute('threadUrl').match(/res\/\d+/)[0];
var node = $('a[href^="'+href+'"]');
window.scrollTo( 0, node.offset().top - 50);
GM_setValue('autorun',true);
}
function resize(evt){
if(timerResize)
clearTimeout(timerResize);
timerResize = setTimeout( function(evt){
//readjust to screen size
toggle();
toggle();
//css();
}, RESIZE_DELAY);
}
function css(){
if(!getById('cssholder'))
{
getByTag('head')[0].appendChild(createElement('style', {id:'cssholder', type:"text/css"}));
}
var height = parseInt(window.innerHeight);
var width = parseInt(window.innerWidth);
topSpace = $('#wstogglelink').offset().top + $('#wstogglelink').outerHeight();
bottomSpace = parseInt(getStyle(pages,'height').match(/\d+/)[0]);
getById('cssholder').innerHTML = ''+
' #divBgHoriz { '+
'position: fixed; top: '+topSpace+'px; left: 0px; z-index:'+(BASE_ZINDEX+1)+';'+
'width: 100%;'+
'height: 100%;'+
'background-color: '+$('.reply').css('background-color')+';'+
'vertical-align: middle !important;'+
'}'+
' #divBgHoriz td{ '+
'color: #dddddd !important;'+
'vertical-align: middle !important;'+
'font-size: 200px !important;'+
'}'+
' #divHoriz { '+
'position: fixed; top: '+topSpace+'px; left: 10px; z-index:'+(BASE_ZINDEX+2)+';'+
'max-height: '+(height - (topSpace+bottomSpace))+'px;'+
'max-width: '+(width - 20)+'px;'+
'width: '+(width - 20)+'px;'+
'overflow: auto;'+
'background-color: '+$('.reply').css('background-color')+';'+
'}'+
' #tableHoriz { '+
'max-height: 100%;'+
'height: 100%;'+
'width: 99%;'+
'}'+
' .tdHoriz { '+
'vertical-align: top;'+
'overflow: auto;'+
'width: '+(100/columns)+'%;'+
'max-width: '+(100/columns)+'%;'+
'}'+
' .tablePages { '+
'position: fixed; bottom: 0px; left: '+parseInt(width/2 - 175)+'px; z-index:'+(BASE_ZINDEX+3)+';'+
'}'+
' .divThread { '+
'max-width: '+parseInt(width/columns)+'px;'+
'background-color: '+$(document.body).css('background-color')+';'+
'padding: 0 10px;'+
'margin: 8px 4px'+
'}'+
'.divThread table{ '+
'max-width: '+((window.innerWidth/columns)-15)+'px;'+
'clear:left; '+
'border: 0;'+
'padding: 0;'+
'}'+
' .divThread, .divThread .reply, .divThread blockquote { '+
'font-size: '+(GM_getValue('opt_textsize') ? GM_getValue('opt_textsize') : DEF_TEXT_SIZE)+'px !important;'+
'word-wrap: break-word !important;'+
'max-width: '+((window.innerWidth/columns)-15)+'px;'+// must be in pixels or some texts will overflow
'min-width: 50% !important;'+
'border: 0;'+
'}'+
' .divThread .reply, .divThread blockquote { '+
'padding: 0 !important;'+
'margin:3px !important;'+
'}'+
' .divThread a img {'+
'margin: 0 5 !important;'+
'}'
+'.divThread .divThreadButtons { float:right; padding:0; margin-top:3; margin-right:3px; }'
+'#divHoriz span.omittedposts {font-size:10px !important;}'
+'#divHoriz span.postername, #divHoriz span.postertrip, #divHoriz span.commentpostername, #divHoriz span.filetitle, #divHoriz .replytitle, #divHoriz span.filesize, #divHoriz a.quotejs, #divHoriz .posttime { font-size: 11px !important; }'
//menu
+'#divMenu { position:fixed; right:35px; top: 2px; z-index:'+(BASE_ZINDEX+998)+'; padding:0 10px; margin:3px; font-size:11px; background-color:'+$(document.body).css('background-color')+'; border:1px solid #789922; }'
+'#divMenu a:hover, #divMenu a:active { color: #789922 !important; }'
+'#divMenu a { float:right; }'
//pagination edges
+'#divPageLeft, #divPageRight { position:absolute; top:0px; height:100%; width:13px; cursor:pointer; z-index:'+BASE_ZINDEX*2+'; vertical-align: middle; background-color:'+$(document.body).css('background-color')+'; opacity:0; overflow:hidden;}'
+'#divPageLeft { left:0px; }'
+'#divPageRight { right:0px; }'
+'.tablePagination { width:100%; height:100%; color:#000000; overflow:hidden; vertical-align:middle !important;}'
+'.tablePagination center { font-size:10px; }'
//shadow
+'.shadowww {box-shadow: 5px 5px 5px #222222;}'
//next page threads
+'.nextpageee {background-color: #eeeeee;}'
//highlight thread
+'.classHighlightThread {border:1px solid #789922;}'
// /b/ackwash compatibility
+'#backwash_tooltip { z-index:'+(BASE_ZINDEX+999)+' !important;}'
+'#divHoriz a.backlink {display:none !important;} ';
debug('CSS() calling distributeThreads()... ')
distributeThreads();
}
function isThreadPage(){
return (location.href.search(/\/res\/\d+/) > -1);
}
function getThreadIdByUrl(url){
try{
return url.match(/\/res\/(\d+)/)[1];
}catch(e){
return null;
}
}
function storeThread(t, type){
var threads = GM_getValue('threads');
if(!threads){//first insertion
threads = {};
threads.t = [];
threads.t.push(t);
if(type=='posted'){
threads.posted
}
}
//store changes
GM_setValue('threads', escape(threads.toSource()))
}
function paginate(keyCode){
var href, delay, currentPage = 0;
try{ currentPage = parseInt(location.href.match(/(\d+)$/)[1]); }catch(e){}
//cleanup
$(document).unbind('keyup.keys');
$('#divPageLeft').remove();
$('#divPageRight').remove();
if(keyCode==37 && currentPage > 0){ // paginate left
if(currentPage<=1)
href = location.href.replace(/(\d+)$/, '');
else
href = location.href.replace(/(\d+)$/, (currentPage-1));
//animate
$('#divBgHoriz').append($('<table>').css({width:'100%',height:'100%'}).html('<tr><td><center></center></tr></td>'));
$('#divHoriz').animate( {left: $('#divHoriz').outerWidth()});
//change page
delay = $('#divHoriz img').length > 20 ? 250 : 20 ;
setTimeout(function(){location.href = href},delay);
}else if(keyCode==39 && currentPage < $('.tablePages').text().match(/\[(\d+)\]\s*$/)[1] ){ // paginate right
$(document).unbind('keyup.keys');
if(currentPage==0)
href = location.href + '1';
else
href = location.href.replace(/(\d+)$/, (currentPage+1));
//animate
$('#divBgHoriz').append($('<table>').css({width:'100%',height:'100%'}).html('<tr><td><center></center></tr></td>'));
$('#divHoriz').animate( {left: -$('#divHoriz').outerWidth()});
//change page
delay = $('#divHoriz img').length > 20 ? 250 : 20 ;
setTimeout(function(){location.href = href}, delay);
}
}
// http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
function setSelectionRange(input, selectionStart, selectionEnd) {
if (input.setSelectionRange) {
input.focus();
input.setSelectionRange(selectionStart, selectionEnd);
}
else if (input.createTextRange) {
var range = input.createTextRange();
range.collapse(true);
range.moveEnd('character', selectionEnd);
range.moveStart('character', selectionStart);
range.select();
}
}
function setCaretToPos (input, pos) {
setSelectionRange(input, pos, pos);
}
function getStyle(elem, style)
{
return window.getComputedStyle(elem,null).getPropertyValue(style);
}
function createElement(type, attrArray, evtListener, html)
{
var node = document.createElement(type);
for (var attr in attrArray) if (attrArray.hasOwnProperty(attr)){
node.setAttribute(attr, attrArray[attr]);
}
if(evtListener){
var a = evtListener.split(' ');
node.addEventListener(a[0], eval(a[1]), eval(a[2]));
}
if(html)
node.innerHTML = html;
return node;
}
function getById(id, parent){
if(!parent)
return document.getElementById(id);
return parent.getElementById(id);
}
function getByTag(name, parent){
if(!parent)
return document.getElementsByTagName(name);
return parent.getElementsByTagName(name);
}
function xp(p, context, doc) {
if (!context)
context = document;
if (!doc)
doc = document;
var i, arr = [], xpr = doc.evaluate(p, context, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = 0; item = xpr.snapshotItem(i); i++)
arr.push(item);
return arr;
}
function debug(str)
{
if(!DEBUG)
return;
var d = document.getElementById('debugg');
if(!d){
var div = document.createElement('div');
div.setAttribute('id','divdebug');
div.setAttribute('style', 'background-color:#000000; position:fixed; bottom:3px; left:3px; width:30%; z-index:9999999999999;');
var closeButton = document.createElement('input');
closeButton.setAttribute('id','closedebug');
closeButton.setAttribute('type', 'button');
closeButton.setAttribute('value', 'close');
closeButton.setAttribute('onClick', 'this.parentNode.parentNode.removeChild(this.parentNode);');
d = document.createElement('textarea');
d.setAttribute('id','debugg');
d.setAttribute('style',"height:120px; width:99%; margin:2px; font-size:11px;");
div.appendChild(d);
div.appendChild(document.createElement('br'));
div.appendChild(closeButton);
document.body.appendChild(div);
}
d.innerHTML += '\n'+str;
d.scrollTop = d.scrollHeight;
}
0 comments:
Post a Comment