最初の6つは同名のgetter, setterあり。
var main = 1; // NORMAL
var extended = 0; // NONE, set extended(value) { this.set(null, value) }
var passNextKey = false;
var passAllKeys = false;
var isRecording = false;
var isReplaying = false; // playing a macro
var lastShown = null;
var modeStack = [];
var mainModes = [self.NONE];
var lastMode = 0;
var modeMap = {};
// main modes, only one should ever be active
self.addMode("NORMAL", false, -1);
self.addMode("INSERT");
self.addMode("VISUAL", false, function () "VISUAL" + (extended & modes.LINE ? " LINE" : ""));
self.addMode("COMMAND_LINE");
self.addMode("CARET"); // text cursor is visible
self.addMode("TEXTAREA"); // text cursor is in a HTMLTextAreaElement
self.addMode("MESSAGE"); // Muttatorで使う
self.addMode("COMPOSE"); // Muttatorで使う
self.addMode("CUSTOM", false, function () plugins.mode);
// extended modes, can include multiple modes, and even main modes
self.addMode("EX", true);
self.addMode("HINTS", true);
self.addMode("INPUT_MULTILINE", true); // commandline.inputMultiline()の実行時に開始されるモード。複数行入力モード。
self.addMode("OUTPUT_MULTILINE", true); // commandline.echo()やg<などの実行時に開始されるモード。複数行出力モード。
self.addMode("SEARCH_FORWARD", true);
self.addMode("SEARCH_BACKWARD", true);
self.addMode("MENU", true); // a popupmenu is active
self.addMode("LINE", true); // TEXTAREAモードでVを入力した時にこのモードになる。ビジュアル行モード。
self.addMode("PROMPT", true);
NONE,
get all() mainModes.slice(),
get inputMode() main & (this.COMMAND_LINE | this.INPUT | this.TEXTAREA | this.COMPOSE), // このthis.INPUTが謎。バグ?
addMode: function (name, extended, display){
let disp = name.replace("_", " ", "g");
this[name] = 1 << lastMode++;
modeMap[name] = modeMap[this[name]] = { // モード名と数字を使ってアクセスできる。
extended: extended, // true/false
mask: this[name], // 数字(bit mask)
name: name,
display: display || function () disp //表示名
};
if (!extended)
mainModes.push(this[name]);
},
getMode: function (name) modeMap[name],
show: function () // show the current mode string in the command line
add: function (mode){ extended |= mode; this.show(); }, // extendedモードの追加。set(null, extended | mode)を実行。
// extended, mainにモードをsetし、mode changeをハンドリングする。silent: モード名を表示しない
// if silent == true, you also need to take care of the mode handling changes yourself
set: function (mainMode, extendedMode, silent, stack){
silent = (silent || main == mainMode && extended == extendedMode); // &&のほうが優先度高い
let oldMain = main, oldExtended = extended;
if (typeof extendedMode === "number") extended = extendedMode;
if (typeof mainMode === "number"){
main = mainMode;
if (!extendedMode) extended = modes.NONE;
if (main != oldMain) handleModeChange(oldMain, mainMode, oldExtended);
}
// 自分でモードの変化をハンドリングする時に使う?(liberator.registerObserver('modeChange', func)のようにする)
liberator.triggerObserver("modeChange", [oldMain, oldExtended], [main, extended], stack);
if (!silent) this.show();
},
push: function (mainMode, extendedMode, silent){
modeStack.push([main, extended]);
this.set(mainMode, extendedMode, silent, { push: modeStack[modeStack.length - 1] });
},
pop: function (silent){
let a = modeStack.pop();
if (a){ this.set(a[0], a[1], silent, { pop: a }); }else{ this.reset(silent); }
}
setCustomMode: function (modestr, oneventfunc, stopfunc){ // 廃止される可能性あり
plugins.mode = modestr;
plugins.onEvent = oneventfunc;
plugins.stop = stopfunc;
},
reset: function (silent){
modeStack = [];
this.set(modes.NORMAL, modes.NONE, silent);
},
remove: function (mode){
if (extended & mode){
extended &= ~mode;
this.show();
}
},
// modes.push
1. commandline.inputMultiline: 複数行入力させる時。hereDocオプションのついたコマンド(js, style, highlight)など。pushされたものは4でpopされる。
2. commandline.input: プロンプトモードに移行した時。extended hintなど。pushされたものはcallback内でpopされる(例えばhintを入力したら直ちにpopされる)
// modes.pop
3. commandline.onEvent: 複数行入力モード以外のコマンドラインモードで、EnterまたはBackSpaceを入力しコマンドラインから抜け出す時。
4. commandline.onMultilineInputEvent: 複数行入力モードの終了文字をタイプしてEnterした時。
5. commandline.onMultilineOutputEvent: 複数行出力モードでキー入力してMOWが閉じる時。
commandline.inputMultiline
// 複数行入力させる inputMultiline: function inputMultiline(untilRegexp, callbackFunc){ let cmd = !commandWidget.collapsed && this.command; modes.push(modes.COMMAND_LINE, modes.INPUT_MULTILINE); // **** /* snip */
この呼び出し箇所。
commands.js 174
Command#execute
if (this.hereDoc){
let matches = args.match(/(.*)<<\s*(\S+)$/);
if (matches && matches[2]){
commandline.inputMultiline(RegExp("^" + matches[2] + "$", "m"), // ****
function (args) { exec(matches[1] + "\n" + args); });
return;
}
}
commandline.input
// プロンプトに入力させる
// 例. commandline.input('type your name: ', function(name){alert(name)})
input: function _input(prompt, callback, extra){
extra = extra || {};
input = {
submit: callback,
change: extra.onChange,
complete: extra.completer,
cancel: extra.onCancel
};
modes.push(modes.COMMAND_LINE, modes.PROMPT); // ****
currentExtendedMode = modes.PROMPT;
setPrompt(prompt, extra.promptHighlight || this.HL_QUESTION);
setCommand(extra.default || "");
commandlineWidget.collapsed = false;
commandWidget.focus();
completions = Completions(commandWidget.inputField);
},
さらにこの呼び出し箇所。
hints.js 645
mappings.add(myModes, [";"],
"Start an extended hint mode",
function (count)
{
extendedhintCount = count;
commandline.input(";", null, // ****
{
promptHighlight: "Normal",
completer: function (context)
{
context.compare = function () 0;
context.completions = [[k, v.prompt] for ([k, v] in Iterator(hintModes))];
},
onChange: function () { modes.pop() }, // 1文字入力したらプロンプトモードから脱出する。 ****
onCancel: function (arg) { arg && setTimeout(function () hints.show(arg), 0); },
});
}, { flags: Mappings.flags.COUNT });
commandline.onEvent
// コマンドラインのイベントを処理する。 else if (event.type == "keypress"){ /* snip */ // <Return>, <C-j>, <C-m>のどれか。つまりコマンドラインでコマンドを実行した時。 if (events.isAcceptKey(key)) { let mode = currentExtendedMode; // save it here, as modes.pop() resets it keepCommand = true; currentExtendedMode = null; // Don't let modes.pop trigger "cancel" modes.pop(!this.silent); // **** return liberator.triggerCallback("submit", mode, command); } /* snip */ else if (key == "<BS>"){ if (command.length == 0){ liberator.triggerCallback("cancel", currentExtendedMode); modes.pop(); // **** } }
この呼び出し箇所。
liberator.xul 85
<window id="&liberator.mainWindow;"><stack orient="horizontal" align="stretch" class="liberator-container" liberator:highlight="CmdLine"> <hbox id="liberator-commandline" hidden="false" collapsed="true" class="liberator-container" liberator:highlight="Normal"> <label class="plain" id="liberator-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/> <textbox class="plain" id="liberator-commandline-command" flex="1" type="timed" timeout="100" oninput="liberator.modules.commandline.onEvent(event);" onkeyup="liberator.modules.commandline.onEvent(event);" onfocus="liberator.modules.commandline.onEvent(event);" onblur="liberator.modules.commandline.onEvent(event);"/> <!-- **** --> </hbox>
↓onkeypressはこっちで定義されている。
events.js 1350
events.onKeyPress
// this keypress handler gets always called first, even if e.g. the commandline has focus
onKeyPress: function (event){
if(...){
/* snip */
// if the key is neither a mapping nor the start of one
else{
if (key != "<Esc>" && key != "<C-[>"){
/* snip */
if (liberator.mode == modes.COMMAND_LINE){
// コマンドラインモードで複数行入力モードではない時。
if (!(modes.extended & modes.INPUT_MULTILINE))
commandline.onEvent(event); // reroute event in command line mode ****
} else if (liberator.mode != modes.INSERT && liberator.mode != modes.TEXTAREA) liberator.beep();
}
commandline.onMultilineInputEvent
// 複数行入力モード。 onMultilineInputEvent: function onMultilineInputEvent(event) { if (event.type == "keypress"){ let key = events.toString(event); // Enterした時。 if (events.isAcceptKey(key)){ let text = multilineInputWidget.value.substr(0, multilineInputWidget.selectionStart); // EOMのような文字列でEnterした時。 if (text.match(multilineRegexp)){ text = text.replace(multilineRegexp, ""); modes.pop(); // **** multilineInputWidget.collapsed = true; multilineCallback.call(this, text); } }else if (events.isCancelKey(key)){ modes.pop(); // **** multilineInputWidget.collapsed = true; } }
さらにこの呼び出し箇所。
liberator.xul 95
<window id="&liberator.mainWindow;"> <vbox class="liberator-container" hidden="false" collapsed="false"> <textbox id="liberator-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true" onkeypress="liberator.modules.commandline.onMultilineInputEvent(event);" oninput="liberator.modules.commandline.onMultilineInputEvent(event);" onblur="liberator.modules.commandline.onMultilineInputEvent(event);"/> <!-- **** --> </vbox>
commandline.onMultilineOutputEvent
// 複数行出力モードで、キー入力してMOWが閉じるような場合。 if (passEvent || closeWindow){ modes.pop(); // **** if (passEvent) events.onKeyPress(event); }else{ commandline.updateMorePrompt(showMorePrompt, showMoreHelpPrompt); }
events.js 1325
events.onEscape
case modes.VISUAL:
if (modes.extended & modes.TEXTAREA)
liberator.mode = modes.TEXTAREA;
else if (modes.extended & modes.CARET)
liberator.mode = modes.CARET;
break;
case modes.CARET:
// setting this option will trigger an observer which will
// care about all other details like setting the NORMAL mode
options.setPref("accessibility.browsewithcaret", false);
break;
VISUALモードでEscすると、TEXTAREA or CARETに戻る。
CARETモードでEscすると、NORMALに戻る。
events.js 1770
events.observe
observe: function (aSubject, aTopic, aData)
{
if (aTopic != "nsPref:changed")
return;
// aSubject is the nsIPrefBranch we're observing (after appropriate QI)
// aData is the name of the pref that's been changed (relative to aSubject)
switch (aData)
{
case "accessibility.browsewithcaret":
let value = options.getPref("accessibility.browsewithcaret", false);
liberator.mode = value ? modes.CARET : modes.NORMAL;
break;
}
}
このようにオブザーバがついているので、about:configでprefをいじっても、モードがCARET, NORMAL間で変わる。
位置・大きさ・色・innerHTMLorDOMを指定して、div要素(ウィジェット)を複数作成できる。
ヒントを使ってウィジェットを選択し、jkhldqで操作できる。
// floatWidget.js var floatWidget = new function(){ var count = 0; function Widget(html, height, width, x, y, color){ var doc = content.document; count++; var ref = (function buildDOM(html, h, w, x, y, c){ var ct = count; var div = doc.createElement('div'); div.setAttribute('id', 'floatWidget' + ct); var a = doc.createElement('a'); with(div.style){ height = h + 'px'; width = w + 'px'; position = 'fixed'; left = x + 'px'; top = y + 'px'; backgroundColor = c || 'white'; zIndex = '1000'; } with(a.style){ height = h + 'px'; width = w + 'px'; position = 'relative'; left = '0px'; top = '0px'; backgroundColor = c || 'white'; zIndex = '1000'; } if(typeof html == 'object'){ a.appendChild(html); }else{ a.innerHTML = html; } div.appendChild(a); function handleThis(e){ var k = events.toString(e); if(k == 'q') modes.reset(); if(k == 'h') div.style.left = (window.parseInt(div.style.left) - 50) + 'px'; if(k == 'l') div.style.left = (window.parseInt(div.style.left) + 50) + 'px'; if(k == 'k') div.style.top = (window.parseInt(div.style.top) - 50) + 'px'; if(k == 'j') div.style.top = (window.parseInt(div.style.top) + 50) + 'px'; if(k == 'd'){ div.removeEventListener('click', selectThis, false); doc.body.removeChild(div); modes.reset(); } } var selectThis = function(){ modes.setCustomMode('floatwidget', handleThis, function(){return}); modes.set(modes.CUSTOM); } div.addEventListener('click', selectThis, false); doc.body.appendChild(div); return div; })(html, height, width, x, y, color); this.ref = ref; this.id = count; this.height = ref.style.height; this.width = ref.style.width; this.x = ref.style.left; this.y = ref.style.top; this.color = ref.style.backgroundColor; } this.make = function(innerDOMorHTML, height, width, x, y, color){ return new Widget(innerDOMorHTML, height, width, x, y, color); }; }; plugins.makeFloatWidget = function(html, h, w, x, y, c){ floatWidget.make(html, h, w, x, y, c) };
使用例。現在のモードを監視する。
:js var div=content.document.createElement('div'); plugins.makeFloatWidget(div,100,100,0,0); var timer1 = setInterval(function(){ div.innerHTML = liberator.mode }, 1000);
snaka722009/06/07 09:25このエントリ以外にもソース解説関連のエントリを Vimperator Wiki の方からリンク貼らせてもらいましたー。