node-wings
Version:
node-wings ==========
1,276 lines (1,241 loc) • 129 kB
JavaScript
/**@license
*| __ _____ ________ __
*| / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / /
*| __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ /
*| / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__
*| \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/
*| \/ /____/ version 0.7.0
* http://terminal.jcubic.pl
*
* Licensed under GNU LGPL Version 3 license
* Copyright (c) 2011-2013 Jakub Jankiewicz <http://jcubic.pl>
*
* Includes:
*
* Storage plugin Distributed under the MIT License
* Copyright (c) 2010 Dave Schindler
*
* jQuery Timers licenced with the WTFPL
* <http://jquery.offput.ca/every/>
*
* Cross-Browser Split 1.1.1
* Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
* Available under the MIT License
*
* Date: Sat, 13 Jul 2013 19:55:28 +0000
*/
/*
TODO:
distinguish between paused and disabled
paused should block keydown in terminal it should disable command line
disable
*/
(function($, undefined) {
"use strict";
// map object to object
$.omap = function(o, fn) {
var result = {};
$.each(o, function(k, v) {
result[k] = fn.call(o, k, v);
});
return result;
};
// ----------------------------------------
// START Storage plugin
// ----------------------------------------
// Private data
var isLS = typeof window.localStorage !== 'undefined';
// Private functions
function wls(n, v) {
var c;
if (typeof n === 'string' && typeof v === 'string') {
localStorage[n] = v;
return true;
} else if (typeof n === 'object' && typeof v === 'undefined') {
for (c in n) {
if (n.hasOwnProperty(c)) {
localStorage[c] = n[c];
}
}
return true;
}
return false;
}
function wc(n, v) {
var dt, e, c;
dt = new Date();
dt.setTime(dt.getTime() + 31536000000);
e = '; expires=' + dt.toGMTString();
if (typeof n === 'string' && typeof v === 'string') {
document.cookie = n + '=' + v + e + '; path=/';
return true;
} else if (typeof n === 'object' && typeof v === 'undefined') {
for (c in n) {
if (n.hasOwnProperty(c)) {
document.cookie = c + '=' + n[c] + e + '; path=/';
}
}
return true;
}
return false;
}
function rls(n) {
return localStorage[n];
}
function rc(n) {
var nn, ca, i, c;
nn = n + '=';
ca = document.cookie.split(';');
for (i = 0; i < ca.length; i++) {
c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nn) === 0) {
return c.substring(nn.length, c.length);
}
}
return null;
}
function dls(n) {
return delete localStorage[n];
}
function dc(n) {
return wc(n, '', -1);
}
/**
* Public API
* $.Storage.set("name", "value")
* $.Storage.set({"name1":"value1", "name2":"value2", etc})
* $.Storage.get("name")
* $.Storage.remove("name")
*/
$.extend({
Storage: {
set: isLS ? wls : wc,
get: isLS ? rls : rc,
remove: isLS ? dls : dc
}
});
// ----------------------------------------
// END Storage plugin
// ----------------------------------------
// START jQuery Timers
// ----------------------------------------
jQuery.fn.extend({
everyTime: function(interval, label, fn, times, belay) {
return this.each(function() {
jQuery.timer.add(this, interval, label, fn, times, belay);
});
},
oneTime: function(interval, label, fn) {
return this.each(function() {
jQuery.timer.add(this, interval, label, fn, 1);
});
},
stopTime: function(label, fn) {
return this.each(function() {
jQuery.timer.remove(this, label, fn);
});
}
});
jQuery.extend({
timer: {
guid: 1,
global: {},
regex: /^([0-9]+)\s*(.*s)?$/,
powers: {
// Yeah this is major overkill...
'ms': 1,
'cs': 10,
'ds': 100,
's': 1000,
'das': 10000,
'hs': 100000,
'ks': 1000000
},
timeParse: function(value) {
if (value === undefined || value === null) {
return null;
}
var result = this.regex.exec(jQuery.trim(value.toString()));
if (result[2]) {
var num = parseInt(result[1], 10);
var mult = this.powers[result[2]] || 1;
return num * mult;
} else {
return value;
}
},
add: function(element, interval, label, fn, times, belay) {
var counter = 0;
if (jQuery.isFunction(label)) {
if (!times) {
times = fn;
}
fn = label;
label = interval;
}
interval = jQuery.timer.timeParse(interval);
if (typeof interval !== 'number' ||
isNaN(interval) ||
interval <= 0) {
return;
}
if (times && times.constructor !== Number) {
belay = !!times;
times = 0;
}
times = times || 0;
belay = belay || false;
if (!element.$timers) {
element.$timers = {};
}
if (!element.$timers[label]) {
element.$timers[label] = {};
}
fn.$timerID = fn.$timerID || this.guid++;
var handler = function() {
if (belay && handler.inProgress) {
return;
}
handler.inProgress = true;
if ((++counter > times && times !== 0) ||
fn.call(element, counter) === false) {
jQuery.timer.remove(element, label, fn);
}
handler.inProgress = false;
};
handler.$timerID = fn.$timerID;
if (!element.$timers[label][fn.$timerID]) {
element.$timers[label][fn.$timerID] = window.setInterval(handler, interval);
}
if (!this.global[label]) {
this.global[label] = [];
}
this.global[label].push(element);
},
remove: function(element, label, fn) {
var timers = element.$timers, ret;
if (timers) {
if (!label) {
for (var lab in timers) {
if (timers.hasOwnProperty(lab)) {
this.remove(element, lab, fn);
}
}
} else if (timers[label]) {
if (fn) {
if (fn.$timerID) {
window.clearInterval(timers[label][fn.$timerID]);
delete timers[label][fn.$timerID];
}
} else {
for (var _fn in timers[label]) {
if (timers[label].hasOwnProperty(_fn)) {
window.clearInterval(timers[label][_fn]);
delete timers[label][_fn];
}
}
}
for (ret in timers[label]) {
if (timers[label].hasOwnProperty(ret)) {
break;
}
}
if (!ret) {
ret = null;
delete timers[label];
}
}
for (ret in timers) {
if (timers.hasOwnProperty(ret)) {
break;
}
}
if (!ret) {
element.$timers = null;
}
}
}
}
});
if (jQuery.browser && jQuery.browser.msie ||
/(msie) ([\w.]+)/.exec(navigator.userAgent.toLowerCase())) {
jQuery(window).one('unload', function() {
var global = jQuery.timer.global;
for (var label in global) {
if (global.hasOwnProperty(label)) {
var els = global[label], i = els.length;
while (--i) {
jQuery.timer.remove(els[i], label);
}
}
}
});
}
// ----------------------------------------
// START CROSS BROWSER SPLIT
// ----------------------------------------
(function(undef) {
// prevent double include
if (!String.prototype.split.toString().match(/\[native/)) {
return;
}
var nativeSplit = String.prototype.split,
compliantExecNpcg = /()??/.exec("")[1] === undef, // NPCG: nonparticipating capturing group
self;
self = function (str, separator, limit) {
// If `separator` is not a regex, use `nativeSplit`
if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
return nativeSplit.call(str, separator, limit);
}
var output = [],
flags = (separator.ignoreCase ? "i" : "") +
(separator.multiline ? "m" : "") +
(separator.extended ? "x" : "") + // Proposed for ES6
(separator.sticky ? "y" : ""), // Firefox 3+
lastLastIndex = 0,
// Make `global` and avoid `lastIndex` issues by working with a copy
separator2, match, lastIndex, lastLength;
separator = new RegExp(separator.source, flags + "g");
str += ""; // Type-convert
if (!compliantExecNpcg) {
// Doesn't need flags gy, but they don't hurt
separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
}
/* Values for `limit`, per the spec:
* If undefined: 4294967295 // Math.pow(2, 32) - 1
* If 0, Infinity, or NaN: 0
* If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
* If negative number: 4294967296 - Math.floor(Math.abs(limit))
* If other: Type-convert, then use the above rules
*/
// ? Math.pow(2, 32) - 1 : ToUint32(limit)
limit = limit === undef ? -1 >>> 0 : limit >>> 0;
while (match = separator.exec(str)) {
// `separator.lastIndex` is not reliable cross-browser
lastIndex = match.index + match[0].length;
if (lastIndex > lastLastIndex) {
output.push(str.slice(lastLastIndex, match.index));
// Fix browsers whose `exec` methods don't consistently return `undefined` for
// nonparticipating capturing groups
if (!compliantExecNpcg && match.length > 1) {
match[0].replace(separator2, function () {
for (var i = 1; i < arguments.length - 2; i++) {
if (arguments[i] === undef) {
match[i] = undef;
}
}
});
}
if (match.length > 1 && match.index < str.length) {
Array.prototype.push.apply(output, match.slice(1));
}
lastLength = match[0].length;
lastLastIndex = lastIndex;
if (output.length >= limit) {
break;
}
}
if (separator.lastIndex === match.index) {
separator.lastIndex++; // Avoid an infinite loop
}
}
if (lastLastIndex === str.length) {
if (lastLength || !separator.test("")) {
output.push("");
}
} else {
output.push(str.slice(lastLastIndex));
}
return output.length > limit ? output.slice(0, limit) : output;
};
// For convenience
String.prototype.split = function (separator, limit) {
return self(this, separator, limit);
};
return self;
})();
// -----------------------------------------------------------------------
/*
function decodeHTML(str) {
if (typeof str === 'string') {
str = str.replace(/&/g, '&');
str = str.replace(/</g, '<').replace(/>/g, '>');
str = str.replace(/	/g, '\t');
str = str.replace(/<br\/?>/g, '\n').replace(/ /g, ' ');
return str;
} else {
return '';
}
}
*/
//split string to array of strings with the same length
function str_parts(str, length) {
var result = [];
var len = str.length;
if (len < length) {
return [str];
}
for (var i = 0; i < len; i += length) {
result.push(str.substring(i, i + length));
}
return result;
}
// -----------------------------------------------------------------------
// CYCLE DATA STRUCTURE
// -----------------------------------------------------------------------
function Cycle(init) {
var data = init ? [init] : [];
var pos = 0;
$.extend(this, {
get: function() {
return data;
},
rotate: function() {
if (data.length === 1) {
return data[0];
} else {
if (pos === data.length - 1) {
pos = 0;
} else {
++pos;
}
return data[pos];
}
},
length: function() {
return data.length;
},
set: function(item) {
for (var i = data.length; i--;) {
if (data[i] === item) {
pos = i;
return;
}
}
this.append(item);
},
front: function() {
return data[pos];
},
append: function(item) {
data.push(item);
}
});
}
// -----------------------------------------------------------------------
// :: STACK DATA STRUCTURE
// -----------------------------------------------------------------------
function Stack(init) {
var data = init ? [init] : [];
$.extend(this, {
size: function() {
return data.length;
},
pop: function() {
if (data.length === 0) {
return null;
} else {
var value = data[data.length - 1];
data = data.slice(0, data.length - 1);
return value;
}
},
push: function(value) {
data = data.concat([value]);
return value;
},
top: function() {
return data.length > 0 ? data[data.length - 1] : null;
}
});
}
// serialize object myself (biwascheme or prototype library do something
// wiked with JSON serialization for Arrays)
$.json_stringify = function(object, level) {
var result = '', i;
level = level === undefined ? 1 : level;
var type = typeof object;
switch (type) {
case 'function':
result += object;
break;
case 'boolean':
result += object ? 'true' : 'false';
break;
case 'object':
if (object === null) {
result += 'null';
} else if (object instanceof Array) {
result += '[';
var len = object.length;
for (i = 0; i < len - 1; ++i) {
result += $.json_stringify(object[i], level + 1);
}
result += $.json_stringify(object[len - 1], level + 1) + ']';
} else {
result += '{';
for (var property in object) {
if (object.hasOwnProperty(property)) {
result += '"' + property + '":' +
$.json_stringify(object[property], level + 1);
}
}
result += '}';
}
break;
case 'string':
var str = object;
var repl = {
'\\\\': '\\\\',
'"': '\\"',
'/': '\\/',
'\\n': '\\n',
'\\r': '\\r',
'\\t': '\\t'};
for (i in repl) {
if (repl.hasOwnProperty(i)) {
str = str.replace(new RegExp(i, 'g'), repl[i]);
}
}
result += '"' + str + '"';
break;
case 'number':
result += String(object);
break;
}
result += (level > 1 ? ',' : '');
// quick hacks below
if (level === 1) {
// fix last comma
result = result.replace(/,([\]}])/g, '$1');
}
// fix comma before array or object
return result.replace(/([\[{]),/g, '$1');
};
// -----------------------------------------------------------------------
// :: HISTORY CLASS
// -----------------------------------------------------------------------
function History(name, size) {
var enabled = true;
if (typeof name === 'string' && name !== '') {
name += '_';
}
var data = $.Storage.get(name + 'commands');
data = data ? new Function('return ' + data + ';')() : [];
var pos = data.length-1;
$.extend(this, {
append: function(item) {
if (enabled) {
if (data[data.length-1] !== item) {
data.push(item);
pos = data.length-1;
if (size && data.length > size) {
data = data.slice(-size);
}
$.Storage.set(name + 'commands', $.json_stringify(data));
}
}
},
data: function() {
return data;
},
next: function() {
if (pos < data.length-1) {
++pos;
}
if (pos !== -1) {
return data[pos];
}
},
reset: function() {
pos = data.length-1;
},
last: function() {
return data[length-1];
},
end: function() {
return pos === data.length-1;
},
position: function() {
return pos;
},
previous: function() {
var old = pos;
if (pos > 0) {
--pos;
}
if (old !== -1) {
return data[old];
}
},
clear: function() {
data = [];
$.Storage.remove(name + 'commands');
},
enable: function() {
enabled = true;
},
purge: function() {
$.Storage.remove(name + 'commands');
},
disable: function() {
enabled = false;
}
});
}
// -----------------------------------------------------------------------
// :: COMMAND LINE PLUGIN
// -----------------------------------------------------------------------
$.fn.cmd = function(options) {
var self = this;
var maybe_data = self.data('cmd');
if (maybe_data) {
return maybe_data;
}
self.addClass('cmd');
self.append('<span class="prompt"></span><span></span>' +
'<span class="cursor"> </span><span></span>');
var clip = $('<textarea/>').addClass('clipboard').appendTo(self);
if (options.width) {
self.width(options.width);
}
var num_chars; // calculates by draw_prompt
var prompt_len;
var reverse_search = false;
var reverse_search_string = '';
var reverse_search_position = null;
var backup_prompt;
var mask = options.mask || false;
var command = '';
var position = 0;
var prompt;
var enabled = options.enabled;
var historySize = options.historySize || 60;
var name, history;
var cursor = self.find('.cursor');
function blink(i) {
cursor.toggleClass('inverted');
}
function draw_reverse_prompt() {
prompt = "(reverse-i-search)`" + reverse_search_string + "': ";
draw_prompt();
}
function clear_reverse_state() {
prompt = backup_prompt;
reverse_search = false;
reverse_search_position = null;
reverse_search_string = '';
}
// if next is not defined or false it search for first item from the end
// if true it search for next item
function reverse_history_search(next) {
var history_data = history.data();
var regex;
var len = history_data.length;
if (next && reverse_search_position > 0) {
len -= reverse_search_position;
}
if (reverse_search_string.length > 0) {
for (var j=reverse_search_string.length; j>0; j--) {
regex = new RegExp('^' + reverse_search_string.substring(0, j));
for (var i=len; i--;) {
if (regex.test(history_data[i])) {
reverse_search_position = history_data.length - i;
position = 0;
self.set(history_data[i], true);
redraw();
if (reverse_search_string.length !== j) {
reverse_search_string = reverse_search_string.substring(0, j);
draw_reverse_prompt();
}
return;
}
}
}
}
}
function change_num_chars() {
var W = self.width();
var w = cursor.innerWidth();
num_chars = Math.floor(W / w);
}
function str_repeat(str, n) {
var result = '';
for (var i = n; i--;) {
result += str;
}
return result;
}
function get_splited_command_line(string) {
var first = string.substring(0, num_chars - prompt_len);
var rest = string.substring(num_chars - prompt_len);
return [first].concat(str_parts(rest, num_chars));
}
var redraw = (function(self) {
var before = cursor.prev();
var after = cursor.next();
function draw_cursor_line(string, position) {
var len = string.length;
if (position === len) {
before.html($.terminal.encode(string));
cursor.html(' ');
after.html('');
} else if (position === 0) {
before.html('');
//fix for tilda in IE
cursor.html($.terminal.encode(string.slice(0, 1)));
//cursor.html($.terminal.encode(string[0]));
after.html($.terminal.encode(string.slice(1)));
} else {
var before_str = $.terminal.encode(string.slice(0, position));
before.html(before_str);
//fix for tilda in IE
var c = string.slice(position, position + 1);
//cursor.html(string[position]));
cursor.html(c === ' ' ? ' ' : $.terminal.encode(c));
if (position === string.length - 1) {
after.html('');
} else {
after.html($.terminal.encode(string.slice(position + 1)));
}
}
}
function div(string) {
return '<div>' + $.terminal.encode(string) + '</div>';
}
function lines_after(lines) {
var last_ins = after;
$.each(lines, function(i, line) {
last_ins = $(div(line)).insertAfter(last_ins).
addClass('clear');
});
}
function lines_before(lines) {
$.each(lines, function(i, line) {
before.before(div(line));
});
}
var count = 0;
return function() {
var string = mask ? command.replace(/./g, '*') : command;
var i, first_len;
self.find('div').remove();
before.html('');
// long line
if (string.length > num_chars - prompt_len - 1 ||
string.match(/\n/)) {
var array;
var tabs = string.match(/\t/g);
var tabs_rm = tabs ? tabs.length * 3 : 0;
//quick tabulation hack
if (tabs) {
string = string.replace(/\t/g, '\x00\x00\x00\x00');
}
// command contain new line characters
if (string.match(/\n/)) {
var tmp = string.split("\n");
first_len = num_chars - prompt_len - 1;
// empty character after each line
for (i=0; i<tmp.length-1; ++i) {
tmp[i] += ' ';
}
// split first line
if (tmp[0].length > first_len) {
array = [tmp[0].substring(0, first_len)];
array = array.concat(str_parts(tmp[0].substring(first_len), num_chars));
} else {
array = [tmp[0]];
}
// process rest of the lines
for (i=1; i<tmp.length; ++i) {
if (tmp[i].length > num_chars) {
array = array.concat(str_parts(tmp[i], num_chars));
} else {
array.push(tmp[i]);
}
}
} else {
array = get_splited_command_line(string);
}
if (tabs) {
array = $.map(array, function(line) {
return line.replace(/\x00\x00\x00\x00/g, '\t');
});
}
first_len = array[0].length;
//cursor in first line
if (first_len === 0 && array.length === 1) {
// skip empty line
} else if (position < first_len) {
draw_cursor_line(array[0], position);
lines_after(array.slice(1));
} else if (position === first_len) {
before.before(div(array[0]));
draw_cursor_line(array[1], 0);
lines_after(array.slice(2));
} else {
var num_lines = array.length;
var offset = 0;
if (position < first_len) {
draw_cursor_line(array[0], position);
lines_after(array.slice(1));
} else if (position === first_len) {
before.before(div(array[0]));
draw_cursor_line(array[1], 0);
lines_after(array.slice(2));
} else {
var last = array.slice(-1)[0];
var from_last = string.length - position;
var last_len = last.length;
var pos = 0;
if (from_last <= last_len) {
lines_before(array.slice(0, -1));
pos = last_len === from_last ? 0 : last_len-from_last;
draw_cursor_line(last, pos+tabs_rm);
} else {
// in the middle
if (num_lines === 3) {
before.before('<div>' + $.terminal.encode(array[0]) +
'</div>');
draw_cursor_line(array[1], position-first_len-1);
after.after('<div class="clear">' +
$.terminal.encode(array[2]) +
'</div>');
} else {
// more lines, cursor in the middle
var line_index;
var current;
pos = position;
for (i=0; i<array.length; ++i) {
var current_len = array[i].length;
if (pos > current_len) {
pos -= current_len;
} else {
break;
}
}
current = array[i];
line_index = i;
// cursor on first character in line
if (pos === current.length) {
pos = 0;
current = array[++line_index];
}
draw_cursor_line(current, pos);
lines_before(array.slice(0, line_index));
lines_after(array.slice(line_index+1));
}
}
}
}
} else {
if (string === '') {
before.html('');
cursor.html(' ');
after.html('');
} else {
draw_cursor_line(string, position);
}
}
};
})(self);
var last_command;
var draw_prompt = (function() {
var prompt_node = self.find('.prompt');
return function() {
if (typeof prompt === 'string') {
prompt_len = skipFormattingCount(prompt);
prompt_node.html($.terminal.format(prompt));
} else {
prompt(function(string) {
prompt_len = skipFormattingCount(string);
prompt_node.html($.terminal.format(string));
});
}
//change_num_chars();
};
})();
// paste content to terminal using hidden textarea
function paste() {
clip.focus();
//wait until Browser insert text to textarea
self.oneTime(1, function() {
self.insert(clip.val());
clip.blur().val('');
});
}
//var prevent_keypress = false;
function keydown_event(e) {
if (typeof options.keydown == 'function') {
var result = options.keydown(e);
if (result !== undefined) {
//prevent_keypress = true;
return result;
}
}
if (enabled) {
var pos, len, result;
// arrows / Home / End / ENTER
if (reverse_search && (e.which === 35 || e.which === 36 ||
e.which === 37 || e.which === 38 ||
e.which === 39 || e.which === 40 ||
e.which === 13 || e.which === 27)) {
clear_reverse_state();
draw_prompt();
if (e.which === 27) { // ESC
command = '';
}
redraw();
// finish reverse search and execute normal event handler
keydown_event.call(this, e);
} else if (e.altKey) {
// Chrome on Windows set ctrlKey and altKey for alt
// need to check for alt first
//if (e.which === 18) { // press ALT
if (e.which === 68) { //ALT+D
self.set(command.slice(0, position) +
command.slice(position).replace(/[^ ]+ |[^ ]+$/, ''),
true);
// chrome jump to address bar
return false;
}
return true;
} else if (e.keyCode === 13) { //enter
if ((history && command) &&
((options.historyFilter &&
options.historyFilter(command)) ||
!options.historyFilter)) {
history.append(command);
}
var tmp = command;
history.reset();
self.set('');
if (options.commands) {
options.commands(tmp);
}
if (typeof prompt === 'function') {
draw_prompt();
}
} else if (e.which === 8) { //backspace
if (reverse_search) {
reverse_search_string = reverse_search_string.slice(0, -1);
draw_reverse_prompt();
} else {
if (command !== '' && position > 0) {
command = command.slice(0, position - 1) +
command.slice(position, command.length);
--position;
redraw();
}
}
} else if (e.which === 9 && !(e.ctrlKey || e.altKey)) { // TAB
self.insert('\t');
} else if (e.which === 46) {
//DELETE
if (command !== '' && position < command.length) {
command = command.slice(0, position) +
command.slice(position + 1, command.length);
redraw();
}
return true;
} else if (history && e.which === 38 ||
(e.which === 80 && e.ctrlKey)) {
//UP ARROW or CTRL+P
if (history.end()) {
last_command = command;
}
self.set(history.previous());
} else if (history && e.which === 40 ||
(e.which === 78 && e.ctrlKey)) {
//DOWN ARROW or CTRL+N
self.set(history.end() ? last_command : history.next());
} else if (e.which === 37 ||
(e.which === 66 && e.ctrlKey)) {
//CTRL+LEFT ARROW or CTRL+B
if (e.ctrlKey && e.which !== 66) {
len = position - 1;
pos = 0;
if (command[len] === ' ') {
--len;
}
for (var i = len; i > 0; --i) {
if (command[i] === ' ' && command[i+1] !== ' ') {
pos = i + 1;
break;
} else if (command[i] === '\n' && command[i+1] !== '\n') {
pos = i;
break;
}
}
self.position(pos);
} else {
//LEFT ARROW or CTRL+B
if (position > 0) {
--position;
redraw();
}
}
} else if (e.which === 82 && e.ctrlKey) { // CTRL+R
if (reverse_search) {
reverse_history_search(true);
} else {
backup_prompt = prompt;
draw_reverse_prompt();
last_command = command;
command = '';
redraw();
reverse_search = true;
}
} else if (e.which == 71 && e.ctrlKey) { // CTRL+G
if (reverse_search) {
prompt = backup_prompt;
draw_prompt();
command = last_command;
redraw();
reverse_search = false;
}
} else if (e.which === 39 ||
(e.which === 70 && e.ctrlKey)) {
//RIGHT ARROW OR CTRL+F
if (e.ctrlKey && e.which !== 70) {
// jump to beginig or end of the word
if (command[position] === ' ') {
++position;
}
var match = command.slice(position).match(/\S[\n\s]{2,}|[\n\s]+\S?/);
if (!match || match[0].match(/^\s+$/)) {
position = command.length;
} else {
if (match[0][0] !== ' ') {
position += match.index + 1;
} else {
position += match.index + match[0].length - 1;
if (match[0][match[0].length-1] !== ' ') {
--position;
}
}
}
redraw();
} else {
if (position < command.length) {
++position;
redraw();
}
}
} else if (e.which === 123) { //F12 - Allow Firebug
return true;
} else if (e.which === 36) { //HOME
self.position(0);
} else if (e.which === 35) {
//END
self.position(command.length);
} else if (e.shiftKey && e.which == 45) { // Shift+Insert
paste();
return true;
} else if (e.ctrlKey || e.metaKey) {
if (e.which === 192) { // CMD+` switch browser window on Mac
return true;
}
if (e.metaKey) {
if(e.which === 82) // CMD+r page reload in Chrome Mac
return true;
if(e.which === 76)
return true; // CMD+l jump into Ominbox on Chrome Mac
}
if (e.shiftKey) { // CTRL+SHIFT+??
if (e.which === 84) {
//CTRL+SHIFT+T open closed tab
return true;
}
//} else if (e.altKey) { //ALT+CTRL+??
} else {
if (e.which === 87) { // CTRL+W
if (command !== '') {
var first = command.slice(0, position);
var last = command.slice(position+1);
var m = first.match(/([^ ]+ *$)/);
position = first.length-m[0].length;
command = first.slice(0, position) + last;
redraw();
}
return false;
} else if (e.which === 72) { // CTRL+H
if (command !== '' && position > 0) {
command = command.slice(0, --position);
if (position < command.length-1) {
command += command.slice(position);
}
redraw();
}
return false;
//NOTE: in opera charCode is undefined
} else if (e.which === 65) {
//CTRL+A
self.position(0);
} else if (e.which === 69) {
//CTRL+E
self.position(command.length);
} else if (e.which === 88 || e.which === 67 || e.which === 84) {
//CTRL+X CTRL+C CTRL+W CTRL+T
return true;
} else if (e.which === 86) {
//CTRL+V
paste();
return true;
} else if (e.which === 75) {
//CTRL+K
if (position === 0) {
self.set('');
} else if (position !== command.length) {
self.set(command.slice(0, position));
}
} else if (e.which === 85) { // CTRL+U
self.set(command.slice(position, command.length));
self.position(0);
} else if (e.which === 17) { //CTRL+TAB switch tab
return false;
}
}
} else {
return true;
}
return false;
} /*else {
if ((e.altKey && e.which === 68) ||
(e.ctrlKey &&
$.inArray(e.which, [65, 66, 68, 69, 80, 78, 70]) > -1) ||
// 68 === D
[35, 36, 37, 38, 39, 40].has(e.which)) {
return false;
}
} */
}
var history_list = [];
$.extend(self, {
name: function(string) {
if (string !== undefined) {
name = string;
history = new History(string, historySize);
history_list.push(history);
return self;
} else {
return name;
}
},
purge: function() {
for (var i=history_list.length; i--;) {
history_list[i].purge();
}
history_list = [];
return self;
},
history: function() {
return history;
},
set: function(string, stay) {
if (string !== undefined) {
command = string;
if (!stay) {
position = command.length;
}
redraw();
if (typeof options.onCommandChange === 'function') {
options.onCommandChange(command);
}
}
return self;
},
insert: function(string, stay) {
if (position === command.length) {
command += string;
} else if (position === 0) {
command = string + command;
} else {
command = command.slice(0, position) +
string + command.slice(position);
}
if (!stay) {
position += string.length;
}
redraw();
if (typeof options.onCommandChange === 'function') {
options.onCommandChange(command);
}
return self;
},
get: function() {
return command;
},
commands: function(commands) {
if (commands) {
options.commands = commands;
return self;
} else {
return commands;
}
},
destroy: function() {
$(document.documentElement || window).unbind('.cmd');
self.stopTime('blink', blink);
self.find('.cursor').next().remove().end().prev().remove().end().remove();
self.find('.prompt, .clipboard').remove();
self.removeClass('cmd').removeData('cmd');
return self;
},
prompt: function(user_prompt) {
if (user_prompt === undefined) {
return prompt;
} else {
if (typeof user_prompt === 'string' ||
typeof user_prompt === 'function') {
prompt = user_prompt;
} else {
throw 'prompt must be a function or string';
}
draw_prompt();
// we could check if command is longer then numchars-new prompt
redraw();
return self;
}
},
position: function(n) {
if (typeof n === 'number') {
position = n < 0 ? 0 : n > command.length ? command.length : n;
redraw();
return self;
} else {
return position;
}
},
visible: (function() {
var visible = self.visible;
return function() {
visible.apply(self, []);
redraw();
draw_prompt();
};
})(),
show: (function() {
var show = self.show;
return function() {
show.apply(self, []);
redraw();
draw_prompt();
};
})(),
resize: function(num) {
if (num) {
num_chars = num;
} else {
change_num_chars();
}
redraw();
return self;
},
enable: function() {
if (!enabled) {
cursor.addClass('inverted');
self.everyTime(500, 'blink'