JavaScript DHTML/Mochkit/InterpreterManager

Материал из Web эксперт
Перейти к: навигация, поиск

Interpreter - JavaScript Interactive Interpreter

  
<!--
MochiKit is dual-licensed software.  It is available under the terms of the
MIT License, or the Academic Free License version 2.1.  The full text of
each license is included below.
-->
<!-- Code revised from MochiKit demo code -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <title>Interpreter - JavaScript Interactive Interpreter</title>
        <style type="text/css">
h1 {
    font-size: 2em;
    color: #4B4545;
    text-align: center;
}
textarea.textbox {
    font-family: Monaco, "lucida console", Courier;
    border: 1px solid #CCCCCC;
    font-size: .60em; 
    padding: 2px 4px;
    margin-top: .3em;
}
input.textbox {
    font-family: Monaco, "lucida console", Courier;
    border: 1px solid #CCCCCC;
    font-size: .60em; 
    padding: 2px 4px;
    margin-top: .3em;
}
#interpreter_area {
    display: block;
    border: 1px solid #CCCCCC;
    padding: 2px 4px;
    margin-top: .3em;
    width: 600px;
    height: 300px;
    overflow: auto;
}
#interpreter_output {
    display: inline;
    font-family: Monaco, "lucida console", Courier;
    font-size: .60em;
}
#interpreter_output span {
    white-space: -moz-pre-wrap; /* Mozilla */
    white-space: -o-pre-wrap; /* Opera 7 */
    white-space: pre-wrap; /* CSS 2.1 */
    white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */
    word-wrap: break-word; /* IE */
    wrap-option: emergency; /* CSS 3 */
}
input.textbox:focus { background-color: #FFFEE3; }
.code { color: blue; }
.data { color: black; }
.error { color: red; }
.banner { color: green; }
.invisible { display: none; }
        
        </style>
        <script type="text/javascript" src="MochiKit-1.4.2/lib/MochiKit/MochiKit.js"></script>
        <script type="text/javascript">
/*
    Interpreter: JavaScript Interactive Interpreter
*/
InterpreterManager = function () {
    bindMethods(this);
};
InterpreterManager.prototype.initialize = function () {
    connect("interpreter_text", "onkeyup", this.keyUp);
    connect("interpreter_textarea", "onkeydown", this.areaKeyDown);
    connect("interpreter_form", "onsubmit", this.submit);
    getElement("interpreter_text").focus();
    this.banner();
    this.lines = [];
    this.history = [];
    this.currentHistory = "";
    this.historyPos = -1;
    this.blockingOn = null;
    if (typeof(this.doEval) == "undefined") {
        // detect broken eval, warn at some point if a namespace ever gets used
        this.doEval = function () {
            return eval(arguments[0]);
        }
    }
    window.help = this.help;
    this.help.NAME = "type help(func) for help on a MochiKit function";
};
InterpreterManager.prototype.banner = function () {
    var _ua = window.navigator.userAgent;
    var ua = _ua.replace(/^Mozilla\/.*?\(.*?\)\s*/, "");
    if (ua == "") {
        // MSIE
        ua = _ua.replace(/^Mozilla\/4\.0 \(compatible; MS(IE .*?);.*$/, "$1");
    }
    appendChildNodes("interpreter_output",
        SPAN({"class": "banner"},
            "MochiKit v" + MochiKit.Base.VERSION + " [" + ua + "]",
            BR(),
            "Type your expression in the input box below and press return, or see the notes below for more information.",
            BR()
        ),
        BR()
    );
};
InterpreterManager.prototype.submit = function (event) {
    if (this.blockingOn) {
        try {
            this.blockingOn.cancel();
        } catch (e) {
            this.showError(e);
        }
        this.blockingOn = null;
    }
    this.doSubmit();
    this.doScroll();
    event.stop();
};
InterpreterManager.prototype.help = function (fn) {
    if (fn && fn.NAME) {
        fn = fn.NAME;
    }
    if (typeof(fn) != "string" || fn.length == 0) {
        writeln("help(func) on any MochiKit function for help");
        return;
    }
    var comps = fn.split(".");
    var base = comps.splice(0, 2);
    var shortfn = comps.join(".");
    var url = "../../doc/html/" + base.join("/") + ".html";
    var d = doXHR(url, {mimeType: "text/xml"});
    d.addCallback(function (req) {
        var els = getElementsByTagAndClassName(
            "a", "mochidef", req.responseXML);
        var match = "#fn-" + shortfn.toLowerCase();
        for (var i = 0; i < els.length; i++) {
            var elem = els[i];
            var href = elem.href;
            var idx = href.indexOf("#");
            if (idx != -1 && href.substring(idx) == match) {
                writeln(A({href: url + match, target: "_blank"},
                    scrapeText(elem)));
                return;
            }
        }
        writeln("documentation for " + fn + " not found");
    });
    blockOn(d);
};

InterpreterManager.prototype.doScroll = function () {
    var p = getElement("interpreter_output").lastChild;
    if (typeof(p) == "undefined" || p == null) {
        return;
    }
    var area = getElement("interpreter_area");
    if (area.offsetHeight > area.scrollHeight) {
        area.scrollTop = 0;
    } else {
        area.scrollTop = area.scrollHeight;
    }
};
InterpreterManager.prototype.moveHistory = function (dir) {
    // totally bogus value
    if (dir == 0 || this.history.length == 0) {
        return;
    }
    var elem = getElement("interpreter_text");
    if (this.historyPos == -1) {
        this.currentHistory = elem.value;
        if (dir > 0) {
            return;
        }
        this.historyPos = this.history.length - 1;
        elem.value = this.history[this.historyPos];
        return;
    }
    if (this.historyPos == 0 && dir < 0) {
        return;
    }
    if (this.historyPos == this.history.length - 1 && dir > 0) {
        this.historyPos = -1;
        elem.value = this.currentHistory;
        return;
    } 
    this.historyPos += dir;
    elem.value = this.history[this.historyPos];
}
InterpreterManager.prototype.runMultipleLines = function (text) {
    var lines = rstrip(text).replace("\r\n", "\n").split(/\n/);
    appendChildNodes("interpreter_output",
        SPAN({"class": "code"}, ">>> ", izip(lines, imap(BR, cycle([null]))))
    );
    this.runCode(text);
}
InterpreterManager.prototype.areaKeyDown = function (e) {
    var mod = e.modifier();
    var hasMod = mod.alt || mod.ctrl || mod.meta;
    if (e.key().string == "KEY_ENTER" && hasMod) {
        var elem = getElement("interpreter_textarea");
        var text = elem.value;
        elem.value = "";
        this.runMultipleLines(text);
        e.stop();
    }
};
InterpreterManager.prototype.keyUp = function (e) {
    var key = e.key();
    // if any meta key is pressed, don"t handle the signal
    if (e.modifier().any) {
        return;
    }
    switch (key.string) {
        case "KEY_ARROW_UP": this.moveHistory(-1); break;
        case "KEY_ARROW_DOWN": this.moveHistory(1); break;
        default: return;
    }
    e.stop();
};
InterpreterManager.prototype.blockOn = function (d) {
    var node = SPAN({"class": "banner"}, "blocking on " + repr(d) + "...");
    this.blockingOn = d;
    appendChildNodes("interpreter_output", node);
    this.doScroll();
    d.addBoth(function (res) {
        swapDOM(node);
        this.blockingOn = null;
        if (res instanceof CancelledError) {
            window.writeln(SPAN({"class": "error"}, repr(d) + " cancelled!"));
            return undefined;
        }
        return res;
    });
    d.addCallbacks(this.showResult, this.showError);
};
InterpreterManager.prototype.showError = function (e) {
    if (typeof(e) != "object") {
        e = new Error(e);
    }
    appendChildNodes("interpreter_output",
        SPAN({"class": "error"}, "Error:"),
        TABLE({"class": "error"},
            THEAD({"class": "invisible"}, TD({"colspan": 2})),
            TFOOT({"class": "invisible"}, TD({"colspan": 2})),
            TBODY(null,
                map(function (kv) {
                    var v = kv[1];
                    if (typeof(v) == "function") {
                        return;
                    }
                    if (typeof(v) == "object") {
                        v = repr(v);
                    }
                    return TR(null,
                        TD({"class": "error"}, kv[0]),
                        TD({"class": "data"}, v)
                    );
                }, sorted(items(e)))
            )
        )
    );
    window.last_exc = e;
    this.doScroll();
};
EvalFunctions = {
    evalWith: function () {
        with (arguments[1] || window) { return eval(arguments[0]); };
    },
    evalCall: function () {
        return eval.call(arguments[1] || window, arguments[0]);
    },
    choose: function () {
        var ns = {__test__: this};
        var e;
        try {
            if (this.evalWith("return __test__", ns) === this) { 
                return this.evalWith;
            }
        } catch (e) {
            // pass
        }
        try {
            if (this.evalCall("return __test__", ns) === this) { 
                return this.evalCall;
            }
        } catch (e) {
            // pass
        }
        return undefined;
    }
};
        
InterpreterManager.prototype.doEval = EvalFunctions.choose();
InterpreterManager.prototype.doSubmit = function () {
    var elem = getElement("interpreter_text");
    var code = elem.value;
    elem.value = "";
    var isContinuation = false;
    if (code.length >= 2 && code.lastIndexOf("//") == code.length - 2) {
        isContinuation = true;
        code = code.substr(0, code.length - 2);
    }
    appendChildNodes("interpreter_output",
        SPAN({"class": "code"}, ">>> ", code),
        BR()
    );
    this.lines.push(code);
    this.history.push(code);
    this.historyPos = -1;
    this.currentHistory = "";
    if (isContinuation) {
        return;
    }
    var allCode = this.lines.join("\n");
    this.lines = [];
    this.runCode(allCode);
    return;
};
InterpreterManager.prototype.runCode = function (allCode) {
    var res;
    try {
        res = this.doEval(allCode);
    } catch (e) {
        // mozilla shows some keys more than once!
        this.showError(e);
        return;
    }
    this.showResult(res);
};
InterpreterManager.prototype.showResult = function (res) {
    if (typeof(res) != "undefined") {
        window._ = res;
    }
    if (typeof(res) != "undefined") {
        appendChildNodes("interpreter_output",
            SPAN({"class": "data"}, repr(res)),
            BR()
        );
        this.doScroll();
    }
};
window.writeln = function () {
    appendChildNodes("interpreter_output",
        SPAN({"class": "data"}, arguments),
        BR()
    );
    interpreterManager.doScroll();
};
window.clear = function () {
    replaceChildNodes("interpreter_output");
    getElement("interpreter_area").scrollTop = 0;
};
window.blockOn = function (d) {
    if (!(d instanceof Deferred)) {
        throw new TypeError(repr(d) + " is not a Deferred!");
    }
    interpreterManager.blockOn(d);
};
window.dir = function (o) {
    // Python muscle memory!
    return sorted(keys(o));
};
window.inspect = function (o) {
    window._ = o;
    if ((typeof(o) != "function" && typeof(o) != "object") || o == null) {
        window.writeln(repr(o));
        return;
    }
    var pairs = items(o);
    if (pairs.length == 0) {
        window.writeln(repr(o));
        return;
    }
    window.writeln(TABLE({"border": "1"},
        THEAD({"class": "invisible"}, TR(null, TD(), TD())),
        TFOOT({"class": "invisible"}, TR(null, TD(), TD())),
        TBODY(null,
            map(
                function (kv) {
                    var click = function () {
                        try {
                            window.inspect(kv[1]);
                        } catch (e) {
                            interpreterManager.showError(e);
                        }
                        return false;
                    }
                    return TR(null,
                        TD(null, A({href: "#", onclick: click}, kv[0])),
                        TD(null, repr(kv[1]))
                    );
                },
                pairs
            )
        )
    ));
};
    
interpreterManager = new InterpreterManager();
addLoadEvent(interpreterManager.initialize);
// rewrite the view-source links
addLoadEvent(function () {
    var elems = getElementsByTagAndClassName("A", "view-source");
    var page = "interpreter/";
    for (var i = 0; i < elems.length; i++) {
        var elem = elems[i];
        var href = elem.href.split(/\//).pop();
        elem.target = "_blank";
        elem.href = "../view-source/view-source.html#" + page + href;
    }
});
        
        </script>
    </head>
    <body>
        <h1>
            Interpreter - JavaScript Interactive Interpreter
        </h1>
        <div>
            <p>
                This demo is a JavaScript interpreter.  Type some code into
                the text input and press enter to see the results.  It uses
                <a href="http://mochikit.ru">MochiKit</a>"s
                <a href="../../doc/html/lib/MochiKit/DOM.html">MochiKit.DOM</a>
                to manipulate the display.  It also supports waiting for
                <a href="../../doc/html/lib/MochiKit/Async.html">MochiKit.Async</a>
                 Deferreds via <tt>blockOn(aDeferred)</tt>.
            </p>
        </div>
        <div>
            View Source: [
            <a href="index.html" class="view-source">index.html</a> |
            <a href="interpreter.js" class="view-source">interpreter.js</a>
            ]
        </div>
        <form id="interpreter_form" autocomplete="off">
            <div id="interpreter_area">
                <div id="interpreter_output"></div>
            </div>
            <div id="oneline">
                <input id="interpreter_text" name="input_text" type="text" class="textbox" size="100" />
            </div>
            <div id="multiline">
                <textarea id="interpreter_textarea" name="input_textarea" type="text" class="textbox" cols="97" rows="10"></textarea>
                <br />
            </div>
        </form>
        <div>
            Notes:
            <ul>
                <li>
                    To show the signature of a MochiKit function and link
                    to its documentation, type help(fn) on any MochiKit
                    function.
                </li>
                <li>
                    To write multi-line code snippets, use the lower text area
                    and press ctrl-enter or cmd-enter to submit.
                </li>
                <li>
                    <tt>function name() {}</tt> syntax might not end up in
                    window scope, so use <tt>name = function () {}</tt>
                    syntax instead
                </li>
                <li>
                    If you want to stuff something into the output window
                    other than the <tt>repr(...)</tt> of the expression
                    result, use the <tt>writeln(...)</tt> function.
                    It accepts anything that MochiKit.DOM does, so you can
                    even put styled stuff in there!
                </li>
                <li>
                    Use <tt>clear()</tt> to clear the interpreter window.
                </li>
                <li>
                    You can use <tt>blockOn(aDeferred)</tt> to wait on a
                    Deferred.  This expression must be used by itself, so
                    the value must be obtained from <tt>_</tt> or
                    <tt>last_exc</tt>.  Typing any expression will
                    cancel the Deferred.
                </li>
                <li>
                    Up and down arrow keys work as a rudimentary history
                </li>
                <li>
                    <tt>_</tt> is the value of the last expression
                    that was not <tt>undefined</tt>, <tt>last_exc</tt> is
                    the value of the last unhandled exception.
                </li>
            </ul>
        </div>
    </body>
</html>