// PLUGIN_INFO//{{{ var PLUGIN_INFO = nextlink mapping "[[", "]]" by AutoPagerize XPath. AutoPagerize 用の XPath より "[[", "]]" をマッピングします。 suVene 0.2.3 1.2 2.0pre ; //}}} liberator.plugins.nextlink = (function() { // initialize //{{{ if (!liberator.plugins.libly) { liberator.log('nextlink: needs _libly.js'); return; } var libly = liberator.plugins.libly; var $U = libly.$U; var logger = $U.getLogger('nextlink'); var isFollowLink = typeof liberator.globalVariables.nextlink_followlink == 'undefined' ? false : $U.eval(liberator.globalVariables.nextlink_followlink); var pageNaviCss = ; //}}} var NextLink = function() {//{{{ this.initialize.apply(this, arguments); }; NextLink.prototype = { initialize: function(pager) { this.WEDATA_AUTOPAGERIZE = 'http://wedata.net/databases/AutoPagerize/items.json'; this.initialized = false; this.isCurOriginalMap = true; this.siteinfo = []; this.cache = {}; // {url: {xpath: xpath, next: element, prev: url}} or null this.pager = pager; this.browserModes = config.browserModes || [modes.NORMAL, modes.VISUAL]; this.is2_0later = config.autocommands.some(function ([k, v]) k == 'DOMLoad'); // toriaezu var req = new libly.Request(this.WEDATA_AUTOPAGERIZE); req.addEventListener('onSuccess', $U.bind(this, function(res) { var json = $U.evalJson(res.responseText); if (!json) return; this.siteinfo = json.map(function(item) item.data) .sort(function(a, b) b.url.length - a.url.length); // sort url.length desc this.initialized = true; } )); req.get(); // for debug /* this.initialized = true; this.siteinfo = [ { url: '^https?://(?:192\\.168(?:\\.\\d+){2}|localhost)(?::\\d+)?/', nextLink: 'id("next")', pageElement: '//*' } ]; */ commands.addUserCommand(['nextlink'], 'map ]] by AutoPagerize XPath.', $U.bind(this, function(args) { this.handler(args); }), null, true ); var loadEvent = autocommands['DOMLoad'] || 'PageLoad'; // for 1.2 logger.log('event:' + loadEvent + (this.is2_0later ? 'DOMLoad' : 'PageLoad') ); liberator.execute(':autocmd! ' + (this.is2_0later ? 'DOMLoad' : 'PageLoad') + ' .* :nextlink onLoad'); liberator.execute(':autocmd! LocationChange .* :nextlink onLocationChange'); }, handler: function(args) { event = args.string || args; this[event](buffer.URL); commandline.echo(''); }, onLoad: function(url) { if (!this.initialized) return; if (this.cache[url] && this.cache[url].hasOwnProperty('xpath')) { this.cache[url].doc = window.content.document; this.onLocationChange(url, true); return; } for (let i = 0, len = this.siteinfo.length; i < len; i++) { if (url.match(this.siteinfo[i].url) && this.siteinfo[i].url != '^https?://.') { this.setCache(url, ['doc', 'xpath', 'siteinfo'], [window.content.document, this.siteinfo[i].nextLink, this.siteinfo[i]] ); this.onLocationChange(url, true); return; } } this.setCache(url, ['doc', 'xpath', 'prev', 'next'], [null, null, null, null]); }, onLocationChange: function(url, isCallerLoaded) { if (!this.initialized || !this.cache[url] || !this.cache[url].hasOwnProperty('xpath')) return; if (this.cache[url]['xpath'] == null) { this.restorOrginalMap(); return; } this.pager.onLocationChange(this, url, isCallerLoaded); this.customizeMap(this, url); this.isCurOriginalMap = false; }, customizeMap: function(context, url) { var cache = this.cache[url]; var prev = cache.prev; var next = cache.next; if (!prev) this.removeMap('[['); if (!next) this.removeMap(']]'); this.pager.customizeMap(context, url, prev, next); }, restorOrginalMap: function() { if (this.isCurOriginalMap) return; this.removeMap('[['); this.removeMap(']]'); this.isCurOriginalMap = true; }, setCache: function(key, subKeys, values) { if (!this.cache[key]) this.cache[key] = {}; values = [].concat(values); [].concat(subKeys).forEach($U.bind(this, function(subKey, i) { this.cache[key][subKey] = values[i]; })); }, removeMap: function(cmd) { try { if (mappings.hasMap(this.browserModes, cmd)) { mappings.remove(this.browserModes, cmd); } return true; } catch (e) { return false; } } };//}}} var Autopager = function() {};//{{{ Autopager.prototype = { onLocationChange: function(context, url, isCallerLoaded) { var cache = context.cache[url]; var doc = cache.doc; var elems = cache.next; var elem; if (isCallerLoaded) { let insertPoint, lastPageElement; if (cache.insertBefore) insertPoint = $U.getNodesFromXPath(cache.siteinfo.insertBefore, doc); if (!insertPoint) lastPageElement = $U.getNodesFromXPath(cache.siteinfo.pageElement, doc).pop(); if (lastPageElement) insertPoint = lastPageElement.nextSibling || lastPageElement.parentNode.appendChild(doc.createTextNode(' ')); if (context.is2_0later) { let css = $U.xmlToDom(pageNaviCss, doc); let node = doc.importNode(css, true); doc.body.insertBefore(node, doc.body.firstChild); //doc.body.appendChild(css); } $U.getNodesFromXPath(cache.xpath, doc, function(item) elem = item, this); context.setCache(url, ['prev', 'next', 'curPage', 'insertPoint', 'terminate', 'lastReqUrl', 'loadedURLs', 'mark'], [[], [elem], 1, insertPoint, 0, null, {}, []] ); } }, customizeMap: function(context, url, prev, next) { var cache = context.cache[url]; var doc = cache.doc; mappings.addUserMap(context.browserModes, ['[['], 'customize by nextlink.js', $U.bind(this, function(count) { if (cache.curPage == 1) { return; } else if (--cache.curPage == 1) { window.content.scrollTo(0, 0); } else { this.focusPagenavi(context, url, cache.curPage); } }), { flags: Mappings.flags.COUNT }); mappings.addUserMap(context.browserModes, [']]'], 'customize by nextlink.js', $U.bind(this, function(count) { var reqUrl, lastReqUrl; reqUrl = $U.pathToURL(cache.next[cache.curPage - 1], doc); lastReqUrl = cache.lastReqUrl; if (cache.isLoading) { logger.echo('loading now...'); return; } if (!reqUrl || cache.curPage == cache.terminate) { logger.echo('end of pages.'); return; } if (cache.loadedURLs[reqUrl]) { this.focusPagenavi(context, url, ++cache.curPage); return; } context.setCache(url, ['lastReqUrl', 'isLoading'], [reqUrl, true]); var req = new libly.Request( reqUrl, null, { asynchronous: true, encoding: doc.characterSet, context: context, url: url } ); req.addEventListener('onSuccess', $U.bind(this, this.onSuccess)); req.addEventListener('onFailure', $U.bind(this, this.onFailure)); req.addEventListener('onException', $U.bind(this, this.onFailure)); req.get(); }), { flags: Mappings.flags.COUNT } ); }, onSuccess: function(res) { var context = res.req.options.context; var url = res.req.options.url; var cache = context.cache[url]; var doc = cache.doc; var page = res.getHTMLDocument(cache.siteinfo.pageElement); var htmlDoc = res.doc; var prev = cache.next[cache.curPage]; var next = $U.getNodesFromXPath(cache.xpath, htmlDoc); cache.isLoading = false; cache.loadedURLs[res.req.url] = true; if (!page || page.length < 1) { context.setCache(url, 'terminate', cache.curPage); return; } cache.curPage++; if (next && next.length) { cache.prev.push(prev); cache.next.push(next[0]); } else { context.setCache(url, 'terminate', cache.curPage); } this.addPage(context, htmlDoc, url, page, res.req.url); this.focusPagenavi(context, url, cache.curPage); }, addPage: function(context, doc, url, page, reqUrl) { var cache = context.cache[url]; var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; //var hr = doc.createElementNS(HTML_NAMESPACE, 'hr'); var p = doc.createElementNS(HTML_NAMESPACE, 'p'); var tagName; if (page[0] && page[0].tagName) tagName = page[0].tagName.toLowerCase(); if (tagName == 'tr') { let insertParent = cache.insertPoint.parentNode; let colNodes = getElementsByXPath('child::tr[1]/child::*[self::td or self::th]', insertParent); let colums = 0; for (let i = 0, l = colNodes.length; i < l; i++) { let col = colNodes[i].getAttribute('colspan'); colums += parseInt(col, 10) || 1; } let td = doc.createElement('td'); // td.appendChild(hr); td.appendChild(p); let tr = doc.createElement('tr'); td.setAttribute('colspan', colums); tr.appendChild(td); insertParent.insertBefore(tr, cache.insertPoint); } else if (tagName == 'li') { let li = doc.createElementNS(HTML_NAMESPACE, 'li'); cache.insertPoint.parentNode.insertBefore(li, cache.insertPoint); li.appendChild(p); } else { //cache.insertPoint.parentNode.insertBefore(hr, cache.insertPoint); cache.insertPoint.parentNode.insertBefore(p, cache.insertPoint); } p.id = 'vimperator-nextlink-' + cache.curPage; p.innerHTML = 'page: ' + cache.curPage + ''; p.className = 'vimperator-nextlink-page'; cache.mark.push(p); return page.map(function(elem) { var pe = doc.importNode(elem, true); cache.insertPoint.parentNode.insertBefore(pe, cache.insertPoint); return pe; }); }, onFailure: function(res) { logger.log('onFailure'); var context = res.req.options.context; var url = res.req.options.url; var cache = context.cache[url]; cache.isLoading = false; logger.echoerr('nextlink: loading failed. ' + '[' + res.status + ']' + res.statusText + ' > ' + res.req.url); res.req.options.context.setCache(res.req.options.url, 'terminate', cache.curPage); }, focusPagenavi: function(context, url, page) { var elem, p; try { elem = context.cache[url].mark[page - 2]; p = $U.getElementPosition(elem); window.content.scrollTo(0, p.top); } catch (e) { logger.log('focusPagenavi: err ' + page + ' ' + e); } } };//}}} var FollowLink = function() {};//{{{ FollowLink.prototype = { onLocationChange: function(context, url, isCallerLoaded) { var cache = context.cache[url]; var doc = cache.doc; var elem; //var matches = buffer.evaluateXPath(this.cache[url]); //for each (let match in matches) elem = match; $U.getNodesFromXPath(cache.xpath, doc, function(item) elem = item, this); var nextURL = $U.pathToURL(elem, doc); var xpath = ['a', 'link'].map(function(e) '//' + e + '[translate(normalize-space(@rel), "PREV", "prev")="prev"]') .join(' | '); var prev = $U.getNodesFromXPath(xpath, doc); if (prev.length) context.setCache(url, 'prev', prev[0]); context.setCache(nextURL, 'prev', url); context.setCache(url, 'next', elem); }, customizeMap: function(context, url, prev, next) { var cache = context.cache[url]; var doc = cache.doc; if (prev) mappings.addUserMap(context.browserModes, ['[['], 'customize by nextlink.js', function(count) { if (prev.href) { buffer.followLink(prev, liberator.CURRENT_TAB); } else { liberator.open(prev, liberator.CURRENT_TAB); } }, { flags: Mappings.flags.COUNT }); if (next) mappings.addUserMap(context.browserModes, [']]'], 'customize by nextlink.js', function(count) { buffer.followLink(next, liberator.CURRENT_TAB); }, { flags: Mappings.flags.COUNT }); } };//}}} var instance = new NextLink((isFollowLink ? new FollowLink() : new Autopager())); return instance; })(); // vim: set fdm=marker sw=4 ts=4 sts=0 et: