iichan catalog

Add catalog to http://iichan.hk/; Добавляет на ычан каталог с подрузкой.

当前为 2016-03-23 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         iichan catalog
// @namespace    http://your.homepage/
// @version      0.4
// @description  Add catalog to http://iichan.hk/; Добавляет на ычан каталог с подрузкой. 
// @author       You
// @match        http://iichan.hk/*
// @grant        none
// @run-at document-end
// ==/UserScript==
/*jshint multistr: true */
"use strict";
var catalog = null;
var scrollBuffer = 300;
var timeToFailure = 15000;
var backgroundColor = '#F0E0D6';

var noNextPage = false;
var activeDoc;
var pending;
var threadMap= {};
var bookmarkMap;
var styleCSS = '<style>\
.thread_wrap{text-align:center; display:inline-block; overflow: hidden; height: 400px; position:relative; vertical-align: top; margin:5px; }\
.thread_wrap:hover{overflow: visible; z-index: 900;}\
.thread_wrap_1:after {\
content: "";\
position: absolute;\
width: 100%;\
height: 30px;\
background: #111111;\
bottom: 0;\
left: 0;\
background: linear-gradient(to bottom, rgba(240,224,214,0.02) 0%,rgba(240,224,214,0.13)\
30%,rgba(240,224,214,0.62) 52%,rgba(240,224,214,0.94) 74%,rgba(240,224,214,1) 100%);} \
.thread_wrap:hover:after {\
content: "";\
width: 0;\
height: 0;}\
.thread_face{width:220px; min-height:400px; border:1px solid #800000; overflow:hidden;\
-webkit-box-sizing: border-box;  -moz-box-sizing: border-box;  box-sizing: border-box;\
padding:5px; text-align:center; display:inline-block;word-wrap: break-word; }\
.bookmark{margin:3px; opacity:0.3; }\
.bookmark:hover{opacity:0.7;}\
.bookmark.activeBtn{opacity:1;}\
</style>';

var sheet = (function() {
	// Create the <style> tag
	var style = document.createElement("style");
	// WebKit hack :(
	style.appendChild(document.createTextNode(""));
	// Add the <style> element to the page
	document.head.appendChild(style);
    
	return style.sheet;
})();

init();

function init(){
    scrollBuffer += window.innerHeight;
    pending = false; 
    activeDoc = document;    
    if(!isMainFrame()){
        return;
    }
    var adminbar = qs('.adminbar')[0];
    if(!adminbar){
        return;
    }

    makeCatalogButton(adminbar);
    loadBookmarkMap();


    catalog = makeCatalog();
    loadFontAwesome();
    window.addEventListener("scroll", testScrollPosition, false);
    //testScrollPosition();
}

function getBoardName(){
    var path = window.location.href;
    var parser = document.createElement('a');
    parser.href = path;
    path = parser.pathname;
    var reg = /^(?:\/)(\w*)(?:\/)/;    
    var res = path.match(reg);
    var boardName = (res.length)?res[0]:'';
    return boardName;
}

function nextDoc(doc){
    activeDoc = doc;
}

function appendPage(href){
    if(!href){
        return;
    }
    function addNextPage(doc){
        catalog.buildCatalog(getFormFromDoc(doc));
        nextDoc(doc);
        pending = false;
    }

    handleXHRDoc(href, addNextPage);    
}

function testScrollPosition(){
    if( noNextPage ){
        return;
    }

    if(isCatalogMode()){
        //Take the max of the two heights for browser compatibility
        if( !pending && window.pageYOffset + scrollBuffer > Math.max( document.documentElement.scrollHeight, document.documentElement.offsetHeight ) )
        {   
            pending = true;
          var  timeout = setTimeout( function(){pending=false;testScrollPosition();}, timeToFailure );
            var nextPage = getNextPage();
            //console.log('Next page', nextPage);
            //debugger;
            if(!!nextPage){
                appendPage(nextPage);
            }
            else{
                noNextPage = true;
            }
        }
    }  
}

function showCatalog(){
    catalog.show();
}

function makeCatalogButton(adminbar){
    var catalogLink = document.createElement('span');
    catalogLink.innerHTML =  '[<a href="#" id="id_catalog">Каталог</a>]';
    //console.log(catalogLink);
    adminbar.appendChild(catalogLink);
    catalogLink.onclick = function(e){
        //console.log(document.querySelector('form#delform'));
        if (catalog.shown) {
            resetCatalog();
        }
        showCatalog();
        e.preventDefault();
    };
}

function getThreadsListFromForm(form){
    //    var form = document.querySelector('form#delform');
    var tempList = form.querySelectorAll('div');
    var threadList = [];
    var thread = null;
    for(var i =0; i<tempList.length;++i){
        if(tempList[i].querySelector('.reflink')){
            thread = makeThread(tempList[i]);
            //threadList.push(tempList[i]);
            threadList.push(thread);
        }
    }
    return threadList;
}

