﻿// ==UserScript==
// @name           AutoPagerize for IE7 with Turnabout
// @namespace      http://watcher.moe-nifty.com/
// @description    loading next page and inserting into current page.
// @include        http://*
// @include        https://*
// @exclude        https://mail.google.com/*
// ==/UserScript==
//
// auther:  tsupo http://watcher.moe-nifty.com/
// version: 0.0.31t5 2008-07-02T23:34:53+09:00
//
// this script based on
// AutoPagerize version: version: version: 0.0.31 2008-06-29T03:49:37+09:00 ( http://userscripts.org/scripts/show/8551 id:swdyh) and
// GoogleAutoPager(http://la.ma.la/blog/diary_200506231749.htm) and
// estseek autopager(http://la.ma.la/blog/diary_200601100209.htm).
// thanks to ma.la.
//
// and used as references from
// xAutoPagerize (http://ss-o.net/xuserbookjs.html)
// AutoPagerlike (http://furyu.tea-nifty.com/annex/2008/03/autopagerlike_s_56ec.html)
// AutoPagerize for IE7Pro(http://kuee.org/mt/2007/12/autopagerize_for_ie7pro.html)
//
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//

var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
// var URL = 'http://userscripts.org/scripts/show/8551';
var URL = 'http://watcher.moe-nifty.com/memo/2008/07/autopagerize.html';
var VERSION = '0.0.31t5';
var DEBUG = false
var AUTO_START = true;
var CACHE_EXPIRE = 24 * 60 * 60 * 1000;
var BASE_REMAIN_HEIGHT = 400;
var FORCE_TARGET_WINDOW = true;
var USE_COUNTER = true;
var SITEINFO_IMPORT_URLS = [
    'http://wedata.net/databases/AutoPagerize/items.json',
];
var COLOR = {
    on: '#0f0',
    off: '#ccc',
    loading: '#0ff',
    terminated: '#00f',
    error: '#f0f'
};
var SITEINFO = [
    /* sample
    {
        url:          'http://(.*).google.+/(search).+',
        nextLink:     'id("navbar")//td[last()]/a',
        pageElement:  '//div[@id="res"]/div',
        exampleUrl:   'http://www.google.com/search?q=nsIObserver'
    },
    */
    /* template
    {
        url:          '',
        nextLink:     '',
        pageElement:  '',
        exampleUrl:   ''
    }
    */
];
var MICROFORMAT = {
    url:          '.*',
    nextLink:     '//a[@rel="next"] | //link[@rel="next"]',
    insertBefore: '//*[contains(@class, "autopagerize_insert_before")]',
    pageElement:  '//*[contains(@class, "autopagerize_page_element")]'
};

//loading animation generated with http://www.ajaxload.info/
// and encoded with http://www.kawa.net/works/js/data-scheme/base64.html
var LOADING_IMAGE = ['url(','data:image/gif;base64,',
			'R0lGODlhEAAQAPQAAAD//wAAAAD4+AA4OACEhAAGBgAmJgDW1gCoqAAWFgB2dgBmZgDk5ACYmADG',
			'xgBISABWVgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH+GkNy',
			'ZWF0ZWQgd2l0aCBhamF4bG9hZC5pbmZvACH5BAAKAAAAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAA',
			'EAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla',
			'+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAAKAAEALAAAAAAQABAAAAVoICCKR9KM',
			'aCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr',
			'6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkEAAoAAgAsAAAAABAA',
			'EAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoL',
			'LoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkEAAoAAwAsAAAA',
			'ABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7',
			'baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAAKAAQALAAAAAAQABAAAAVgICCO',
			'ZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYA',
			'qrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAAKAAUALAAAAAAQABAAAAVf',
			'ICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0Ua',
			'FBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==',')'].join('');
var naviType = 'number'; // 'link' or 'number'; /* {@@} */

var AutoPager = function(info) {
alert2("Autopagerize: initializing...");
    this.pageNum = 1;
    this.info = info;
    this.state = AUTO_START ? 'enable' : 'disable';
    var self = this;
    var url = this.getNextURL(info.nextLink, document);

    if ( !url ) {
        debug("getNextURL returns null.", info.nextLink);
        return;
    }
    if (info.insertBefore) {
        this.insertPoint = getFirstElementByXPath(info.insertBefore);
    }

    if (!this.insertPoint) {
        var lastPageElement = getElementsByXPath(info.pageElement).pop();
        if (lastPageElement) {
            this.insertPoint = lastPageElement.nextSibling ||
                lastPageElement.parentNode.appendChild(document.createTextNode(' '));
        }
    }

    if (!this.insertPoint) {
        debug("insertPoint not found.", lastPageElement, info.pageElement)
        return;
    }

    this.requestURL = url;
    this.loadedURLs = {};
    var toggle = function() {self.stateToggle()};
    this.toggle = toggle;
    GM_registerMenuCommand('AutoPagerize - on/off', toggle);
    this.scroll= function() { self.onScroll() };
//  window.addEventListener("scroll", this.scroll, false);
    window.attachEvent("on" + "scroll", this.scroll);

    this.initIcon();
    this.initHelp();
//  this.icon.addEventListener("mouseover",
//      function(){self.viewHelp()}, true);
    this.icon.attachEvent("on" + "mouseover",
        function(){self.viewHelp()});
    var scrollHeight = getScrollHeight();
    var bottom = getElementPosition(this.insertPoint).top ||
        this.getPageElementsBottom() ||
        (Math.round(scrollHeight * 0.8));
    this.remainHeight = scrollHeight - bottom + BASE_REMAIN_HEIGHT;
    this.onScroll();

    // append space to show scrollbar surely. (from http://ss-o.net/userjs/oAutoPagerize.user.js)
    var pageHeight = document.documentElement.clientHeight;
    if ( (window.innerHeight || document.body.offsetHeight) >= pageHeight) {
        var st = document.body.appendChild(document.createElement('div')).style;
        st.position = "absolute";
        st.bottom ="-1px";
        st.height ="1px";
        st.width  ="1px";
    }
}

AutoPager.prototype.getPageElementsBottom = function() {
    try {
        var elem = getElementsByXPath(this.info.pageElement).pop();
        return getElementBottom(elem);
    }
    catch(e) {}
}

AutoPager.prototype.initHelp = function() {
    var helpDiv = document.createElement('div');
    helpDiv.setAttribute('id', 'autopagerize_help');
 /* helpDiv.setAttribute('style', 'padding:5px;position:fixed;' +
                     'top:-200px;right:3px;font-size:10px;' +
                     'background:#fff;color:#000;border:1px solid #ccc;' +
                     'z-index:256;text-align:left;font-weight:normal;' +
                     'line-height:120%;font-family:verdana;'); */
    helpDiv.style.cssText = 'padding:5px;position:absolute;' +
                     'top:3px;right:3px;font-size:10px;' +
                     'background:#fff;color:#000;border:1px solid #ccc;' +
                     'z-index:256;text-align:left;font-weight:normal;' +
                     'line-height:120%;font-family:verdana;'

    var toggleDiv = document.createElement('div');
 // toggleDiv.setAttribute('style', 'margin:0 0 0 50px;');
    toggleDiv.style.cssText = 'margin:0 0 0 50px;font-size:10px;font-family:verdana;';
    var a = document.createElement('a');
    a.setAttribute('class', 'autopagerize_link');
    a.innerHTML = 'on/off';
    a.href = 'javascript:void(0)';
    var self = this;
    var toggle = function() {
        self.stateToggle();
     // helpDiv.style.top = '-200px';
        helpDiv.style.display = 'none';
    }
//  a.addEventListener('click', toggle, false);
    a.attachEvent("on" + "click", toggle);
    toggleDiv.appendChild(a);

//  var s = '<div style="width:100px; float:left;">';
    var s = '<div style="width:100px; float:left;display:inline;margin:0;padding:0;">';
    for (var i in COLOR) {
     // s += '<div style="float:left;width:1em;height:1em;' +
     //     'margin:1px 3px;background-color:' + COLOR[i] + ';' +
     //     '"></div><div style="margin:0 3px">' + i + '</div>';
        s += '<div style="clear:left;float:left;width:1em;height:1em;' +
             'margin:1px 3px;display:inline;background-color:' + COLOR[i] + ';' +
             '"></div><div style="margin:1px 3px; font-size:10px;font-family:verdana;">' + i + '</div>';
    }
    s += '</div>';
    var colorDiv = document.createElement('div');
    colorDiv.innerHTML = s;
    helpDiv.appendChild(colorDiv);
    helpDiv.appendChild(toggleDiv);

    var versionDiv = document.createElement('div');
//  versionDiv.setAttribute('style', 'clear:both;');
    versionDiv.style.cssText = 'clear:both; font-size:10px;font-family:verdana;';
    versionDiv.innerHTML = '<a href="' + URL +
        '">AutoPagerize</a> ver ' + VERSION;
    helpDiv.appendChild(versionDiv);
    document.body.appendChild(helpDiv);

    var proc = function(e) {
     // var c_style = document.defaultView.getComputedStyle(helpDiv, '');
        var c_style = helpDiv.currentStyle || document.defaultView.getComputedStyle(helpDiv, '');
        var s = ['top', 'left', 'height', 'width'].map(function(i) {
         // return parseInt(c_style.getPropertyValue(i)) });
            return parseInt(c_style[i]) });     /* {@@} */
        if (e.clientX < s[1] || e.clientX > (s[1] + s[3] + 11) ||
            e.clientY < s[0] || e.clientY > (s[0] + s[2] + 11)) {
             // helpDiv.style.top = '-200px';
                helpDiv.style.display = 'none'; /* {@@} */
        }
    }
//  helpDiv.addEventListener('mouseout', proc, false);
    helpDiv.attachEvent("on" + "mouseout", proc);
    this.helpLayer = helpDiv;
}

