Skip to content

Instantly share code, notes, and snippets.

@uk-ar
Created May 10, 2011 10:33
Show Gist options
  • Save uk-ar/964243 to your computer and use it in GitHub Desktop.
Save uk-ar/964243 to your computer and use it in GitHub Desktop.
minibuffer for firefox4
// ==UserScript==
// @name Minibuffer
// @namespace http://white.s151.xrea.com/
// @description Minibuffer
// @include *
// ==/UserScript==
var VERSION = "2009.12.06";
var Class = function(){return function(){this.initialize.apply(this,arguments)}};
// string key
var Key = new Class();
// 32-40 space pageup pagedown end home left up right down
Key.keyCodeStr = {
8: 'BAC',
9: 'TAB',
10: 'RET',
13: 'RET',
27: 'ESC',
33: 'PageUp',
34: 'PageDown',
35: 'End',
36: 'Home',
37: 'Left',
38: 'Up',
39: 'Right',
40: 'Down',
45: 'Insert',
46: 'Delete',
112: 'F1',
113: 'F2',
114: 'F3',
115: 'F4',
116: 'F5',
117: 'F6',
118: 'F7',
119: 'F8',
120: 'F9',
121: 'F10',
122: 'F11',
123: 'F12'
};
Key.whichStr = {
32: 'SPC'
};
Key.specialKeys = values(Key.keyCodeStr).concat(values(Key.whichStr));
Key.getKeyIdentifier = function(aEvent){
// http://www.w3.org/TR/DOM-Level-3-Events/keyset.html
return ((aEvent.keyCode in this.keyCodeStr) && this.keyCodeStr[aEvent.keyCode]) ||
((aEvent.which in this.whichStr) && this.whichStr[aEvent.which]) ||
String.fromCharCode(aEvent.which);
};
Key.prototype = {
initialize: function(){
this.orig_string = arguments[0];
this.key = this.orig_string.replace(/[ACMS]-/g,'');
this.special = !!~Key.specialKeys.indexOf(this.key);
},
has: function(modifier){return this.orig_string.indexOf(modifier) > -1},
equal: function (e, ch){
return (this.key == ch &&
this.has('C-') == e.ctrlKey &&
((this.special)? this.has('S-') == e.shiftKey : true) &&
(e.metaKey || e.altKey) == (this.has('A-') || this.has('M-')))
}
};
var ShortcutKey = new Class();
ShortcutKey.prototype = {
initialize: function(){
this.hash = {};
this.state_available = false;
this.prevent_event = true;
this.through_input_elements = false;
this.parent = null;
this.html = null;
this.descriptions = [];
},
setParent: function(parent){this.parent = parent; return this},
isAvailable: function(){return this.state_available},
addCommand: function(opt){
var lst = opt.key.split(' ');
var last = lst.last();
if(this.id) this.addDescription(opt);
var self = this;
var idx = 0;
var last_idx = lst.length - 1;
lst.forEach(function(k){
if(idx++ != last_idx){
var new_shortcutkey;
if(self.hash[k]){
new_shortcutkey = self.hash[k][1];
}else{
new_shortcutkey = new ShortcutKey().setParent(self);
new_shortcutkey.prevent_event = self.prevent_event;
new_shortcutkey.setParameter(self.target, self.event, function(e){new_shortcutkey.listener(e)}, self.capture);
}
self.hash[k] = [new Key(k), new_shortcutkey];
self = new_shortcutkey;
}else{
self.hash[k] = [new Key(k), opt.command];
}
});
},
addDescription: function(opt){
var getKeyHTML = function(key, description){
return $N('div',{},
[$N('kbd',{},key),
$N('div',{},description)]);
};
var div = getKeyHTML(opt.key, opt.description);
this.html.appendChild(div);
this.descriptions.push({key:opt.key, html:div});
},
removeCommand: function(key){
delete this.hash[key];
},
findByEvent: function(e, ch){return values(this.hash).find(function(kf){return kf[0].equal(e, ch) && kf})},
removeEventListener: function(){
this.disable();
this.setParameter(null, null, null, null);
},
addEventListener: function(target, event, capture){ // (document, 'keypress', true)
var self = this;
this.setParameter(target, event, function(e){self.listener(e)}, capture);
this.enable();
},
setParameter: function(target, event, observer, capture){
this.target = target;
this.event = event;
this.observer = observer;
this.capture = capture;
},
// enable/disable temporary
enable: function(){this.state_available = true;this.target.addEventListener(this.event, this.observer, this.capture);},
disable: function(){this.state_available = false; this.target.removeEventListener(this.event, this.observer, this.capture)},
throughEvent: function(){this.prevent_event = false; return this},
throughInputElements: function(){this.through_input_elements = true; return this},
getAllKeys: function(){return keys(this.hash)},
backToRoot: function(){
if(!this.parent) return;
var tmp = this;
while(tmp.parent){
tmp.disable();
tmp = tmp.parent;
}
tmp.enable();
},
listener: function(aEvent){
if(!this.capture &&
this.through_input_elements &&
/^(?:input|textarea)$/.test(aEvent.target.nodeName.toLowerCase())) return;
var ch = Key.getKeyIdentifier(aEvent);
var kf = this.findByEvent(aEvent, ch);
var preventDefault = this.prevent_event;
// log(aEvent.keyCode, aEvent.which, ch,kf, aEvent.shiftKey);
if(kf){
var fn = kf[1];
if(ShortcutKey.prototype.isPrototypeOf(fn)){
this.disable();
fn.enable();
}else{
fn(aEvent);
this.backToRoot();
}
}else{
this.backToRoot();
preventDefault = false;
}
if(preventDefault) {
aEvent.preventDefault();
aEvent.stopPropagation();
}
},
initHelp: function(id){
var box = $N('div',{id:'gm_minibuffer_'+id}, [$N('h1',{},'Shortcut Keys')]);
this.html = box;
this.id = box.id;
var id = 'div#' + box.id;
var inherit = 'background:inherit; background-image:inherit; background-color:inherit; color:inherit; text-align:inherit; font-size:inherit; font-style:inherit; font-weight:inherit; margin:inherit; opacity:inherit; text-decoration:inherit; border:0px; height:100%; padding:0; margin:inherit; font-family:inherit; vertical-align:inherit; line-height:inherit; font-stretch:inherit; font-variant:inherit; font-size-adjust:inherit; letter-spacing:inherit;';
GM_addStyle([id,'{', 'right: 10px;', 'left: 10px;', 'top: 10px;', 'line-height: 100%;', 'vertical-align: baseline;', 'border: 1px dotted #444;', 'font-family: sans-serif;', 'text-decoration: none;', 'font-weight: normal;', 'font-style: normal;', 'font-size: medium;', 'font-stretch: normal;', 'font-variant: normal;', 'font-size-adjust: none;', 'letter-spacing: normal;', 'background: none;', 'text-align: left;', 'position: fixed;', 'margin: 0;', 'padding: 20px;', 'background-color: #000;', 'background-image: none;', 'color: #aaa;', '-moz-border-radius: 10px;border-radius: 10px;', 'opacity:0.8;', 'z-index:1000;', '}\n',
id,' div{', inherit, 'opacity:1.0;','text-align:center;','}',
id,' > div{', inherit, 'margin: 0px 20px 20px 0px;', 'opacity:1.0;','text-align:center;','}',
id,' kbd{', inherit, 'font-size: 120%;','font-weight: bold;', 'color: #B83E3B;', 'text-align: right;', 'width:50%;','float:left;','}\n',
id,' kbd + div{', 'margin-left:50%;', 'text-align:left;','}\n',
id,' kbd + div:before{', 'content:": ";' ,'}\n',
id,' h1{', inherit, 'margin: 20px auto;','background-image: none;', "opacity:1.0;", 'font-weight: bold;', 'font-size: 150%;', 'color:#fff;','padding-left: 20px;', 'text-align: center;', '}\n',
].join(''));
return this;
},
bindHelp: function(){return (this.hideHelpMessage() || this.showHelpMessage())},
showHelpMessage: function(){document.body.appendChild(this.html)},
hideHelpMessage: function(){
var help = document.getElementById(this.id);
if(!help) return false;
document.body.removeChild(this.html);
return true;
},
};
// to use history/alias in Minibuffer, define getter/setter like below
// history is Array of string
// e.g. ["foo", "bar"]
// alias is hash of alias:expand
// e.g. {"foo":"bar111|bar222", "baz":"qux111|qux222"}
//
// obj.setHistoryGetter(function() {
// var res = eval(GM_getValue('history', '[]'));
// return res;
// });
// obj.setHistorySetter(function(new_history) {
// GM_setValue('history',uneval(new_history));
// });
//
var Minibuffer = new Class();
Minibuffer.prototype = {
MAX_CANDIDATES: 20,
KEYBIND: {
// move caret
'C-a' : 'bindBeginningOfLine',
'Home' : 'bindBeginningOfLine',
'C-e' : 'bindEndOfLine',
'End' : 'bindEndOfLine',
'C-f' : 'bindForwardChar',
'C-b' : 'bindBackwardChar',
'M-b' : 'bindBackwardWord',
'M-f' : 'bindForwardWord',
// delete character
'C-d' : 'bindDeleteForwardChar',
'C-h' : 'bindDeleteBackwardChar',
'BAC' : 'bindDeleteBackwardChar',
'C-w' : 'bindDeleteBackwardWord',
'M-h' : 'bindDeleteBackwardWord',
'M-d' : 'bindDeleteForwardWord',
'C-u' : 'bindDeleteAllStrings',
// history
'C-r' : 'bindSearchHistoryBackward',
'C-s' : 'bindSearchHistoryForward',
// alias
'C-c' : 'bindRegisterOrUnregisterAlias',
'M-c' : 'bindExpandAlias',
// other
'ESC' : 'bindExit',
'C-g' : 'bindExit',
'C-[' : 'bindExit',
'C-m' : 'bindDecision',
'RET' : 'bindDecision',
'C-n' : 'bindSelectNext',
'Down' : 'bindSelectNext',
'C-p' : 'bindSelectPrevious',
'Up' : 'bindSelectPrevious',
'C-v' : 'bindScrollNext',
'PageDown' : 'bindScrollNext',
'M-v' : 'bindScrollPrev',
'PageUp' : 'bindScrollPrev',
'TAB' : 'bindComplete',
'C-i' : 'bindComplete',
'C-/' : 'bindCompleteAndPipe',
},
initialize: function(){
this.separator = '|';
this.state_available = false;
this.candidates = [];
this.last_completed_string = '';
this.current = -1;
this.initHTML(); // html
this.shortcutkey = new ShortcutKey();
this.shortcutkey.addEventListener(this.html.input, 'keypress', true);
var self = this;
var chars = "!\"#$%&'()=~|-^_[]{}:;+*@`?\\".split('');
for(var i=48; i<58; i++){ // 0-9
chars[chars.length] = String.fromCharCode(i);
}
for(var i=65; i<91; i++){ // A-z
chars[chars.length] = String.fromCharCode(i); // == chars.push(String.fromCharCode(i))
chars[chars.length] = String.fromCharCode(i+32);
}
chars.forEach(function(ch){
self.shortcutkey.addCommand({
key: ch,
command: function(){self.bindInputChar(ch)}
});
});
for(var key in self.KEYBIND){
(function(a){
self.shortcutkey.addCommand({key:key, command:function(){a.call(self)}});
})(self[self.KEYBIND[key]]);
}
},
isAvailable: function(){return this.state_available},
initHistoryVariable: function(){
if(!this.hasOwnProperty('history')) return;
this.history_max = 100;
this.history_search_count = -1;
this.history_search_regexp = null;
},
// setter
setSeparator: function(arg){
this.separator = arg;
return this;
},
setPrompt: function(str){
this.html.prompt.innerHTML = str;
return this;
},
setCandidates: function(lst){
this.candidates = lst;
return this;
},
setHistorySetter: function(fn){this.__defineSetter__('history',fn); return this;},
setHistoryGetter: function(fn){this.__defineGetter__('history',fn); return this;},
setAliasSetter: function(fn){this.__defineSetter__('alias',fn); return this;},
setAliasGetter: function(fn){this.__defineGetter__('alias',fn); return this;},
// html
initHTML: function(){
var INPUT_ID = 'gm_minibuffer_input_area';
var COMPLETION_ID = 'gm_minibuffer_completion';
var CONTAINER_ID = 'gm_minibuffer_container';
this.html = {};
this.html.completion = $N('ul',{id:COMPLETION_ID});
this.html.message = $N('div');
this.html.prompt = $N('span',{},"$");
this.html.input = $N('input',{id:INPUT_ID});
this.html.container = $N('div',{id:CONTAINER_ID, style:"background-color:#000;"},
[this.html.completion,
this.html.message,
this.html.prompt,
this.html.input]);
var inherit = 'background:inherit; background-image:inherit; background-color:inherit; color:inherit; text-align:inherit; font-size:inherit; font-style:inherit; font-weight:inherit; margin:inherit; opacity:inherit; text-decoration:inherit; border:0px; height:100%; padding:0; margin:inherit; font-family:inherit; vertical-align:inherit; line-height:inherit; font-stretch:inherit; font-variant:inherit; font-size-adjust:inherit; letter-spacing:inherit;';
GM_addStyle(['#', CONTAINER_ID,'{', 'right: 0px;', 'left: 0px;', 'bottom: 0px;', 'line-height: 100%;', 'vertical-align: baseline;', 'border: 1px dotted #444;', 'font-family: sans-serif;', 'text-decoration: none;', 'font-weight: normal;', 'font-style: normal;', 'font-size: medium;', 'font-stretch: normal;', 'font-variant: normal;', 'font-size-adjust: none;', 'letter-spacing: normal;', 'background: none;', 'text-align: left;', 'position: fixed;', 'margin: 0;', 'padding: 20px;', 'background-image: none;', 'color: #aaa;', '-moz-border-radius: 10px 10px 0px 0px;border-radius: 10px 10px 0px 0px;', 'opacity:0.8;', 'z-index:999;', '}\n',
'#', CONTAINER_ID, ' > span',INPUT_ID, '{', 'color: #CB6161;','display:inline;','}',
'#', CONTAINER_ID, ' > span {', inherit, 'color: #ccc;', 'display:inline;','margin-right: 5px;','}\n',
'#', INPUT_ID, '{', inherit, 'width:90%;','}',
'#', COMPLETION_ID, '{', inherit, 'margin-bottom: 20px;','border-bottom: 1px dotted #444;' ,'}\n',
'#', COMPLETION_ID, ' > span {', inherit, 'color: #ccc;', 'margin: 10px;','display:block;','}\n',
'#', COMPLETION_ID, ' > li {', inherit, 'color: #ccc;', 'padding: 2px;','margin-bottom:10px;','margin-left: 10px;','}\n',
'#', COMPLETION_ID, ' > li.gm_minibuffer_selected{', inherit, 'color: #CB6161;', 'padding: 2px;','margin-bottom:10px;','margin-left: 10px;','}\n',
].join(''));
},
updateComplationList: function(scroll){
var c = this.html.completion,
i = this.html.input,
old_lst = this.candidates,
new_lst = [];
if(this.hasOwnProperty('alias')) old_lst = old_lst.concat(keys(this.alias)); // alias
var input_str = i.value.slice(0,i.selectionStart).match(new RegExp("[^"+this.separator+"]*$"))[0].replace(/^\s+/,'');
var test = function(str, l){
var regexp = new RegExp(str, str.toLowerCase() == str ? 'i' : '');
return l.filter(function(el){return el.match(regexp)});
};
// all candidates
if(input_str == '') new_lst = old_lst;
// prefix match
if(!new_lst.length) new_lst = test('^' + input_str.replace(/^\^/, '').escapeRegexp(), old_lst);
// substring match
if(!new_lst.length) new_lst = test(input_str.escapeRegexp(), old_lst);
// to check whether list has change by STRING
var completed_str = new_lst.join('\n');
var len = new_lst.length;
if(len && this.last_completed_string != completed_str){
c.innerHTML = '';
for(var i=0,l=Math.min(new_lst.length, this.MAX_CANDIDATES); i<l; i++){
c.appendChild($N('li',{}, new_lst[i]));
}
if(old_lst.length > this.MAX_CANDIDATES){
this.html.message.innerHTML = 'Page 1/' + (Math.floor(new_lst.length / this.MAX_CANDIDATES) +1);
}
}else if(len == 0){
c.innerHTML = '';
}
this.last_completed_string = completed_str;
this.current = -1;
this.initHistoryVariable();
},
scrollPageNext: function(stop){ // stop at the bottom
var c = this.html.completion;
var lst = this.last_completed_string.split(/\r?\n|\r/);
if(this.MAX_CANDIDATES < lst.length){
var txt = c.lastChild.innerHTML;
var pos = lst.position(txt) + 1;
if(pos == lst.length){
if(stop) return false;
pos = 0;
}
var next_candidates = lst.slice(pos, pos + this.MAX_CANDIDATES);
c.innerHTML = '';
next_candidates.forEach(function(e){
c.appendChild($N('li',{}, e));
});
this.html.message.innerHTML = 'Page ' + (Math.floor(pos / this.MAX_CANDIDATES) +1) + '/' + (Math.floor(lst.length / this.MAX_CANDIDATES)+1);
}
return true;
},
scrollPagePrev: function(stop){ // stop at the top
var c = this.html.completion;
var lst = this.last_completed_string.split(/\r?\n|\r/);
if(this.MAX_CANDIDATES < lst.length){
var txt = c.firstChild.innerHTML;
var pos = lst.position(txt) - this.MAX_CANDIDATES;
if(pos < 0){
if(stop) return false;
pos = lst.length - (lst.length % this.MAX_CANDIDATES);
}
var next_candidates = lst.slice(pos, pos + this.MAX_CANDIDATES);
c.innerHTML = '';
next_candidates.forEach(function(e){
c.appendChild($N('li',{}, e));
});
this.html.message.innerHTML = 'Page '+ (Math.floor(pos / this.MAX_CANDIDATES) +1) + '/' + (Math.floor(lst.length / this.MAX_CANDIDATES)+1);
}
return true;
},
getCompletedString: function(){
var lst = this.last_completed_string.split(/\r?\n|\r/);
var fn = function(a,b){
if(a.length==0) return a;
var tmp=0, i=1, len=a.length;
while(tmp = b.indexOf(a.slice(0,i)) == 0) if(len == i++) break;
return a.slice(0,--i);
};
// ["ab1", "ab2", "ab3"] => "ab"
return lst.reduce(fn);
},
deleteAllStrings: function(){
this.last_completed_string = '';
this.html.input.value = '';
this.html.completion.innerHTML = '';
},
exit: function(result){
this.html.input.blur();
this.deleteAllStrings();
this.initHistoryVariable();
this.current = -1;
document.body.removeChild(this.html.container);
this.callback(result);
this.dispatchEvent("hide_minibuffer");
this.state_available = false;
},
complete: function(callback){
this.initHistoryVariable();// history
this.callback = callback;
this.keepSelection();
document.body.appendChild(this.html.container);
this.html.input.focus();
this.dispatchEvent("show_minibuffer", null);
this.state_available = true;
},
selectCandidate: function(newNode, oldNode){ // highlight node
var i = this.html.input;
if(newNode){
i.value = (i.value.match(new RegExp('.*'+this.separator.escapeRegexp()+'\\s*')) || '') + newNode.innerHTML;
newNode.setAttribute('class', 'gm_minibuffer_selected');
}
if(oldNode){
oldNode.removeAttribute('class',0);
}
},
keepSelection: function(arg){
this.selection = {};
// selected text
this.selection.text = getSelectionText();
// selected node
this.selection.node = getSelectionNode();
},
// event (hook)
// usage:
//
// var minibuffer = new Minibuffer();
// var obj = {
// 'show_minibuffer': function(){alert('show')},
// 'hide_minibuffer': function(){alert('hide')}
// };
// minibuffer.addEventListener(obj);
listeners: [],
removeEventListener: function(obj){
this.listeners = this.listeners.remove(obj);
},
addEventListener: function(obj){
this.listeners[this.listeners.length] = obj;
},
dispatchEvent: function(event_name, data){
this.listeners.forEach(function(listener){
if(event_name in listener){
try{
listener[event_name].apply(listener, [data]);
}catch(e){ log(e); }
}
});
},
// ShortcutKey
bindSelectNext: function(){
var c = this.html.completion;
if(!this.last_completed_string) this.updateComplationList();
var last = this.current != -1 && c.childNodes[this.current];
if(++this.current >= c.childNodes.length){
if(this.MAX_CANDIDATES <= this.last_completed_string.split(/\r?\n|\r/).length){
this.scrollPageNext();
}
this.current = 0;
}
this.selectCandidate(c.childNodes[this.current], last);
},
bindSelectPrevious: function(){
var c = this.html.completion;
if(!this.last_completed_string) this.updateComplationList();
var last = this.current != -1 && c.childNodes[this.current];
if(--this.current < 0) {
if(this.MAX_CANDIDATES <= this.last_completed_string.split(/\r?\n|\r/).length){
this.scrollPagePrev();
}
this.current = c.childNodes.length - 1;
}
this.selectCandidate(c.childNodes[this.current], last);
},
bindScrollNext: function(){
var c = this.html.completion;
var last_position = this.current;
var last = this.current != -1 && c.childNodes[this.current];
var res = this.scrollPageNext(true);
if(!res) return;
this.current = 0;
this.selectCandidate(c.childNodes[this.current]);
},
bindScrollPrev: function(){
var c = this.html.completion;
var last_position = this.current;
var last = this.current != -1 && c.childNodes[this.current];
var res = this.scrollPagePrev(true);
if(!res) return;
this.current = 0;
this.selectCandidate(c.childNodes[this.current]);
},
bindComplete: function(){
var self = this;
var setString = function(str){
if(!str) return;
var i=self.html.input, b=i.selectionStart, e=i.selectionEnd;
// "a | b | c" => "a | b | "
var pstr = i.value.slice(0,b).match(new RegExp('.*'+self.separator.escapeRegexp()+'\\s*'));
i.value = (pstr ? pstr[0] : '') + str;
var l=i.value.length;
i.setSelectionRange(l,l);
return i.value;
};
var getUniqueCandidate = function(){
var lst = self.last_completed_string.split(/\r?\n|\r/);
return lst.length == 1 ? lst[0] : false;
};
var str = self.getCompletedString();
if(!str){
this.updateComplationList();
str = getUniqueCandidate();
}
setString(str);
return getUniqueCandidate();
},
bindDecision: function(){
// history
if(this.hasOwnProperty('history')){ // ensure to set setter/getter
var history = this.history,
string = this.html.input.value;
history = history.remove(string);// to avoid duplicated
history.unshift(string); //
if(history.length > this.history_max) history.pop();
this.history = history;
}
this.exit(this.html.input.value);
},
bindExit: function(){
this.exit();
},
// move caret
bindBeginningOfLine: function(){this.html.input.setSelectionRange(0, 0)},
bindEndOfLine: function(){var i=this.html.input, l=i.value.length; i.setSelectionRange(l,l)},
bindForwardChar: function(){var i=this.html.input, p=i.selectionEnd+1; i.setSelectionRange(p,p)},
bindBackwardChar: function(){var i=this.html.input, p=i.selectionStart-1; i.setSelectionRange(p,p)},
bindForwardWord: function(){
var i=this.html.input, e=i.selectionEnd, t=i.value.slice(e).match(/[a-zA-Z0-9]+|[^a-zA-Z0-9]+/),l=i.value.slice(0,e).length + (!!t ? t[0].length : 1);
i.setSelectionRange(l,l);
},
bindBackwardWord: function(){
var i=this.html.input, l=i.value.slice(0,i.selectionStart).replace(/[a-zA-Z0-9]+$|[^a-zA-Z0-9]+$/,'').length;
i.setSelectionRange(l,l);
},
// delete character
bindDeleteBackwardChar: function(){
var i= this.html.input, b=i.selectionStart, e=i.selectionEnd;
if(b==e) b--;
i.value = i.value.slice(0,b)+i.value.slice(e);
i.setSelectionRange(b,b);
this.updateComplationList();
},
bindDeleteBackwardWord: function(){
var i=this.html.input, b=i.selectionStart, e=i.selectionEnd;
var tx = i.value, tr=tx.slice(0,b-1).replace(/[^a-zA-Z0-9]+$/,'').replace(/[a-zA-Z0-9]+$/,''), l=tr.length;
i.value = tr+tx.slice(e);
i.setSelectionRange(l,l);
this.updateComplationList();
},
bindDeleteForwardChar: function(){
var i=this.html.input, b=i.selectionStart, e=i.selectionEnd;
if(b == e) e++;
i.value=i.value.slice(0,b)+i.value.slice(e+1);
i.setSelectionRange(b,b);
if(i.value=='') this.updateComplationList();
},
bindDeleteForwardWord: function(){
var i=this.html.input, b=i.selectionStart, e=i.selectionEnd;
var t=i.value, m=t.slice(e).match(/[a-zA-Z0-9]+|[^a-zA-Z0-9]+/);
i.value = t.slice(0,b)+t.slice(!!m?e+m[0].length:e);
i.setSelectionRange(b,b);
if(i.value == '') this.updateComplationList();
},
bindDeleteAllStrings: function(){
this.deleteAllStrings();
this.updateComplationList();
},
// insert
bindInputChar: function(key){
var i=this.html.input, b=i.selectionStart, e=i.selectionEnd, t=i.value;
i.value = t.slice(0,b++) + key + t.slice(e);
i.setSelectionRange(b,b);
this.updateComplationList();
},
bindCompleteAndPipe: function(){
if(this.separator!='|' ||
(this.current<0 && !this.bindComplete())) return;
var i=this.html.input, b=i.selectionStart, str=i.value;
var trim = function(str){
return str.replace(/^\s|\s$/g,'');
};
i.value = trim(str.slice(0,b)) + ' ' + this.separator + ' ' + trim(str.slice(b));
var p = i.selectionEnd + this.separator.length;
i.setSelectionRange(p,p);
// eliminate highlight
var c = this.html.completion;
var last = this.current != -1 && c.childNodes[this.current];
this.selectCandidate(null, last);
this.updateComplationList();
},
// history
bindSearchHistoryBackward: function(){
if(!this.hasOwnProperty('history')) return;
var history = this.history, self = this, i = this.html.input;
this.history_search_regexp = this.history_search_regexp || new RegExp('^' + i.value);
var count = history.position(function(e, n){
return e.match(self.history_search_regexp) && n > self.history_search_count;
});
if(typeof count != 'number') count = this.history_search_count;
if(count > -1) i.value = history[count];
this.history_search_count = count;
},
bindSearchHistoryForward: function(){
if(!this.hasOwnProperty('history') || !this.history_search_regexp) return;
var history = this.history, self = this;
var count = history.slice(0,this.history_search_count).reverse().position(function(e, i){return e.match(self.history_search_regexp)});
if(typeof(count) == 'number') this.html.input.value = history[this.history_search_count -= count+1];
},
// alias
bindRegisterOrUnregisterAlias: function(){
if(!this.hasOwnProperty('alias')) return;
var alias = this.alias;
var t = this.html.input.value;
if(typeof alias[t] == 'undefined'){
// register as alias
var a = prompt('input alias of '+t+'');
alias[a] = t;
this.alias = alias;
}else{
// unregister from alias
delete alias[t];
this.alias = alias;
}
},
bindExpandAlias: function(){
var i = this.html.input;
var alias = this.alias[i.value];
if(alias) i.value = alias;
this.updateComplationList();
}
};
var Shell = {
TT: {
arg: 'arg',
control: 'control'
},
Parser: {
buffer: null,
init: function (buffer) {
this.buffer = buffer;
},
get_token: function ( ) {
// surround('foo', '[]') => '[foo]'
// surround('foo', '/') => '/foo/'
var surround = function (s, c) {
var d = (c.length > 1 ? c[1] : c) ;
return c[0] + s + d;
};
var quote_chars = '"' + "'";
var meta_chars = "|";
// ((["'])(.+?)(\3))
var quoted_arg = '(([' + quote_chars + '])(.+?)(\\3))';
// [^|<>;"'\s]+
var bare_arg = surround( '^' + meta_chars + quote_chars + '\\s', '[]') + '+';
var job_controlers = meta_chars;
var exp = '^\\s*';
exp += surround( [
quoted_arg, surround(bare_arg, "()"),
surround( surround(meta_chars, "[]"), "()" )
].join("|"), "()" );
var re = new RegExp(exp);
if ( re.test(this.buffer) ) {
// huuum, we need to count parenthesis index from constructed expression....
// ^\s*(((["'])(.+?)(\3))|([^|<>;"']+)|([|<>;]))
//4 or 6 or 7
var token = RegExp.$4 ? {type: Shell.TT.arg, literal: RegExp.$4} :
RegExp.$6 ? {type: Shell.TT.arg, literal: RegExp.$6} :
{type: Shell.TT.control, literal: RegExp.$7 };
this.buffer = RegExp.rightContext;
return token;
} else {
return null;
}
},
},
Command: {
commands: [],
state: null,
current_command: null,
init: function () {
this.commands = [];
this.state = this.need_command;
},
add_token: function (token) {
this.state.apply(this, [token]);
},
need_command: function (token) {
if ( token.type != Shell.TT.arg ) {
trace("syntax error", token);
} else {
this.current_command = { name: token.literal, args: [] };
this.state = this.search_for_end;
}
},
search_for_end: function (token) {
if ( token.type == Shell.TT.control ) {
this.end();
} else {
this.current_command.args.push( token.literal );
}
},
end: function () {
this.commands.push(this.current_command);
this.current_command = null;
this.state = this.need_command;
}
},
buffer: null,
parse: function (buffer) {
this.Parser.init(buffer);
this.Command.init();
var token;
while ( token = this.Parser.get_token() ) {
this.Command.add_token ( token );
}
this.Command.end();
return this.Command.commands;
},
execute: function (commands) {
var obj = null;
commands.forEach( function ( command ) {
var proc = Bin[command.name];
if ( proc ) {
obj = proc.apply( this, [command.args, obj] );
} else {
trace("command not found.", command.name);
}
} );
return stdin;
}
};
// copied from FLASH KEY (c) id:brazil
// http://userscripts.org/scripts/show/11996
// slightly modified.
var FlashMessage = new function(){
GM_addStyle((<><![CDATA[
#FLASH_MESSAGE{
position : fixed;
font-size : 500%;
z-index : 10000;
padding : 50px;
left : 50%;
top : 50%;
margin : -1em;
background-color : #444;
color : #FFF;
-moz-border-radius: 0.3em;
border-radius: 0.3em;
min-width : 1em;
text-align : center;
}
]]></>).toString());
var opacity = 0.9;
var flash = $N('div',{id:'FLASH_MESSAGE'});
hide(flash);
document.body.appendChild(flash);
var canceler;
this.showFlashMessageWindow = function (string, duration) {
duration = duration || 400;
canceler && canceler();
flash.innerHTML = string;
flash.style.opacity = opacity;
show(flash);
flash.style.marginLeft = (-(flash.offsetWidth/2))+'px';
canceler = callLater(function(){
canceler = tween(function(value){
flash.style.opacity = opacity * (1-value);
}, 100, 5);
}, duration);
};
// ----[Utility]-------------------------------------------------
function callLater(callback, interval){
var timeoutId = setTimeout(callback, interval);
return function(){
clearTimeout(timeoutId)
}
}
function tween(callback, span, count){
count = (count || 20);
var interval = span / count;
var value = 0;
var calls = 0;
var intervalId = setInterval(function(){
callback(calls / count);
if(count == calls){
canceler();
return;
}
calls++;
}, interval);
var canceler = function(){
clearInterval(intervalId)
hide(flash)
}
return canceler;
}
function hide(target){
target.style.display='none';
}
function show(target, style){
target.style.display=(style || '');
}
};
var Status = new Class();
Status.id = 'gm_minibuffer_flash_status';
Status.hash = {};
Status.prototype = {
initialize: function(){
this.initContainer();
var [name,status,time_limit] = arguments;
var hash = this.getHash();
var del = function(){
delete hash[name];
}
if(typeof status != "string"){
if(hash[name]){
this.fadeout(hash[name]);
del();
}
}else{
var img = $N('img',{src:"data:image/gif;base64,R0lGODlhEAAQAOMIAAAAABoaGjMzM0xMTGZmZoCAgJmZmbKysv///////////////////////////////yH/C05FVFNDQVBFMi4wAwEAAAAh+QQBCgAIACwAAAAAEAAQAAAESBDJiQCgmFqbZwjVhhwH9n3hSJbeSa1sm5GUIHSTYSC2jeu63q0D3PlwCB1lMMgUChgmk/J8LqUIAgFRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+UKgmFqbpxDV9gAA9n3hSJbeSa1sm5HUMHTTcTy2jeu63q0D3PlwDx2FQMgYDBgmk/J8LqWPQuFRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+YSgmFqb5xjV9gQB9n3hSJbeSa1sm5EUQXQTADy2jeu63q0D3PlwDx2lUMgcDhgmk/J8LqUPg+FRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+cagmFqbJyHV9ggC9n3hSJbeSa1sm5FUUXRTEDy2jeu63q0D3PlwDx3FYMgAABgmk/J8LqWPw+FRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+QihmFqbZynV9gwD9n3hSJbeSa1sm5GUYXSTIDy2jeu63q0D3PlwDx3lcMgEAhgmk/J8LqUPAOBRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+UqhmFqbpzHV9hAE9n3hSJbeSa1sm5HUcXTTMDy2jeu63q0D3PlwDx0FAMgIBBgmk/J8LqWPQOBRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+YyhmFqb5znV9hQF9n3hSJbeSa1sm5EUAHQTQTy2jeu63q0D3PlwDx0lEMgMBhgmk/J8LqUPgeBRhV6z2q0VF94iJ9pOBAAh+QQBCgAPACwAAAAAEAAQAAAESPDJ+c6hmFqbJwDV9hgG9n3hSJbeSa1sm5FUEHRTUTy2jeu63q0D3PlwDx1FIMgQCBgmk/J8LqWPweBRhV6z2q0VF94iJ9pOBAA7"});
var lst = typeof time_limit == 'number' ? [status]: [img ,status];
var div = $N('div', {}, lst);
if(hash[name]){
this.replace(div, hash[name]);
}else{
this.add(div);
}
hash[name] = div;
if(typeof time_limit == 'number'){
this.fadeout.later(time_limit).call(this, hash[name]);
del.later(time_limit)();
}
}
},
initContainer: function(){
if(!document.getElementById(Status.id)){
GM_addStyle((<><![CDATA[
#gm_minibuffer_flash_status{
position : fixed;
font-size: 150%;
z-index : 10000;
right : 20px;
bottom : 0px;
opacity: 0.9;
background-color : #000;
padding: 10px;
color : #FFF;
-moz-border-radius: 0.3em;
border-radius: 0.3em;
}
#gm_minibuffer_flash_status img {
margin-right: 10px;
}
]]></>).toString());
var container = $N('div',{id:Status.id, style:'display:block;'});
document.body.appendChild(container);
}
},
getHash: function(){return Status.hash},
add: function(node){
var container = document.getElementById(Status.id);
if(container){
container.appendChild(node);
}
if(container.style.display == 'none'){
container.style.display = 'block';
}
},
fadeout: function(node){
var setOpacity = function(node, opacity){
node.style.opacity = opacity;
}
var max = 15;
var base = 1000;
for(var i=0; i<max; i++){
setOpacity.later(i/max*base)(node, 1-i/max);
}
this.remove.later(base*1.2)(node);
},
remove: function(node){
var container = document.getElementById(Status.id);
container.removeChild(node);
if(!container.hasChildNodes()){
container.style.display = 'none';
}
},
replace: function(new_node, old_node){
var container = document.getElementById(Status.id);
container.replaceChild(new_node, old_node);
}
};
var Command = new Class();
Command.prototype = {
initialize: function(){
this.command = {};
},
get selection(){
return this.minibuffer.isAvailable() ?
this.minibuffer.selection :
{
node: getSelectionNode(),
text: getSelectionText()
}
},
/* argument of addCommand
* name: "string",
* command: function(stdin){ return stdout; },
*
* todo:
* // description: "string",
* // argument: function(){ return ["option1", "option2"]; },
*/
addCommand: function(hash){
// to keep compatibility
if(typeof hash.name == 'undefined'){
for(var name in hash){
this.command[name] = hash[name];
}
return;
}
// var description = hash['description'];
// var argument = hash['argument'];
this.command[hash['name']] = hash['command'];
},
addShortcutkey: function(opt){
this.shortcutkey.addCommand(opt);
},
attachEvent: function(){
var self = this;
var fn = function(){
self.shortcutkey.disable();
self.minibuffer.setCandidates(keys(self.command));
self.minibuffer.complete(function(a){self.callback(a)});
}
this.addShortcutkey({ key:'M-x', description:'Open Minibuffer', command:fn});
this.addShortcutkey({ key:':', description:'Open Minibuffer', command:fn});
this.addShortcutkey({
key:'?',
description: 'Toggle help',
command: function(){self.shortcutkey.bindHelp.call(self.shortcutkey)},
});
},
hoge: function(){
var self = this;
var s = new ShortcutKey();
s.throughEvent();
s.addEventListener(document, 'keypress', false);
s.addCommand({key: 'Up Up Down Down Left Right Left Right b a', command:function(){FlashMessage.showFlashMessageWindow.eachLater(1000).apply(this,atob("NSA0IDMgMiAxIEJPTUIhISE=").split(' ').map(function(e){return [e,800]}))}});
s.addCommand({key: 'Up x Down b l y r a', command: function(){var tmp='';FlashMessage.showFlashMessageWindow.eachLater(500).apply(this,'\u30ab \u30ab \u30ed \u30c3 \u30c8 \u30fb \u30fb \u30fb'.split(' ').map(function(e){return [tmp+=e, 500]}))}});
s.addCommand({key: 'Down r Up l y b x a', command: function(){
var h=self.minibuffer.html.container;
var f=function(){return Math.floor(Math.random()*256).toString(16)};
var c='#'+f()+f()+f();
FlashMessage.showFlashMessageWindow(c,1000);
h.style.backgroundColor=c;
}});
},
detachEvent: function(){
this.shortcutkey.removeCommand('M-x');
},
execute: function(commandline, stdin){
if(!commandline) return;
var alias = this.alias_getter()[commandline];
var commands = Shell.parse(alias ? alias : commandline);
var self = this;
if(typeof stdin == 'undefined') stdin = [];
var ret = commands.forEach(function(command){
var fn = self.command[command.name];
if(!fn) return null;
var cmd = {
func: fn,
args: command.args,
name: command.name
};
stdin = cmd.func(stdin);
});
return stdin;
},
callback: function(commandline){
this.shortcutkey.enable();
if(!commandline) return;
this.execute(commandline);
},
setup: function(){
// setup minibuffer
var define_setter = function(type){return function(arg){ GM_setValue(type, uneval(arg))}};
this.alias_getter = function(){return eval(GM_getValue('alias', '({})'))};
this.minibuffer = new Minibuffer()
.setHistoryGetter(function(){return eval(GM_getValue('history', '[]'))})
.setAliasGetter(this.alias_getter)
.setHistorySetter(define_setter('history'))
.setAliasSetter(define_setter('alias'));
// setup shortcut key
this.shortcutkey = new ShortcutKey()
.initHelp('command')
.throughInputElements();
this.shortcutkey.addEventListener(document, 'keypress', false);
this.attachEvent();
this.hoge();
},
};
function $N(name, attr, childs) {
var ret = document.createElement(name);
for (var k in attr) if (attr.hasOwnProperty(k)) {
var v = attr[k];
if (k == "class") ret.className = v;
else ret.setAttribute(k, v);
}
switch(typeof childs){
case "string":
ret.appendChild(document.createTextNode(childs));
break;
case "object":
for (var i=0, len=childs.length; i<len; i++) {
var child = childs[i];
if (typeof child == "string") {
ret.appendChild(document.createTextNode(child));
} else {
ret.appendChild(child);
}
}
}
return ret;
}
// via http://github.com/hatena/hatena-bookmark-xul/blob/master/chrome/content/common/05-HTMLDocumentCreator.js
function createDocumentFromString(source){
var doc = document.implementation.createHTMLDocument ?
document.implementation.createHTMLDocument('hogehoge') :
document.implementation.createDocument(null, 'html', null);
var range = document.createRange();
range.selectNodeContents(document.documentElement);
var fragment = range.createContextualFragment(source);
var headChildNames = {title: true, meta: true, link: true, script: true, style: true, /*object: true,*/ base: true/*, isindex: true,*/};
var child, head = doc.getElementsByTagName('head')[0] || doc.createElement('head'),
body = doc.getElementsByTagName('body')[0] || doc.createElement('body');
while ((child = fragment.firstChild)) {
if (
(child.nodeType === doc.ELEMENT_NODE && !(child.nodeName.toLowerCase() in headChildNames)) ||
(child.nodeType === doc.TEXT_NODE &&/\S/.test(child.nodeValue))
)
break;
head.appendChild(child);
}
body.appendChild(fragment);
doc.documentElement.appendChild(head);
doc.documentElement.appendChild(body);
return doc;
}
// $X on XHTML
// @target Freifox3, Chrome3, Safari4, Opera10
// @source http://gist.github.com/184276.txt
function $X (exp, context) {
context || (context = document);
var _document = context.ownerDocument || context,
documentElement = _document.documentElement,
isXHTML = documentElement.tagName !== 'HTML' && _document.createElement('p').tagName === 'p',
defaultPrefix = null;
if (isXHTML) {
defaultPrefix = '__default__';
exp = addDefaultPrefix(exp, defaultPrefix);
}
function resolver (prefix) {
return context.lookupNamespaceURI(prefix === defaultPrefix ? null : prefix) ||
documentElement.namespaceURI || "";
}
var result = _document.evaluate(exp, context, resolver, XPathResult.ANY_TYPE, null);
switch (result.resultType) {
case XPathResult.STRING_TYPE : return result.stringValue;
case XPathResult.NUMBER_TYPE : return result.numberValue;
case XPathResult.BOOLEAN_TYPE: return result.booleanValue;
case XPathResult.UNORDERED_NODE_ITERATOR_TYPE:
// not ensure the order.
var ret = [], i = null;
while (i = result.iterateNext()) ret.push(i);
return ret;
}
}
// XPath 式中の接頭辞のない名前テストに接頭辞 prefix を追加する
// e.g. '//body[@class = "foo"]/p' -> '//prefix:body[@class = "foo"]/prefix:p'
// http://nanto.asablo.jp/blog/2008/12/11/4003371
function addDefaultPrefix(xpath, prefix) {
var tokenPattern = /([A-Za-z_\u00c0-\ufffd][\w\-.\u00b7-\ufffd]*|\*)\s*(::?|\()?|(".*?"|'.*?'|\d+(?:\.\d*)?|\.(?:\.|\d+)?|[\)\]])|(\/\/?|!=|[<>]=?|[\(\[|,=+-])|([@$])/g;
var TERM = 1, OPERATOR = 2, MODIFIER = 3;
var tokenType = OPERATOR;
prefix += ':';
function replacer(token, identifier, suffix, term, operator, modifier) {
if (suffix) {
tokenType =
(suffix == ':' || (suffix == '::' && (identifier == 'attribute' || identifier == 'namespace')))
? MODIFIER : OPERATOR;
} else if (identifier) {
if (tokenType == OPERATOR && identifier != '*')
token = prefix + token;
tokenType = (tokenType == TERM) ? OPERATOR : TERM;
} else {
tokenType = term ? TERM : operator ? OPERATOR : MODIFIER;
}
return token;
}
return xpath.replace(tokenPattern, replacer);
}
// Usage:: with (D()) { your code }
// JSDefeered 0.2.1 (c) Copyright (c) 2007 cho45 ( www.lowreal.net )
// See http://coderepos.org/share/wiki/JSDeferred
function D () {
function Deferred () { return (this instanceof Deferred) ? this.init(this) : new Deferred() }
Deferred.prototype = {
init : function () {
this._next = null;
this.callback = {
ok: function (x) { return x },
ng: function (x) { throw x }
};
return this;
},
next : function (fun) { return this._post("ok", fun) },
error : function (fun) { return this._post("ng", fun) },
call : function (val) { return this._fire("ok", val) },
fail : function (err) { return this._fire("ng", err) },
cancel : function () {
(this.canceller || function () {})();
return this.init();
},
_post : function (okng, fun) {
this._next = new Deferred();
this._next.callback[okng] = fun;
return this._next;
},
_fire : function (okng, value) {
var self = this, next = "ok";
try {
value = self.callback[okng].call(self, value);
} catch (e) {
next = "ng";
value = e;
}
if (value instanceof Deferred) {
value._next = self._next;
} else {
if (self._next) self._next._fire(next, value);
}
return this;
}
};
Deferred.parallel = function (dl) {
var ret = new Deferred(), values = {}, num = 0;
for (var i in dl) if (dl.hasOwnProperty(i)) {
(function (d, i) {
d.next(function (v) {
values[i] = v;
if (--num <= 0) {
if (dl instanceof Array) {
values.length = dl.length;
values = Array.prototype.slice.call(values, 0);
}
ret.call(values);
}
}).error(function (e) {
ret.fail(e);
});
num++;
})(dl[i], i);
}
if (!num) Deferred.next(function () { ret.call() });
ret.canceller = function () {
for (var i in dl) if (dl.hasOwnProperty(i)) {
dl[i].cancel();
}
};
return ret;
};
Deferred.wait = function (n) {
var d = new Deferred(), t = new Date();
var id = setTimeout(function () {
clearTimeout(id);
d.call((new Date).getTime() - t.getTime());
}, n * 1000)
d.canceller = function () { try { clearTimeout(id) } catch (e) {} };
return d;
};
Deferred.next = function (fun) {
var d = new Deferred();
var id = setTimeout(function () { clearTimeout(id); d.call() }, 0);
if (fun) d.callback.ok = fun;
d.canceller = function () { try { clearTimeout(id) } catch (e) {} };
return d;
};
Deferred.call = function (f, args) {
args = Array.prototype.slice.call(arguments);
f = args.shift();
return Deferred.next(function () {
return f.apply(this, args);
});
};
Deferred.loop = function (n, fun) {
var o = {
begin : n.begin || 0,
end : n.end || (n - 1),
step : n.step || 1,
last : false,
prev : null
};
var ret, step = o.step;
return Deferred.next(function () {
function _loop (i) {
if (i <= o.end) {
if ((i + step) > o.end) {
o.last = true;
o.step = o.end - i + 1;
}
o.prev = ret;
ret = fun.call(this, i, o);
if (ret instanceof Deferred) {
return ret.next(function (r) {
ret = r;
return Deferred.call(_loop, i + step);
});
} else {
return Deferred.call(_loop, i + step);
}
} else {
return ret;
}
}
return Deferred.call(_loop, o.begin);
});
};
Deferred.register = function (name, fun) {
this.prototype[name] = function () {
return this.next(Deferred.wrap(fun).apply(null, arguments));
};
};
Deferred.wrap = function (dfun) {
return function () {
var a = arguments;
return function () {
return dfun.apply(null, a);
};
};
};
Deferred.register("loop", Deferred.loop);
Deferred.register("wait", Deferred.wait);
Deferred.define = function (obj, list) {
if (!list) list = ["parallel", "wait", "next", "call", "loop"];
if (!obj) obj = (function () { return this })();
list.forEach(function (i) {
obj[i] = Deferred[i];
});
return Deferred;
};
function xhttp (opts) {
var d = Deferred();
if (opts.onload) d = d.next(opts.onload);
if (opts.onerror) d = d.error(opts.onerror);
opts.onload = function (res) {
d.call(res);
};
opts.onerror = function (res) {
d.fail(res);
};
GM_xmlhttpRequest(opts);
return d;
}
xhttp.get = function (url) { return xhttp({method:"get", url:url}) };
xhttp.post = function (url, data) { return xhttp({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) };
function http (opts) {
var d = Deferred();
var req = new XMLHttpRequest();
req.open(opts.method, opts.url, true);
if (opts.headers) {
for (var k in opts.headers) if (opts.headers.hasOwnProperty(k)) {
req.setRequestHeader(k, opts.headers[k]);
}
}
req.onreadystatechange = function () {
if (req.readyState == 4) d.call(req);
};
req.send(opts.data || null);
d.xhr = req;
return d;
}
http.get = function (url) { return http({method:"get", url:url}) };
http.post = function (url, data) { return http({method:"post", url:url, data:data, headers:{"Content-Type":"application/x-www-form-urlencoded"}}) };
Deferred.Deferred = Deferred;
Deferred.http = http;
Deferred.xhttp = xhttp;
return Deferred;
}// End of JSDeferred
function keys(hash){
var tmp = [];
for(var key in hash)tmp.push(key);
return tmp;
}
function values(hash){
var tmp = [];
for(var key in hash)tmp.push(hash[key]);
return tmp;
}
var getSelectionText = function(){
return String(window.getSelection()).split(/\r?\n|\r/).remove("");
};
var getSelectionNode = function(){
var s=window.getSelection(), res=[], len=s.rangeCount;
for(var i=0; i<len; i++){
var ret = document.createElement('root');
ret.appendChild(s.getRangeAt(i).cloneContents());
res[res.length] = ret;
}
return res;
};
String.prototype.escapeRegexp = function(){
return this.replace(/^(?=[?*])/, '.').replace(/(?=[|+])/g, '\\').replace(/\([^)]*$/, '').replace(/\[[^\]]*$/, '');
};
Array.prototype.position = function(obj){
var test = (typeof(obj) == 'function') ? obj : function(a){return a == obj};
for(var i=0;i<this.length; i++) if(test(this[i], i)) return i;
return false;
};
Array.prototype.last = function(){
return this[this.length-1];
};
Array.prototype.find = function(obj){
var i = this.position(obj);
return typeof(i) == 'number' ? this[i] : false;
};
Array.prototype.remove = function(obj){
var test = (typeof(obj) == 'function') ? obj : function(a){return a == obj};
return this.filter(function(e){return !test(e)})
};
Array.prototype.reduce = function(fn ,initial){
var len = this.length;
if(typeof fn != "function" || (len == 0 && arguments.length == 1)) throw new TypeError();
var i = 0;
if(arguments.length >= arguments.callee.length){
var rv = arguments[1];
}else{
do{
if(i in this){
rv = this[i++];
break;
}
if(++i >= len)throw new TypeError();
}while (true);
}
for (;i<len;i++) if(i in this) rv=fn.call(null, rv, this[i], i, this);
return rv;
};
Function.prototype.later = function(ms){
var self = this;
return function(){
var args = arguments;
var thisObject = this;
var res = {
arg: args,
complete: false,
cancel: function(){clearTimeout(PID);},
notify: function(){clearTimeout(PID);later_func()}
};
var later_func = function(){
self.apply(thisObject,args);
res.complete = true;
};
var PID = setTimeout(later_func,ms);
return res;
};
};
// usage:
// var lst = (function(e){console.log(e)}).eachLater(500)([1],[2],[3],[4],[5],[6],[7]);
// (function(){lst.forEach(function(e){e.complete || e.cancel()})}).later(2000)();
Function.prototype.eachLater = function(ms){
var self = this;
return function(){
var tmp=0, lst=[];
for(var i=0;i<arguments.length; i++) lst[lst.length] = self.later(tmp+=ms).apply(this,arguments[i]);
return lst;
}
};
function log(){console.log.apply(console, Array.slice(arguments));}
//// register command
if(document.body){
var command = new Command();
window.Minibuffer = {
getMinibuffer : function(){return new Minibuffer()}
, getShortcutKey : function(){return new ShortcutKey()}
, addShortcutkey : function(a){command.addShortcutkey(a)}
, addCommand : function(a){command.addCommand(a)}
, execute : function(a, stdin){return command.execute(a, stdin)}
, message : FlashMessage.showFlashMessageWindow
, status : function(name, status, timelimit){new Status(name, status,timelimit)}
, $X : $X
, $N : $N
, D : D
, createDocumentFromString : createDocumentFromString
};
window.Minibuffer.addCommand({
name: 'Minibuffer::Exit',
command : command.detachEvent,
});
// nothing => list of current URL
window.Minibuffer.addCommand({
name: 'location',
command: function(){return [location.href]},
});
// nothing => list of string (divided by \n)
window.Minibuffer.addCommand({
name: 'selected-text',
command: function(){return command.selection.text},
});
// nothing => list of selected node
window.Minibuffer.addCommand({
name: 'selected-node',
command: function(){return command.selection.node}
});
// node list => list of string
window.Minibuffer.addCommand({
name: 'innerHTML',
command: function(stdin){return stdin.map(function(a){return a.innerHTML})}
});
// list of node or nothing => list of node
// args: 'tag'
window.Minibuffer.addCommand({
name: 'filter-by-tag-name',
command: function(stdin){
var tag = this.args.shift();
if(stdin.length == 0) stdin.push(document);
var res = [];
for(var i=0,k=stdin.length; i<k; i++){
var lst = stdin[i].getElementsByTagName(tag);
for(var j=0,l=lst.length; j<l; j++){
res.push(lst[j]);
}
}
return res;
}
});
// list of URL or nothing => list of URL
// args: count
window.Minibuffer.addCommand({
name: "upper-directory",
command: function(stdin){
var urls = stdin.length ? stdin : [location.href];
var count = this.args.shift() || 1;
var rep = new RegExp('[^/]+/?$');
var host = new RegExp('[a-z]+://[^/]+/');
return urls.map(function(url){
for(var i=0; i<count; i++){
var newurl = url.replace(rep, '');
if(newurl.match(host)) url = newurl;
}
return url;
});
}
});
// object => object
window.Minibuffer.addCommand({
name: 'echo',
command: function(obj){
log(obj);
return obj;
}
});
// list of node or nothing => list of node
// args: 'XPath'
window.Minibuffer.addCommand({
name: 'xpath',
command: function(stdin) {
var exp = this.args.shift();
if(stdin.length == 0) stdin.push(document);
var res = [];
for(var i=0,l=stdin.length; i<l; i++){
var lst = window.Minibuffer.$X(exp, stdin[i]);
for(var j=0,l=lst.length; j<l; j++){
res.push(lst[j]);
}
}
return res;
}
});
// list of node or list of string => list of node or list of string
// args: 'regexp' 'attribute' 'flag'
window.Minibuffer.addCommand({
name: 'grep',
command: function (stdin) {
var regexp = this.args.shift();
var attr = this.args.shift();
var flag = this.args.shift();
var re = new RegExp(regexp, typeof(flag) != 'undefined' ? flag : regexp.toLowerCase() == regexp ? 'i':'');
return stdin.filter(function(obj) {
if(typeof(obj) == 'string'){
return obj.match(re);
}else if(obj.nodeType == 3){
return obj.nodeValue.match(re);
}else if(obj.nodeType == 1 && attr && obj.getAttribute(attr)){
return obj.getAttribute(attr).match(re);
}else if(obj.nodeType == 1 && obj.text){
return obj.text.match(re);
}
});
}
});
// list of anchor node or list of URL
// args: 'target'
window.Minibuffer.addCommand({
name: 'open',
command: function(stdin){
var target = this.args.shift();
if(target == 'top' || target == 'blank') target = '_' + target;
stdin.forEach(function(url){
if(target){
window.open(url, target);
}else{
GM_openInTab(url);
}
});
return stdin;
}
});
// list => reversed list
window.Minibuffer.addCommand({
name: 'reverse',
command: function(stdin){
return stdin.reverse();
}
});
// list of URL => list of URL
window.Minibuffer.addCommand({
name: 'web-archive',
command: function(stdin){
return stdin.map(function(url){return 'http://web.archive.org/web/*/' + url})
}
});
// list of URL => list of URL
window.Minibuffer.addCommand({
name: 'google-cache',
command: function(stdin){
return stdin.map(function(url){return 'http://www.google.com/search?q=cache:' + url})
}
});
// list of URL => list of URL
window.Minibuffer.addCommand({
name: 'web-gyotaku',
command: function(stdin){
return stdin.map(function(url){return 'http://megalodon.jp/?url=' + url})
}
});
// list => list
window.Minibuffer.addCommand({
name: 'scrollto-top',
command: function(stdin){
window.scrollTo(0,0);
return stdin;
}
});
// list => list
window.Minibuffer.addCommand({
name: 'scrollto-bottom',
command: function(stdin){
window.scrollTo(0, window.scrollMaxY);
return stdin;
}
});
// // tako3
// window.Minibuffer.addCommand({
// name: 'tako3',
// command: function(stdin){
// return stdin.map(function(url){return "http://tako3.com/" + url})
// }
// });
// setup
command.setup();
// shortcut key sample
// window.Minibuffer.addShortcutkey({
// key: 'C-o',
// description: 'Open Google cache',
// command: function(){
// window.Minibuffer.execute('pinned-or-current-link | google-cache | open | clear-pin');
// }
// });
// // vi like
// window.Minibuffer.addShortcutkey({
// key: 'g g',
// description: 'scroll to top',
// command: function(){window.Minibuffer.execute('scrollto-top')}
// });
// window.Minibuffer.addShortcutkey({
// key: 'G',
// description: 'scroll to bottom',
// command: function(){window.Minibuffer.execute('scrollto-bottom')}
// });
var ev = document.createEvent('Events');
ev.initEvent('GM_MinibufferLoaded', false, true);
window.dispatchEvent(ev);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment