Skip to content

Instantly share code, notes, and snippets.

Created May 4, 2011 09:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save anonymous/954964 to your computer and use it in GitHub Desktop.
Save anonymous/954964 to your computer and use it in GitHub Desktop.
userMenu.uc.js for Fx4/Fx6
// ==UserScript==
// @name UCJSToolkit
// @author 80
// @version 0.42 mod4 2011/05/05 08:00 xxx Fx6 対応(regexp(str) が使えないようなので regexp.test(str) に、exec でもよかったけど部分文字列は使ってなかったので)
// @version 0.42 mod3 2009/01/30 07:00 Alice0775
// @description library for userChrome.js scripts.
// @include main
// ==/UserScript==
// userChrome.js 用のライブラリ 使い方は注釈文にて.
// クラスなので new UCJSToolkit(); でオブジェクトを作ってから.
// 各メソッドはオブジェクトの外に持ち出したり,別のオブジェクト下に置いても動くと思う.一応そう言う設計にした.
var UCJSToolkit = (function(){
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const IO_SERVICE = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
const SUBSCRIPT_LOADER = Cc['@mozilla.org/moz/jssubscript-loader;1'].getService(Ci.mozIJSSubScriptLoader);
const CONSOLE_SERVICE = Cc['@mozilla.org/consoleservice;1'].getService(Ci.nsIConsoleService);
const UNICODE_CONVERTER = Cc['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Ci.nsIScriptableUnicodeConverter);
const REGEX_META_START = /^\s*[/][/]\s*==UserScript==/;
const REGEX_META_END = /^\s*[/][/]\s*==[/]UserScript==/;
const REGEX_META_DATA = /^\s*[/][/]\s*@([a-zA-Z][\w-]*)\s+(.+?)\s*$/;
function Toolkit(){
/^[^@]*@(.*?)[^/]+:\d+$/.test(Error().stack.split('\n')[2]);
var p0 = RegExp.$1;
var p1 = Error().fileName.replace(/[^/]+$/,'').split(' -> ').pop();
this.SCRIPT_PATH = (p0 == p1) ? [p0] : [p0,p1];
this.ERROR = null;
var _this = this;
var errorDisplay = 2;
// file で指定したファイルを読み込んで文字列で返す.
// file に指定できるのは URL かローカルパスの文字列.或いは nsIFile オブジェクト.
// 成功で真を返し,失敗すると偽を返し,プロパティ ERROR にエラー情報が入る.
this.loadFile = function(file){
try{
var stream;
switch(_this._typeof(file)){
case 'file':
stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
stream.init(file,1,0,false);
break;
case 'string':
if(/^[a-z]+:[/][/]/i.test(file)) stream = IO_SERVICE.newChannelFromURI(IO_SERVICE.newURI(file,null,null)).open();
else{
var f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
f.initWithPath(file);
stream = Cc['@mozilla.org/network/file-input-stream;1'].createInstance(Ci.nsIFileInputStream);
stream.init(f,1,0,false);
}
break;
default: throw 'unknown type';
}
var ss = Cc['@mozilla.org/scriptableinputstream;1'].createInstance(Ci.nsIScriptableInputStream);
ss.init(stream);
var data = ss.read(ss.available());
ss.close();
stream.close();
return data;
}
catch(e){ _this.ERROR = e; return null; }
};
// buf(文字列)を file で指定したファイルに書き出す.
// file に指定できるのは URL かローカルパスの文字列.或いは nsIFile オブジェクト.
// 成功で真を返し,失敗すると偽を返し,プロパティ ERROR にエラー情報が入る.
this.saveFile = function(file,buf){
try{
var f;
switch(_this._typeof(file)){
case 'file': f = file; break;
case 'string':
if(/^[a-z]+:[/][/]/i.test(file)) f = IO_SERVICE.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getFileFromURLSpec(file);
else{
var f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
f.initWithPath(file);
}
break;
default: throw 'unknown type';
}
var stream = Cc['@mozilla.org/network/file-output-stream;1'].createInstance(Ci.nsIFileOutputStream);
if(f.exists()) f.remove(true);
f.create(f.NORMAL_FILE_TYPE,0666);
stream.init(f,2,0x200,false);
stream.write(buf,buf.length);
stream.close();
return true;
}
catch(e){ _this.ERROR = e; return false; }
};
// name(文字列)で指定した名前のテンポラリファイルを作成し,そのnsIFile オブジェクトを返す.
// 失敗すると null を返し,プロパティ ERROR にエラー情報が入る.
this.createTMPFile = function(name){
try{
var file = Cc['@mozilla.org/file/directory_service;1'].getService(Components.interfaces.nsIProperties).get('TmpD',Ci.nsIFile);
file.append(name);
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE,0664);
return file;
}
catch(e){ _this.ERROR = e; return null; }
};
// 文字列 src の中のメタデータを連想配列 meta に置き換えた物を返す.
// meta の仕様は readMeta で返されるものと同じ.
// 改行コードが全て「\n」になる副作用がある.
this.replaceMeta = function(src,meta){
var m = [];
for(var k in meta){
var a = meta[k].split('\n');
for(var i = 0,max = a.length;i < max;++i) m.push('// @' + k + ' ' + a[i]);
}
var a = src.split(/\r\n|\r|\n/);
var data = [];
for(var i = 0,max = a.length;i < max;++i){
if(REGEX_META_START.test(a[i])){
for(;i < max;++i){
if(REGEX_META_END.test(a[i])){ data.push(m.join('\n')); break; }
else if(!REGEX_META_DATA.test(a[i])) data.push(a[i]);
}
for(;i < max;++i) data.push(a[i]);
break;
}
else data.push(a[i]);
}
return data.join('\n');
};
// filename(文字列)の指すファイルをオブジェクト scope 内で実行する.
// filename は相対パス.一致するファイルをこの関数の実行したディレクトリと,SCRIPT_PATH の中から探す.
// 成功で真を返し,失敗すると偽を返し,プロパティ ERROR にエラー情報が入る.
this.require = function(filename,scope){
/^[^@]*@(.*?)[^/]+:\d+$/.test(Error().stack.split('\n')[2]);
var cur = RegExp.$1;
var path = (/^[a-z]+:[/][/]/i.test(filename)) ? [''] : [cur].concat(_this.SCRIPT_PATH);
var scp = scope || ((this === _this) ? undefined : this);
for(var i = 0;i < path.length;++i){
if(!_this.isFileExists(path[i] + filename)) continue;
try{ SUBSCRIPT_LOADER.loadSubScript(path[i] + filename,scp); return true; }
catch(e){ this.ERROR = e; return false; }
}
_this.ERROR = new Error('file not found.');
return false;
};
// エラーメッセージを出力する.
// name はエラーのヘッダに付加して出力される文字列.err は Error オブジェクトかエラーメッセージの文字列.
// display は出力先の指定.1 : dump / 2 : エラーコンソール / 4: alert を論理和を用いて指定可能.省略可.
this.errorMessage = function(name,err,display){
var disp = display || errorDisplay;
/^[^@]*@.*?([^/]+):\d+$/,test(Error().stack.split('\n')[2]);
var head = RegExp.$1 + ' / ' + name;
function _stack(stack){
if(typeof(stack) != 'string') return '';
var c = 0;
return stack.split('\n').map(function(v){
/^([^@]*)@(.*?):(\d+)$/.test(v);
if(!RegExp.$1 && !RegExp.$2) return '-------- stack ' + c++ + ' - n/a\n';
if(!RegExp.$1) return '-------- stack ' + c++ + '\nfile : ' + RegExp.$2 + ' - line : ' + RegExp.$3 + '\n';
if(!RegExp.$2) return '-------- stack ' + c++ + '\nfunction : ' + RegExp.$1 + '\n';
return '-------- stack ' + c++ + '\nfunction : ' + RegExp.$1 + '\nfile : ' + RegExp.$2 + ' - line : ' + RegExp.$3 + '\n';
}).join('');
};
try{
if(disp & 1) dump( head + '\n' + err + '\n' + ((_this._typeof(err) == 'error' && 'stack' in err) ? _stack(err.stack) : ''));
if(disp & 4) alert(head + '\n' + err + '\n');
if(disp & 2){
var error = Cc['@mozilla.org/scripterror;1'].createInstance(Ci.nsIScriptError);
if(_this._typeof(err) == 'error') error.init(head + ' ' + err.name + ' : ' + err.message,err.fileName || null,null,err.lineNumber,null,2,err.name);
else error.init(head + '\n' + err + '\n',null,null,null,null,2,null);
CONSOLE_SERVICE.logMessage(error);
}
}
catch(e){ dump('errorMessage error.\n' + e + '\n'); }
};
// errorMessage の display 省略時の出力先の設定.errorMessage の display と同じ値を指定する.
this.setErrorDisplay = function(display){ return errorDisplay = display; };
}
// SCRIPT_FILE プロパティ(getter) スクリプトファイル名の URL 表記文字列を返す.
Toolkit.prototype.__defineGetter__('SCRIPT_FILE',function(){ /^[^@]*@(.+):\d+$/.test(Error().stack.split('\n')[2]); return RegExp.$1; });
// SCRIPT_LINE プロパティ(getter) 実行した行番号を返す.
Toolkit.prototype.__defineGetter__('SCRIPT_LINE',function(){ /^[^@]*@.+:(\d+)$/.test(Error().stack.split('\n')[2]); return RegExp.$1; });
// mozIJSSubScriptLoader へのショートカット.
// 第二引数を省略するとこのオブジェクト内のスコープで実行される.
Toolkit.prototype.loadSubScript = SUBSCRIPT_LOADER.loadSubScript;
// typeof の改良版.
// 文字列,数値,真偽値,配列,関数,エラー等のオブジェクトに対応.オマケで nsIFile オブジェクトだと 'file' を返す.
Toolkit.prototype._typeof = function(obj){
var type = typeof(obj);
if(type == 'object'){
var c = obj.constructor;
if (c === eval('String')) return 'string';
else if(c === eval('Number')) return 'number';
else if(c === eval('Boolean')) return 'boolean';
else if(c === eval('Array')) return 'array';
else if(c === eval('Function')) return 'function';
else if(c === eval('Date')) return 'date';
else if(c === eval('RegExp')) return 'regexp';
else if(c === eval('Error')) return 'error';
else if(c === eval('EvalError')) return 'error';
else if(c === eval('RangeError')) return 'error';
else if(c === eval('ReferenceError')) return 'error';
else if(c === eval('SyntaxError')) return 'error';
else if(c === eval('TypeError')) return 'error';
else if(c === eval('URIError')) return 'error';
else if(obj instanceof Ci.nsIFile) return 'file';
}
return type;
};
// str の指す文字列の文字コードを返す.
// us-ascii / ISO-2022-JP / Shift_JIS / EUC-JP / UTF-8 に対応.
Toolkit.prototype.getCharset = function(str){
if(/\x1B\x24(?:[\x40\x42]|\x28\x44)/.test(str)) return 'ISO-2022-JP';
if(/[\x80-\xFE]/.test(str)){
var buf = RegExp.lastMatch + RegExp.rightContext;
if(/[\xC2-\xFD][^\x80-\xBF]|[\xC2-\xDF][\x80-\xBF][^\x00-\x7F\xC2-\xFD]|[\xE0-\xEF][\x80-\xBF][\x80-\xBF][^\x00-\x7F\xC2-\xFD]/.test(buf)) return (/[\x80-\xA0]/.test(buf)) ? 'Shift_JIS' : 'EUC-JP';
if(/^(?:[\x00-\x7F\xA1-\xDF]|[\x81-\x9F\xE0-\xFC][\x40-\x7E\x80-\xFC])+$/.test(buf)) return 'Shift_JIS';
if(/[\x80-\xA0]/.test(buf)) return 'UTF-8';
return 'EUC-JP';
}
else return 'us-ascii';
};
// url(文字列)の指すファイルが存在するか否かを返す.
Toolkit.prototype.isFileExists = function(url){
try{ return IO_SERVICE.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getFileFromURLSpec(IO_SERVICE.newURI(url,null,null).spec).exists(); }
catch(e){
try{ var s = IO_SERVICE.newChannelFromURI(IO_SERVICE.newURI(url,null,null)).open(); s.close(); return true; }catch(e){}
return false;
}
};
// ローカルパス path(文字列)の URL 表記文字列を返す.
Toolkit.prototype.fromLocalPath = function(path){
try{
var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
file.initWithPath(path);
return Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService).getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromFile(file);
}
catch(e){ return null; }
};
// url(文字列)のローカルパス文字列を返す.
Toolkit.prototype.toLocalPath = function(url){
try{ return IO_SERVICE.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getFileFromURLSpec(url).path; }
catch(e){ return null; }
};
// nsIFile オブジェクト file の ファイルパスを URL 表記文字列で返す.
Toolkit.prototype.fileToURL = function(file){
try{ return Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService).getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getURLSpecFromFile(file); }
catch(e){ return null; }
};
// ワイルドカードを含む文字列 wcstr を正規表現オブジェクトにして返す.
Toolkit.prototype.wildcardToRegex = function( pattern ) {
var s = new String(pattern);
var res = new String("^");
for (var i = 0 ; i < s.length ; i++) {
switch(s[i]) {
case "*" :
res += ".*";
break;
case "." :
case "?" :
case "^" :
case "$" :
case "+" :
case "{" :
case "[" :
case "|" :
case "(" :
case ")" :
case "]" :
res += "\\" + s[i];
break;
case "\\" :
res += "\\\\";
break;
case " " :
// Remove spaces from URLs.
break;
default :
res += s[i];
break;
}
}
var tldRegExp = new RegExp("^(\\^(?:[^/]*)(?://)?(?:[^/]*))(\\\\\\.tld)((?:/.*)?)$")
var tldRes = res.match(tldRegExp);
if (tldRes) {
// build the mighty TLD RegExp
var tldStr = "\.(?:demon\\.co\\.uk|esc\\.edu\\.ar|(?:c[oi]\\.)?[^\\.]\\.(?:vt|ne|ks|il|hi|sc|nh|ia|wy|or|ma|vi|tn|in|az|id|nc|co|dc|nd|me|al|ak|de|wv|nm|mo|pr|nj|sd|md|va|ri|ut|ct|pa|ok|ky|mt|ga|la|oh|ms|wi|wa|gu|mi|tx|fl|ca|ar|mn|ny|nv)\\.us|[^\\.]\\.(?:(?:pvt\\.)?k12|cc|tec|lib|state|gen)\\.(?:vt|ne|ks|il|hi|sc|nh|ia|wy|or|ma|vi|tn|in|az|id|nc|co|dc|nd|me|al|ak|de|wv|nm|mo|pr|nj|sd|md|va|ri|ut|ct|pa|ok|ky|mt|ga|la|oh|ms|wi|wa|gu|mi|tx|fl|ca|ar|mn|ny|nv)\\.us|[^\\.]\\.vt|ne|ks|il|hi|sc|nh|ia|wy|or|ma|vi|tn|in|az|id|nc|co|dc|nd|me|al|ak|de|wv|nm|mo|pr|nj|sd|md|va|ri|ut|ct|pa|ok|ky|mt|ga|la|oh|ms|wi|wa|gu|mi|tx|fl|ca|ar|mn|ny|nvus|ne|gg|tr|mm|ki|biz|sj|my|hn|gl|ro|tn|co|br|coop|cy|bo|ck|tc|bv|ke|aero|cs|dm|km|bf|af|mv|ls|tm|jm|pg|ky|ga|pn|sv|mq|hu|za|se|uy|iq|ai|com|ve|na|ba|ph|xxx|no|lv|tf|kz|ma|in|id|si|re|om|by|fi|gs|ir|li|tz|td|cg|pa|am|tv|jo|bi|ee|cd|pk|mn|gd|nz|as|lc|ae|cn|ag|mx|sy|cx|cr|vi|sg|bm|kh|nr|bz|vu|kw|gf|al|uz|eh|int|ht|mw|gm|bg|gu|info|aw|gy|ac|ca|museum|sk|ax|es|kp|bb|sa|et|ie|tl|org|tj|cf|im|mk|de|pro|md|fm|cl|jp|bn|vn|gp|sm|ar|dj|bd|mc|ug|nu|ci|dk|nc|rw|aq|name|st|hm|mo|gq|ps|ge|ao|gr|va|is|mt|gi|la|bh|ms|bt|gb|it|wf|sb|ly|ng|gt|lu|il|pt|mh|eg|kg|pf|um|fr|sr|vg|fj|py|pm|sn|sd|au|sl|gh|us|mr|dz|ye|kn|cm|arpa|bw|lk|mg|tk|su|sc|ru|travel|az|ec|mz|lb|ml|bj|edu|pr|fk|lr|nf|np|do|mp|bs|to|cu|ch|yu|eu|mu|ni|pw|pl|gov|pe|an|ua|uk|gw|tp|kr|je|tt|net|fo|jobs|yt|cc|sh|io|zm|hk|th|so|er|cz|lt|mil|hr|gn|be|qa|cv|vc|tw|ws|ad|sz|at|tg|zw|nl|info\\.tn|org\\.sd|med\\.sd|com\\.hk|org\\.ai|edu\\.sg|at\\.tt|mail\\.pl|net\\.ni|pol\\.dz|hiroshima\\.jp|org\\.bh|edu\\.vu|net\\.im|ernet\\.in|nic\\.tt|com\\.tn|go\\.cr|jersey\\.je|bc\\.ca|com\\.la|go\\.jp|com\\.uy|tourism\\.tn|com\\.ec|conf\\.au|dk\\.org|shizuoka\\.jp|ac\\.vn|matsuyama\\.jp|agro\\.pl|yamaguchi\\.jp|edu\\.vn|yamanashi\\.jp|mil\\.in|sos\\.pl|bj\\.cn|net\\.au|ac\\.ae|psi\\.br|sch\\.ng|org\\.mt|edu\\.ai|edu\\.ck|ac\\.yu|org\\.ws|org\\.ng|rel\\.pl|uk\\.tt|com\\.py|aomori\\.jp|co\\.ug|video\\.hu|net\\.gg|org\\.pk|id\\.au|gov\\.zw|mil\\.tr|net\\.tn|org\\.ly|re\\.kr|mil\\.ye|mil\\.do|com\\.bb|net\\.vi|edu\\.na|co\\.za|asso\\.re|nom\\.pe|edu\\.tw|name\\.et|jl\\.cn|gov\\.ye|ehime\\.jp|miyazaki\\.jp|kanagawa\\.jp|gov\\.au|nm\\.cn|he\\.cn|edu\\.sd|mod\\.om|web\\.ve|edu\\.hk|medecin\\.fr|org\\.cu|info\\.au|edu\\.ve|nx\\.cn|alderney\\.gg|net\\.cu|org\\.za|mb\\.ca|com\\.ye|edu\\.pa|fed\\.us|ac\\.pa|alt\\.na|mil\\.lv|fukuoka\\.jp|gen\\.in|gr\\.jp|gov\\.br|gov\\.ac|id\\.fj|fukui\\.jp|hu\\.com|org\\.gu|net\\.ae|mil\\.ph|ltd\\.je|alt\\.za|gov\\.np|edu\\.jo|net\\.gu|g12\\.br|org\\.tn|store\\.co|fin\\.tn|ac\\.nz|gouv\\.fr|gov\\.il|org\\.ua|org\\.do|org\\.fj|sci\\.eg|gov\\.tt|cci\\.fr|tokyo\\.jp|net\\.lv|gov\\.lc|ind\\.br|ca\\.tt|gos\\.pk|hi\\.cn|net\\.do|co\\.tv|web\\.co|com\\.pa|com\\.ng|ac\\.ma|gov\\.bh|org\\.zw|csiro\\.au|lakas\\.hu|gob\\.ni|gov\\.fk|org\\.sy|gov\\.lb|gov\\.je|ed\\.cr|nb\\.ca|net\\.uy|com\\.ua|media\\.hu|com\\.lb|nom\\.pl|org\\.br|hk\\.cn|co\\.hu|org\\.my|gov\\.dz|sld\\.pa|gob\\.pk|net\\.uk|guernsey\\.gg|nara\\.jp|telememo\\.au|k12\\.tr|org\\.nz|pub\\.sa|edu\\.ac|com\\.dz|edu\\.lv|edu\\.pk|com\\.ph|net\\.na|net\\.et|id\\.lv|au\\.com|ac\\.ng|com\\.my|net\\.cy|unam\\.na|nom\\.za|net\\.np|info\\.pl|priv\\.hu|rec\\.ve|ac\\.uk|edu\\.mm|go\\.ug|ac\\.ug|co\\.dk|net\\.tt|oita\\.jp|fi\\.cr|org\\.ac|aichi\\.jp|org\\.tt|edu\\.bh|us\\.com|ac\\.kr|js\\.cn|edu\\.ni|com\\.mt|fam\\.pk|experts-comptables\\.fr|or\\.kr|org\\.au|web\\.pk|mil\\.jo|biz\\.pl|org\\.np|city\\.hu|org\\.uy|auto\\.pl|aid\\.pl|bib\\.ve|mo\\.cn|br\\.com|dns\\.be|sh\\.cn|org\\.mo|com\\.sg|me\\.uk|gov\\.kw|eun\\.eg|kagoshima\\.jp|ln\\.cn|seoul\\.kr|school\\.fj|com\\.mk|e164\\.arpa|rnu\\.tn|pro\\.ae|org\\.om|gov\\.my|net\\.ye|gov\\.do|co\\.im|org\\.lb|plc\\.co\\.im|net\\.jp|go\\.id|net\\.tw|gov\\.ai|tlf\\.nr|ac\\.im|com\\.do|net\\.py|tozsde\\.hu|com\\.na|tottori\\.jp|net\\.ge|gov\\.cn|org\\.bb|net\\.bs|ac\\.za|rns\\.tn|biz\\.pk|gov\\.ge|org\\.uk|org\\.fk|nhs\\.uk|net\\.bh|tm\\.za|co\\.nz|gov\\.jp|jogasz\\.hu|shop\\.pl|media\\.pl|chiba\\.jp|city\\.za|org\\.ck|net\\.id|com\\.ar|gon\\.pk|gov\\.om|idf\\.il|net\\.cn|prd\\.fr|co\\.in|or\\.ug|red\\.sv|edu\\.lb|k12\\.ec|gx\\.cn|net\\.nz|info\\.hu|ac\\.zw|info\\.tt|com\\.ws|org\\.gg|com\\.et|ac\\.jp|ac\\.at|avocat\\.fr|org\\.ph|sark\\.gg|org\\.ve|tm\\.pl|net\\.pg|gov\\.co|com\\.lc|film\\.hu|ishikawa\\.jp|hotel\\.hu|hl\\.cn|edu\\.ge|com\\.bm|ac\\.om|tec\\.ve|edu\\.tr|cq\\.cn|com\\.pk|firm\\.in|inf\\.br|gunma\\.jp|gov\\.tn|oz\\.au|nf\\.ca|akita\\.jp|net\\.sd|tourism\\.pl|net\\.bb|or\\.at|idv\\.tw|dni\\.us|org\\.mx|conf\\.lv|net\\.jo|nic\\.in|info\\.vn|pe\\.kr|tw\\.cn|org\\.eg|ad\\.jp|hb\\.cn|kyonggi\\.kr|bourse\\.za|org\\.sb|gov\\.gg|net\\.br|mil\\.pe|kobe\\.jp|net\\.sa|edu\\.mt|org\\.vn|yokohama\\.jp|net\\.il|ac\\.cr|edu\\.sb|nagano\\.jp|travel\\.pl|gov\\.tr|com\\.sv|co\\.il|rec\\.br|biz\\.om|com\\.mm|com\\.az|org\\.vu|edu\\.ng|com\\.mx|info\\.co|realestate\\.pl|mil\\.sh|yamagata\\.jp|or\\.id|org\\.ae|greta\\.fr|k12\\.il|com\\.tw|gov\\.ve|arts\\.ve|cul\\.na|gov\\.kh|org\\.bm|etc\\.br|or\\.th|ch\\.vu|de\\.tt|ind\\.je|org\\.tw|nom\\.fr|co\\.tt|net\\.lc|intl\\.tn|shiga\\.jp|pvt\\.ge|gov\\.ua|org\\.pe|net\\.kh|co\\.vi|iwi\\.nz|biz\\.vn|gov\\.ck|edu\\.eg|zj\\.cn|press\\.ma|ac\\.in|eu\\.tt|art\\.do|med\\.ec|bbs\\.tr|gov\\.uk|edu\\.ua|eu\\.com|web\\.do|szex\\.hu|mil\\.kh|gen\\.nz|okinawa\\.jp|mob\\.nr|edu\\.ws|edu\\.sv|xj\\.cn|net\\.ru|dk\\.tt|erotika\\.hu|com\\.sh|cn\\.com|edu\\.pl|com\\.nc|org\\.il|arts\\.co|chirurgiens-dentistes\\.fr|net\\.pa|takamatsu\\.jp|net\\.ng|org\\.hu|net\\.in|net\\.vu|gen\\.tr|shop\\.hu|com\\.ae|tokushima\\.jp|za\\.com|gov\\.eg|co\\.jp|uba\\.ar|net\\.my|biz\\.et|art\\.br|ac\\.fk|gob\\.pe|com\\.bs|co\\.ae|de\\.net|net\\.eg|hyogo\\.jp|edunet\\.tn|museum\\.om|nom\\.ve|rnrt\\.tn|hn\\.cn|com\\.fk|edu\\.dz|ne\\.kr|co\\.je|sch\\.uk|priv\\.pl|sp\\.br|net\\.hk|name\\.vn|com\\.sa|edu\\.bm|qc\\.ca|bolt\\.hu|per\\.kh|sn\\.cn|mil\\.id|kagawa\\.jp|utsunomiya\\.jp|erotica\\.hu|gd\\.cn|net\\.tr|edu\\.np|asn\\.au|com\\.gu|ind\\.tn|mil\\.br|net\\.lb|nom\\.co|org\\.la|mil\\.pl|ac\\.il|gov\\.jo|com\\.kw|edu\\.sh|otc\\.au|gmina\\.pl|per\\.sg|gov\\.mo|int\\.ve|news\\.hu|sec\\.ps|ac\\.pg|health\\.vn|sex\\.pl|net\\.nc|qc\\.com|idv\\.hk|org\\.hk|gok\\.pk|com\\.ac|tochigi\\.jp|gsm\\.pl|law\\.za|pro\\.vn|edu\\.pe|info\\.et|sch\\.gg|com\\.vn|gov\\.bm|com\\.cn|mod\\.uk|gov\\.ps|toyama\\.jp|gv\\.at|yk\\.ca|org\\.et|suli\\.hu|edu\\.my|org\\.mm|co\\.yu|int\\.ar|pe\\.ca|tm\\.hu|net\\.sb|org\\.yu|com\\.ru|com\\.pe|edu\\.kh|edu\\.kw|org\\.qa|med\\.om|net\\.ws|org\\.in|turystyka\\.pl|store\\.ve|org\\.bs|mil\\.uy|net\\.ar|iwate\\.jp|org\\.nc|us\\.tt|gov\\.sh|nom\\.fk|go\\.th|gov\\.ec|com\\.br|edu\\.do|gov\\.ng|pro\\.tt|sapporo\\.jp|net\\.ua|tm\\.fr|com\\.lv|com\\.mo|edu\\.uk|fin\\.ec|edu\\.ps|ru\\.com|edu\\.ec|ac\\.fj|net\\.mm|veterinaire\\.fr|nom\\.re|ingatlan\\.hu|fr\\.vu|ne\\.jp|int\\.co|gov\\.cy|org\\.lv|de\\.com|nagasaki\\.jp|com\\.sb|gov\\.za|org\\.lc|com\\.fj|ind\\.in|or\\.cr|sc\\.cn|chambagri\\.fr|or\\.jp|forum\\.hu|tmp\\.br|reklam\\.hu|gob\\.sv|com\\.pl|saitama\\.jp|name\\.tt|niigata\\.jp|sklep\\.pl|nom\\.ni|co\\.ma|net\\.la|co\\.om|pharmacien\\.fr|port\\.fr|mil\\.gu|au\\.tt|edu\\.gu|ngo\\.ph|com\\.ve|ac\\.th|gov\\.fj|barreau\\.fr|net\\.ac|ac\\.je|org\\.kw|sport\\.hu|ac\\.cn|net\\.bm|ibaraki\\.jp|tel\\.no|org\\.cy|edu\\.mo|gb\\.net|kyoto\\.jp|sch\\.sa|com\\.au|edu\\.lc|fax\\.nr|gov\\.mm|it\\.tt|org\\.jo|nat\\.tn|mil\\.ve|be\\.tt|org\\.az|rec\\.co|co\\.ve|gifu\\.jp|net\\.th|hokkaido\\.jp|ac\\.gg|go\\.kr|edu\\.ye|qh\\.cn|ab\\.ca|org\\.cn|no\\.com|co\\.uk|gov\\.gu|de\\.vu|miasta\\.pl|kawasaki\\.jp|co\\.cr|miyagi\\.jp|org\\.jp|osaka\\.jp|web\\.za|net\\.za|gov\\.pk|gov\\.vn|agrar\\.hu|asn\\.lv|org\\.sv|net\\.sh|org\\.sa|org\\.dz|assedic\\.fr|com\\.sy|net\\.ph|mil\\.ge|es\\.tt|mobile\\.nr|co\\.kr|ltd\\.uk|ac\\.be|fgov\\.be|geek\\.nz|ind\\.gg|net\\.mt|maori\\.nz|ens\\.tn|edu\\.py|gov\\.sd|gov\\.qa|nt\\.ca|com\\.pg|org\\.kh|pc\\.pl|com\\.eg|net\\.ly|se\\.com|gb\\.com|edu\\.ar|sch\\.je|mil\\.ac|mil\\.ar|okayama\\.jp|gov\\.sg|ac\\.id|co\\.id|com\\.ly|huissier-justice\\.fr|nic\\.im|gov\\.lv|nu\\.ca|org\\.sg|com\\.kh|org\\.vi|sa\\.cr|lg\\.jp|ns\\.ca|edu\\.co|gov\\.im|edu\\.om|net\\.dz|org\\.pl|pp\\.ru|tm\\.mt|org\\.ar|co\\.gg|org\\.im|edu\\.qa|org\\.py|edu\\.uy|targi\\.pl|com\\.ge|gub\\.uy|gov\\.ar|ltd\\.gg|fr\\.tt|net\\.qa|com\\.np|ass\\.dz|se\\.tt|com\\.ai|org\\.ma|plo\\.ps|co\\.at|med\\.sa|net\\.sg|kanazawa\\.jp|com\\.fr|school\\.za|net\\.pl|ngo\\.za|net\\.sy|ed\\.jp|org\\.na|net\\.ma|asso\\.fr|police\\.uk|powiat\\.pl|govt\\.nz|sk\\.ca|tj\\.cn|mil\\.ec|com\\.jo|net\\.mo|notaires\\.fr|avoues\\.fr|aeroport\\.fr|yn\\.cn|gov\\.et|gov\\.sa|gov\\.ae|com\\.tt|art\\.dz|firm\\.ve|com\\.sd|school\\.nz|edu\\.et|gob\\.pa|telecom\\.na|ac\\.cy|gz\\.cn|net\\.kw|mobil\\.nr|nic\\.uk|co\\.th|com\\.vu|com\\.re|belgie\\.be|nl\\.ca|uk\\.com|com\\.om|utazas\\.hu|presse\\.fr|co\\.ck|xz\\.cn|org\\.tr|mil\\.co|edu\\.cn|net\\.ec|on\\.ca|konyvelo\\.hu|gop\\.pk|net\\.om|info\\.ve|com\\.ni|sa\\.com|com\\.tr|sch\\.sd|fukushima\\.jp|tel\\.nr|atm\\.pl|kitakyushu\\.jp|com\\.qa|firm\\.co|edu\\.tt|games\\.hu|mil\\.nz|cri\\.nz|net\\.az|org\\.ge|mie\\.jp|net\\.mx|sch\\.ae|nieruchomosci\\.pl|int\\.vn|edu\\.za|com\\.cy|wakayama\\.jp|gov\\.hk|org\\.pa|edu\\.au|gov\\.in|pro\\.om|2000\\.hu|szkola\\.pl|shimane\\.jp|co\\.zw|gove\\.tw|com\\.co|net\\.ck|net\\.pk|net\\.ve|org\\.ru|uk\\.net|org\\.co|uu\\.mt|com\\.cu|mil\\.za|plc\\.uk|lkd\\.co\\.im|gs\\.cn|sex\\.hu|net\\.je|kumamoto\\.jp|mil\\.lb|edu\\.yu|gov\\.ws|sendai\\.jp|eu\\.org|ah\\.cn|net\\.vn|gov\\.sb|net\\.pe|nagoya\\.jp|geometre-expert\\.fr|net\\.fk|biz\\.tt|org\\.sh|edu\\.sa|saga\\.jp|sx\\.cn|org\\.je|org\\.ye|muni\\.il|kochi\\.jp|com\\.bh|org\\.ec|priv\\.at|gov\\.sy|org\\.ni|casino\\.hu|res\\.in|uy\\.com)"
// insert it
res = tldRes[1] + tldStr + tldRes[3];
}
return new RegExp(res + "$", "i");
};
// len で指定した長さのランダム文字列を返す.
// character に使う文字の羅列を入れておくとそれらの文字を使い,省略すると英数字を使う.
Toolkit.prototype.createRandomString = function(len,character){
if(!character) character = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
var buf = '';
for(var i = 0,slen = character.length;i < len;++i) buf += character.charAt(Math.floor(Math.random() * slen));
return buf;
};
// code(文字列)から Greasemonkey 方式のメタデータを読み取り,連想配列にして返す.
// 同じ項目名で値が複数ある場合は '\n' を区切り文字として使う.
Toolkit.prototype.readMeta = function(code){
if(!code) return [];
var a = code.split(/\r\n|\r|\n/);
var max = a.length;
var meta = false;
var data = [];
for(var i = 0;i < max;++i){
if (!meta && REGEX_META_START.test(a[i])) meta = true;
else if( meta && REGEX_META_END.test(a[i])) break;
else if( meta && REGEX_META_DATA.test(a[i])){
if(data[RegExp.$1]) data[RegExp.$1] += '\n' + RegExp.$2;
else data[RegExp.$1] = RegExp.$2;
}
}
return data;
};
// name(文字列)で指定した項目名の設定を,値に適した型で返す.設定値が無ければ null を返す.
Toolkit.prototype.getPref = function(name){
const PREF = Cc['@mozilla.org/preferences;1'].getService(Ci.nsIPrefBranch);
try{
switch(PREF.getPrefType(name)){
case Ci.nsIPrefBranch.PREF_STRING: return PREF.getCharPref(name);
case Ci.nsIPrefBranch.PREF_INT: return PREF.getIntPref(name);
case Ci.nsIPrefBranch.PREF_BOOL: return PREF.getBoolPref(name);
default: return null; }
}
catch(e){ return null; }
};
// name(文字列)で指定した項目名の設定に value を書き込む.
// 失敗で null を返すかも.
Toolkit.prototype.setPref = function(name,value){
const PREF = Cc['@mozilla.org/preferences;1'].getService(Ci.nsIPrefBranch);
switch(Toolkit.prototype._typeof(value)){
case 'string': return PREF.setCharPref(name,value);
case 'number': return PREF.setIntPref(name,value);
case 'boolean': return PREF.setBoolPref(name,value);
default: return null; }
};
// ローカルパス path(文字列)で指定したファイルを引数の配列 arg を伴って実行する.
// wait が真ならプロセスの終了まで待つ.
Toolkit.prototype.process = function(path,arg,wait){
var file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
var process = Cc['@mozilla.org/process/util;1'].createInstance(Ci.nsIProcess);
file.initWithPath(path);
process.init(file);
process.run(wait,arg,arg.length);
};
// 番号表クラス.new UCJSToolkit().NumberTable(retired);
// オブジェクトに番号付けをする.
// get(obj) で obj の指すオブジェクトに付けられた番号,又は新規に付けた番号を返す.
// delete(obj) で obj の指すオブジェクトに付けられた番号を削除する.
// retired が真なら delete() した番号は永久欠番となり,そうでないのならその番号は再び使われる.
Toolkit.prototype.NumberTable = function(retired){
var table = [];
var empty = [0];
this.get = function(obj){
var id = table.indexOf(obj); if(id != -1) return id;
if(retired) return table.push(obj) - 1;
table[id = empty.pop()] = obj;
if(!empty.length) empty.push(table.length);
/*Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService)
.logStringMessage('Create WindowId = ' + id);
*/
return id;
};
this.delete = function(obj){
var id = table.indexOf(obj); if(id == -1) return null;
delete table[id];
if(!retired) empty.push(id);
/*
Components.classes["@mozilla.org/consoleservice;1"]
.getService(Components.interfaces.nsIConsoleService)
.logStringMessage('Delete WindowId = ' + id);
*/
return id;
};
};
// scope オブジェクトのスコープのプロトタイプを拡張する.
// extend は拡張するプロトタイプを指定した配列.'string' で文字列,'regexp' で正規表現のプロトタイプが拡張される.
// 文字列のプロトタイプには toUnicode fromeUnicode getCharset が加わる.
// 正規表現のプロトタイプには globalMatch が加わる.
Toolkit.prototype.extendPrototype = function(scope,extend){
var strflag = true;
var regflag = true;
if(Toolkit.prototype._typeof(extend) == 'array'){
strflag = (extend.indexOf('string') == -1) ? false : true;
regflag = (extend.indexOf('regexp') == -1) ? false : true;
}
if(strflag){
// charset で指定した文字コードからユニコード(UTF-16)へ変換した文字列を返す.
// charset を省略すると文字コードを自動判別する.
scope.String.prototype.toUnicode = function(charset){
UNICODE_CONVERTER.charset = charset || this.getCharset();
return UNICODE_CONVERTER.ConvertToUnicode(this);
};
// charset で指定した文字コードへユニコード(UTF-16)から変換した文字列を返す.
scope.String.prototype.fromUnicode = function(charset){
if(!charset) return this;
UNICODE_CONVERTER.charset = charset;
return UNICODE_CONVERTER.ConvertFromUnicode(this);
};
// 前述の getCharset() と同様.
scope.String.prototype.getCharset = function(){ return Toolkit.prototype.getCharset(this); };
}
if(regflag){
// 文字列 str のマッチする全てにマッチし,func の指す関数をコールバックする.
// 正規表現の g 指定は不要で,マッチすれば最終的にマッチした箇所の右側を返し,そうでなければ null を返す.
// コールバック関数の第一引数は,マッチした箇所の左側(以前にマッチした箇所から今回マッチした箇所まで)が入り,
// 第二引数には後方参照の配列が入り,第三引数にはマッチした箇所の右側が入る.
scope.RegExp.prototype.globalMatch = function(str,func){
if(Toolkit.prototype._typeof(func) != 'function') return null;
var flag = false;
while(this(str)){ str = RegExp.rightContext; func(RegExp.leftContext,[RegExp.lastMatch,RegExp.$1,RegExp.$2,RegExp.$3,RegExp.$4,RegExp.$5,RegExp.$6,RegExp.$7,RegExp.$8,RegExp.$9],RegExp.rightContext); flag = true; }
return (flag) ? str : null;
};
}
};
return Toolkit;
})();
(function(Toolkit){
const METADATA = <><![CDATA[
// ==UserScript==
// @name userMenu.js
// @author 80
// @version 1.21mod8 2011/05/05 08:00 xxx Fx6 対応(regexp(str) が使えないようなので regexp.test(str) または regexp.exec(str) に)
// @version 1.21mod7 2011/05/04 18:00 xxx 常に全てのアイテムが出ていたのを修正(MenuEntry をコンストラクタでなく直接 element を返すただの関数に)
// @version 1.21mod6 2011/05/04 11:30 xxx Fx4 対応(MenuEntry オブジェクトが __proto__ でメニューアイテムを持っていたのが何かうまくいかないので element に持つように、他)
// @version 1.21mod5 2009/07/07 13:00 Alice0775 Minefield/3.6a1pre ID:20090706042748で例外が投げられるのを修正 (Bug 482788 - (slimwrapper) Lightweight DOM wrappers)
// @version 1.21mod4 2009/05/21 09:30 Alice0775 window が消えると番号表からその window に割り振られた番号を消す修正
// @version 1.21mod3 2009/05/21 00:00 Alice0775 xxx
// @version 1.21mod2 2009/05/30 00:00 Alice0775 Bug 452498 - (upvar2) TM: Can we jit heavyweight functions? (upvar, part deux)
// @include main
// @include chrome://browser/content/browser.xul
// @include chrome://navigator/content/navigator.xul
// ==/UserScript==
]]></>;
// --- config ---
const DIRNAME_TOOLMENU = 'toolmenu';
const DIRNAME_CONTEXTMENU = 'contextmenu';
const DIRNAME_TABMENU = 'tabmenu';
const DIRNAME_EXTRAMENU = 'extramenu';
const FILENAME_PRELOAD = 'userMenu.pl.js';
const LABEL_EXTRAMENU = 'user';
const NLALLOWIMAGE = true;
const USE_SORT = false;
const READ_META = 1;
const ERROR_DISPLAY = 1|2;
// --- config ---
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const IO_SERVICE = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService);
const ID_EXTRAMENU = 'usermenujs-extramenu';
const MENUNAME = ['tool','context','tab','extra'];
const VIEWTYPE = {gl : 'global',li : 'link',im : 'image',se : 'selection',it : 'inputtext',no : 'nothing',nl : 'nothingandlink',ct : 'contenttab',tb : 'tabbar'};
const REGEX_SUBMENU = /^(?:[0-9]+\.)?(.+?)((?:\.(?:(?:[a-zA-Z])|(?:gl|li|im|se|it|no|nl|ct|tb)))*)$/;
const REGEX_SCRIPTFILE = /^(?:[0-9]+\.)?(.+?)((?:\.(?:(?:[a-zA-Z])|(?:gl|li|im|se|it|no|nl|ct|tb)|(?:auto)|(?:nonpriv)))*)(\.user)?\.js$/;
const REGEX_SEPARATORFILE = /(?:\.(gl|li|im|se|it|no|nl|ct|tb))?\.---$/;
const SEAMONKEY = / *SeaMonkey$/.test(document.title);
const PRIVILEGE = true;
const WORK_SANDBOX = Components.utils.Sandbox(this);
WORK_SANDBOX.__proto__ = this;
var preload = null;
var toolkit = new Toolkit();
var _typeof = toolkit._typeof;
var errMsg = toolkit.errorMessage;
toolkit.setErrorDisplay(ERROR_DISPLAY);
toolkit.extendPrototype(WORK_SANDBOX);
// プリロードファイルの読み込み.
function loadPreloadFile(filename){
if(!filename) return null;
var safe = Components.utils.Sandbox(this);
[safe.Cc,safe.Ci,safe.Cr] = [Cc,Ci,Cr];
var tk = safe.__proto__ = new Toolkit();
tk.__proto__.__proto__ = this;
tk.extendPrototype(safe);
try{
if(!safe.require(filename)) throw tk.ERROR;
var pl = new safe.preload();
return (pl.setup()) ? pl : null;
}
catch(e){ errMsg('load preload file',e); return null; }
}
// getter / setter に対応したオブジェクトの輸入.直下の要素を代入しているだけ.
function importObject(dest,src){
for(let i in src){
let getter = src.__lookupGetter__(i),setter = src.__lookupSetter__(i);
if(getter || setter){ if(getter) dest.__defineGetter__(i,getter); if(setter) dest.__defineSetter__(i,setter); }
else{ if(i in dest) delete dest[i]; dest[i] = src[i]; }
}
}
// UTF-8 のバイト列の文字列を,UTF-8 の文字列にする.出来なかった場合は文字コードを自動判別して UTF-16 に.
function utf(str){
try{ return decodeURIComponent(escape(str)); }
catch(e){
try{ return (new WORK_SANDBOX.String(str)).toUnicode(); }
catch(e){ return str; }
}
}
// スクリプトエントリクラス.スクリプトの情報を入れておく.
function ScriptEntry(obj,type,name,nonpriv){
switch(type || _typeof(obj)){
case 'string':
if(/^[a-z]+:[/]/.test(obj)){
this.fileName = /([^/]+)$/.exec(obj)[1];
this.url = obj;
}
else{
this.fileName = '';
this.url = '';
this.codeString = obj;
}
break;
case 'function':
this.fileName = '';
this.url = '';
this.codeString = uneval(obj) + '();';
break;
case 'file':
this.fileName = obj.leafName;
this.url = toolkit.fileToURL(obj);
break;
default:
this.fileName = '';
this.url = '';
break; }
this.dir = this.url.replace(/[^/]+$/,'');
this.name = name || this.fileName;
this.metaData = null;
this.allow = null;
this.deny = null;
this.nonPrivilege = !!nonpriv;
this.keepVariable = [];
var destructor = null;
this.setDestructor = function(dest){ return (_typeof(dest) == 'function') ? !!(destructor = dest) : false; };
this.runDestructor = function(){ if(destructor) try{ destructor(); }catch(e){ errMsg(this.fileName,e); } destructor = null; };
}
// スクリプトの実行.
ScriptEntry.prototype.run = function(win, event, impobj){
if(!win || !event) return false;
var script = this;
var tk = new Toolkit(); tk.SCRIPT_PATH.unshift(script.url.replace(/[^/]+$/,''));
var ns = createNamespace(win, (preload) ? preload.createObject(event, script.url, win,script) :
{}, script.nonPrivilege);
ns.__defineGetter__ = __defineGetter__;
ns.__defineSetter__ = __defineSetter__;
// getter を使うと非特権からアクセスできなくなるので,各要素へのアクセス制限として利用.
ns.__defineGetter__('event',function(){ return event; });
ns.__defineGetter__('_this',function(){ return script; });
ns.__defineGetter__('SCRIPT_FILE',function(){ return script.url; });
ns.__defineGetter__('SCRIPT_NAME',function(){ return script.name; });
ns.__defineGetter__('SCRIPT_PATH',function(){ return tk.SCRIPT_PATH; });
ns.__defineGetter__('KEEP_VARIABLE',function(){ return script.keepVariable; });
ns.__defineGetter__('setDestructor',function(){ return script.setDestructor; });
ns.__defineGetter__('require',function(){ return tk.require; });
ns.__defineGetter__('ucjsToolkit',function(){ return tk; });
ns.__defineGetter__('gContextMenu',function(){ return gContextMenu; });
ns.__defineGetter__('setVariableLabel',function(){ return function(func){ return (_typeof(func) == 'function') ? !!(event.target.variableLabel = func) : false; } });
ns.__defineGetter__('isVariableLabel',function(){ return function(){ return event.target.variableLabel && _typeof(event.target.variableLabel) == 'function'; } });
importObject(ns,impobj);
delete ns.__defineGetter__;
delete ns.__defineSetter__;
try{
// eval しないと getter によるアクセス制限が効かなかったり,UTF-8 でも文字化けしたりする.
// var st = new Date();
if(script.nonPrivilege && !script.codeString) script.codeString = toolkit.loadFile(script.url);
if(script.codeString && !script._codeString) script._codeString = 'data:application/javascript,' + encodeURI('eval(_this.codeString);');
if(script.codeString) {
Components.utils.evalInSandbox('(function(){ ' + script.codeString + '\n})();', ns);
} else {
toolkit.loadSubScript(script.url, ns);
// dump(new Date() - st + ' ms : ' + script.name + '\n');
}
return true;
}
catch(e){
if(!e) return true; // 評価して偽なら中断と見なす.
if(toolkit._typeof(e) == 'error' && e.fileName == script._codeString) e.fileName = script.url;
errMsg(script.url.replace(/^.+?([^/]+)$/,'$1') || script.name,e);
return false;
}
};
// url 上でスクリプトを適応させるか否か.実際にはメニュー表示 / 非表示が切り替わる.
ScriptEntry.prototype.isAllow = function(url){
if(!this.allow && !this.deny) return true;
if( this.deny && this.deny.some(function(rx){ return rx.test(url); })) return false;
if(this.allow) return this.allow.some(function(rx){ return rx.test(url); });
else return true;
};
// メニューエントリクラス.XUL 要素を継承しているので,XUL 要素と同様に扱えると思う.多分.
function MenuEntry(menuname,type,obj,name,key,viewtype,nonpriv){
var element;
if(type == 'separator') element = document.createElement('menuseparator');
else{
let objtype = _typeof(obj);
let label = name;
if(type == 'script'){
if(objtype != 'string' && objtype != 'file' && objtype !='function') return;
let script = new ScriptEntry(obj,objtype,name,nonpriv);
element = document.createElement('menuitem');
element.setAttribute('oncommand','this.run(event);');
element.script = script;
element.run = function(event){
event.button = getMouseButton();
var win = (this.menuName == MENUNAME[1] && gContextMenu) ? gContextMenu.target.ownerDocument.defaultView
: (this.menuName == MENUNAME[2] && gBrowser.mContextTab && gBrowser.mContextTab.linkedBrowser) ? gBrowser.mContextTab.linkedBrowser.contentWindow : content.window;
this.script.run(win, event);
};
if(READ_META == 2 || (READ_META == 1 && /[.]user[.]js$/.test(script.url))){
let meta = script.metaData = toolkit.readMeta(toolkit.loadFile(obj));
if(meta.label) label = utf(meta.label);
if(meta.name) script.name = meta.name;
if(meta.privilege) script.nonPrivilege = (meta.privilege == 'true') ? false : (meta.privilege == 'false') ? true : nonpriv;
if(meta.description) element.setAttribute('tooltiptext',utf(meta.description));
if(meta.autorun && meta.autorun == 'true') element.autoRun = true;
script.allow = (meta.include) ? meta.include.split(/\n/).map(toolkit.wildcardToRegex) : null;
script.deny = (meta.exclude) ? meta.exclude.split(/\n/).map(toolkit.wildcardToRegex) : null;
}
}
else if(type == 'directory') (element = document.createElement('menu')).appendChild(document.createElement('menupopup'));
else return;
if(key) element.setAttribute('accesskey',key);
element.setAttribute('label',label);
element.menuName = menuname;
let icon = ((objtype == 'string' && /^[a-z]+:[/]/.test(obj)) ? obj
: (objtype == 'file') ? toolkit.fileToURL(obj) : '').replace(/[^/]+[/]?$/,'') + name + '.ico';
if(toolkit.isFileExists(icon)){
element.setAttribute('image',icon);
element.className = (type == 'script') ? 'menuitem-iconic' : 'menu-iconic';
}
}
element.userMenu_js = true;
element.viewType = VIEWTYPE[viewtype] || viewtype || '';
element.remove = function(){ if(element.script) element.script.runDestructor(); element.parentNode.removeChild(element); };
element.createChild = (type != 'directory') ? function(){ return null; }
: function(type,obj,name,key,viewtype,auto,nonpriv){
var child = MenuEntry(this.menuName,type,obj,name,key,viewtype || this.viewType,nonpriv); if(!child.userMenu_js) return null;
this.firstChild.appendChild(child);
if((auto || child.autoRun) && type == 'script') child.doCommand();
return child;
};
return element;
//this.__proto__ = element;
//this.__proto__ = (new XPCNativeWrapper(element)).wrappedJSObject; //Minefield/3.6a1pre ID:20090713044326 例外が投げられるのを...
}
// メニューエントリリストクラス.
function MenuList(root){
this.data = [];
for(let i = 0,max = root.length;i < max;++i){
let {menuName:menuname, menuElement:element, flagInsert:flagins} = root[i];
let vtype = VIEWTYPE[root[i].viewType] || root[i].viewType || '';
if(!element){ errMsg(menuname + ' menu',new Error('not found.')); continue; }
this.data[menuname] = {
createChild : function(type,obj,name,key,viewtype,auto,nonpriv){
var child = MenuEntry(menuname,type,obj,name,key,viewtype || vtype,nonpriv); if(!child.userMenu_js) return null;
if(flagins) element.parentNode.insertBefore(child,element);
else ((element.nodeName =='menu') ? element.firstChild : element).appendChild(child);
if((auto || child.autoRun) && type == 'script') child.doCommand();
return child;
},
remove : function(){}
};
}
}
MenuList.prototype = {
get length(){ return this.data.length; },
destroy : function(){ for(let i in this.data) this.data[i].remove(); },
remove : function(key){ if(this.data[key]){ this.data[key].remove(); delete this.data[key]; } },
run : function(key){ if(this.data[key]) this.data[key].doCommand(); },
add : function(pos,obj,name,key,viewtype,auto,nonpriv){ // メニューに追加する.
// ファイル名からオプションを取得.デフォルトを引数で指定.
function _getOption(str,name,key,viewtype,auto,nonpriv){
var a = str.split('.');
var obj = {name:name,key:key,viewType:viewtype,autoRun:!!auto,nonPriv:nonpriv};
for(let i = a.length;i >= 0;--i){
if(!a[i]) continue;
switch(a[i].length){
case 1 : obj.key = a[i]; break;
case 2 : obj.viewType= a[i]; break;
case 4 : obj.autoRun = true; break;
case 7 : obj.nonPriv = true; break; }
}
return obj;
}
// 存在しない階層を作成.
{
let _this = this;
if(!(function(pos){
if(_this.data[pos]) return _this.data[pos];
var ppos = pos.replace(/[/][^/]+$/,'');
if(!/[/]/.test(pos) || !arguments.callee(ppos)) return null;
REGEX_SUBMENU.test(pos.split('/').pop());
var opt = _getOption(RegExp.$2,RegExp.$1,null,_this.data[ppos].viewType);
return _this.data[pos] = _this.data[ppos].createChild('directory',null,opt.name,opt.key,opt.viewType);
})(pos)) return null;
}
var keyname,entry,objtype = _typeof(obj);
if(objtype == 'string' || objtype == 'function'){
keyname = pos + '/' + name + ((key) ? '.' + key : '') + ((viewtype) ? '.' + viewtype : '') + ((auto) ? '.auto' : '') + ((nonpriv) ? '.nonpriv' : '');
if(!this.data[keyname]) entry = this.data[pos].createChild('script',obj,name,key,viewtype,auto,nonpriv);
}
else if(objtype == 'file'){
keyname = pos + '/' + obj.leafName;
if(this.data[keyname]) return null;
if(obj.isDirectory() && REGEX_SUBMENU.test(obj.leafName)){
let opt = _getOption(RegExp.$2,name || RegExp.$1,key,viewtype,auto,nonpriv);
entry = this.data[pos].createChild('directory',obj,opt.name,opt.key,opt.viewType);
}
else if(obj.isFile() && REGEX_SCRIPTFILE.test(obj.leafName)){
let opt = _getOption(RegExp.$2,name || RegExp.$1,key,viewtype,auto,nonpriv);
entry = this.data[pos].createChild('script',obj,opt.name,opt.key,opt.viewType,opt.autoRun,(opt.nonPriv == null) ? !PRIVILEGE : opt.nonPriv);
}
else if(REGEX_SEPARATORFILE.test(obj.leafName)) entry = this.data[pos].createChild('separator',obj,'','',RegExp.$1);
}
if(!entry) return null;
entry.keyName = keyname;
return this.data[keyname] = entry;
}
};
// ディレクトリツリーを作成する.
// nsIFile を継承したオブジェクトの配列で,子を持つ場合にプロパティ child が設定される.
function createDirectoryTree(dir){
if(!dir.exists() || !dir.isDirectory()) return null;
function _f(file){ if(file.isDirectory()) this.child = createDirectoryTree(file); this.leafNameUpper = null; this.toString = function(){ return this.leafNameUpper || (this.leafNameUpper = this.leafName.toUpperCase()) }; this.__proto__ = file; };
var tree = [],list = dir.directoryEntries;
while(list.hasMoreElements()){ tree.push(new _f(list.getNext().QueryInterface(Ci.nsIFile))); }
if(USE_SORT) tree.sort();
return (tree.length) ? tree : null;
}
// メニューの構築.
function buildMenu(hostdirectory,dirname,root){
var list = new MenuList(root);
var dir = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
for(let i = 0,max = dirname.length;i < max;++i){
dir.setRelativeDescriptor(hostdirectory,dirname[i]);
(function(pos,tree){
if(!tree) return;
for(let i = 0,max = tree.length;i < max;++i) if(list.add(pos,tree[i]) && tree[i].child) arguments.callee(pos + '/' + tree[i].leafName,tree[i].child);
})(MENUNAME[i],createDirectoryTree(dir));
}
document.getElementById(ID_EXTRAMENU).setAttribute('hidden',(function(){ for(let i in list.data){ if(/^extra[/]./.test(i)) return false; } return true; })());
return list;
}
// 名前空間作成用のブロック.
{
let prototype = [];
let windowID = new toolkit.NumberTable(); // window オブジェクトに番号を振るためのクラス.
// window が消えると番号表からその window に割り振られた番号を消す.
let callback = function(event){
var win = event.originalTarget.defaultView;
win.addEventListener('pagehide',function(event){
var doc = event.target;
var tabIndex = gBrowser.getBrowserIndexForDocument(doc);
if(tabIndex == -1 || true){ // xxx true: GM互換 毎回window オブジェクト解放
delete prototype[windowID.delete(win)];
}
},false);
};
gBrowser.addEventListener('DOMContentLoaded', callback, true);
document.getElementById('sidebar-box').addEventListener('DOMContentLoaded', callback, true);
// 名前空間のプロトタイプを作る.getter でアクセス制限.
let createPrototype = function(win){
function dummy(){}
var console = ("FirebugConsole" in window) ? new window.FirebugConsole(window.FirebugContext,win) : {log:dummy,debug:dummy,info:dummy,warn:dummy,error:dummy,assert:dummy,dir:dummy,dirxml:dummy,trace:dummy,group:dummy,groupEnd:dummy,time:dummy,timeEnd:dummy,profile:dummy,profileEnd:dummy,count:dummy};
var safewin = new XPCNativeWrapper(win);
var p = {
get userContent(){ return window.userContent_js; },
get userMenu(){ return window.userMenu_js; },
get chromeWindow(){ return window; },
get gBrowser(){ return getBrowser(); },
unsafeWindow : win,
window : safewin,
console : console,
Cc : Cc,
Ci : Ci,
Cr : Cr,
XPathResult : XPathResult,
NodeFilter : NodeFilter,
Components : Components };
p.__proto__ = safewin;
return p;
};
// 名前空間を作る.
function createNamespace(win,obj,nonpriv){
var id = windowID.get(win);
var pr = prototype[id] || (prototype[id] = createPrototype(win));
var sb = Components.utils.Sandbox((nonpriv) ? pr.window : window);
sb.document = pr.window.document;
sb.unsafeDocument = win.document;
importObject(sb,obj);
toolkit.extendPrototype(sb);
sb.Function.prototype.__defineGetter__('nonPriv',function(){
var safe = Components.utils.Sandbox(pr.window);
toolkit.extendPrototype(safe);
safe.__proto__ = sb;
return eval(uneval(this),safe);
});
sb.__proto__ = pr;
return sb;
}
}
// メニュー表示時のコールバックを設定.
{
let callback = function(event){
var url,flag = null;
if(event.currentTarget.getAttribute('anonid') == 'tabContextMenu' || event.currentTarget.parentNode.getAttribute('anonid') == 'strip'){
flag = {};
flag[VIEWTYPE['gl']] = true;
flag[VIEWTYPE['ct']] = (gBrowser.mContextTab.nodeName == 'tab') || (gBrowser.mContextTab.nodeName == 'xul:tab');
flag[VIEWTYPE['tb']] = (gBrowser.mContextTab.nodeName == 'tabs') || (gBrowser.mContextTab.nodeName == 'xul:tabs');
url = (flag[VIEWTYPE['ct']]) ? gBrowser.mContextTab.linkedBrowser.contentDocument.URL : content.window.document.URL;
}
else if(gContextMenu){
flag = {};
flag[VIEWTYPE['gl']] = true;
flag[VIEWTYPE['li']] = gContextMenu.onLink;
flag[VIEWTYPE['im']] = gContextMenu.onImage;
flag[VIEWTYPE['se']] = gContextMenu.isTextSelected;
flag[VIEWTYPE['it']] = gContextMenu.onTextInput;
flag[VIEWTYPE['no']] = (!flag[VIEWTYPE['li']] && !flag[VIEWTYPE['im']] && !flag[VIEWTYPE['se']] && !flag[VIEWTYPE['it']]);
flag[VIEWTYPE['nl']] = (NLALLOWIMAGE) ? (flag[VIEWTYPE['li']] || flag[VIEWTYPE['no']]) : (!flag[VIEWTYPE['im']] && !flag[VIEWTYPE['se']] && !flag[VIEWTYPE['it']]);
if (!gContextMenu.target) return;
url = gContextMenu.target.ownerDocument.URL;
}
else url = content.document.URL;
for(let i = 0,pop = event.originalTarget,item;i < pop.childNodes.length;++i){
item = pop.childNodes[i]; if(!item.userMenu_js) continue;
if(item.script && !item.script.isAllow(url)) item.setAttribute('hidden',true);
else{
if(flag && item.viewType && !flag[item.viewType]){ item.setAttribute('hidden',true); continue; }
item.setAttribute('hidden',false);
try{ if(item.variableLabel && flag[item.viewType]) item.setAttribute('label',item.variableLabel(event)); }
catch(e){ errMsg(item.getAttribute('label'),e); };
}
}
};
// 渡されたメニューのリストにコールバックを設定する.
function addOnpopupshowing(list){ for(let i = 0,max = list.length,pop;i < max;++i) list[i].addEventListener('popupshowing',callback,false); }
}
// いつでもクリックされたマウスのボタンを知る事ができるように,マウスイベントを監視して,ボタン情報を更新する.
{
var mouseButton = null;
window.addEventListener('mousedown',function(event){ mouseButton = event.button; },false);
window.addEventListener('mouseup',function(event){ mouseButton = event.button; setTimeout(function(){ mouseButton = null; },0); },false);
function getMouseButton(){ return mouseButton; }
}
// メインブロック
{
let afterToolMenu = document.getElementById((SEAMONKEY) ? 'sep_switchprofile' : 'menu_preferences') || document.getElementById((SEAMONKEY) ? 'taskPopup' : 'menu_ToolsPopup').lastChild;
let afterContextMenu = document.getElementById('context-sep-stop') || document.getElementById('contentAreaContextMenu').lastChild;
let parentTabMenu = document.getElementById('tabContextMenu') || document.getAnonymousElementByAttribute(gBrowser,'anonid','tabContextMenu') || (function(){
var strip = document.getAnonymousElementByAttribute(gBrowser,'anonid','strip').childNodes;
for(let i = strip.length - 1;i >= 0;--i) if(strip[i].tagName == 'xul:menupopup') return strip[i];
return null;
})();
let parentExtraMenu = (function(){
var menu = document.getElementById('main-menubar').appendChild(document.createElement('menu'));
menu.id = ID_EXTRAMENU;
menu.setAttribute('label',LABEL_EXTRAMENU);
return menu.appendChild(document.createElement('menupopup'));
})();
let hostDirectory = (function(){ var d = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile); d.initWithPath(IO_SERVICE.getProtocolHandler('file').QueryInterface(Ci.nsIFileProtocolHandler).getFileFromURLSpec(IO_SERVICE.newURI(Error().fileName.replace(/[^/]+$/,'').split(' -> ').pop(),null,null).spec).path); return d; })();
let directoryName = [DIRNAME_TOOLMENU,DIRNAME_CONTEXTMENU,DIRNAME_TABMENU,DIRNAME_EXTRAMENU];
let metaData = toolkit.readMeta(METADATA);
let userMenu = null;
let addedMenu = null;
let menuRoot = [{menuName:MENUNAME[0],menuElement:afterToolMenu, flagInsert:true, viewType:''},
{menuName:MENUNAME[1],menuElement:afterContextMenu,flagInsert:true, viewType:'no'},
{menuName:MENUNAME[2],menuElement:parentTabMenu, flagInsert:false,viewType:'gl'},
{menuName:MENUNAME[3],menuElement:parentExtraMenu, flagInsert:false,viewType:''}];
let directory = {host:Error().fileName.replace(/[^/]+$/,'').split(' -> ').pop()};
directory[MENUNAME[0]] = directory.host + DIRNAME_TOOLMENU.replace(/([^/])$/,'$1/');
directory[MENUNAME[1]] = directory.host + DIRNAME_CONTEXTMENU.replace(/([^/])$/,'$1/');
directory[MENUNAME[2]] = directory.host + DIRNAME_TABMENU.replace(/([^/])$/,'$1/');
directory[MENUNAME[3]] = directory.host + DIRNAME_EXTRAMENU.replace(/([^/])$/,'$1/');
// ホストオブジェクト
window.userMenu_js = {
version : metaData.version,
rebuild : function(){ userMenu.destroy(); userMenu = buildMenu(hostDirectory,directoryName,menuRoot); for each(let i in addedMenu) userMenu.add.apply(null,i); },
repreload : function(){ preload = loadPreloadFile(FILENAME_PRELOAD); },
getDirectory : function(k){ var s; return directory[k] || (((s = this.get(k)) && s.script) ? s.script.dir : null); },
run : function(k){ return userMenu.run(k); },
remove : function(k){ if(addedMenu[k]) delete addedMenu[k]; userMenu.remove(k); },
get : function(k){ return userMenu.data[k] || null; },
add : function(pos,obj,name,key,viewtype,auto,nonpriv){
var entry = userMenu.add(pos,obj,name,key,viewtype,auto,nonpriv); if(!entry) return null;
addedMenu[entry.keyName] = [pos,obj,name,key,viewtype,auto,nonpriv];
return entry.keyName;
},
get keyNames(){ var a = []; for(let i in userMenu.data) a.push(i); return a; } };
preload = loadPreloadFile(FILENAME_PRELOAD);
userMenu = buildMenu(hostDirectory,directoryName,menuRoot);
addOnpopupshowing([afterToolMenu.parentNode,afterContextMenu.parentNode,parentTabMenu,parentExtraMenu]);
window.addEventListener('unload',function(event){ if(event.originalTarget == window.document) userMenu.destroy(); },false);
}
})(UCJSToolkit);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment