Skip to content

Instantly share code, notes, and snippets.

@mallowlabs
Created July 20, 2009 11:43
Show Gist options
  • Save mallowlabs/150277 to your computer and use it in GitHub Desktop.
Save mallowlabs/150277 to your computer and use it in GitHub Desktop.
migemo-unite.js
//==UserScript==
//@name migemo-unite.js
//@author mallowlabs (http://d.hatena.ne.jp/mallowlabs)
//@version 0.0.1
//@description : Inclemental migemo search with Opera Unite
//@include *
//==/UserScript==
// Usage
// Ctrl/Cmd + "/" : 検索ボックス表示
// 検索ボックス表示状態
// Shift + G : 次を選択
// Shift + Ctrl + G : 前を選択
// Enter : 選択項目のリンクを開く
// Ctrl/Cmd + "/" : リンクのみから検索モードとトグル(文字が赤い時はリンクのみ)
// Escape : 検索ボックス非表示
(function(document) {
var MigemoServer = {
MIGEMO_URL : "http://home.mallowlabs.operaunite.com/migemo/?",
SCRIPT_ID : "opera-migemo-script",
isBusy : false,
send : function(query) {
if (this.isBusy || query == "" || query.length < 3) return;
this.isBusy = true;
// JSONP のコールバックを登録
window["migemoCallback"] = this.callback;
// 前回appendしたスクリプトがあれば削除
var head = document.getElementsByTagName("head")[0];
var old = document.getElementById(MigemoServer.SCRIPT_ID);
if (old != null) head.removeChild(old);
// migemo-serverから正規表現を受け取る.
// migemo-serverと通信.関数migemoCallbackが呼ばれる.
var s = head.appendChild(document.createElement("script"));
s.id = MigemoServer.SCRIPT_ID;
s.type = "text/javascript";
s.charset = "utf-8";
s.src = this.MIGEMO_URL + query;
},
callback : function(json) {
SearchBox.setResult(json);
MigemoServer.isBusy = false;
}
};
var SearchBox = {
SEARCH_BOX_ID : "opera-migemo-search-box",
INPUT_BOX_ID : "opera-migemo-input-box",
currentWord : "",
intervalId : null,
linkonly : false,
create: function(linkonly) {
this.linkonly = !!linkonly;
var d = document.createElement("div");
d.setAttribute("id", SearchBox.SEARCH_BOX_ID);
d.setAttribute("style", "position:fixed!important;bottom:0.5%;left:0.5%;border:silver 2px solid;opacity:0.7;z-index:50");
var i = document.createElement("input");
i.setAttribute("id", SearchBox.INPUT_BOX_ID);
i.setAttribute("size", "50");
var self = this;
i.addEventListener('keyup',
function(event) { self.currentWord = i.value; },
false);
i.addEventListener('keypress',
function(e) {
switch(e.keyCode) {
case 13: // enter
self.onEnterKey(e);
break;
case 27: // escape
self.onEscapeKey(e);
break;
case 71: // G
self.onNext(e);
e.preventDefault();
break;
case 47: // '/'
self.onToggle(e);
break;
}
},
false);
d.appendChild(i);
document.body.appendChild(d);
intervalId = window.setInterval(function() { self.search(); }, 100);
i.focus();
},
onEscapeKey : function(e) {
//終了する
window.clearInterval(this.intervalId);
TextHilighter.unhilightAll();
// 検索ボックスを削除
var m = document.getElementById(SearchBox.SEARCH_BOX_ID);
m.parentNode.removeChild(m);
},
onEnterKey : function(e) {
TextHilighter.followLink();
},
onNext : function(e) {
if (e.ctrlKey || e.metaKey) {
TextHilighter.hilightNext(-1);
} else {
TextHilighter.hilightNext(1);
}
},
onToggle : function(e) {
if (e.ctrlKey || e.metaKey) {
this.linkonly = !this.linkonly;
var i = document.getElementById(SearchBox.INPUT_BOX_ID);
if (this.linkonly) {
i.setAttribute("style", "color:red");
} else {
i.setAttribute("style", "");
}
}
},
searchWord : "",
search : function() {
if (this.currentWord == "") {
TextHilighter.unhilightAll();
} else if (!MigemoServer.isBusy && this.currentWord != this.searchWord) {
this.searchWord = this.currentWord;
MigemoServer.send(this.searchWord);
}
},
setResult : function(re) {
TextHilighter.hilightAll(re, this.linkonly);
TextHilighter.hilightNext(1);
}
}
var TextHilighter = {
hilightNodes : [],
currentIndex : -1,
hilightAll : function(re, linkonly) {
var xpath = "descendant::text()";
if (!!linkonly) {
xpath = "descendant::a/descendant::text()";
}
this.unhilightAll();
var result = document.evaluate(xpath, document.body, null, 7, null);
for (var i = 0; i < result.snapshotLength; i ++) {
var t = result.snapshotItem(i);
var f = t.textContent.search(RegExp(re, "i"));
if (f != -1) {
this.hilightNode(t, f, re);
}
}
},
hilightNode : function(node, f, re) {
var str = node.textContent;
var pre = str.substring(0, f);
var post = str.substring(f);
var mid = post.match(RegExp(re, "i"))[0];
post = post.substring(mid.length);
var parentNode = node.parentNode;
var e1 = document.createTextNode(pre);
var e2 = document.createElement("span")
e2.setAttribute("style", "color:black;background:yellow");
e2.appendChild(document.createTextNode(mid));
var e3 = document.createTextNode(post)
parentNode.insertBefore(e1, node);
parentNode.insertBefore(e2, node);
parentNode.insertBefore(e3, node);
parentNode.removeChild(node);
this.hilightNodes.push(e2);
// recursive call
var f2 = e3.textContent.search(RegExp(re, "i"));
if (f2 != -1) {
this.hilightNode(e3, f2, re);
}
},
unhilightAll : function() {
for (var i = 0, length = this.hilightNodes.length; i < length; i++ ) {
var node = this.hilightNodes[i];
var text = node.textContent;
var tn = document.createTextNode(text);
var parentNode = node.parentNode;
parentNode.insertBefore(tn, node);
parentNode.removeChild(node);
parentNode.normalize();
}
this.hilightNodes = [];
this.currentIndex = -1;
},
hilightNext : function(dir) {
if (this.hilightNodes.length == 0) return;
if (this.currentIndex >= 0) {
this.hilightNodes[this.currentIndex]
.setAttribute("style", "color:black;background:yellow");
}
this.currentIndex += dir;
if (this.currentIndex >= this.hilightNodes.length) {
this.currentIndex = 0;
} else if (this.currentIndex < 0) {
this.currentIndex = this.hilightNodes.length -1;
}
var n = this.hilightNodes[this.currentIndex];
if (n != null) {
n.setAttribute("style", "color:white;background:green");
this.scrollToElem(n);
}
},
scrollToElem : function(ele) {
window.scrollTo(0, getElementYpos(ele) - window.innerHeight / 2);
// 指定したエレメントのY座標を取得
function getElementYpos(ele){
var y = 0;
var tmp = ele;
while (tmp.nodeName != "BODY") {
y += tmp.offsetTop;
tmp = tmp.offsetParent;
}
return y;
}
},
followLink : function() {
if (this.hilightNodes.length == 0) return;
var node = this.hilightNodes[this.currentIndex];
var name = node.nodeName;
while (name != "BODY") {
if (name == "A") {
var url = node.getAttribute("href");
if (url != "" && url != null) {
SearchBox.onEscapeKey(null);
node.click();
break;
}
}
node = node.parentNode;
name = node.nodeName;
}
}
}
document.addEventListener("keypress",
function(e) {
if (e.keyCode == 47 && (e.ctrlKey || e.metaKey)) { // ctrl/cmd + "/"
if (document.getElementById(SearchBox.INPUT_BOX_ID) == null) {
SearchBox.create();
}
}
},
false);
})(document);
This file has been truncated, but you can view the full file.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment