AutoPagerize

loading next page and inserting into current page.

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

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

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

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

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

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

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name           AutoPagerize
// @namespace      http://swdyh.yu.to/
// @description    loading next page and inserting into current page.
// @include        http://*
// @include        https://*
// @exclude        https://mail.google.com/*
// @exclude        http://b.hatena.ne.jp/*
// @exclude        http://www.facebook.com/plugins/like.php*
// @exclude        http://api.tweetmeme.com/button.js*
// @version        0.0.66
// @icon           http://autopagerize.net/img/icons/icon_032.png
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_addStyle
// @grant          GM_log
// @grant          GM_xmlhttpRequest
// @grant          GM_registerMenuCommand
// ==/UserScript==
//
// auther:  swdyh http://d.hatena.ne.jp/swdyh/
// version: 0.0.66 2012-08-31T18:23:34+09:00
//
// this script based on
// GoogleAutoPager(http://la.ma.la/blog/diary_200506231749.htm) and
// estseek autopager(http://la.ma.la/blog/diary_200601100209.htm).
// thanks to ma.la.
//
// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//

if (isGreasemonkey()) {
    var ep = getPref('exclude_patterns')
    if (ep && isExclude(ep)) {
        // FIXME
        // return
    }
}
else {
    gmCompatible()
}

var URL = 'http://autopagerize.net/'
var VERSION = '0.0.66'
var DEBUG = false
var AUTO_START = true
var CACHE_EXPIRE = 24 * 60 * 60 * 1000
var BASE_REMAIN_HEIGHT = 400
var FORCE_TARGET_WINDOW = getPref('force_target_window', true)
var XHR_TIMEOUT = 30 * 1000
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")]',
}

