var PLUGIN_INFO =
{NAME}
Hatena Bookmark UserSearch
はてなブックマークユーザ検索
2.0pre
2.0pre
http://svn.coderepos.org/share/lang/javascript/vimperator-plugins/trunk/hatena-bookmark-search.js
Yuichi Tateno
MPL 1.1/GPL 2.0/LGPL 2.1
0.1.2
||
:bs[earch][!] word
:tabbs[earch][!] word
||<
ログインしているユーザのブックマークを、URL, コメント, タイトル から検索します。
はてなブックマークユーザページの右上の検索のローカル版のようなイメージです。
XUL/Migemo が入っている場合は Migemo を使い正規表現検索します。
Migemo を利用した検索語の絞り込みはスペース区切りの 2単語までとなります。
Migemo を利用すると検索が重くなるので、遅いマシンやインクリメンタル検索環境下では、以下の設定をすることで migemo 検索をしなくなります。
>||
liberator.globalVariables.hatena_bookmark_no_migemo = true;
||<
:bs word では、選択している URL を開きます。:bs! word では、選択している URL のはてなブックマークエントリーページを開きます。:bs と単語を入力しないと、http://b.hatena.ne.jp/my を開きます。:bs! では http://b.hatena.ne.jp/ トップページを開きます。
初回検索時にデータを構築しますが、強制的にデータをロードし直したい時などは
>||
:bs -reload
||<
としてください。
(invalid options エラーが出る場合は、適当な文字を後ろに付加するか、最新(Nightly)の Vimperator を使ってください)
また、:open, :tabopen の補完で、completeオプションに "H"
を追加することではてなブックマークの検索が可能です。
>||
:set complete+=H
||<
.vimperatorrcに書く場合は
>||
autocmd VimperatorEnter .* :set complete+=H
||<
としてください。
]]>
;
liberator.plugins.HatenaBookmark = (function(){
var p = function(arg) {
Application.console.log(arg);
// liberator.log(arg);
}
const HatenaBookmark = {};
HatenaBookmark.Data = new Struct('url', 'title', 'comment', 'icon');
HatenaBookmark.Data.defaultValue('icon', function() bookmarks.getFavicon(this.url));
HatenaBookmark.Data.prototype.__defineGetter__('stext', function() {
if (typeof this._stext == 'undefined') {
this._stext = this.comment + "\0" + this.title + "\0" + this.url;
}
return this._stext;
});
HatenaBookmark.Data.prototype.__defineGetter__("extra", function () [
["comment", this.comment, "Comment"],
].filter(function (item) item[1]));
var XMigemoCore;
var XMigemoTextUtils;
try {
XMigemoCore = Cc['@piro.sakura.ne.jp/xmigemo/factory;1']
.getService(Ci.pIXMigemoFactory)
.getService("ja");
XMigemoTextUtils = Cc['@piro.sakura.ne.jp/xmigemo/text-utility;1'].getService(Ci.pIXMigemoTextUtils);
} catch (e if e instanceof TypeError) {
}
HatenaBookmark.useMigemo = !!(!liberator.globalVariables.hatena_bookmark_no_migemo && XMigemoCore);
HatenaBookmark.Command = {
templateDescription: function (item, text) {
return <>
{
!(item.extra && item.extra.length) ? "" :
}
>
},
templateTitleIcon: function (item, text) {
var simpleURL = text.replace(/^https?:\/\//, '');
if (simpleURL.indexOf('/') == simpleURL.length-1)
simpleURL = simpleURL.replace('/', '');
return <>{item.icon ?
: <>>}{item.item.title}
>
},
filter: function (_item) {
var item = _item.item;
// 'this' is context object.
if (HatenaBookmark.useMigemo) {
if (!this.migemo) {
this.migemo = HatenaBookmark.Command.compileRegexp(this.filter);
}
var migemo = this.migemo;
return migemo.test(item.stext);
} else {
return this.match(item.url) || this.match(item.comment) || this.match(item.title);
}
},
compileRegexp: function(str) {
let a;
with (XMigemoTextUtils) {
a = sanitize(trim(str)).split(/\s+/).join(' ');
}
return new RegExp(XMigemoTextUtils.getANDFindRegExpFromTerms(XMigemoCore.getRegExps(a)), 'gim');
},
execute: function(args) {
if (args['-reload']) {
HatenaBookmark.UserData.reload();
liberator.echo('HatenaBookmark data reloaded.');
return;
}
var url = HatenaBookmark.Command.genURL(args);
liberator.open(url);
},
executeTab: function(args) {
var url = HatenaBookmark.Command.genURL(args);
liberator.open(url, liberator.NEW_TAB);
},
genURL: function(args) {
var url = (args.string || '').replace(/\s/g, '');
if (url.length) {
if (args.bang) {
return 'http://b.hatena.ne.jp/entry/' + url.replace('#', '%23');
} else {
return url;
}
} else {
if (args.bang) {
return 'http://b.hatena.ne.jp/';
} else {
return 'http://b.hatena.ne.jp/my';
}
}
},
createCompleter: function(titles) {
return function(context) {
context.format = {
anchored: true,
title: titles,
keys: { text: "url", description: "url", icon: "icon", extra: "extra"},
process: [
HatenaBookmark.Command.templateTitleIcon,
HatenaBookmark.Command.templateDescription,
],
}
context.ignoreCase = true;
if (context.migemo) delete context.migemo;
context.filters = [HatenaBookmark.Command.filter];
context.completions = HatenaBookmark.UserData.bookmarks;
}
}
}
HatenaBookmark.Command.options = {
completer: HatenaBookmark.Command.createCompleter(['TITLE', 'Info']),
literal: 0,
argCount: '*',
bang: true,
options: [
[['-reload'], commands.OPTION_NOARG]
],
}
commands.addUserCommand(
['bs[earch]'],
'Hatena Bookmark UserSearch',
HatenaBookmark.Command.execute,
HatenaBookmark.Command.options,
true
);
commands.addUserCommand(
['tabbs[earch]'],
'Hatena Bookmark UserSearch',
HatenaBookmark.Command.executeTab,
HatenaBookmark.Command.options,
true
);
completion.addUrlCompleter("H", "Hatena Bookmarks", HatenaBookmark.Command.createCompleter(["Hatena Bookmarks"]));
HatenaBookmark.UserData = {
get bookmarks() {
this.init();
return this._bookmarks;
},
reload: function() {
this._inited = false;
this.init();
},
init: function() {
if (!this._inited) {
if (this._bookmarks)
delete this._bookmarks;
this._inited = true;
this.preloadLimit = 500;
this.preload();
}
},
preload: function() {
this.load({
offset: 0,
limit: this.preloadLimit
});
},
load: function(query) {
var url = 'http://b.hatena.ne.jp/my/search.data';
var xhr = new XMLHttpRequest();
var self = this;
if (query.async) {
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
self.completeHandler(xhr)
} else {
liberator.echoerr('XHR Error: ' + xhr.statusText);
// throw new Error(xhr.statusText);
}
}
}
}
xhr.open('POST', url, query.async);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(this.makeQuery(query));
if (!query.async) {
if (xhr.status == 200) {
this.completeHandler(xhr);
} else {
liberator.echoerr('XHR Error: ' + xhr.statusText);
// throw new Error(xhr.statusText);
}
}
},
makeQuery: function(data) {
var pairs = [];
var regexp = /%20/g;
for (var k in data) {
if (typeof data[k] == 'undefined') continue;
var v = data[k].toString();
var pair = encodeURIComponent(k).replace(regexp,'+') + '=' +
encodeURIComponent(v).replace(regexp,'+');
pairs.push(pair);
}
return pairs.join('&');
},
completeHandler: function(res) {
if (this._loaded) return;
if (!this._bookmarks) {
this.createDataStructure(res.responseText || '');
if (this._bookmarks.length == this.preloadLimit) {
this.load({
offset: this.preloadLimit,
async: true
});
} else {
this._loaded = 1;
}
} else {
this.updateDataStructure(res.responseText || '');
this._loaded = 1;
}
},
updateDataStructure: function(data) {
this.pushData(this._bookmarks, data);
},
createDataStructure: function(data) {
this._bookmarks = [];
this.pushData(this._bookmarks, data);
},
pushData: function(ary, data) {
var infos = data.split("\n");
var tmp = infos.splice(0, infos.length * 3/4);
var len = tmp.length;
for (var i = 0; i < len; i+=3) {
ary.push(new HatenaBookmark.Data(tmp[i+2]/* url */, tmp[i]/* title */, tmp[i+1]/* comment */));
}
},
mapFunc: function(item) {
item = item.split("\n");
return {
url: item[2],
comment: item[1],
title: item[0],
}
},
};
return HatenaBookmark;
})();