UNPKG

jquery.terminal

Version:

jQuery Terminal Emulator is a plugin for creating command line interpreters in your applications.

258 lines (251 loc) 9.28 kB
/**@license * __ _____ ________ __ * / // _ /__ __ _____ ___ __ _/__ ___/__ ___ ______ __ __ __ ___ / / * __ / // // // // // _ // _// // / / // _ // _// // // \/ // _ \/ / * / / // // // // // ___// / / // / / // ___// / / / / // // /\ // // / /__ * \___//____ \\___//____//_/ _\_ / /_//____//_/ /_/ /_//_//_/ /_/ \__\_\___/ * \/ /____/ * http://terminal.jcubic.pl * * This file is part of jQuery Terminal that create base class for animation * * Copyright (c) 2014-2025 Jakub Jankiewicz <https://jcubic.pl/me> * Released under the MIT license * */ /* global define */ (function(factory) { var root; if (typeof window !== 'undefined') { root = window; } else if (typeof self !== 'undefined') { root = self; } else if (typeof global !== 'undefined') { root = global; } else { throw new Error('Unknow context'); } if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. // istanbul ignore next define(['jquery', 'jquery.terminal'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function(root, jQuery) { if (jQuery === undefined) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if (typeof window !== 'undefined') { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } if (!jQuery.fn.terminal) { if (typeof window !== 'undefined') { require('jquery.terminal'); } else { require('jquery.terminal')(jQuery); } } factory(jQuery); return jQuery; }; } else { // Browser // istanbul ignore next factory(root.jQuery); } })(function($) { class Renderer { /* eslint-disable no-unused-vars */ constructor(render, { color = '#cccccc', background = 'black', font = 'monospace', char = {width: 7, height: 14} } = {}) { /* eslint-enable no-unused-vars */ this._options = { background, color, char }; this._render = render; } option(arg, value) { if (typeof arg === 'object') { Object.assign(this._options, arg); } else if (typeof value === 'undefined') { return this._options[arg]; } else { this._options[arg] = value; } } render() { const char = this.option('char'); const lines = this._render(); const max = Math.max(...lines.map(l => l.length)); const width = max * char.width; const size = char.height; const height = lines.length * size; this.clear({width, height}); for (let line = 0; line < lines.length; ++line) { const text = lines[line]; this.line(text, 0, size * line); } } /* eslint-disable no-unused-vars */ line(text, x, y) { throw new Error('Renderer::line invalid Invocation'); } clear({width, height, size} = {}) { throw new Error('Renderer::clear invalid Invocation'); } /* eslint-enable no-unused-vars */ } // ----------------------------------------------------------------------------------- class CanvasRenderer extends Renderer { constructor(render, options = {}) { super(render, options); var $canvas = $('<canvas/>'); this.canvas = $canvas[0]; this.ctx = this.canvas.getContext('2d'); } clear({width, height}) { this.canvas.width = width; this.canvas.height = height; this.ctx.fillStyle = this.option('background'); this.ctx.fillRect(0, 0, width, height); this.ctx.font = `1em ${this.option('font')}`; this.ctx.textBaseline = 'hanging'; this.ctx.fillStyle = this.option('color'); } line(text, x, y) { this.ctx.fillText(text, x, y); } } class FormattingCanvasRenderer extends CanvasRenderer { render() { this._char = this.option('char'); var stripped = []; const lines = this._render().map(function(line) { if ($.terminal.have_formatting(line)) { stripped.push($.terminal.strip(line)); } else { stripped.push(line); } return $.terminal.process_formatting(line); }); const max = Math.max(...stripped.map(l => l.length)); const width = max * this._char.width; const size = this._char.height; const height = stripped.length * size; this.clear({width, height}); for (let index = 0; index < lines.length; ++index) { const line = lines[index]; this.line(line, 0, size * index); } } line(line, x, y) { var ctx = this.ctx; var color = this.option('color'); var char_width = Math.ceil(this._char.width); var char_height = this._char.height; line.forEach(function(arr) { var text = arr[3]; var len = $.terminal.length(text); if (arr[2]) { ctx.fillStyle = arr[2]; ctx.fillRect(x, y, char_width * len, char_height); } if (arr[1]) { ctx.fillStyle = arr[1]; } else { ctx.fillStyle = color; } ctx.fillText(text, x, y); x += len * char_width; }); } } // ----------------------------------------------------------------------------------- class Animation { constructor(fps = null, renderer = CanvasRenderer) { this._fps = fps; this._Renderer = renderer; } start(term) { this.renderer = new this._Renderer(() => this.render(term)); term.echo(this.renderer.canvas, { onClear: () => { this.stop(); this.unmount(); }, finalize: (div) => { div.addClass('animation'); this.mount(div); } }); var self = this; self.run = true; const delay = self._fps === null ? null : 1000 / self._fps; (function loop() { if (self.run) { var style = getComputedStyle(term[0]); var color = style.getPropertyValue('--color') || '#cccccc'; var background = style.getPropertyValue('--background') || 'black'; var font = style.getPropertyValue('--font') || 'monospace'; self.renderer.option({ char: term.geometry().char, background, font, color }); self.renderer.render(); if (delay === null) { requestAnimationFrame(loop); } else { setTimeout(loop, delay); } } })(); } stop() { this.run = false; } /* eslint-disable no-unused-vars */ render(term) { throw new Error('Animation::render You need to overwrite this method'); } /* eslint-enable no-unused-vars */ /* eslint-disable no-empty-function */ mount() { } unmount() { } /* eslint-enable no-empty-function */ } // ----------------------------------------------------------------------------------- class FramesAnimation extends Animation { constructor(frames, ...args) { super(...args); this._i = 0; this._frames = frames; this._max = frames.length - 1; } render() { if (this._i >= this._max) { this._i = 0; } else { this._i++; } return this._frames[this._i]; } } $.terminal.Renderer = Renderer; $.terminal.CanvasRenderer = CanvasRenderer; $.terminal.FormattingCanvasRenderer = FormattingCanvasRenderer; $.terminal.Animation = Animation; $.terminal.FramesAnimation = FramesAnimation; });