var AutoPager = function(info) {
    this.pageNum = 1
    this.info = info
    this.state = AUTO_START ? 'enable' : 'disable'
    var self = this
    var url = this.getNextURL(info.nextLink, document, location.href)

    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 = {}
    this.loadedURLs[location.href] = true
    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)

    if (isFirefoxExtension()) {
        var div = document.createElement("div")
        div.setAttribute('id', 'autopagerize_icon')
        div.style.display = 'none'
        document.body.appendChild(div)
        this.icon = div
    }
    else if (isChromeExtension() || isSafariExtension() || isJetpack()) {
        var frame = document.createElement('iframe')
        frame.style.display = 'none'
        frame.style.position = 'fixed'
        frame.style.bottom = '0px'
        frame.style.left = '0px'
        frame.style.height = '25px'
        frame.style.border = '0px'
        frame.style.opacity = '0.8'
        frame.style.zIndex = '1000'
        frame.width = '100%'
        frame.scrolling = 'no'
        this.messageFrame = frame
        var u = settings['extension_path'] ?
            settings['extension_path'] + 'loading.html' :
            'http://autopagerize.net/files/loading.html'
        this.messageFrame.src = u
        document.body.appendChild(frame)
        if (isSafariExtension()) {
            safari.self.tab.dispatchMessage('launched', {url: location.href })
        }
        else if (isChromeExtension()) {
            chrome.extension.connect({name: "launched"}).postMessage()
        }
        if (isJetpack()) {
            postMessage({name: 'launched', data: location.href })
        }
   }
    else {
        this.initIcon()
        this.initHelp()
        GM_addStyle('@media print{#autopagerize_icon, #autopagerize_help {display: none !important;}}')
        GM_addStyle('hr.autopagerize_page_separator {clear: both;}')
        this.icon.addEventListener("mouseover", function() {
            self.viewHelp()
        }, true)
    }

    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()

    var that = this
    document.addEventListener('AutoPagerizeToggleRequest', function() {
        that.toggle()
    }, false)
    document.addEventListener('AutoPagerizeUpdateIconRequest', function() {
        that.updateIcon()
    }, false)
    that.updateIcon()
}

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;')

    var toggleDiv = document.createElement('div')
    toggleDiv.setAttribute('style', 'margin:0 0 0 50px;')
    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'
    }
    a.addEventListener('click', toggle, false)
    toggleDiv.appendChild(a)

    var s = '<div style="width:100px; float:left;">'
    for (var i in COLOR) {
        s += '<div style="float:left;width:1em;height:1em;' +
            'margin:0 3px;background-color:' + COLOR[i] + ';' +
            '"></div><div style="margin:0 3px">' + 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.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 s = ['top', 'left', 'height', 'width'].map(function(i) {
            return parseInt(c_style.getPropertyValue(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.addEventListener('mouseout', proc, false)
    this.helpLayer = helpDiv
    GM_addStyle('#autopagerize_help a { color: #0f0; text-decoration: underline;}')
}

AutoPager.prototype.viewHelp = function() {
    this.helpLayer.style.top = '3px'
}

AutoPager.prototype.onScroll = function() {
    var scrollHeight = Math.max(document.documentElement.scrollHeight,
                                document.body.scrollHeight)
    var remain = scrollHeight - window.innerHeight - window.scrollY
    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.updateIcon()
}

AutoPager.prototype.disable = function() {
    this.state = 'disable'
    this.updateIcon()
}

AutoPager.prototype.updateIcon = function(state) {
    var st = state || this.state
    var rename = {'enable': 'on', 'disable': 'off' }
    if (rename[st]) {
        st = rename[st]
    }
    var color = COLOR[st]
    if (color) {
        if (isFirefoxExtension()) {
            chlorine.pageAction.update(color, location.href)
        }
        else if (isChromeExtension()) {
            chrome.extension.connect({name: "pageActionChannel"}).postMessage(color)
        }
        else if (isSafariExtension() || isJetpack()) {
        }
        else {
            this.icon.style.background = color
        }
    }
}

AutoPager.prototype.request = function() {
    if (!this.requestURL || this.lastRequestURL == this.requestURL) {
        return
    }
    this.lastRequestURL = this.requestURL
    var self = this
    var mime = 'text/html; charset=' + document.characterSet
    var headers = {}

    if (isSameDomain(this.requestURL)) {
        headers.Cookie = document.cookie
    }
    else {
        this.error()
        return
    }
    var opt = {
        method: 'get',
        url: this.requestURL,
        headers: headers,
        overrideMimeType: mime,
        onerror: function(res) {
            self.error()
        },
        onload: function(res) {
            if (res.finalUrl) {
                var url_s = res.finalUrl.split(/[\/\?]/)
                if (url_s[0] == location.protocol && location.host == url_s[2]) {
                    self.requestLoad.apply(self, [res])
                    return
                }
            }
            self.error()
        }
    }
    AutoPager.requestFilters.forEach(function(i) { i(opt) }, this)
    if (opt.stop) {
        this.requestURL = opt.url
    }
    else {
        this.showLoading(true)
        GM_xmlhttpRequest(opt)
    }
}

AutoPager.prototype.showLoading = function(sw) {
    if (sw) {
        this.updateIcon('loading')
        if (this.messageFrame && settings['display_message_bar']) {
            this.messageFrame.style.display = 'block'
        }
    }
    else {
        this.updateIcon('enable')
        if (this.messageFrame) {
            this.messageFrame.style.display = 'none'
        }
    }
}

AutoPager.prototype.requestLoad = function(res) {
    AutoPager.responseFilters.forEach(function(i) {
        i(res, this.requestURL)
    }, this)
    var htmlDoc = createHTMLDocumentByString(res.responseText)
    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, this.requestURL)
    }
    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()
    }
    var ev = document.createEvent('Event')
    ev.initEvent('GM_AutoPagerizeNextPageLoaded', true, false)
    document.dispatchEvent(ev)
}

AutoPager.prototype.addPage = function(htmlDoc, page) {
    var HTML_NS  = 'http://www.w3.org/1999/xhtml'
    var hr = document.createElementNS(HTML_NS, 'hr')
    var p  = document.createElementNS(HTML_NS, 'p')
    hr.setAttribute('class', 'autopagerize_page_separator')
    p.setAttribute('class', 'autopagerize_page_info')
    var self = this

    if (page[0] && /tr/i.test(page[0].tagName)) {
        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.replace(/&/g, '&amp;') + '">' + (++this.pageNum) + '</a>'
    return page.map(function(i) {
        var pe = document.importNode(i, true)
        self.insertPoint.parentNode.insertBefore(pe, self.insertPoint)
        var ev = document.createEvent('MutationEvent')
        ev.initMutationEvent('AutoPagerize_DOMNodeInserted', true, false,
                             self.insertPoint.parentNode, null,
                             self.requestURL, null, null)
        pe.dispatchEvent(ev)
        return pe
    })
}

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 = '10px'
        height = '10px'
        zIndex = '255'
        if (this.state != 'enable') {
            background = COLOR['off']
        }
    }
    document.body.appendChild(div)
    this.icon = div
}

AutoPager.prototype.getNextURL = function(xpath, doc, url) {
    var nextLink = getFirstElementByXPath(xpath, doc)
    if (nextLink) {
        var nextValue = nextLink.getAttribute('href') ||
            nextLink.getAttribute('action') || nextLink.value
        if (nextValue.match(/^http(s)?:/)) {
            return nextValue
        }
        else {
            var base = getFirstElementByXPath('//base[@href]', doc)
            return resolvePath(nextValue, (base ? base.href : url))
        }
    }
}

AutoPager.prototype.terminate = function() {
    window.removeEventListener('scroll', this.scroll, false)
    this.updateIcon('terminated')
    var self = this
    setTimeout(function() {
        if (self.icon) {
            self.icon.parentNode.removeChild(self.icon)
        }
        if (isSafariExtension()) {
            var mf = self.messageFrame
            mf.parentNode.removeChild(mf)
        }
    }, 1500)
}

AutoPager.prototype.error = function() {
    this.updateIcon('error')
    window.removeEventListener('scroll', this.scroll, false)
    if (isSafariExtension() || isChromeExtension() || isJetpack()) {
        var mf = this.messageFrame
        var u = settings['extension_path'] ?
            settings['extension_path'] + 'error.html' :
            'http://autopagerize.net/files/error.html'
        mf.src = u
        mf.style.display = 'block'
        setTimeout(function() {
            mf.parentNode.removeChild(mf)
        }, 3000)
    }
}

