iichan catalog

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

目前為 2016-03-23 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);
                }
            }
        };
    }