function makeCatalog(){
   var catalogElt = document.createElement('div');
    var formElt = getFormFromDoc(document);
    var formParent = formElt.parentElement; 
    var newForm = formElt.cloneNode(true);
    newForm.setAttribute('id', '');    
    formParent.insertBefore(newForm, formElt.nextSibling);
    newForm.innerHTML ='';
    newForm.setAttribute('id', 'catalog-form');    
    
    //var catalog = new Catalog(catalogElt, formElt);
    catalog = new Catalog(catalogElt, newForm, formElt);
    catalog.reset();
    return catalog;
}

function makeThread(threadElt){
    return new Thread(threadElt);
}

function getFormFromDoc(doc){
    var form =  doc.querySelectorAll('form#delform')[0];
    if(!form){
        throw new Error('Form element not found');
    }
    return form;
}

function getPathFromURL(url){
    var parser = document.createElement('a');
    parser.href = url;
   var path = parser.pathname;
    return path;
}

function getByRegex(text, regex){
    var match = text.match(regex);
    if(match)
    {
        if(match.length){
            return match;
        }
    }
}

//Get number of post/img from text by regexp.
function parseCount(text, regex){
    var result = getByRegex(text, regex);
    var count = 0;
    if(result){
        count = result[1];
    }
    if(!count){
        return 0;
    }
    return count;
}

function Thread (thread){
    if (!thread) {
        return;
    }
    this.element = undefined;
    this.threadLink = thread.querySelector('.reflink a');   
    this.threadLink = this.threadLink.getAttribute('href');
    this.threadLink = getPathFromURL(this.threadLink);
    this.imgSrc = thread.querySelector('a img');
    this.imgSrc = this.imgSrc.getAttribute('src');
    this.threadName = thread.querySelector('.filetitle');
    this.threadName = this.threadName.textContent;
    this.threadText = thread.querySelector('blockquote');
    this.threadText = this.threadText.innerHTML;
    this.postNum = 0;
    this.imgNum = 0;
    this.lastPost ='';

    var omitted = thread.querySelector('.omittedposts');
    if(omitted){
        omitted= omitted.textContent;     
        if(!!omitted){
            this.postNum = parseInt(parseCount(omitted, /([0-9]*)(?:\s)*(?:сообщений)/),10);
            this.imgNum = parseInt(parseCount(omitted, /([0-9]*)(?:\s)*(?:изображений)/),10);
        }
    }

    var posts = thread.querySelectorAll('.reply');
    var date = '';
    var post = null;
    if (!!posts) {
        this.postNum+=posts.length;
        for (var i = 0; i < posts.length; i++) {
            post = posts[i];
            if (!!post.querySelector('img')) {
                this.imgNum+=1;
            }
            if (i === posts.length-1) {
                date = post.querySelector('label').textContent;
                date = date.replace(/(\D*)/,'');
                this.lastPost = date;
            }
        }
    }   
}

Thread.threadFromThread = function(threadElt, href) {
    var thread = new Thread();
    thread.threadLink = href;
    thread.imgSrc = threadElt.querySelector('a img').getAttribute('src');
    thread.threadName = threadElt.querySelector('label .filetitle').textContent;
    thread.threadText = threadElt.querySelector('blockquote').innerHTML;
    thread.postNum = 0;
    thread.imgNum = 0; 
    var post = null;
    var posts = threadElt.querySelectorAll('.reply');
    var date;
    if (!!posts) {
        thread.postNum+=posts.length;
        for (var i = 0; i < posts.length; i++) {
            post = posts[i];
            if (!!post.querySelector('img')) {
                thread.imgNum+=1;
            }
            if (i === posts.length-1) {
                date = post.querySelector('label').textContent;
                date = date.replace(/(\D*)/,'');
                thread.lastPost = date;
            }
        }
    }
    thread.element = buildCatalogPost(thread).element;
    return thread; 
};

function buildCatalogPost(thread){
    var template = '<div class = "thread_wrap"><div class = "thread_face reply">\
<a href="'+ thread.threadLink+ '"><img src="' + thread.imgSrc+'" class="thumb"  style=" display: block; margin: 0 auto; float:none;"></a>\
<span class="filetitle thread-title" style=" display:inline-block">'+thread.threadName+'</span>\
<p style= "margin-top:0.6em;">О:'+thread.postNum+'  И:'+thread.imgNum+ '<div style="margin-top:-10px;"></div>\
<span>L: ' + thread.lastPost +'</p></span>\
<span>' + thread.threadText + '</span></div></div>';

    //console.log(template);

    var post = document.createElement('div');
    post.innerHTML = template; 
    post = post.firstChild;
    thread.element = post; 
       
    addBookmarkBtn(thread);
    return thread; 
}
//<p>О: ' + thread.lastPost + '</p>\
function isBookmark(name){
    if (bookmarkMap[name]!==undefined) {
        return true;
    }
    else{
        return false;
    }
}

