diff options
-rw-r--r-- | _libly.js | 20 | ||||
-rwxr-xr-x | nextlink.js | 309 |
2 files changed, 285 insertions, 44 deletions
@@ -5,7 +5,7 @@ var PLUGIN_INFO = <description>vimperator plugins library?</description> <description lang="ja">適当なライブラリっぽいものたち。</description> <author mail="suvene@zeromemory.info" homepage="http://zeromemory.sblo.jp/">suVene</author> - <version>0.1.7</version> + <version>0.1.8</version> <minVersion>1.2</minVersion> <maxVersion>2.0pre</maxVersion> <detail><![CDATA[ @@ -66,6 +66,11 @@ createHTMLDocument(str): 引数 str より、HTMLFragment を作成します。 getNodesFromXPath(xpath, doc, callback, obj): xpath を評価し snapshot の配列を返却します。 +xmlSerialize(xml): + xml を文字列化します。 +getElementPosition(elem): + elem の offset を返却します。 + {top: 0, left: 0} ]]></detail> </VimperatorPlugin>; //}}} @@ -244,6 +249,19 @@ libly.$U = {//{{{ .replace(/<!--(?:[^-]|-(?!->))*-->/g, '') .replace(/<[^>]+>/g, function(all) all.toLowerCase()); } catch (e) { return '' } + }, + getElementPosition: function(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 {top: offsetTop, left: offsetLeft}; } // }}} }; diff --git a/nextlink.js b/nextlink.js index 4b7d4a4..78f0e99 100755 --- a/nextlink.js +++ b/nextlink.js @@ -5,17 +5,25 @@ var PLUGIN_INFO = <description>mapping "[[", "]]" by Autopagerize XPath.</description>
<description lang="ja">Autopagerize 用の XPath より "[[", "]]" をマッピングします。</description>
<author mail="suvene@zeromemory.info" homepage="http://zeromemory.sblo.jp/">suVene</author>
- <version>0.1.0</version>
+ <version>0.2.0</version>
<minVersion>2.0</minVersion>
<maxVersion>2.0pre</maxVersion>
<detail><![CDATA[
== NEEDS LIBLARY ==
-_libly.js(ver.0.1.7)
+_libly.js(ver.0.1.8)
@see http://coderepos.org/share/browser/lang/javascript/vimperator-plugins/trunk/_libly.js
+== Option ==
+let g:nextlink_followlink = "true"
+と設定することにより、"[[", "]]" の動作は、カレントのタブに新しくページを読み込むようになります。
+
== Command ==
:nextlink
autocmd によって呼び出されます。
+
+== TODO ==
+- microformats prev 対応.
+- document cache clear.
]]></detail>
</VimperatorPlugin>;
//}}}
@@ -30,19 +38,44 @@ liberator.plugins.nextlink = (function() { var logger = $U.getLogger('nextlink');
var isFollowLink = typeof liberator.globalVariables.nextlink_followlink == 'undefined' ?
- true : $U.eval(liberator.globalVariables.nextlink_followlink);
+ false : $U.eval(liberator.globalVariables.nextlink_followlink);
+
+ var pageNaviCss =
+ <style type="text/css"><![CDATA[
+ .vimperator-nextlink-page {
+ background-color: #333;
+ color: #fff;
+ opacity: .85;
+ filter: alpha(opacity = 85);
+ zoom: 1;
+ padding: 10px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ font-weight: bold;
+ text-align: left;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ }
+ .vimperator-nextlink-page a:link {
+ color: #EF6D29;
+ }
+ .vimperator-nextlink-page a:visited {
+ color: #A5000;
+ }
+ ]]></style>;
var NextLink = function() {//{{{
this.initialize.apply(this, arguments);
};
NextLink.prototype = {
- initialize: function() {
+ 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;
var req = new libly.Request(this.WEDATA_AUTOPAGERIZE);
req.addEventListener('onSuccess', $U.bind(this,
@@ -55,8 +88,16 @@ liberator.plugins.nextlink = (function() { }
));
req.get();
+ /* // for debug
+ this.initialized = true;
+ this.siteinfo = [{
+ url: 'http:\/\/192\.168\.',
+ nextLink: 'id("next")',
+ pageElement: '//*'
+ }];
+ */
- commands.addUserCommand(['nextlink'], 'map ]] by Autopegrize XPath.',
+ 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
@@ -73,20 +114,25 @@ liberator.plugins.nextlink = (function() { if (!this.initialized) return;
if (this.cache[url] &&
this.cache[url].hasOwnProperty('xpath')) {
- this.onLocationChange(url);
+ 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.setCache(url, 'xpath', this.siteinfo[i].nextLink)
- this.onLocationChange(url);
+ this.setCache(url,
+ ['doc', 'xpath', 'siteinfo'],
+ [window.content.document, this.siteinfo[i].nextLink, this.siteinfo[i]]
+ );
+ this.onLocationChange(url, true);
return;
}
}
- this.setCache(url, ['xpath', 'next', 'prev'], [null, null, null]);
+ this.setCache(url, ['doc', 'xpath', 'prev', 'next'], [null, null, null, null]);
},
- onLocationChange: function(url) {
+ onLocationChange: function(url, isCallerLoaded) {
+
if (!this.initialized ||
!this.cache[url] ||
@@ -96,45 +142,24 @@ liberator.plugins.nextlink = (function() { this.restorOrginalMap();
return;
}
- if (this.cache[url].next) this.cache[url].next.style.backgroundColor = '#F00';
-
- var elem;
- //var matches = buffer.evaluateXPath(this.cache[url]);
- //for each (let match in matches) elem = match;
- $U.getNodesFromXPath(this.cache[url].xpath, window.content.document,
- function(item) elem = item, this);
- var nextUrl = $U.pathToURL(elem, window.content.document);
- this.setCache(url, 'next', elem);
- this.setCache(nextUrl, 'prev', url);
- this.customizeMap(elem, url, this.cache[url].prev, (nextUrl || null));
+ this.pager.onLocationChange(this, url, isCallerLoaded);
+ this.customizeMap(this, url);
+ this.isCurOriginalMap = false;
},
- customizeMap: function(elem, url, prevUrl, nextUrl) {
+ customizeMap: function(context, url) {
- if (!prevUrl) {
+ var cache = this.cache[url];
+ var prev = cache.prev;
+ var next = cache.next;
+
+ if (!prev)
mappings.remove(config.browserModes, "[[");
- } else {
- if (isFollowLink) {
- mappings.addUserMap(config.browserModes, ["[["], "customize by nextlink.js",
- function(count) { liberator.open(prevUrl, liberator.CURRENT_TAB); },
- { flags: Mappings.flags.count });
- } else {
- }
- }
- if (!nextUrl) {
+ if (!next)
mappings.remove(config.browserModes, "]]");
- } else {
- if (isFollowLink) {
- mappings.addUserMap(config.browserModes, ["]]"], "customize by nextlink.js",
- function(count) { buffer.followLink(elem, liberator.CURRENT_TAB); },
- { flags: Mappings.flags.COUNT });
- } else {
- // TODO: pagirize!
- }
- }
- this.isCurOriginalMap = false;
+ this.pager.customizeMap(context, url, prev, next);
},
restorOrginalMap: function() {
@@ -152,7 +177,205 @@ liberator.plugins.nextlink = (function() { }
}//}}}
- var instance = new NextLink();
+ 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(' '))
+
+ let css = util.xmlToDom(pageNaviCss, doc);
+ doc.body.appendChild(util.xmlToDom(pageNaviCss, doc));
+
+ $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(config.browserModes, ["[["], "customize by nextlink.js",
+ $U.bind(this, function(count) {
+ if (cache.curPage == 1) {
+ return;
+ } else {
+ cache.curPage--;
+ if (cache.curPage == 1) {
+ window.content.scrollTo(0, 0);
+ } else {
+ this.focusPagenavi(context, url, cache.curPage);
+ }
+ }
+ }),
+ { flags: Mappings.flags.count });
+
+ mappings.addUserMap(config.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 (!reqUrl || cache.curPage == cache.terminate) {
+ return;
+ }
+ if (cache.loadedURLs[reqUrl]) {
+ cache.curPage++;
+ this.focusPagenavi(context, url, cache.curPage);
+ return;
+ }
+ context.setCache(url, 'lastReqUrl', reqUrl);
+ var req = new libly.Request(
+ //reqUrl + '?' + new Date(), null,
+ reqUrl, null,
+ { asynchronous: false, encoding: doc.characterSet,
+ context: context, url: url }
+ );
+ req.addEventListener('onSuccess', $U.bind(this, this.onSuccess));
+ req.addEventListener('onFailure', $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, htmlDoc, prev, next;
+
+ page = res.getHTMLDocument(cache.siteinfo.pageElement);
+ htmlDoc = res.doc;
+ prev = cache.next[cache.curPage];
+ next = $U.getNodesFromXPath(cache.xpath, htmlDoc);
+
+ 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);
+ } 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 doc = cache.doc;
+ var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
+ var hr = doc.createElementNS(HTML_NAMESPACE, 'hr');
+ var p = doc.createElementNS(HTML_NAMESPACE, 'p');
+
+ if (page[0] && page[0].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 {
+ cache.insertPoint.parentNode.insertBefore(hr, cache.insertPoint);
+ cache.insertPoint.parentNode.insertBefore(p, cache.insertPoint);
+ }
+
+ hr.id = 'vimperator-nextlink-' + cache.curPage;
+ p.innerHTML = 'page: <a href="' + reqUrl + '">' + cache.curPage + '</a>';
+ p.className = 'vimperator-nextlink-page';
+ cache.mark.push(p);
+
+ return page.map(function(i) {
+ var pe = doc.importNode(i, true);
+ cache.insertPoint.parentNode.insertBefore(pe, cache.insertPoint);
+ return pe;
+ });
+ },
+ onFailure: function(res) {
+ res.res.options.context.setCache(res.req.options.url, 'terminate', cache.curPage);
+ },
+ focusPagenavi: function(context, url, page) {
+ try {
+ var elem = context.cache[url].mark[page - 2];
+ var 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);
+ 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(config.browserModes, ["[["], "customize by nextlink.js",
+ function(count) { liberator.open(prev, liberator.CURRENT_TAB); },
+ { flags: Mappings.flags.count });
+
+ if (next)
+ mappings.addUserMap(config.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;
})();
|