AutoPager.prototype.viewHelp = function() {
 // this.helpLayer.style.top = '3px';
    this.helpLayer.style.display = 'block'; /* {@@} */
}

AutoPager.prototype.onScroll = function() {
    var scrollHeight = Math.max(document.documentElement.scrollHeight,
                                document.body.scrollHeight);
//  var remain = scrollHeight - window.innerHeight - window.scrollY;
    var remain = scrollHeight - (window.innerHeight || document.body.clientHeight) - (document.body.scrollTop || document.documentElement.scrollTop)
    if (this.state == 'enable' && remain < this.remainHeight) {
          this.request();
    }
}

AutoPager.prototype.stateToggle = function() {
    if (this.state == 'enable') {
        this.disable();
    }
    else {
        this.enable();
    }
}

AutoPager.prototype.enable = function() {
    this.state = 'enable';
    this.icon.style.background = COLOR['on'];
    this.icon.style.opacity = 1;
}

AutoPager.prototype.disable = function() {
    this.state = 'disable';
    this.icon.style.background = COLOR['off'];
    this.icon.style.opacity = 0.5;
}

AutoPager.prototype.request = function() {
    if (!this.requestURL || this.lastRequestURL == this.requestURL) {
        return;
    }
//  if (!this.requestURL.match(/^http:/)) {
//      this.requestURL = pathToURL(location.href, this.requestURL);
//  }
    if ( !this.canHandleCrossDomainRequest() ) {
        return;
    }

    this.lastRequestURL = this.requestURL;
    var self = this;
    var mime = 'text/html; charset=' + document.characterSet;
    var opt = {
        method: 'get',
        url: this.requestURL,
        overrideMimeType: mime,
        onerror: this.error,
        onload: function(res){
            self.requestLoad.apply(self, [res])
        }
    };
    this.showLoading(true);
    GM_xmlhttpRequest(opt);
}

AutoPager.prototype.showLoading = function(sw) {
    if (sw) {
        this.icon.style.background = COLOR['loading'];
	this.icon.style.backgroundImage=LOADING_IMAGE; /* {@@} */
	this.icon.style.backgroundRepeat='no-repeat';  /* {@@} */
	this.icon.style.backgroundPosition='center';   /* {@@} */
    }
    else {
        this.icon.style.background = COLOR['on'];
	this.icon.style.backgroundImage=''; /* {@@} */
	if (this.cleanup) {                 /* {@@} */
		this.cleanup();             /* {@@} */
		this.cleanup = null;        /* {@@} */
	}                                   /* {@@} */
    }
}