function loadBookmarkMap (){
    var ls = getBoardName() + '-'+'bookmarks';
    //console.log(ls);
    var entry = localStorage.getItem(ls);
    if (!!entry) {
        bookmarkMap = JSON.parse(entry);        
    }
    else{
        bookmarkMap = {};
    }
}

function updateBookmarkStorage(){
 var ls = getBoardName() + '-'+'bookmarks';
     //console.log(bookmarkMap);
     var value = JSON.stringify(bookmarkMap);
        localStorage.setItem(ls, value);
}

function setBookmarkThread(btn, val){
    var threadLink = btn.bindedElt.getAttribute('date-thread'); 
    if (val){
        bookmarkMap[threadLink]=true;       
    }
    else{        
        delete bookmarkMap[threadLink];        
    }
    updateBookmarkStorage();    
}

function removeBookmarkFromThread(threadLink){
    delete bookmarkMap[threadLink];
    updateBookmarkStorage(); 
}

function bookmarkThread(btn){  
    setBookmarkThread(btn, true);
}

function unbookmarkThread(btn){
    setBookmarkThread(btn, false);   
}

function addBookmarkBtn(thread){
    var post =thread.element;
    var nameElt = post.querySelector('.thread-title');
    if (!!nameElt) {
        var bookmark = elementFromText('<span class="bookmark"></span>');
        nameElt.insertBefore(bookmark, nameElt.firstChild);
        bookmark.setAttribute('date-thread',thread.threadLink);
        var boolBookmark = isBookmark(thread.threadLink);
        var settings = {            
        name:'bookmark',
        active:boolBookmark,
        glyphOn:'fa-bookmark-o',
        glyphOff:'fa-bookmark-o',
        callbackOn:bookmarkThread,
        callbackOff:unbookmarkThread};
        makeToggleButton(bookmark,settings);
    }
}

function resetCatalog(){

    function reset (doc){
        //console.log(' reset ');
        activeDoc = doc;
        //var catalog = makeCatalog();
        catalog.reset();
    }
    //console.log(' reset Catalog');
    handleXHRDoc(getBoardName(), reset);
}

function isMainFrame(){
    if(qs('form#delform').length){
        //console.log('iichan main frame');
        return true;
    }
    return false;
}

function getNextPage(pagePane){
    pagePane = getPagePane();
    var pageList = pagePane.querySelectorAll('td')[2];
    var next = pageList.querySelectorAll('td form')[0];

    if(!next){
        return false;
    }
   var href = next.getAttribute('action');   
    //console.log(href);
    return href;
}


function getFirstPage(pagePane){
    pagePane = getPagePane();
    if(!pagePane){
        return false;
    }
    var pageList = pagePane.querySelectorAll('td')[1];

    var first = pageList.querySelectorAll('a')[0];

    var href = first.getAttribute('href');   
    //console.log(href);

    return href;
}

function getPagePane(doc){
    doc = activeDoc;
    var paginator = doc.querySelector('body>table>tbody>tr');
    //console.log(pagePane);
    return paginator;
}

function isCatalogMode(){
    return catalog.isShown();
}




