git @ Cat's Eye Technologies yoob.js / master src / yoob / text-terminal.js
master

Tree @master (Download .tar.gz)

text-terminal.js @masterraw · history · blame

/*
 * This file is part of yoob.js version 0.11
 * Available from https://github.com/catseye/yoob.js/
 * This file is in the public domain.  See http://unlicense.org/ for details.
 */
if (window.yoob === undefined) yoob = {};

/*
 * A text-based-console simulation in Javascript.  It is, in actuality, a
 * facade for a yoob.Playfield and a yoob.Cursor.
 *
 * The yoob.TextTerminal has no concern for display; in MVC terms it is a
 * "model" and you will need to use a "view" such as yoob.PlayfieldCanvasView
 * or yoob.PlayfieldHTMLView to display it.
 *
 * Create a new yoob.TextTerminal object t, then call t.init({ columns: 80, rows: 25}),
 * then call t.write() to write text to the console.
 *
 * You can also call t.setColors() to set the text and background colors of
 * the next text to be written, between calls to t.write().  You can call
 * t.reset() to clear the simulated screen (to the selected backgroundColor.)
 *
 * Because the TextTerminal is backed by a Playfield, you can read characters and
 * colors at any position on the console.
 *
 * Note that this is not a completely "dumb" or "raw" console.  Some methods do
 * understand terminal control codes.  But you don't have to use them.
 */
yoob.TextTerminal = function() {
    
    // Inner Class
    var upper = this;
    var ConsoleCell = function() {
        this.init = function(c, tc, bc) {
            this.character = c;
            this.textColor = tc;
            this.backgroundColor = bc;
            return this;
        };
        
        this.draw = function(ctx, pfX, pfY, canvasX, canvasY,
                             cellWidth, cellHeight) {
            // only draw the background if the cursor is not here
            if (upper.cursor.x !== pfX || upper.cursor.y !== pfY) {
                ctx.fillStyle = this.backgroundColor;
                ctx.fillRect(canvasX, canvasY, cellWidth, cellHeight);
            }
            ctx.fillStyle = this.textColor;
            ctx.fillText(this.character, canvasX, canvasY);
        };
    };

    this.init = function(cfg) {
        var $this = this;

        cfg = cfg || {};
        this.rows = cfg.rows || 25;
        this.cols = cfg.columns || 80;
        this.textColor = cfg.textColor || "green";
        this.backgroundColor = cfg.backgroundColor || "black";
        this.cursor = new yoob.Cursor().init({ dx: 1 });
        this.defaultCell = new ConsoleCell().init(' ', 'green', 'black');

        this.pf = new yoob.Playfield().init({
            defaultValue: this.defaultCell,
            cursors: [this.cursor]
        });
        this.pf.getLowerX = function() { return 0; };
        this.pf.getLowerY = function() { return 0; };
        this.pf.getUpperX = function() { return $this.cols - 1; };
        this.pf.getUpperY = function() { return $this.rows - 1; };

        this.reset();
        return this;
    };

    this.createPlayfieldCanvasView = function(cfg) {
        cfg.playfield = this.pf;
        cfg.fixedPosition = true;
        var view = new yoob.PlayfieldCanvasView().init(cfg);
        return view;
    };

    this.getPlayfield = function() {
        return this.pf;
    };

    this.setColors = function(textColor, backgroundColor) {
        if (textColor !== undefined) {
            this.textColor = textColor;
        }
        if (backgroundColor !== undefined) {
            this.backgroundColor = backgroundColor;
        }
        return this;
    };

    this.getCharAt = function(x, y) {
        return this.pf.get(x, y).character;
    };

    this.getTextColorAt = function(x, y) {
        return this.pf.get(x, y).textColor;
    };

    this.getBackgroundColorAt = function(x, y) {
        return this.pf.get(x, y).backgroundColor;
    };

    this.setCharAt = function(x, y, c) {
        var cell = this.pf.get(x, y);
        if (cell === this.defaultCell) {
            cell = new ConsoleCell().init(' ', 'green', 'black');
            this.pf.put(x, y, cell);
        }
        cell.character = c;
        return this;
    };

    this.setTextColorAt = function(x, y, style) {
        var cell = this.pf.get(x, y);
        if (cell === this.defaultCell) {
            cell = new ConsoleCell().init(' ', 'green', 'black');
            this.pf.put(x, y, cell);
        }
        this.pf.get(x, y).textColor = style;
        return this;
    };

    this.setBackgroundColorAt = function(x, y, style) {
        var cell = this.pf.get(x, y);
        if (cell === this.defaultCell) {
            cell = new ConsoleCell().init(' ', 'green', 'black');
            this.pf.put(x, y, cell);
        }
        this.pf.get(x, y).backgroundColor = style;
        return this;
    };

    /*
     * Clear the TextTerminal to the current backgroundColor,
     * make the cursor visible(?), and home it.
     */
    this.reset = function() {
        this.cursor.moveTo(0, 0);
        this.pf.clear();
    };

    /*
     * Advance the cursor to the next row, scrolling the
     * TextTerminal display if necessary.
     */
    this.advanceRow = function() {
        this.cursor.setX(0).moveBy(0, 1);
        while (this.cursor.getY() >= this.rows) {
            this.pf.scrollRectangleY(-1, 0, 0, this.cols-1, this.rows-1);
            this.pf.clearRectangle(0, this.rows-1, this.cols-1, this.rows-1);
            this.cursor.moveBy(0, -1);
        }
    };

    /*
     * Advance the cursor to the next column, advancing to the
     * next row if necessary.
     */
    this.advanceCol = function() {
        this.cursor.moveRight();
        if (this.cursor.getX() >= this.cols) {
            this.advanceRow();
        }
    };

    /*
     * Write a character to the console.  Control characters are not heeded.
     */
    this.writeChar = function(c) {
        this.pf.write(
            new ConsoleCell().init(c, this.textColor, this.backgroundColor)
        );
        this.advanceCol();
    };

    /*
     * Write a string to the TextTerminal.  Control characters are not heeded.
     */
    this.writeRaw = function(string) {
        for (var i = 0; i < string.length; i++) {
            this.writeChar(string.charAt(i));
        };
    };

    /*
     * Write a string to the TextTerminal.  Basic control characters are
     * heeded: backspace moves the cursor one position to the left (but
     * does not overwrite the character there; and it has no effect if
     * the cursor is already in column 0) and LF causes the cursor to
     * move to column 0 on the next line.
     */
    this.write = function(string) {
        for (var i = 0; i < string.length; i++) {
            var c = string.charAt(i);
            if (c === '\n') {
                this.advanceRow();
            } else if (c === '\b') {
                if (this.cursor.getX()  > 0) {
                    this.cursor.moveLeft();
                }
            } else {
                this.writeChar(c);
            }
        }
    };

    /*
     * Move the cursor around the TextTerminal.  x is the column number
     * (0-based) and y is the row number (also 0-based.)
     */
    this.gotoxy = function(x, y) {
        this.cursor.moveTo(x, y);
    };
};