AutoPager.documentFilters = []
AutoPager.requestFilters = []
AutoPager.responseFilters = []
AutoPager.filters = []

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) {
    if (list.length == 0) {
        return
    }
    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 {
                ap = new AutoPager(list[i])
                return
            }
        }
        catch(e) {
            log(e)
            continue
        }
    }
}
var clearCache = function() {
    GM_setValue('cacheInfo', '')
}
var getCache = function() {
    try {
        return JSON.parse(GM_getValue('cacheInfo')) || {}
    }
    catch(e) {
        return {}
    }
}
var getCacheCallback = function(res, url) {
    if (res.status != 200) {
        return getCacheErrorCallback(url)
    }

    var info
    try {
        info = JSON.parse(res.responseText).map(function(i) { return i.data })
    }
    catch(e) {
        info = []
    }
    if (info.length > 0) {
        info = info.filter(function(i) { return ('url' in i) })
        info.sort(function(a, b) { return (b.url.length - a.url.length) })

        var r_keys = ['url', 'nextLink', 'insertBefore', 'pageElement']
        info = info.map(function(i) {
            var item = {}
            r_keys.forEach(function(key) {
                if (i[key]) {
                    item[key] = i[key]
                }
            })
            return item
        })

        cacheInfo[url] = {
            url: url,
            expire: new Date(new Date().getTime() + CACHE_EXPIRE),
            info: info
        }
        GM_setValue('cacheInfo', JSON.stringify(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
        launchAutoPager(cacheInfo[url].info)
    }
    else {
        cacheInfo[url] = {
            url: url,
            expire: expire,
            info: []
        }
    }
    GM_setValue('cacheInfo', cacheInfo.toSource())
}

var linkFilter = function(doc, url) {
    var base = getFirstElementByXPath('//base[@href]', doc)
    var baseUrl = base ? base.href : url
    var isSameBase = isSameBaseUrl(location.href, baseUrl)
    if (!FORCE_TARGET_WINDOW && isSameBase) {
        return
    }

    var anchors = getElementsByXPath('descendant-or-self::a[@href]', doc)
    anchors.forEach(function(i) {
        var attrHref = i.getAttribute('href')
        if (FORCE_TARGET_WINDOW && !attrHref.match(/^#|^javascript:/) &&
            i.className.indexOf('autopagerize_link') < 0) {
            i.target = '_blank'
        }
        if (!isSameBase && !attrHref.match(/^#|^\w+:/)) {
            i.href = resolvePath(i.getAttribute('href'), baseUrl)
        }
    })

    if (!isSameBase) {
        var images = getElementsByXPath('descendant-or-self::img', doc)
        images.forEach(function(i) {
            i.src = resolvePath(i.getAttribute('src'), baseUrl)
        })
    }
}
AutoPager.documentFilters.push(linkFilter)

fixResolvePath()

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)
    }
    window.AutoPagerize.addResponseFilter = function(f) {
        AutoPager.responseFilters.push(f)
    }
    window.AutoPagerize.addRequestFilter = function(f) {
        AutoPager.requestFilters.push(f)
    }
    window.AutoPagerize.launchAutoPager = launchAutoPager

    var ev = document.createEvent('Event')
    ev.initEvent('GM_AutoPagerizeLoaded', true, false)
    document.dispatchEvent(ev)
}

var settings = {}
var ap = null
if (isChromeExtension()) {
    var port = chrome.extension.connect({name: "settingsChannel"})
    port.postMessage()
    port.onMessage.addListener(function(res) {
        settings = res
        if (res['exclude_patterns'] && isExclude(res['exclude_patterns'])) {
            return
        }
        launchAutoPager(SITEINFO)
        var port_ = chrome.extension.connect({name: "siteinfoChannel"})
        port_.postMessage({ url: location.href })
        port_.onMessage.addListener(function(res) {
            launchAutoPager(res)
            chrome.extension.onConnect.addListener(function(port) {
                if (port.name == "toggleRequestChannel") {
                    port.onMessage.addListener(function(msg) {
                        if (ap) {
                            ap.toggle()
                        }
                    })
                }
            })
        })
    })
}
else if (isSafariExtension()) {
    var re_exclude = /^(about:|safari-extension:)/
    if (!location.href.match(re_exclude)) {
        safari.self.addEventListener('message', function(event) {
            if (event.name === 'settings') {
                settings = event.message
                safari.self.tab.dispatchMessage('siteinfoChannel', {url: location.href })
            }
            else if (event.name === 'siteinfoChannel') {
                if (!settings['exclude_patterns'] || !isExclude(settings['exclude_patterns'])) {
                    launchAutoPager(SITEINFO)
                    launchAutoPager([MICROFORMAT])
                    launchAutoPager(event.message)
                }
            }
            else if (event.name === 'toggleRequestChannel') {
                if (ap) {
                    ap.toggle()
                }
            }
            else if (event.name === 'updateSettings') {
                settings = event.message
            }
        }, false)
        safari.self.tab.dispatchMessage('settings')
    }
}
else if (isJetpack()) {
    postMessage({ name: 'settings' })
    onMessage = function(message) {
        if (message.name == 'siteinfo') {
            // launchAutoPager(SITEINFO)
            launchAutoPager(message.data)
        }
        else if (message.name == 'settings') {
            settings = message.data
            if (settings['exclude_patterns'] && isExclude(settings['exclude_patterns'])) {
                // return
            }
            else  {
                postMessage({ name: 'siteinfo', url: location.href })
                launchAutoPager([MICROFORMAT])
            }
        }
    }
}
else {
    launchAutoPager(SITEINFO)
    GM_registerMenuCommand('AutoPagerize - clear cache', clearCache)
    var cacheInfo = getCache()
    var xhrStates = {}
    SITEINFO_IMPORT_URLS.forEach(function(i) {
        if (!cacheInfo[i] || new Date(cacheInfo[i].expire) < new Date()) {
            var opt = {
                method: 'get',
                url: i,
                onload: function(res) {
                    xhrStates[i] = 'loaded'
                    getCacheCallback(res, i)
                },
                onerror: function(res){
                    xhrStates[i] = 'error'
                    getCacheErrorCallback(i)
                },
            }
            xhrStates[i] = 'start'
            GM_xmlhttpRequest(opt)
            setTimeout(function() {
                if (xhrStates[i] == 'start') {
                    getCacheErrorCallback(i)
                }
            }, XHR_TIMEOUT)
        }
        else {
            launchAutoPager(cacheInfo[i].info)
        }
    })
    launchAutoPager([MICROFORMAT])
}


// new google search sucks!
if (location.href.match('^http://[^.]+\.google\.(?:[^.]{2,3}\.)?[^./]{2,3}/.*(&fp=)')) {
    var to = location.href.replace(/&fp=.*/, '')
    // console.log([location.href, to])
    location.href = to
}




// utility functions.
function createHTMLDocumentByString(str) {
    if (document.documentElement.nodeName != 'HTML') {
        return new DOMParser().parseFromString(str, 'application/xhtml+xml')
    }
    var html = strip_html_tag(str)
    var htmlDoc
    try {
        // We have to handle exceptions since Opera 9.6 throws
        // a NOT_SUPPORTED_ERR exception for |document.cloneNode(false)|
        // against the DOM 3 Core spec.
        htmlDoc = document.cloneNode(false)
        htmlDoc.appendChild(htmlDoc.importNode(document.documentElement, false))
    }
    catch(e) {
        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
}

function getElementsByXPath(xpath, node) {
    var nodesSnapshot = getXPathResult(xpath, node,
        XPathResult.ORDERED_NODE_SNAPSHOT_TYPE)
    var data = []
    for (var i = 0; i < nodesSnapshot.snapshotLength; i++) {
        data.push(nodesSnapshot.snapshotItem(i))
    }
    return data
}

function getFirstElementByXPath(xpath, node) {
    var result = getXPathResult(xpath, node,
        XPathResult.FIRST_ORDERED_NODE_TYPE)
    return result.singleNodeValue
}

function getXPathResult(xpath, node, resultType) {
    var node = node || document
    var doc = node.ownerDocument || node
    var resolver = doc.createNSResolver(node.documentElement || node)
    // Use |node.lookupNamespaceURI('')| for Opera 9.5
    // A workaround for bugs of Node.lookupNamespaceURI(null)
    // https://bugzilla.mozilla.org/show_bug.cgi?id=693615
    // https://bugzilla.mozilla.org/show_bug.cgi?id=694754
    var defaultNS = null
    try {
        // This follows the spec: http://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespaceURIAlgo
        if (node.nodeType == node.DOCUMENT_NODE) {
            defaultNS = node.documentElement.lookupNamespaceURI(null)
        }
        else {
            defaultNS = node.lookupNamespaceURI(null)
        }
    }
    catch(e) {}

    if (defaultNS) {
        const defaultPrefix = '__default__'
        xpath = addDefaultPrefix(xpath, defaultPrefix)
        var defaultResolver = resolver
        resolver = function (prefix) {
            return (prefix == defaultPrefix)
                ? defaultNS : defaultResolver.lookupNamespaceURI(prefix)
        }
    }
    return doc.evaluate(xpath, node, resolver, resultType, null)
}

function addDefaultPrefix(xpath, prefix) {
    const tokenPattern = /([A-Za-z_\u00c0-\ufffd][\w\-.\u00b7-\ufffd]*|\*)\s*(::?|\()?|(".*?"|'.*?'|\d+(?:\.\d*)?|\.(?:\.|\d+)?|[\)\]])|(\/\/?|!=|[<>]=?|[\(\[|,=+-])|([@$])/g
    const TERM = 1, OPERATOR = 2, MODIFIER = 3
    var tokenType = OPERATOR
    prefix += ':'
    function replacer(token, identifier, suffix, term, operator, modifier) {
        if (suffix) {
            tokenType =
                (suffix == ':' || (suffix == '::' &&
                 (identifier == 'attribute' || identifier == 'namespace')))
                ? MODIFIER : OPERATOR
        }
        else if (identifier) {
            if (tokenType == OPERATOR && identifier != '*') {
                token = prefix + token
            }
            tokenType = (tokenType == TERM) ? OPERATOR : TERM
        }
        else {
            tokenType = term ? TERM : operator ? OPERATOR : MODIFIER
        }
        return token
    }
    return xpath.replace(tokenPattern, replacer)
}

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 ) {
        if (console.log.apply) {
            console.log.apply(console, arguments)
        }
        else {
            Function.prototype.apply.apply(console.log, [console, 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 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 isSameDomain(url) {
    if (url.match(/^\w+:/)) {
        var url_s = url.split(/[\/\?]/)
        return url_s[0] == location.protocol && location.host == url_s[2]
    }
    else {
        return true
    }
}

function isSameBaseUrl(urlA, urlB) {
    return (urlA.replace(/[^/]+$/, '') == urlB.replace(/[^/]+$/, ''))
}

function resolvePath(path, base) {
    if (path.match(/^https?:\/\//)) {
        return path
    }
    if (path.match(/^\?/)) {
        return base.replace(/\?.+$/, '') + path;
    }
    if (path.match(/^[^\/]/)) {
        return base.replace(/[^/]+$/, '') + path
    }
    else {
        return base.replace(/([^/]+:\/\/[^/]+)\/.*/, '\$1') + path
    }
}

function fixResolvePath() {
    if (resolvePath('', 'http://resolve.test/') == 'http://resolve.test/') {
        return
    }
    // A workaround for WebKit and Mozilla 1.9.2a1pre,
    // which don't support XML Base in HTML.
    // https://bugs.webkit.org/show_bug.cgi?id=17423
    // https://bugzilla.mozilla.org/show_bug.cgi?id=505783
    var XML_NS = 'http://www.w3.org/XML/1998/namespace'
    var baseElement = document.createElementNS(null, 'base')
    var pathElement = document.createElementNS(null, 'path')
    baseElement.appendChild(pathElement)
    resolvePath = function resolvePath_workaround(path, base) {
        baseElement.setAttributeNS(XML_NS, 'xml:base', base)
        pathElement.setAttributeNS(XML_NS, 'xml:base', path)
        return pathElement.baseURI
    }
}

function strip_html_tag(str) {
    var chunks = str.split(/(<html(?:[ \t\r\n][^>]*)?>)/)
    if (chunks.length >= 3) {
        chunks.splice(0, 2)
    }
    str = chunks.join('')
    chunks = str.split(/(<\/html[ \t\r\n]*>)/)
    if (chunks.length >= 3) {
        chunks.splice(chunks.length - 2)
    }
    return chunks.join('')
}

function getPref(key, defaultValue) {
    var value = GM_getValue(key)
    return (typeof value == 'undefined') ? defaultValue : value
}

function wildcard2regep(str) {
    return '^' + str.replace(/([-()\[\]{}+?.$\^|,:#<!\\])/g, '\\$1').replace(/\x08/g, '\\x08').replace(/\*/g, '.*')
}

function isExclude(patterns) {
    var rr = /^\/(.+)\/$/
    var eps = (patterns || '').split(/[\r\n ]+/)
    for (var i = 0; i < eps.length; i++) {
        var reg = null
        if (rr.test(eps[i])) {
            reg = eps[i].match(rr)[1]
        }
        else {
            reg = wildcard2regep(eps[i])
        }
        if (location.href.match(reg)) {
            return true
        }
    }
    return false
}
// obsolete
function isFirefoxExtension() {
    return (typeof chlorine == 'object')
}

function isChromeExtension() {
    return (typeof chrome == 'object') &&
        (typeof chrome.extension == 'object')
}

function isSafariExtension() {
    return (typeof safari == 'object') &&
        (typeof safari.extension == 'object')
}

function isGreasemonkey() {
    return (typeof GM_log == 'function')
}

function isJetpack() {
    // isFirefoxExtension is obsolete
    return (!isGreasemonkey() && !isSafariExtension() &&
            !isChromeExtension() && !isFirefoxExtension())
}

function gmCompatible() {
    GM_registerMenuCommand = function() {}
    GM_setValue = function() {}
    GM_getValue = function() {}
    GM_addStyle = function() {}
    uneval = function() {}
    fixResolvePath = function() {}
    resolvePath = function (path, base) { return path }

    if (isChromeExtension() || isSafariExtension()) {
        createHTMLDocumentByString = function(str) {
            if (document.documentElement.nodeName != 'HTML') {
                return new DOMParser().parseFromString(str, 'application/xhtml+xml')
            }
            // FIXME
            var html = str.replace(/<script(?:[ \t\r\n][^>]*)?>[\S\s]*?<\/script[ \t\r\n]*>|<\/?(?:i?frame|html|script|object)(?:[ \t\r\n][^<>]*)?>/gi, ' ')
            var htmlDoc = document.implementation.createHTMLDocument ?
                document.implementation.createHTMLDocument('apfc') :
                document.implementation.createDocument(null, 'html', null)
            var range = document.createRange()
            range.selectNodeContents(document.documentElement)
            htmlDoc.documentElement.appendChild(range.createContextualFragment(html))
            return htmlDoc
        }
    }
    return true
}