// ==UserScript==
// @name espre
// @namespace http://gyu.que.jp/
// @description emulates syntax highlighting of ?G-Diary in ?D
// @include http://d.hatena.ne.jp/*
// ==/UserScript==
///////////////////////////////////////////////
// 各言語のトークン
///////////////////////////////////////////////
LexPresets = {
ecma4: {
label: "ECMAScript",
ml_comment: ["\\/\\*([^*]|\\*[^\\/])*\\*\\/"], // 複数行コメント
sl_comment: ["\\/\\/[^\\n\\r]*"], // 単行コメント
regexp: ["\\/([^\\/\\n\\r\\\\]|\\\\.)+\\/"],
keywords: ["as|break|case|catch|class|const|continue|default|delete|do|else|export|extends|false|finally|for|function|if|import|in|instanceof|is|namespace|new|null|package|private|public|return|super|switch|this|throw|true|try|typeof|use|var|void|while|with"], // 予約語
string_literal: ["\\\"(([^\\\"\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*\\\"", "'(([^'\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*'"], // 文字列
idents: ["[\u0024\u0041-\u005a\u005f\u0061-\u007a\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u1fff\u3040-\u318f\u3300-\u337f\u3400-\u3d2d\u4e00-\u9fff\uf900-\ufaff][\u0024\u0041-\u005a\u005f\u0061-\u007a\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u1fff\u3040-\u318f\u3300-\u337f\u3400-\u3d2d\u4e00-\u9fff\uf900-\ufaff\u0030-\u0039\u0660-\u0669\u06f0-\u06f9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be7-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u1040-\u1049]*"] // 識別子(誤爆防止)
},
java: {
label: "Java",
ml_comment: ["\\/\\*([^*]|\\*[^\\/])*\\*\\/"], // 複数行コメント
sl_comment: ["\\/\\/[^\\n\\r]*"], // 単行コメント
regexp: null, // 正規表現リテラル
keywords: ["abstract|assert|break|case|catch|class|const|continue|default|do|else|extends|false|final|finally|for|goto|if|implements|import|instanceof|interface|native|new|null|package|private|protected|public|return|static|super|switch|synchronized|this|throw|throws|transient|true|try|volatile|while|strictfp", "boolean|byte|char|double|float|int|void|short|long"], // 予約語
// ↑配列の要素ごとに、予約語0、予約語1…とグループ分けされます。グループごとにep_keywords_0、ep_keywords_1...と違うCSSクラス名がつくので、色を変えて表示できます。このJavaの例では、データ型名だけ別のグループにしています。
string_literal: ["\\\"(([^\\\"\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*\\\"", "'(([^'\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*'"], // 文字列
idents: ["[\u0024\u0041-\u005a\u005f\u0061-\u007a\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u1fff\u3040-\u318f\u3300-\u337f\u3400-\u3d2d\u4e00-\u9fff\uf900-\ufaff][\u0024\u0041-\u005a\u005f\u0061-\u007a\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u1fff\u3040-\u318f\u3300-\u337f\u3400-\u3d2d\u4e00-\u9fff\uf900-\ufaff\u0030-\u0039\u0660-\u0669\u06f0-\u06f9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be7-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0e50-\u0e59\u0ed0-\u0ed9\u1040-\u1049]*"] // 識別子(誤爆防止)
},
ruby: {
label: "Ruby",
ml_comment: ["=begin(.|[\\r\\n])*?=end"], // 複数行コメント
sl_comment: ["#[^\\n\\r]*"], // 単行コメント
regexp: ["\\/([^\\/\\n\\r\\\\]|\\\\.)*\\/"],
keywords: ["__LINE__|__FILE__|BEGIN|END|alias|and|begin|break|case|class|def|defined\\?|do|else|elsif|end|ensure|false|for|if|in|module|next|nil|not|or|redo|rescue|retry|return|self|super|then|true|undef|unless|until|when|while|yield"], // 予約語
string_literal: ["\\\"(([^\\\"\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*\\\"", "'(([^'\\\\\\n\\r])|(\\\\([ntbrf\\\\'\\\"]|[0-7][0-7]?|[0-3][0-7][0-7])))*'"],
idents: ["[a-zA-Z_][a-zA-Z0-9_]*", "[a-zA-Z_][a-zA-Z0-9_]*(!|\\?)?"]
}
};
///////////////////////////////////////////////
var CSSCLASS_PREFIX = "ep_";
var ID_TEXTAREA = "textarea-edit";
var edittext = null;
function Highlighter(l)
{
this.lex = l;
this.init();
}
Highlighter.prototype = {
escapeRegex : function(s)
{
s = s.replace("\\","\\\\");
var m = s.match(/[\[\]+*?\^$|{}]/);
for (var i = 0;i < m.length;i++)
s = s.replace(m[i], "\\"+m[i]);
},
init : function()
{
this.regex = new RegExp( "("+ ((this.lex.regexp!=null) ? (this.lex.regexp.join(")|(")+")|(") : "")+this.lex.idents.join(")|(")+")|("+this.lex.keywords.join(")|(")+")|("+this.lex.string_literal.join(")|(")+")|("+this.lex.ml_comment.join(")|(")+")|("+this.lex.sl_comment.join(")|(")+")" , "m");
this.part_regex = {};
var makePart = function(type, lexdata, partdata) {
partdata[type] = [];
for (var i = 0;i < lexdata[type].length;i++)
partdata[type][i] = new RegExp(lexdata[type][i], "m");
};
makePart("keywords", this.lex, this.part_regex);
makePart("string_literal", this.lex, this.part_regex);
makePart("ml_comment", this.lex, this.part_regex);
makePart("sl_comment", this.lex, this.part_regex);
if (this.lex.regexp != null)
makePart("regexp", this.lex, this.part_regex);
},
run : function(src)
{
src = src.replace(//g,">");
src = src.replace(/^\*/mg," *");
src = src.replace(/^-/mg," -");
var i;
var out = "";
for(var nextsrc = src;;)
{
if (nextsrc.match(this.regex) == null) break;
nextsrc = RegExp.rightContext;
var $last = RegExp.lastMatch;
var $left = RegExp.leftContext;
out += $left;
var kwclass = null;
for (type in this.part_regex)
{
for (i = 0;i < this.part_regex[type].length;i++)
{
if ($last.match(this.part_regex[type][i]) == null) continue;
if ($last == RegExp.lastMatch)
{
kwclass = CSSCLASS_PREFIX+type+"_"+i;
break;
}
}
if (kwclass != null) break;
}
if (kwclass != null)
out += ""+$last+""
else
out += $last;
}
out += nextsrc;
return out;
}
};
///////////////////////////////////////////////
function resume()
{
textSelection = new unsafeWindow.TextInputSelection(edittext);
if (!textSelection)
return;
var r = /([^<]*?)<\/em>/m;
out = "";
var nextsrc = textSelection.getSelectedText();
for(;;)
{
if (nextsrc.match(r) == null) break;
nextsrc = RegExp.rightContext;
out += RegExp.leftContext + RegExp["$1"];
}
out += nextsrc;
out = out.replace(/</g,"<")
out = out.replace(/>/g,">")
textSelection.replaceSelectedText(out);
}
function runHighlighter(menuitem)
{
var preset_name = menuitem.getValue();
if (preset_name == "_resume_")
return resume();
textSelection = new unsafeWindow.TextInputSelection(edittext);
if (!textSelection)
return;
var h = new Highlighter( LexPresets[preset_name] );
var after = h.run( textSelection.getSelectedText() );
textSelection.replaceSelectedText(after);
}
function setup()
{
edittext = document.getElementById(ID_TEXTAREA);
var tb = document.getElementById("text-decoration-buttons");
var trg = document.createElement("img");
trg.src = "http://f.hatena.ne.jp/images/fotolife/g/gyuque/20060908/20060908132519.png";
trg.setAttribute("title", "\u30b7\u30f3\u30bf\u30c3\u30af\u30b9\u30cf\u30a4\u30e9\u30a4\u30c8");
trg.setAttribute("alt", "SH");
trg.style.cssText = "cursor: pointer;";
tb.appendChild(trg);
var listBox = new unsafeWindow.ListBoxMenu(trg);
var menuitems = [];
for (p in LexPresets)
menuitems.push( {text: LexPresets[p].label, value: p} );
menuitems.push( {text: "\u5143\u306b\u623b\u3059", value: "_resume_"} );
for (var i = 0;i < menuitems.length;i++)
{
var obj = menuitems[i];
var item = new unsafeWindow.ListBoxItem();
item.setText(obj.text);
item.setValue(obj.value);
if (obj.value == "_resume_")
item.setStyle({
borderTop: "1px dotted #555"
});
listBox.addItem(item);
}
listBox.addEventListener('select', runHighlighter);
}
unsafeWindow.Event.observe(window, "load", setup, false);