function Catalog (catalogElt, formElt, oldForm){
    this.threadList = [];
    this.catalogElt = catalogElt;
    this.shown = false;
    this.formElt = formElt;
    this.oldForm = oldForm;
    this.threadMap = {};
    this.isShown = function(){
        return this.shown;
    };
    this.bookmarkShown = false;
    this.makeBookmarks = function(){
        var self = this;
        this.bookmarkShown = true;
        var keys = [];
        var index;
        for(index in bookmarkMap) { 
           if (bookmarkMap.hasOwnProperty(index)) {
               var attr = bookmarkMap[index];
               keys.push(index);
           }
        }
        index = 0; 
        if (keys.length) {
            handleXHRDoc(keys[index], testSuccess, testFail);
            index++;
        }
        function testSuccess(doc, reqString){
           var threadElt = doc.querySelector('#delform>div');
           var thread = Thread.threadFromThread(threadElt,reqString);
           //console.log(thread);
           self.appendBookmarkPost(thread.element);
            //console.log(thread.element);          
           if (index<keys.length) {
                handleXHRDoc(keys[index], testSuccess, testFail);
                index++;
           }
        }

       function testFail(doc, reqString){
        console.log('fail xhr');
        removeBookmarkFromThread(reqString);
        if (index<keys.length) {
            handleXHRDoc(keys[index], testSuccess, testFail);
            index++;
            }
        }
    };
    this.show = function (){
        formElt.innerHTML ='';
        formElt.appendChild(catalog.catalogElt);
        
        //Hide dollchan forms when catalog is shown
        //oldForm.style.display='none';
        sheet.insertRule("body>form:not(#catalog-form) { display:none; }",0);
        sheet.insertRule("#de-main + .de-parea{ display:none; }",0);       
        var postArea = document.querySelector('.postarea');
        if(!!postArea){
            console.log(postArea);
            postArea.parentElement.removeChild(postArea);
        }
//        var newTreadForms = document.querySelectorAll('.de-parea');
//        if(newTreadForms.lenght==2){
 //       var first = newTreadForms[0];
 //           first.parentElement.removeChild(first);
//        }
        
        this.shown = true;
    };

    this.reset = function(){
        var style = styleCSS;
        //console.log(style);
        threadMap ={};        
        this.bookmarkShown = false;
        this.catalogElt.innerHTML = style;
        appendPage(getBoardName());
    };

    this.appendBookmarkPost = function(post){
        this.catalogElt.insertBefore(post,this.catalogElt.firstChild); 
    };

    this.buildCatalog = function (form){
        var catalogElement = this.catalogElt;
        var threadList = getThreadsListFromForm(form);
        var threadLink = ''; 
        var post = null;
        var thread =null;
        for(var i =0; i< threadList.length;++i){
            thread = buildCatalogPost(threadList[i]);
            threadLink = thread.threadLink;
            if ((threadMap[threadLink])===undefined) {
                threadMap[threadLink] = thread;
                post = thread.element;
                if (isBookmark(threadLink)) {
                    //this.appendBookmarkPost(post); 
                }
                else{
                    catalogElement.appendChild(post);  
                }
                              
            }
            else{
                console.log('duble thread '+ threadLink);
            }

        }

        if (!this.bookmarkShown) {
            this.makeBookmarks();
        }
        //console.log(catalogElement);
        return catalogElement;
    };

}

function loadFontAwesome(){
   var fontAwesomeLoader = elementFromText('<link rel="stylesheet" type="text/css" media="screen" href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.css" />');    
   document.head.appendChild(fontAwesomeLoader);
}


function handleXHRDoc(reqString, callback, errorCallback){
    var doc = document.implementation.createHTMLDocument("example");

    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open('GET', reqString, true);
    xmlhttp.send(null);
    xmlhttp.onreadystatechange = handle;

    function handle(){              
        if (xmlhttp.readyState == 4) {
            if(xmlhttp.status == 200) {
                doc.documentElement.innerHTML = xmlhttp.responseText;
                //console.log(doc);
                callback(doc, reqString);
            }           
            else{ 
                if (!!errorCallback) {
                    errorCallback(xmlhttp.status, reqString);                    
                }
                else{
                 console.log('Error xhr of ' + reqString);                    
                }
            }
        }
    }    
}

function elementFromText(text){
    var div = document.createElement('div');
    div.innerHTML = text;
    var element = div.firstChild;
    return element; 
}

function qs(selector){
    return document.querySelectorAll(selector);
}



function makeToggleButton(elem, settings){
    var btn = new ButtonToggle(settings);
    btn.bindElement(elem);
    btn.load();
    elem.addEventListener('click', btn.handlerClick, false);
    //console.log(btn);
    return btn;
}

 function ButtonToggle(settings) {
        var self = this;

        var modGliph = '';
        this.name = settings.name;

        this.active = !!settings.active;
        this.bindedElt = null;
        this.glyphElt = document.createElement('i');
        this.bindElement = function (elem) {
            self.bindedElt = elem;
            if (self.bindedElt.length > 0) {
                elem.insertChildBefore(self.glyphElt, elem.FirstChild);
            }
            else {
                elem.appendChild(self.glyphElt);
            }
        };

        this.glyphOn = 'fa ' + settings.glyphOn + ' ' + modGliph;
        this.glyphOff = 'fa ' + settings.glyphOff + ' ' + modGliph;
        this.callbackOn = settings.callbackOn;
        this.callbackOff = settings.callbackOff;
        this.save = function () {
            
        };
        this.load = function () {
            self.setActive(self.active);
        };
        this.handlerClick = function (e) {
            if (self.active) {
                self.setActive(false);
            }

            else {
                self.setActive(true);
            }
        };

        this.setActive = function (val) {
            val = !!val;
            self.active = val;
            self.save(val);

            if (val) {
                if (!!self.bindedElt) {
                    self.bindedElt.classList.add('activeBtn');
                    self.glyphElt.className = self.glyphOn;
                }

                if (!!self.callbackOn) {
                    self.callbackOn(self);
                }
            }

            else {
                if (!!self.bindedElt) {
                    self.bindedElt.classList.remove('activeBtn');
                    self.glyphElt.className = self.glyphOff;
                }

                if (!!self.callbackOff) {
                    self.callbackOff(self);
                }
            }
        };
    }