AutoPager.prototype.requestLoad = function(res) {
    if ( !this.canHandleCrossDomainRequest() ) {
        return;
    }

    var t = res.responseText;
    var htmlDoc = createHTMLDocumentByString(t);
    AutoPager.documentFilters.forEach(function(i) {
        i(htmlDoc, this.requestURL, this.info)
    }, this);
    try {
        var page = getElementsByXPath(this.info.pageElement, htmlDoc);
        var url = this.getNextURL(this.info.nextLink, htmlDoc);
    }
    catch(e){
        log(e);
        this.error();
        return;
    }

    if (!page || page.length < 1 ) {
        debug('pageElement not found.' , this.info.pageElement);
        this.terminate();
        return;
    }

    if (this.loadedURLs[this.requestURL]) {
        debug('page is already loaded.', this.requestURL, this.info.nextLink);
        this.terminate();
        return;
    }

    this.loadedURLs[this.requestURL] = true;
    page = this.addPage(htmlDoc, page);
    AutoPager.filters.forEach(function(i) {
        i(page);
    });
    this.requestURL = url;
    this.showLoading(false);
    this.onScroll();
    if (!url) {
        debug('nextLink not found.', this.info.nextLink, htmlDoc);
        this.terminate();
    }
}

AutoPager.prototype.addPage = function(htmlDoc, page) {
/***
    var hr = document.createElementNS(HTML_NAMESPACE, 'hr')
    var p = document.createElementNS(HTML_NAMESPACE, 'p')
    var self = this

    if (page[0] && page[0].tagName == 'TR') {
        var insertParent = this.insertPoint.parentNode
        var colNodes = getElementsByXPath('child::tr[1]/child::*[self::td or self::th]', insertParent)

        var colums = 0
        for (var i = 0, l = colNodes.length; i < l; i++) {
            var col = colNodes[i].getAttribute('colspan')
            colums += parseInt(col, 10) || 1
        }
        var td = document.createElement('td')
        // td.appendChild(hr)
        td.appendChild(p)
        var tr = document.createElement('tr')
        td.setAttribute('colspan', colums)
        tr.appendChild(td)
        insertParent.insertBefore(tr, this.insertPoint)
    }
    else {
        this.insertPoint.parentNode.insertBefore(hr, this.insertPoint)
        this.insertPoint.parentNode.insertBefore(p, this.insertPoint)
    }

    p.innerHTML = 'page: <a class="autopagerize_link" href="' +
        this.requestURL + '">' + (++this.pageNum) + '</a>'
    return page.map(function(i) {
        var pe = document.importNode(i, true)
        self.insertPoint.parentNode.insertBefore(pe, self.insertPoint)
        return pe
    })
***/

    // from http://ss-o.net/userjs/oAutoPagerize.user.js
    var pib = this.insertPoint.parentNode;
    var div = document.createElement('div');
    var tmpl = {
        number:'<hr style="clear:both;" /><p>page: <a class="autopagerize_link" href="%s">%n</a></p>'
        ,link:'<hr style="clear:both;" /><p>AutoPagerized: <a class="autopagerize_link" href="%s">%s</a></p>'
    }
    div.innerHTML = (tmpl[naviType]||tmpl['number']).replace(/%s/g, this.requestURL).replace(/%n/g,++this.pageNum);
    pib.insertBefore(div, this.insertPoint);
    var self = this;
    var fragmentsStrings = [];
    page.forEach(function(node) {
        fragmentsStrings.push(node.outerHTML);
    });
    var portNode = document.createElement('div');
    portNode.innerHTML = fragmentsStrings.join('\n');
    this.insertPoint.parentNode.insertBefore(portNode, this.insertPoint);

    return page; /* {@@} */
}

AutoPager.prototype.initIcon = function() {
    var div = document.createElement("div");
    div.setAttribute('id', 'autopagerize_icon');
    with (div.style) {
        fontSize   = '12px';
        position   = 'fixed';
        top        = '3px';
        right      = '3px';
        background = COLOR['on'];
        color      = '#fff';
        width      = '16px'; /* {@@} */
        height     = '16px'; /* {@@} */
        zIndex = '255';
        if (this.state != 'enable') {
            background = COLOR['off'];
        }
    }

    if (div.style.setExpression) { // via http://d.hatena.ne.jp/shogo4405/20060919/1158664960 /* {@@} */
	div.style.position = 'absolute';
	var root = document.documentElement.scrollHeight > document.body.scrollHeight ? 'document.documentElement' : 'document.body';
	div.style.setExpression("top", "3 + parseInt("+root+".scrollTop) + 'px'");
    };                                                                                        /* {@@} */

    document.body.appendChild(div);
    this.icon = div;
}

AutoPager.prototype.getNextURL = function(xpath, doc) {
    var next = getFirstElementByXPath(xpath, doc);
    if (next) {
        var url = next.href || next.action || next.value;
        if (!url.match(/^http:/)) {
            url = pathToURL(url);
        }
        return url;
    }
}

AutoPager.prototype.canHandleCrossDomainRequest = function(url) {
    if ( !supportsFinalUrl() ) {
        if (!isSameDomain(this.requestURL)) {
            this.error();
            return false;
        }
    }
    return true;
}

AutoPager.prototype.terminate = function() {
    this.icon.style.background = COLOR['terminated'];
//  window.removeEventListener('scroll', this.scroll, false);
    window.detachEvent('on' + 'scroll', this.scroll);
    var self = this;
    setTimeout(function() {
        self.icon.parentNode.removeChild(self.icon);
    }, 1500);
}

AutoPager.prototype.error = function() {
    this.icon.style.background = COLOR['error'];
//  window.removeEventListener('scroll', this.scroll, false);
    window.detachEvent('on' + 'scroll', this.scroll);
}

AutoPager.documentFilters = [];
AutoPager.filters = [];

function Counter() {}
Counter.DATA_KEY = 'counter_data';

Counter.get = function() {
    return eval(GM_getValue(Counter.DATA_KEY)) || {};
}

Counter.set = function(val) {
    return GM_setValue(Counter.DATA_KEY, uneval(val));
}

Counter.up = function() {
    var date = new Date();
    var date_y = date.getFullYear();
    var date_m = date.getMonth() + 1;
    var date_d = date.getDate();
    var counter_data = Counter.get();
    counter_data[date_y] = counter_data[date_y] || {};
    counter_data[date_y][date_m] = counter_data[date_y][date_m] || {};
    counter_data[date_y][date_m][date_d] =
        (counter_data[date_y][date_m][date_d] || 0) + 1;
    Counter.set(counter_data);
    return counter_data[date_y][date_m][date_d];
}

Counter.reset = function() {
    return Counter.set({});
}

Counter.total = function() {
    var total = 0;
    var counter_data = Counter.get();
    for (var year in counter_data) {
        for (var month in counter_data[year]) {
            for (var date in counter_data[year][month]) {
                total += counter_data[year][month][date];
            }
        }
    }
    return total;
}

Counter.view = function() {
    var div = Counter.layer();
    var couter_data = Counter.get();
    var comp = function(a, b) { return b - a; };
    Counter.each(couter_data,function(year, year_data) {
        Counter.each(year_data, function(month, month_data) {
            var img = document.createElement('img');
            img.src = Counter.chart(year, month, month_data);
            div.appendChild(img);
        }, comp);
    }, comp);
    window.scrollTo(0, 0);
}

Counter.layer = function() {
    var id = 'autopagerize_count_chart';
    var e = document.getElementById(id);
    if (e) {
        e.parentNode.removeChild(e);
    }
    var div = document.createElement('div');
    div.id = id;
    div.style.position = 'absolute';
    div.style.top = '0px';
    div.style.left = '0px';
    div.style.width = '100%';
    div.style.border = '1px solid #ccc';
    div.style.backgroundColor = '#fff';
    div.style.zIndex = '100';
    document.body.appendChild(div);
    var h1 = document.createElement('h1');
    h1.appendChild(document.createTextNode('AutoPagerize Count Chart: ' + Counter.total()));
    div.appendChild(h1);
    return div;
}

Counter.each = function(obj, func, comp) {
    var ks = [];
    for (var k in obj) {
        ks.push(k);
    }
    if (comp) {
        ks.sort(comp);
    }
    for (var i = 0; i < ks.length; i++) {
        func(ks[i], obj[ks[i]]);
    }
}

Counter.chart = function(year, month, month_data) {
    var max = 0;
    var total = 0;
    var x_label = [];
    var val = [];
    for (var i = 1; i <= 31; i++) {
        var v = month_data[i] || 0;
        x_label.push(i);
        val.push(v);
        max = Math.max(max, v);
        total += v;
    }
    var range = Counter.ceil(max);
    var y_label = [];
    for (var i = 0; i <= 10; i++) {
        y_label.push(range / 10 * i);
    }
    var xl = function(num, list) {
        return num + ':|' + list.join('|') + '|';
    }
    var url = 'http://chart.apis.google.com/chart?' +
        'cht=bvs&chs=500x250&chbh=10&chxt=x,y,x&chco=adff2f' +
        '&chd=t:' + val.join(',') +
        '&chxl=' + xl(0, x_label) + xl(1, y_label) + xl(2, ['  total: ' + total]) +
        '&chds=0,' + (range * 1.1) +
        '&chtt=' + year + '/' + month;
    return url;
}

