widgetJavascriptConsole.js
Summary
No overview generated for 'widgetJavascriptConsole.js'
deriveNewWidget("JavascriptConsole", Widget);
JavascriptConsole.prototype.content = {
tagName: "div", className: "Console", children: [
{tagName: "h3", name: "features", children: [
"JavaScript Shell (XBLinJS), based on ",
{tagName: "a",
href: "http://www.squarefree.com/shell/shell.html",
children: ["JavaScript Shell 1.1"]}
]},
{tagName: "div", className: "ConsoleShellCommands", name: "shellComDiv"},
{tagName: "div", className: "ConsoleOutput", name: "output"},
{widgetType: "DOMWrap", domNode: {tagName: "input",
className: "ConsoleInputBox"},
name: "input"}
]
}
JavascriptConsole.prototype.defaults = {
keepfocus: false,
clear_input: true,
tooManyMatches: null
}
JavascriptConsole.prototype.initDOM = function (atts) {
Widget.prototype.initDOM.apply(this, arguments);
this.historyWidget = new HistoryAddin({target: this.input});
this.shellFuncs = {};
this.scope = {};
this.answer = {ans: undefined};
this.helpMessages = {};
var commandCollections = [];
var commands = {};
for (var name in this) {
if (name.substr(0, 12) == "shellCommand") {
commandCollections.push(this[name]);
}
if (name.substr(0, 6) == "shell_") {
commands[name.substr(6)] = this[name];
}
}
commandCollections.push(commands);
var commands = {};
for (var idx in commandCollections) {
var commandList = commandCollections[idx];
for (var commandName in commandList) {
commands[commandName] = commandList[commandName];
}
}
var commandKeyList = getKeys(commands);
commandKeyList.sort();
var shownCommand = false;
for (var idx in commandKeyList) {
var commandName = commandKeyList[idx];
var command = commands[commandName];
if (!command) continue;
if (typeof command == FUNCTION_TYPE) {
command = {func: command};
}
if (command.hide) continue;
this.shellFuncs[commandName] = closureMaker({jc: this,
shellFunc: command.func},
function (args, params) {
args = arrayCopy(args);
args.unshift(params.jc);
params.shellFunc.apply(params.shellFunc, args);
});
var func = command.func;
var accessKey = command.accessKey;
var defaultExe = command.defaultExe;
if (accessKey && !defaultExe) {
defaultExe = commandName + "(ans)";
}
var helpText = command.helpText;
var displayedName = command.displayedName;
if (!displayedName) displayedName = commandName + "(ans)";
var node = this.create("span", {className: "ConsoleShellCommand"});
var text;
if (defaultExe) {
text = this.create("a", {href: "javascript:WidgetGlobals.Widgets[" +
this.widgetId + "].go(unescape('"
+ escape(defaultExe) + "'))",
accesskey: accessKey}, displayedName);
} else {
text = textNode(displayedName);
}
node.appendChild(text);
if (helpText) {
var help = this.create("a", {href: "javascript: void(0)",
className: "ConsoleHelp"},
"?");
this.helpMessages[commandName] = helpText;
if (typeof helpText == FUNCTION_TYPE) {
help.onclick = closureMaker({jc: this,
helpFunc: helpText},
function (args, params) {
args = arrayCopy(args);
args.unshift(params.jc);
params.helpFunc.apply(params.helpText, args);
params.jc.refocus();
});
} else {
help.onclick = closureMaker({jc: this, key: commandName},
function (args, params) {
with (params) {
jc.printWithRunin("Help for " + key, jc.helpMessages[key],
"ConsoleHelpDisplay");
jc.refocus();
}
});
}
node.appendChild(help);
}
if (accessKey) {
var keyHelp = this.create("span", {className: "ConsoleAccessKey"},
" (ALT-" + accessKey.toUpperCase() + ")");
node.appendChild(keyHelp);
}
if (!shownCommand) {
this.shellComDiv.appendChild(textNode("Shell Commands: "));
shownCommand = true;
} else {
this.shellComDiv.appendChild(textNode(", "));
}
this.shellComDiv.appendChild(node);
if (this.isIE) {
node.outerHTML = node.outerHTML;
}
}
}
JavascriptConsole.prototype.initWidget = function (atts) {
Widget.prototype.initWidget.apply(this, arguments);
var js = this;
this.getGlobals().onloadFunctions.push(function () {js.refocus()});
}
JavascriptConsole.prototype.println = function (s, type) {
if (s = String(s)) {
var newDiv = this.create("div", {className: type}, s);
this.output.appendChild(newDiv);
return newDiv;
}
}
JavascriptConsole.prototype.printWithRunin = function (h, s, type) {
var div = this.println(s, type);
var head = this.create("strong", {}, h + ": ");
div.insertBefore(head, div.firstChild);
}
JavascriptConsole.prototype.printQuestion = function (q) {
this.println(q, "ConsoleInput");
}
JavascriptConsole.prototype.printAnswer = function (a) {
if (a !== undefined) {
this.println(a, "ConsoleNormalOutput");
if (this.shellCommands) {
this.answer.ans = a;
}
}
}
JavascriptConsole.prototype.printError = function (er) {
if (er.name) {
this.println(er.name + ": " + er.message, "ConsoleError");
} else {
this.println(er, "ConsoleError");
}
}
JavascriptConsole.prototype.set_keepfocus = function (val) {
val = !!val;
this.keepfocus = val;
var jc = this;
if (val && !this.registeredKeepFocusListener &&
window.getSelection && document.addEventListener) {
var listener = function (e) {
if (!jc.keepfocus) return;
var targetNode = e.target;
while (!targetNode.tagName)
targetNode = targetNode.parentNode;
var tagName = targetNode.tagName.toUpperCase();
if (tagName != "A" && tagName != "INPUT" &&
!String(window.getSelection()))
jc.refocus();
}
document.addEventListener("click", listener, false);
this.registeredKeepFocusListener = true;
}
}
JavascriptConsole.prototype.onkeydown_input = function (event) {
var jc = this;
if (event.keyCode == 9) {
this.tabComplete();
setTimeout(function () {jc.refocus();}, 0);
return false;
}
}
JavascriptConsole.prototype.onkeypress_input = function (event) {
if (event.keyCode == 13) {
this.go();
}
}
JavascriptConsole.prototype.go = function (_s_) {
var _question_ = _s_ ? _s_ : this.input.getAttribute("value");
if (_question_.toLowerCase() == "help") {
_question_ = "help()";
}
if (_question_ == "") return;
this.historyWidget.addToHistory(_question_);
var answer;
this.printQuestion(_question_);
try {
with (this.shellFuncs)
with (this.scope)
with (this.answer)
this.printAnswer(eval(_question_));
} catch (e) {
this.printError(e);
}
if (this.clear_input) {
setAttributeOn(this.input, "value", "");
}
this.refocus();
}
JavascriptConsole.prototype.tabComplete = function () {
var jc = this;
function findbeginning(s, from, stopAtDot)
{
function equalButNotEscaped(s,i,q)
{
if(s.charAt(i) != q) // not equal go no further
return false;
if(i==0) // beginning of string
return true;
if(s.charAt(i-1) == '\\') // escaped?
return false;
return true;
}
var nparens = 0;
var i;
for(i=from; i>=0; i--)
{
if(s.charAt(i) == ' ')
break;
if(stopAtDot && s.charAt(i) == '.')
break;
if(s.charAt(i) == ')')
nparens++;
else if(s.charAt(i) == '(')
nparens--;
if(nparens < 0)
break;
if(s.charAt(i) == '\'' || s.charAt(i) == '\"') //"
{
var quot = s.charAt(i);
i--;
while(i >= 0 && !equalButNotEscaped(s,i,quot)) {
i--;
}
}
}
return i;
}
function getcaretpos(inp)
{
if(inp.selectionEnd)
return inp.selectionEnd;
if(inp.createTextRange)
{
var docrange = this.window.document.selection.createRange();
var inprange = inp.createTextRange();
inprange.setEndPoint('EndToStart', docrange);
return inprange.text.length;
}
return inp.value.length; // sucks, punt
}
function setselectionto(inp,pos)
{
if(inp.selectionStart) {
inp.selectionStart = inp.selectionEnd = pos;
}
else if(inp.createTextRange) {
var docrange = this.window.document.selection.createRange();
var inprange = inp.createTextRange();
inprange.move('character',pos);
inprange.select();
}
else { // err...
}
}
var caret = getcaretpos(DOM(this.input));
if(caret) {
var dotpos, spacepos, complete, obj;
dotpos = findbeginning(this.input.getAttribute("value"), caret-1, true);
if(dotpos == -1 || this.input.getAttribute("value").charAt(dotpos) != '.') {
dotpos = caret;
}
spacepos = findbeginning(this.input.getAttribute("value"), dotpos-1, false);
if(spacepos == dotpos || spacepos+1 == dotpos || dotpos == caret)
{
if(this.input.getAttribute("value").charAt(dotpos) == '(' ||
(this.input.getAttribute("value").charAt(spacepos) == '(' && (spacepos+1) == dotpos))
{
var fn,fname;
var from = (this.input.getAttribute("value").charAt(dotpos) == '(') ? dotpos : spacepos;
spacepos = findbeginning(this.input.getAttribute("value"), from-1, false);
fname = this.input.getAttribute("value").substr(spacepos+1,from-(spacepos+1));
try {
with(this.window)
fn = eval(fname);
}
catch(er) {
return;
}
if(fn == undefined) {
return;
}
if(fn instanceof Function)
{
if(!fn.toString().match(/function .+?\(\) +\{\n +\[native code\]\n\}/))
this.println(fn.toString().match(/function .+?\(.*?\)/), "ConsoleTabComplete");
}
return;
}
else
obj = this.window;
}
else
{
var objname = this.input.getAttribute("value").substr(spacepos+1,dotpos-(spacepos+1));
try {
with(this.window)
obj = eval(objname);
}
catch(er) {
this.printError(er);
return;
}
if(obj == undefined) {
return;
}
}
if(dotpos == caret)
{
if(spacepos+1 == dotpos || spacepos == dotpos)
{
return;
}
complete = this.input.getAttribute("value").substr(spacepos+1,dotpos-(spacepos+1));
}
else {
complete = this.input.getAttribute("value").substr(dotpos+1,caret-(dotpos+1));
}
var matches = [];
var bestmatch = null;
for(var a in obj)
{
if(a.substr(0,complete.length) == complete) {
matches.push(a);
if(bestmatch == null)
{
bestmatch = a;
}
else {
function min(a,b){ return ((a<b)?a:b); }
var i;
for(i=0; i< min(bestmatch.length, a.length); i++)
{
if(bestmatch.charAt(i) != a.charAt(i))
break;
}
bestmatch = bestmatch.substr(0,i);
}
}
}
bestmatch = (bestmatch || "");
var objAndComplete = (objname || obj) + "." + bestmatch;
if(matches.length > 1 && (this.tooManyMatches == objAndComplete || matches.length <= 10)) {
this.printWithRunin("Matches", matches.join(', '), "ConsoleTabComplete");
this.tooManyMatches = null;
}
else if(matches.length > 10)
{
this.println(matches.length + " matches. Press tab again to see them all", "ConsoleTabComplete");
this.tooManyMatches = objAndComplete;
}
else {
this.tooManyMatches = null;
}
if(bestmatch != "")
{
var sstart;
if(dotpos == caret) {
sstart = spacepos+1;
}
else {
sstart = dotpos+1;
}
setAttributeOn(this.input, "value",
getAttributeFrom(this.input, "value").substr(0, sstart)
+ bestmatch
+ getAttributeFrom(this.input, "value").substr(caret));
setselectionto(DOM(this.input),caret + (bestmatch.length - complete.length));
}
}
}
JavascriptConsole.prototype.refocus = function () {
DOM(this.input).blur();
DOM(this.input).focus();
}
JavascriptConsole.prototype.shellCommands = {
load: {
func: function load(jc, url)
{
var s;
s = jc.window.document.createElement("script");
s.type = "text/javascript";
s.src = url;
jc.window.document.getElementsByTagName("head")[0].appendChild(s);
jc.println("Loading " + url + "...", "message");
},
displayedName: "load(scriptURL)",
helpText: "Loads the script at scriptURL into the current window."
},
print: {
func: function print(jc, s) { jc.println(s, "print"); },
displayedName: "print(string)",
helpText: "Print the given string out, converting to string via new String()"
},
pr: {
func: function pr(jc, s) {
jc.shellCommands.print(s);
return s;
},
displayedName: "pr(string)",
helpText: "Print out the given string and also return it. This"+
" is useful in the middle of an expression: x = "+
" pr(2 + 2) * 5;"
},
props: {
func: function props(jc, e) {
var ns = ["Methods", "Fields", "Unreachables"];
var as = [[], [], []]; // array of (empty) arrays of arrays!
var protoLevels = 0;
for (var p = e; p; p = p.__proto__)
{
for (var i=0; i<ns.length; ++i)
as[i][protoLevels] = [];
++protoLevels;
}
for(var a in e)
{
var protoLevel = -1;
try
{
for (var p = e; p && (a in p); p = p.__proto__)
++protoLevel;
}
catch(er) { protoLevel = 0; } // "in" operator throws when param to props() is a string
var type = 1;
try
{
if ((typeof e[a]) == "function")
type = 0;
}
catch (er) { type = 2; }
as[type][protoLevel].push(a);
}
function times(s, n) { return n ? s + times(s, n-1) : ""; }
for (var j=0; j<protoLevels; ++j)
for (var i=0;i<ns.length;++i)
if (as[i][j].length)
jc.printWithRunin(ns[i] + times(" of prototype", j), as[i][j].join(", "), "ConsolePropList");
},
helpText: ("Display the methods and fields of an object, " +
"broken down by how they are inherited"),
displayedName: "props(object)",
accessKey: "p"
},
blink: {
func: function blink(jc, e) {
if (!e) throw("blink: e is null or undefined.");
e = DOM(e);
if (!e.TEXT_NODE) throw("blink: e must be a node.");
if (e.nodeType == e.TEXT_NODE) throw("blink: e must not be a text node");
if (e.documentElement) throw("blink: e must not be the document object");
function setOutline(e,o) { e.style.MozOutline = o; }
function focusIt(a) { a.focus(); }
if (e.ownerDocument)
setTimeout(focusIt, 0, e.ownerDocument.defaultView.top);
for(var i=1;i<7;++i)
setTimeout(setOutline, i*100, e, (i%2)?'3px solid red':'none');
setTimeout(focusIt, 800, window);
},
hide: (!navigator || !navigator.userAgent ||
(navigator.userAgent.toLowerCase().indexOf("gecko") == -1)),
helpText: "Blink the given non-text DOM node or Widget (try 'blink(this.text)')",
accessKey: "b",
displayedName: "blink(node)"
},
scope: {
func: function scope(jc, sc) {
if (!sc) sc = {};
jc.scope = sc;
jc.println("Scope is now " + sc + ". If a variable is not found in this scope, window will also be searched. New variables will still go on window.", "ConsoleMessage");
},
helpText: ("Changes the scope the Widget searches when executing"+
" commands. Example: The 'math' command works by "+
"executing 'scope(Math)', so all Math methods and "+
"constants are available without prefix."),
displayedName: "scope(object)"
},
math: {
func: function (jc) {
jc.shellFuncs.scope(Math);
jc.printWithRunin("Math constants", "E, LN2, LN10, LOG2E, LOG10E, PI, SQRT1_2, SQRT2", "propList");
jc.printWithRunin("Math methods", "abs, acos, asin, atan, atan2, ceil, cos, exp, floor, log, max, min, pow, random, round, sin, sqrt, tan", "propList");
},
helpText: "Turns this console into a simple calculator by "+
"changing the scope to the Math object",
accessKey: "m",
defaultExe: "math()",
displayedName: "math()"
},
help: {
func: function (jc) {
jc.println("This is a Javascript Console, implemented in XBLinJS,"+
" as derived from Javascript Shell 1.1.",
"ConsoleMessage");
jc.println("Use it as you would Javascript, which will not be "+
"explained here.", "ConsoleMessage");
jc.println("Note that the last result from the command line "+
"is available as 'ans', and that the 'Features' "+
"are additional functions provided for your use; "+
"see documentation for JavascriptConsoleWidget " +
"for information about customization.",
"ConsoleMessage");
jc.println("Use history with up/down arrows, use TAB for "+
"completion.", "ConsoleMessage");
},
accessKey: "h",
displayedName: "help",
defaultExe: "help()"
},
toggleKeepFocus: {
func: function (jc) {
jc.set("keepfocus", !jc.get("keepfocus"));
var focus = jc.get("keepfocus");
if (focus) {
jc.println("Console will now try to hold onto the focus.");
} else {
jc.println("Console will not hold focus.");
}
},
hide: !(window.getSelection && document.addEventListener),
displayedName: "toggleKeepFocus()",
accessKey: "f",
defaultExe: "toggleKeepFocus()",
helpText: function (jc) {
var focused = jc.get("keepfocus");
var help = ("Toggles whether the widget tries to retain the focus,"+
" even when other parts of the document are clicked."+
" Some people like this. Currently, this setting is ")
help += focused ? "on." : "off.";
jc.printWithRunin("Help for toggleKeepFocus", help, "ConsoleHelpDisplay");
}
},
clear: {
func: function (jc) {
emptyNode(jc.output);
},
displayedName: "clear()",
accessKey: "c",
defaultExe: "clear()",
helpText: "Clears the console's output history."
}
};
deriveNewWidget("HistoryAddin", Widget);
HistoryAddin.prototype.content = false;
HistoryAddin.prototype.initWidget = function (atts) {
this.processAtt(atts, "target");
this.histList = [];
this.histPos = undefined;
this.tempHist = undefined;
this.target.registerEventHandler("keydown", "handle_onkeydown",
false, this);
}
HistoryAddin.prototype.addToHistory = function (s) {
if (!(s == "" || !s || s == this.histList[this.histList.length - 1]))
this.histList.push(s);
this.tempHist = undefined;
this.reset();
}
HistoryAddin.prototype.reset = function () {
this.histPos = undefined;
}
HistoryAddin.prototype.handle_onkeydown = function (event) {
var keyCode = event.keyCode;
var histList = this.histList;
var historyAddin = this;
if (keyCode == 38 || keyCode == 57373) {
var setTo;
if (this.histPos == undefined) {
this.histPos = this.histList.length - 1;
setTo = this.histList[this.histPos];
this.tempHist = getAttributeFrom(historyAddin.target, "value");
} else if (this.histPos > 0) {
this.histPos--;
setTo = this.histList[this.histPos];
} else {
return false;
}
setTimeout(function () {
setAttributeOn(historyAddin.target, "value", "");
setAttributeOn(historyAddin.target, "value", setTo);
}, 0);
return false;
}
if (keyCode == 40 || keyCode == 57373) {
if (this.histPos == undefined) {
return false; // eat it, but do nothing
} else if (this.histPos == this.histList.length - 1) {
this.histPos = undefined;
setAttributeOn(this.target, "value", this.tempHist);
} else {
this.histPos++;
setAttributeOn(this.target, "value", this.histList[this.histPos]);
}
return false;
}
return true;
}
Documentation generated by
JSDoc on Tue May 3 17:16:26 2005