Counter.ceil = function(val) {
    var n = 1;
    var limit = 100;
    for (var i = 0; i < limit; i++) {
        if (n > val) {
            return n;
        }
        n = n * 5;
        if (n > val) {
            return n;
        }
        n = n * 2;
    }

    return n; /* {@@} */
}

if (USE_COUNTER) {
    GM_registerMenuCommand('AutoPagerize - count chart', Counter.view);
    AutoPager.documentFilters.push(function() {
        Counter.up();
    });
}

var parseInfo = function(str) {
    var lines = str.split(/\r\n|\r|\n/);
    var re = /(^[^:]*?):(.*)$/;
    var strip = function(str) {
        return str.replace(/^\s*/, '').replace(/\s*$/, '');
    };
    var info = {};
    for (var i = 0; i < lines.length; i++) {
        if (lines[i].match(re)) {
            info[RegExp.$1] = strip(RegExp.$2);
        }
    }
    var isValid = function(info) {
        var infoProp = ['url', 'nextLink', 'pageElement'];
        for (var i = 0; i < infoProp.length; i++) {
            if (!info[infoProp[i]]) {
                return false;
            }
        }
        return true;
    };
    return isValid(info) ? info : null;
}
var launchAutoPager = function(list) {
alert1("Autopagerize: launchAutoPager... length = " + list.length);
    if (list.length == 0) {
        return;
    }
 // list = list.filter(function(i) { return ('url' in i); }); /* ← IE では動かない */
    list.sort(function(a, b) { return (b.url.length - a.url.length); });

    for (var i = 0; i < list.length; i++) {
        try {
            if (ap) {
                return;
            }
            else if (!location.href.match(list[i].url)) {
            }
            else if (!getFirstElementByXPath(list[i].nextLink)) {
                // FIXME microformats case detection.
                // limiting greater than 12 to filter microformats like SITEINFOs.
                if (list[i].url.length > 12 ) {
                    debug("nextLink not found.", list[i].nextLink);
                }
            }
            else if (!getFirstElementByXPath(list[i].pageElement)) {
                if (list[i].url.length > 12 ) {
                    debug("pageElement not found.", list[i].pageElement);
                }
            }
            else {
alert2("Autopagerize: #"+ i +"...");
                ap = new AutoPager(list[i]);
                return;
            }
        }
        catch(e) {
alert2("Autopagerize: "+ e +"...");
            log(e);
            continue;
        }
    }
}
var clearCache = function() {
    GM_setValue('cacheInfo', '');
}
var getCache = function() {
    return eval(GM_getValue('cacheInfo')) || {};
}
var getCacheCallback = function(res, url) {
    if (res.status != 200) {
        return getCacheErrorCallback(url);
    }

    var info;
    try {
        info = eval(res.responseText).map(function(i) { return i.data });
    }
    catch(e) {
        info = [];
        var matched = false;
        var hdoc = createHTMLDocumentByString(res.responseText);
        var textareas = getElementsByXPath(
            '//*[@class="autopagerize_data"]', hdoc);
        textareas.forEach(function(textarea) {
            var d = parseInfo(textarea.value);
            if (d) {
                info.push(d);
                if (!matched && location.href.match(d.url)) {
                    matched = d;
                }
            }
        })
    }

    if (info.length > 0) {
        cacheInfo[url] = {
            url: url,
            expire: new Date(new Date().getTime() + CACHE_EXPIRE),
            info: info
        };
     // GM_setValue('cacheInfo', cacheInfo.toSource());
        GM_setValue('cacheInfo', uneval(cacheInfo));
        launchAutoPager(info);
    }
    else {
        getCacheErrorCallback(url);
    }
}
var getCacheErrorCallback = function(url) {
    var expire = new Date(new Date().getTime() + CACHE_EXPIRE);
    if (cacheInfo[url]) {
        cacheInfo[url].expire = expire;
alert2("Autopagerize: reading cacheInfo[" + url + "]...");
        launchAutoPager(cacheInfo[url].info);
    }
    else {
        cacheInfo[url] = {
            url: url,
            expire: expire,
            info: []
        };
    }
//  GM_setValue('cacheInfo', cacheInfo.toSource());
    GM_setValue('cacheInfo', uneval(cacheInfo))
}

if (FORCE_TARGET_WINDOW) {
    var setTargetBlank = function(doc) {
        var anchers = getElementsByXPath('descendant-or-self::a', doc);
        anchers.forEach(function(i) {
            try {     /* {@@} */
            if (i.getAttribute('href') &&
                !i.getAttribute('href').match(/^#/) &&
                !i.getAttribute('href').match(/^javascript\:/) &&
                i.className.indexOf('autopagerize_link') < 0) {
                i.target = '_blank';
            }
            }         /* {@@} */
            catch (e) /* {@@} */
            {         /* {@@} */ // 処理対象の中身が html としては文法的におかしいと「未定義のエラー」になるっぽい
            }         /* {@@} */
        })
    };
    AutoPager.documentFilters.push(setTargetBlank);
}
if (typeof(window.AutoPagerize) == 'undefined') {
    window.AutoPagerize = {};
    window.AutoPagerize.addFilter = function(f) {
        AutoPager.filters.push(f);
    };
    window.AutoPagerize.addDocumentFilter = function(f) {
        AutoPager.documentFilters.push(f);
    };
}

alert2("Autopagerize: loading...");
GM_registerMenuCommand('AutoPagerize - clear cache', clearCache);
var ap = null;
alert1("Autopagerize: reading SITEINFO...");
launchAutoPager(SITEINFO);
alert2("Autopagerize: reading cache ...");
var cacheInfo = getCache();
SITEINFO_IMPORT_URLS.forEach(function(i) {
alert1("Autopagerize: processing [" + i + "] ...");
    if (!cacheInfo[i] || cacheInfo[i].expire < new Date()) {
alert1("Autopagerize: getting [" + i + "] ...");
        var opt = {
            method: 'get',
            url: i,
            onload: function(res) {getCacheCallback(res, i)},
            onerror: function(res){getCacheErrorCallback(i)}
        };
        GM_xmlhttpRequest(opt);
    }
    else {
alert1("Autopagerize: reading cacheInfo[" + i + "]...");
        launchAutoPager(cacheInfo[i].info);
    }
});

alert2("Autopagerize: reading MICROFPRMATS...");
launchAutoPager([MICROFORMAT]);
alert2("Autopagerize: ready!");
return;

// utility functions.
function createHTMLDocumentByString(str) {
    var html = str.replace(/<!DOCTYPE.*?>/, '').replace(/<html.*?>/, '').replace(/<\/html>.*/, '');
    var htmlDoc  = document.implementation.createDocument(null, 'html', null);
    var fragment = createDocumentFragmentByString(html);
    try {
        fragment = htmlDoc.adoptNode(fragment);
    } catch(e) {
        fragment = htmlDoc.importNode(fragment, true);
    }
    htmlDoc.documentElement.appendChild(fragment);
    return htmlDoc;
}

// ↓↓ http://kuee.org/autopagerize/AutoPagerize.ieuser.js より
function createHTMLDocumentByString(str) {
    var html = str.replace(/<!DOCTYPE.*?>/, '').replace(/<html.*?>/, '').replace(/<\/html>.*/, '');

// 案1 [欠点: xml(xhtml) 的に illegal だとエラーになる]
//    var htmlDoc  = new ActiveXObject("Microsoft.XMLDOM")
//    htmlDoc.loadXML(html)
//    return htmlDoc.documentElement

/* 案2
      if (document.getElementById("tempdiv")) {
          document.body.removeChild(document.getElementById("tempdiv"));
      }
      var div = document.createElement('div');
      div.id = "tempdiv";
      div.style.display = 'none';
      document.body.appendChild(div);
      div.innerHTML = html;
      return div;
*/


/*
  if (document.charset.match(/shift_jis/i)) { // とりあえず、Shift_JIS のみ対象 (本当は UTF-8 以外は全部変換する必要あり)
      // ITmedia 等、文字化けするページへの対策 [今のところ、期待通り動いていないので、コメントアウトしておく]
alert("... shift jis → utf-8")
      var str = window.document.sjis2utf(html); // html の中身がおかしい? みたいで、期待通りに変換できない
      if (str.length > 0)                       //   たぶん、Shift_JIS なのに、UTF-8 であると(ブラウザ側で)
          html = str;                           //   解釈して無理やり Unicode に変換してしまっているせい
  }                                             //   →  ということはいったん Unicode → UTF-8 変換して(本
                                                //       来の Shift_JIS に戻して)から sjis2utf() すれば、
                                                //       期待通りの変換が行なわれるのだろうか?
  // 変換する必要のないページもある (meta タグで charset の宣言をしている場所に依存)
  //   charset 宣言より前に ASCII ではない文字コードの文字がある場合に(文字化けが発生するので)、変換が必要
  //      例: ITMedia (Shift_JIS)
  //   「最初の ASCII ではない文字コードの文字」の出現位置が charset 宣言より後の場合は、文字化けしない(ので変換は不要)
  //      例: CodeZine (Shift_JIS)
*/

  // 案3 (風柳さんのコメントより)
  var htmlDoc = new ActiveXObject('htmlfile');
  htmlDoc.designMode = 'on'; // これがないとセキュリティ警告が出ることがある
  htmlDoc.open('text/html');
  /****
  if (document.charset.match(/shift_jis/i)) { // 試しにやってみたけど、全然効果がありませんでした [2008.04.02]
   // htmlDoc.write('<head><meta http-equiv=content-type content="text/html; charset=' + document.charset + '" /></head>');
      htmlDoc.write('<meta http-equiv=content-type content="text/html; charset=' + document.charset + '" />');
  }
  ****/
  htmlDoc.write(html);
  htmlDoc.close();
  return htmlDoc;
}
// ↑↑

function getElementsByXPath(xpath, node) {
    var node = node || document;
 // var doc = node.ownerDocument ? node.ownerDocument : node;
 // var nodesSnapshot = doc.evaluate(xpath, node, null,
 //     XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

    var nodesSnapshot = document.evaluate(xpath, node, null, /* {@@} */
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);       /* {@@} */

    var data = [];
    for (var i = 0; i < nodesSnapshot.snapshotLength; i++) {
        data.push(nodesSnapshot.snapshotItem(i));
    }
    return data;
}

function getFirstElementByXPath(xpath, node0) {
    var node = node0 || document;
    var doc = node.ownerDocument ? node.ownerDocument : node;
    if (doc.evaluate == null) {
        doc = document;
        if (doc.evaluate == null)
            alert("cannot use XPath ...");
    }
    var result = doc.evaluate(xpath, node, null,
        XPathResult.FIRST_ORDERED_NODE_TYPE, null);
    return result.singleNodeValue ? result.singleNodeValue : null;
}

function createDocumentFragmentByString(str) {
    var range = document.createRange();
    range.setStartAfter(document.body);
    return range.createContextualFragment(str);
}

function log(message) {
    if (typeof console == 'object') {
        console.log(message);
    }
    else {
        GM_log(message);
    }
}

function debug() {
    if ( typeof DEBUG != 'undefined' && DEBUG ) {
        console.log.apply(this, arguments);
    }
}

function getElementPosition(elem) {
    var offsetTrail = elem;
    var offsetLeft  = 0;
    var offsetTop   = 0;
    while (offsetTrail) {
        offsetLeft += offsetTrail.offsetLeft;
        offsetTop  += offsetTrail.offsetTop;
        offsetTrail = offsetTrail.offsetParent;
    }
    offsetTop = offsetTop || null;
    offsetLeft = offsetLeft || null;
    return {left: offsetLeft, top: offsetTop};
}

function getElementBottom(elem) {
//  var c_style = document.defaultView.getComputedStyle(elem, '');
    var c_style = elem.currentStyle || document.defaultView.getComputedStyle(elem, '');
    var height  = 0;
    var prop    = ['height', 'borderTopWidth', 'borderBottomWidth',
                   'paddingTop', 'paddingBottom',
                   'marginTop', 'marginBottom'];
    prop.forEach(function(i) {
        var h = parseInt(c_style[i]);
        if (typeof h == 'number') {
            height += h;
        }
    });
    var top = getElementPosition(elem).top;
    return top ? (top + height) : null;
}

function getScrollHeight() {
    return Math.max(document.documentElement.scrollHeight,
                                document.body.scrollHeight);
}

function pathToURL(path) {
    var link = document.createElement('a');
    link.href = path;
    return link.href;
}

function isSameDomain(url) {
    return location.host == url.split('/')[2];
}

function supportsFinalUrl() {
 // return (GM_getResourceURL);
    return (true); /* @@ */
}


